2009-12-14 13 views
29

Se ho due liste parallele e voglio ordinarli per l'ordine degli elementi della prima, è molto facile:Come posso "ordinare zip" array di numpy parallelo?

>>> a = [2, 3, 1] 
>>> b = [4, 6, 2] 
>>> a, b = zip(*sorted(zip(a,b))) 
>>> print a 
(1, 2, 3) 
>>> print b 
(2, 4, 6) 

Come posso fare lo stesso usando array NumPy senza spacchettamento in liste Python convenzionali ?

+1

@ YGA, l'array di input "a" avrà mai valori non univoci? Se sì, come vorresti che il tipo si comportasse in quel caso? Ordine arbitrario? Sorta stabile? Ordinamento secondario usando i valori corrispondenti nell'array "b"? –

risposta

40

b[a.argsort()] dovrebbe fare il trucco.

Ecco come funziona. Per prima cosa devi trovare una permutazione che generi a. argsort è un metodo che calcola questo:

>>> a = numpy.array([2, 3, 1]) 
>>> p = a.argsort() 
>>> p 
[2, 0, 1] 

Si può facilmente verificare che questo è giusto:

>>> a[p] 
array([1, 2, 3]) 

Ora applicare la stessa permutazione a B.

>>> b = numpy.array([4, 6, 2]) 
>>> b[p] 
array([2, 4, 6]) 
+2

Questo non usa 'b' per" ordinamento ausiliario ", per esempio quando' a' ha elementi che si ripetono. Si prega di vedere la mia risposta per i dettagli. –

+1

otoh, l'ordinamento ausiliario non è sempre desiderato. – tacaswell

19

Ecco un approccio che non crea liste Python intermedi, anche se richiede un NumPy "allineamento record" da utilizzare per l'ordinamento. Se i due array di input sono effettivamente correlati (come le colonne in un foglio di calcolo), questo potrebbe aprire un modo vantaggioso di trattare i tuoi dati in generale, piuttosto che mantenere due array distinti per tutto il tempo, nel qual caso avresti già un array di record e il tuo problema originale dovrebbe essere risolto semplicemente chiamando sort() sul tuo array.

Questo fa un in-place sort dopo l'imballaggio entrambe le matrici in una matrice di registrazione:

>>> from numpy import array, rec 
>>> a = array([2, 3, 1]) 
>>> b = array([4, 6, 2]) 
>>> c = rec.fromarrays([a, b]) 
>>> c.sort() 
>>> c.f1 # fromarrays adds field names beginning with f0 automatically 
array([2, 4, 6]) 

Modificato utilizzare rec.fromarrays() per semplicità, saltare dtype ridondante, uso ordinamento predefinito chiave, nomi di campo predefiniti uso invece di specificare (basato su this example).

+0

Grazie! Vorrei davvero poter accettare due risposte. Questo è meno semplice ma più generale. L'ho svalutato comunque, come minimo potevo fare :-) – YGA

2

Questo potrebbe essere il modo più semplice e generico di fare ciò che si desidera. (Ho usato tre matrici qui, ma questo funzionerà su array di qualsiasi forma, siano essi due colonne o duecento).

import numpy as NP 
fnx = lambda : NP.random.randint(0, 10, 6) 
a, b, c = fnx(), fnx(), fnx() 
abc = NP.column_stack((a, b, c)) 
keys = (abc[:,0], abc[:,1])   # sort on 2nd column, resolve ties using 1st col 
indices = NP.lexsort(keys)  # create index array 
ab_sorted = NP.take(abc, indices, axis=0) 

Uno scherzo w/lexsort è che è necessario specificare le chiavi in ​​ordine inverso, vale a dire, mettere la seconda chiave primaria e la chiave secondaria di primo. Nel mio esempio, voglio ordinare usando la seconda colonna come chiave primaria, quindi la elenco seconda; la prima colonna risolve solo i legami, ma è elencata per prima).

+0

bella cattura Brendan, grazie. – doug