Ordinamento di matrici in NumPy per colonna


336

Come posso ordinare un array in NumPy dall'ennesima colonna?

Per esempio,

a = array([[9, 2, 3],
           [4, 5, 6],
           [7, 0, 5]])

Vorrei ordinare le righe in base alla seconda colonna, in modo da tornare indietro:

array([[7, 0, 5],
       [9, 2, 3],
       [4, 5, 6]])

8
Questo è davvero un cattivo esempio poiché np.sort(a, axis=0)sarebbe una soluzione soddisfacente per la matrice data. Ho suggerito una modifica con un esempio migliore ma sono stato respinto, anche se in realtà la domanda sarebbe molto più chiara. L'esempio dovrebbe essere simile a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]])all'output desideratoarray([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
David

29
David, non capisci il punto della domanda. Vuole mantenere lo stesso ordine all'interno di ogni riga.
marcorossi,

@marcorossi Ho capito il punto, ma l'esempio è stato formulato in modo pessimo perché, come ho detto, c'erano più risposte possibili (che, tuttavia, non avrebbero soddisfatto la richiesta del PO). Una modifica successiva basata sul mio commento è stata effettivamente approvata (divertente che il mio sia stato respinto, però). Quindi ora va tutto bene.
David,

Risposte:


141

La risposta di @steve è in realtà il modo più elegante di farlo.

Per il modo "corretto", consultare l'argomento della parola chiave order di numpy.ndarray.sort

Tuttavia, dovrai visualizzare il tuo array come un array con campi (un array strutturato).

Il modo "corretto" è abbastanza brutto se inizialmente non hai definito l'array con i campi ...

A titolo di esempio, per ordinarlo e restituire una copia:

In [1]: import numpy as np

In [2]: a = np.array([[1,2,3],[4,5,6],[0,0,1]])

In [3]: np.sort(a.view('i8,i8,i8'), order=['f1'], axis=0).view(np.int)
Out[3]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

Per ordinarlo sul posto:

In [6]: a.view('i8,i8,i8').sort(order=['f1'], axis=0) #<-- returns None

In [7]: a
Out[7]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

@ Steve è davvero il modo più elegante per farlo, per quanto ne so ...

L'unico vantaggio di questo metodo è che l'argomento "ordine" è un elenco dei campi per cui ordinare la ricerca. Ad esempio, è possibile ordinare in base alla seconda colonna, quindi alla terza colonna, quindi alla prima colonna fornendo order = ['f1', 'f2', 'f0'].


3
Nel mio intorpidito 1.6.1rc1, si alzaValueError: new type not compatible with array.
Clippit

9
Avrebbe senso presentare una richiesta di funzionalità per rendere meno brutto il modo "corretto"?
endolith

4
Cosa succede se i valori nell'array sono float? Dovrei cambiare qualcosa?
Marco,

1
E per il tipo ibrido a = np.array([['a',1,2,3],['b',4,5,6],['c',0,0,1]])quale approccio devo seguire?
ePascoal

10
Uno dei principali vantaggi di questo metodo rispetto a quello di Steve è che consente di disporre in ordine array molto grandi. Per un array sufficientemente grande, gli indici restituiti da np.argsortessi possono occupare molta memoria e, inoltre, l'indicizzazione con un array genererà anche una copia dell'array che viene ordinato.
ali_m

737

Suppongo che funzioni: a[a[:,1].argsort()]

Questo indica la seconda colonna ae la ordina in base ad essa di conseguenza.


2
Questo non è chiaro, cosa c'è 1qui? l'indice da ordinare?
orezvani,

29
[:,1]indica la seconda colonna di a.
Steve Tjoa,

60
Se si desidera l'ordinamento inverso, modificarlo come seguea[a[:,1].argsort()[::-1]]
Steven C. Howell,

1
Sembra semplice e funziona! È più veloce np.sorto no?
Václav Pavlík,

14
Lo trovo più facile da leggere:ind = np.argsort( a[:,1] ); a = a[ind]
poppie,

32

Puoi ordinare su più colonne secondo il metodo di Steve Tjoa usando un ordinamento stabile come mergesort e ordinando gli indici dalle colonne meno significative a quelle più significative:

a = a[a[:,2].argsort()] # First sort doesn't need to be stable.
a = a[a[:,1].argsort(kind='mergesort')]
a = a[a[:,0].argsort(kind='mergesort')]

Questo ordina per colonna 0, quindi 1, quindi 2.


4
Perché First Sort non deve essere stabile?
Tavolini Bobby,

10
Buona domanda: stabile significa che quando c'è un pareggio si mantiene l'ordine originale e l'ordine originale del file non ordinato è irrilevante.
JJ,

Questo sembra un punto davvero molto importante. avere un elenco che non ordina in silenzio sarebbe male.
Gatto

19

Nel caso in cui qualcuno desideri utilizzare l'ordinamento in una parte critica dei propri programmi, ecco un confronto delle prestazioni per le diverse proposte:

import numpy as np
table = np.random.rand(5000, 10)

%timeit table.view('f8,f8,f8,f8,f8,f8,f8,f8,f8,f8').sort(order=['f9'], axis=0)
1000 loops, best of 3: 1.88 ms per loop

%timeit table[table[:,9].argsort()]
10000 loops, best of 3: 180 µs per loop

import pandas as pd
df = pd.DataFrame(table)
%timeit df.sort_values(9, ascending=True)
1000 loops, best of 3: 400 µs per loop

Quindi, sembra che l'indicizzazione con argsort sia il metodo più veloce finora ...


19

Dal wiki della documentazione di Python , penso che tu possa fare:

a = ([[1, 2, 3], [4, 5, 6], [0, 0, 1]]); 
a = sorted(a, key=lambda a_entry: a_entry[1]) 
print a

L'output è:

[[[0, 0, 1], [1, 2, 3], [4, 5, 6]]]

21
Con questa soluzione, si ottiene un elenco anziché un array NumPy, quindi questo potrebbe non essere sempre conveniente (richiede più memoria, è probabilmente più lento, ecc.).
Eric O Lebigot,

questa "soluzione" è più lenta della risposta più votata da un fattore di ... beh, in realtà vicino all'infinito
Jivan

16

Dalla mailing list di NumPy , ecco un'altra soluzione:

>>> a
array([[1, 2],
       [0, 0],
       [1, 0],
       [0, 2],
       [2, 1],
       [1, 0],
       [1, 0],
       [0, 0],
       [1, 0],
      [2, 2]])
>>> a[np.lexsort(np.fliplr(a).T)]
array([[0, 0],
       [0, 0],
       [0, 2],
       [1, 0],
       [1, 0],
       [1, 0],
       [1, 0],
       [1, 2],
       [2, 1],
       [2, 2]])

3
La generalizzazione corretta è a[np.lexsort(a.T[cols])]. dove cols=[1]nella domanda originale.
Radio Controlled

5

Ho avuto un problema simile.

Il mio problema:

Voglio calcolare un SVD e ho bisogno di ordinare i miei autovalori in ordine decrescente. Ma voglio mantenere la mappatura tra autovalori e autovettori. I miei autovalori erano nella prima riga e il corrispondente autovettore sotto di esso nella stessa colonna.

Quindi voglio ordinare un array bidimensionale per colonna secondo la prima riga in ordine decrescente.

La mia soluzione

a = a[::, a[0,].argsort()[::-1]]

Quindi come funziona?

a[0,] è solo la prima riga che voglio ordinare.

Ora utilizzo argsort per ottenere l'ordine degli indici.

Uso [::-1]perché ho bisogno di un ordine decrescente.

Infine, utilizzo a[::, ...]per ottenere una vista con le colonne nell'ordine giusto.


1

Un lexsortesempio un po 'più complicato : scendendo sulla 1a colonna, salendo secondariamente sulla 2a. I trucchi con lexsortsono che ordina sulle righe (da qui il .T) e dà la priorità all'ultimo.

In [120]: b=np.array([[1,2,1],[3,1,2],[1,1,3],[2,3,4],[3,2,5],[2,1,6]])
In [121]: b
Out[121]: 
array([[1, 2, 1],
       [3, 1, 2],
       [1, 1, 3],
       [2, 3, 4],
       [3, 2, 5],
       [2, 1, 6]])
In [122]: b[np.lexsort(([1,-1]*b[:,[1,0]]).T)]
Out[122]: 
array([[3, 1, 2],
       [3, 2, 5],
       [2, 1, 6],
       [2, 3, 4],
       [1, 1, 3],
       [1, 2, 1]])

0

Ecco un'altra soluzione considerando tutte le colonne (modo più compatto della risposta di JJ );

ar=np.array([[0, 0, 0, 1],
             [1, 0, 1, 0],
             [0, 1, 0, 0],
             [1, 0, 0, 1],
             [0, 0, 1, 0],
             [1, 1, 0, 0]])

Ordina con lexsort,

ar[np.lexsort(([ar[:, i] for i in range(ar.shape[1]-1, -1, -1)]))]

Produzione:

array([[0, 0, 0, 1],
       [0, 0, 1, 0],
       [0, 1, 0, 0],
       [1, 0, 0, 1],
       [1, 0, 1, 0],
       [1, 1, 0, 0]])

0

Semplicemente usando l'ordinamento, utilizzare il numero di colonna in base al quale si desidera ordinare.

a = np.array([1,1], [1,-1], [-1,1], [-1,-1]])
print (a)
a=a.tolist() 
a = np.array(sorted(a, key=lambda a_entry: a_entry[0]))
print (a)

0

È una vecchia domanda, ma se è necessario generalizzare questo in array di dimensioni superiori a 2, ecco la soluzione che può essere facilmente generalizzata:

np.einsum('ij->ij', a[a[:,1].argsort(),:])

Questo è un eccesso per due dimensioni e a[a[:,1].argsort()]sarebbe sufficiente per la risposta di @ steve, tuttavia quella risposta non può essere generalizzata a dimensioni superiori. Potete trovare un esempio di array 3D in questa domanda.

Produzione:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.