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

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


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.


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.


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 ...


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

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

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.


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

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

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)

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]]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.