Ordenar matrices en NumPy por columna

336

¿Cómo puedo ordenar una matriz en NumPy por la enésima columna?

Por ejemplo,

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

Me gustaría ordenar las filas por la segunda columna, de modo que regrese:

array([[7, 0, 5],
       [9, 2, 3],
       [4, 5, 6]])
Paul Wintz
fuente
8
Este es un mal ejemplo, ya np.sort(a, axis=0)que sería una solución satisfactoria para la matriz dada. Sugerí una edición con un mejor ejemplo, pero fue rechazado, aunque en realidad la pregunta sería mucho más clara. El ejemplo debería ser algo así como a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]])con la salida deseadaarray([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
David
29
David, no entiendes el punto de la pregunta. Quiere mantener el orden dentro de cada fila igual.
marcorossi
@marcorossi Entendí el punto, pero el ejemplo estaba muy mal formulado porque, como dije, había múltiples respuestas posibles (que, sin embargo, no hubieran satisfecho la solicitud del OP). Una edición posterior basada en mi comentario ha sido aprobada (es curioso que la mía haya sido rechazada). Entonces ahora todo está bien.
David

Respuestas:

141

La respuesta de @steve es en realidad la forma más elegante de hacerlo.

Para la forma "correcta", vea el argumento de palabra clave de orden de numpy.ndarray.sort

Sin embargo, deberá ver su matriz como una matriz con campos (una matriz estructurada).

La forma "correcta" es bastante fea si no definiste inicialmente tu matriz con campos ...

Como ejemplo rápido, para ordenarlo y devolver 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]])

Para ordenarlo en el lugar:

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 realmente es la forma más elegante de hacerlo, que yo sepa ...

La única ventaja de este método es que el argumento "ordenar" es una lista de los campos para ordenar la búsqueda. Por ejemplo, puede ordenar por la segunda columna, luego la tercera columna, luego la primera columna proporcionando orden = ['f1', 'f2', 'f0'].

Joe Kington
fuente
3
En mi numpy 1.6.1rc1, subeValueError: new type not compatible with array.
Clippit
99
¿Tendría sentido presentar una solicitud de función para que la forma "correcta" sea menos fea?
endolito
44
¿Qué pasa si los valores en la matriz son float? ¿Debo cambiar algo?
Marco
1
Y para el tipo híbrido, a = np.array([['a',1,2,3],['b',4,5,6],['c',0,0,1]])¿qué enfoque debo seguir?
ePascoal
10
Una de las principales ventajas de este método sobre el de Steve es que permite que se ordenen arreglos muy grandes en su lugar. Para una matriz lo suficientemente grande, los índices devueltos por np.argsortsí mismos pueden ocupar bastante memoria, y además de eso, la indexación con una matriz también generará una copia de la matriz que se está ordenando.
ali_m
738

Supongo que esto funciona: a[a[:,1].argsort()]

Esto indica la segunda columna de ay ordenarla en función de ella en consecuencia.

Steve Tjoa
fuente
2
Esto no está claro, ¿qué hay 1aquí? el índice por el que se ordenará?
orezvani
29
[:,1]indica la segunda columna de a.
Steve Tjoa
6060
Si desea el orden inverso, modifique esto para que seaa[a[:,1].argsort()[::-1]]
Steven C. Howell
1
¡Parece simple y funciona! ¿Es más rápido np.sorto no?
Václav Pavlík
14
Esto me resulta más fácil de leer:ind = np.argsort( a[:,1] ); a = a[ind]
poppie
32

Puede ordenar en varias columnas según el método de Steve Tjoa utilizando una ordenación estable como mergesort y ordenando los índices de las columnas menos significativas a las más significativas:

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')]

Esto ordena por columna 0, luego 1, luego 2.

JJ
fuente
44
¿Por qué First Sort no necesita ser estable?
Little Bobby Tables
10
Buena pregunta: estable significa que cuando hay un vínculo, mantiene el orden original y el orden original del archivo sin clasificar es irrelevante.
JJ
Esto parece un punto realmente súper importante. tener una lista que silenciosamente no ordena sería malo.
Gato torpe
19

En caso de que alguien quiera usar la clasificación en una parte crítica de sus programas, aquí hay una comparación de rendimiento para las diferentes propuestas:

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

Entonces, parece que indexar con argsort es el método más rápido hasta ahora ...

prl900
fuente
19

Desde el wiki de documentación de Python , creo que puedes hacer:

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

El resultado es:

[[[0, 0, 1], [1, 2, 3], [4, 5, 6]]]
usuario541064
fuente
21
Con esta solución, se obtiene una lista en lugar de una matriz NumPy, por lo que esto no siempre es conveniente (toma más memoria, probablemente sea más lento, etc.).
Eric O Lebigot
esta "solución" es más lenta por la respuesta más votada por un factor de ... bueno, casi al infinito en realidad
Jivan hace
16

De la lista de correo NumPy , aquí hay otra solución:

>>> 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]])
fgregg
fuente
3
La generalización correcta es a[np.lexsort(a.T[cols])]. donde cols=[1]en la pregunta original.
Radio controlado
5

Tuve un problema similar.

Mi problema:

Quiero calcular un SVD y necesito ordenar mis valores propios en orden descendente. Pero quiero mantener el mapeo entre valores propios y vectores propios. Mis valores propios estaban en la primera fila y el vector propio correspondiente debajo de ellos en la misma columna.

Entonces, quiero ordenar una matriz bidimensional en forma de columna por la primera fila en orden descendente.

Mi solución

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

Entonces, ¿cómo funciona esto?

a[0,] es solo la primera fila por la que quiero ordenar.

Ahora uso argsort para obtener el orden de los índices.

Lo uso [::-1]porque necesito un orden descendente.

Por último, uso a[::, ...]para obtener una vista con las columnas en el orden correcto.

xuma202
fuente
1

Un lexsortejemplo un poco más complicado : descender en la primera columna, ascender secundariamente en la segunda. Los trucos lexsortson que se ordena en filas (de ahí el .T) y da prioridad a la última.

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]])
hpaulj
fuente
0

Aquí hay otra solución considerando todas las columnas (forma más compacta de la respuesta de 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]])

Ordenar con lexsort,

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

Salida:

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]])
Sefa
fuente
0

Simplemente usando sort, use el número de coloumn basado en el que desea ordenar.

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)
Jerin
fuente
0

Es una pregunta antigua, pero si necesita generalizar esto a matrices de más de 2 dimensiones, esta es la solución que puede generalizarse fácilmente:

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

Esta es una exageración para dos dimensiones y a[a[:,1].argsort()]sería suficiente según la respuesta de @ steve, sin embargo, esa respuesta no puede generalizarse a dimensiones superiores. Puede encontrar un ejemplo de matriz 3D en esta pregunta.

Salida:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
Ehsan
fuente