2016-05-01 6 views
2

Hallo Ich versuche, ein Array von Zahlen zu ihren Reihen zuordnen. So würde zum Beispiel [2,5,3] [0,2,1] werden.Karte Array von Zahlen effizient in Python Rang

Ich benutze derzeit np.where, um den Rang in einem Array nachzuschlagen, aber das erweist sich als sehr lange Zeit, da ich dies für ein sehr großes Array (über 2 Millionen Datenpunkte) tun muss.

Wenn jemand irgendwelche Vorschläge hat, wie ich das erreichen könnte, würde ich es sehr schätzen!

[EDIT] Dies ist, was der Code eine bestimmte Zeile zur Zeit aussieht zu ändern:

def change_nodes(row): 
    a = row 
    new_a = node_map[node_map[:,1] == a][0][0] 
    return new_a 

[EDIT 2] duplizierte Nummern zusätzlich den gleichen Rang

[EDIT 3] Zusätzlich haben sollte, eindeutige Zahlen sollten nur einmal auf das Ranking zählen. Zum Beispiel wäre das Ranking für diese Liste [2,3,3,4,5,7,7,7,7,8,1]:

{1: 0, 2: 1, 3 : 2, 4: 3, 5: 4, 7: 5, 8: 6}

+0

Haben Sie 'list.sort()' und 'list.index()' 'gesehen? – StardustGogeta

+1

danke, np.argsort war genau das, was ich brauchte! – chris

+0

Entschuldigung, ich wollte auch hinzufügen, dass, wenn eine Nummer in der Liste wiederholt wird, sie jedes Mal denselben Rang haben muss. – chris

Antwort

2

ist eine effiziente Lösung und ein Vergleich mit der Lösung unter Verwendung index (die index Lösung ist auch nicht korrekt mit der zusätzlichen (edit 3) Beschränkung auf die Frage)

import numpy as np 

def rank1(x): 
    # Sort values i = 0, 1, 2, .. using x[i] as key 
    y = sorted(range(len(x)), key = lambda i: x[i]) 
    # Map each value of x to a rank. If a value is already associated with a 
    # rank, the rank is updated. Iterate in reversed order so we get the 
    # smallest rank for each value. 
    rank = { x[y[i]]: i for i in xrange(len(y) -1, -1 , -1) } 
    # Remove gaps in the ranks 
    kv = sorted(rank.iteritems(), key = lambda p: p[1]) 
    for i in range(len(kv)): 
     kv[i] = (kv[i][0], i) 
    rank = { p[0]: p[1] for p in kv } 
    # Pre allocate a array to fill with ranks 
    r = np.zeros((len(x),), dtype=np.int) 
    for i, v in enumerate(x): 
     r[i] = rank[v] 
    return r 

def rank2(x): 
    x_sorted = sorted(x) 
    # creates a new list to preserve x 
    rank = list(x) 
    for v in x_sorted: 
     rank[rank.index(v)] = x_sorted.index(v) 
    return rank 

Vergleichsergebnisse

>>> d = np.arange(1000) 
>>> random.shuffle(d) 
>>> %timeit rank1(d) 
100 loops, best of 3: 1.97 ms per loop 
>>> %timeit rank2(d) 
1 loops, best of 3: 226 ms per loop 

>>> d = np.arange(10000) 
>>> random.shuffle(d) 
>>> %timeit rank1(d) 
10 loops, best of 3: 32 ms per loop 
>>> %timeit rank2(d) 
1 loops, best of 3: 24.4 s per loop 

>>> d = np.arange(100000) 
>>> random.shuffle(d) 
>>> %timeit rank1(d) 
1 loops, best of 3: 433 ms per loop 

>>> d = np.arange(2000000) 
>>> random.shuffle(d) 
>>> %timeit rank1(d) 
1 loops, best of 3: 11.2 s per loop 

Das Problem mit der index Lösung ist, dass die Komplexität der Zeit ist O (n^2). Die zeitliche Komplexität meiner Lösung ist O (n lg n), also die Sortierzeit.

+0

das ist genial! Danke! so viel schneller – chris

+0

warten, gibt Rang1 eigentlich nur die ursprüngliche Liste zurück? – chris

+0

Sorry, das war ein Tippfehler beim Kopieren des Codes. Ich habe es repariert. – malbarbo

3

ist numpy.argsort Was Sie verwenden möchten:

>>> import numpy as np 
>>> x = np.array([2, 5, 3]) 
>>> x.argsort() 
array([0, 2, 1]) 

this question und seine Antworten für Gedanken über die Anpassung sehen Sie, wie Bindungen abgewickelt.

+0

Sollte Zeile 3 nicht 'x = np.argsort (x)' sein? – StardustGogeta

+0

@StardustGogeta nicht für meine Zwecke, nein. 'x.argsort()' ist das gleiche wie 'np.argsort (x)'. und ich wollte 'x' nicht durch die sortierten Argumente ersetzen. Ich wollte nur die sortierten Argumente auf dem Bildschirm anzeigen, um zu zeigen, dass die Antwort richtig ist.Ich würde mir vorstellen, dass der Benutzer dieser Antwort so etwas wie "Ränge = x.argsort()" machen möchte. – dbliss

+0

Okay, ich verstehe was du meinst. – StardustGogeta

2

Ich habe eine Variante mit nur Python Vanille:

a = [2,5,3] 
aSORT = list(a) 
aSORT.sort() 
for x in aSORT: 
    a[a.index(x)] = aSORT.index(x) 
print(a) 

In meinen Tests, die numpy Version hier gepostet hat 0,1406 Sekunden die Liste [2,5,3,62,5,2,5,1000,100,-1,-9] im Vergleich zu nur 0,0154 Sekunden mit meiner Methode zu sortieren. Hier