2010-04-24 17 views
23

Tengo una matriz numpy 2D de forma (N, 2) que tiene N puntos (coordenadas xey). Por ejemplo:Ordenar un conjunto numpy 2D por varios ejes

array([[3, 2], 
     [6, 2], 
     [3, 6], 
     [3, 4], 
     [5, 3]]) 

me gustaría a solucionar el problema de tal manera que mis puntos están clasificadas por coordenada x, y luego por y en los casos en que la coordenada x es el mismo. Por lo que la gama anterior debería tener este aspecto:

array([[3, 2], 
     [3, 4], 
     [3, 6], 
     [5, 3], 
     [6, 2]]) 

Si esto era una lista de Python normal, yo simplemente definir un comparador para hacer lo que quiera, pero por lo que puedo decir, función de clasificación de numpy no lo hace aceptar comparadores definidos por el usuario. ¿Algunas ideas?


EDIT: Gracias por las ideas! Configuré un caso de prueba rápido con 1000000 puntos enteros aleatorios y comparaté los que podía ejecutar (lo siento, no puedo actualizar el numpy en este momento).

Mine: 4.078 secs 
mtrw: 7.046 secs 
unutbu: 0.453 secs 

Respuesta

39

Usando lexsort:

import numpy as np  
a = np.array([(3, 2), (6, 2), (3, 6), (3, 4), (5, 3)]) 

ind = np.lexsort((a[:,1],a[:,0]))  

a[ind] 
# array([[3, 2], 
#  [3, 4], 
#  [3, 6], 
#  [5, 3], 
#  [6, 2]]) 

a.ravel() devuelve una vista si es aC_CONTIGUOUS. Si eso es cierto, @ars's method, ligeramente modifed utilizando ravel en lugar de flatten, produce una buena manera de ordenar aen el lugar:

a = np.array([(3, 2), (6, 2), (3, 6), (3, 4), (5, 3)]) 
dt = [('col1', a.dtype),('col2', a.dtype)] 
assert a.flags['C_CONTIGUOUS'] 
b = a.ravel().view(dt) 
b.sort(order=['col1','col2']) 

Desde b es una vista de a, clasificación b tipo a como bien:

print(a) 
# [[3 2] 
# [3 4] 
# [3 6] 
# [5 3] 
# [6 2]] 
+0

Ah, había visto Lexsort en los documentos, pero no pude entender cómo se aplicaría a este problema. ¡Gracias! – perimosocordiae

+3

Sí, a menudo tengo dificultades para comprender la documentación. Los ejemplos tienden a ser mucho más esclarecedores. El problema es que, después de jugar con ejemplos, volví a leer los documentos y descubrí que los documentos estaban perfectamente claros ... :-) – unutbu

+0

Esto está haciendo una copia de la matriz, ¿no? – g33kz0r

2

EDITAR: eliminó la respuesta incorrecta.

Aquí hay una manera de hacerlo utilizando una matriz estructurada intermedia:

from numpy import array 

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

b = a.flatten() 
b.dtype = [('x', '<i4'), ('y', '<i4')] 
b.sort() 
b.dtype = '<i4' 
b.shape = a.shape 

print b 

que da la salida deseada:

[[3 2] 
[3 4] 
[3 6] 
[5 3] 
[6 2]] 

No estoy seguro si esto es absolutamente la mejor manera de ir sobre él sin embargo .

+0

que no funciona del todo, porque pierde la asociación entre X e y para mis puntos. – perimosocordiae

+0

Oh, tienes razón; lo siento. Actualizado mi respuesta. – ars

+0

Hm. Cuando ejecuto eso, aparece un error en la línea 'b.shape = a.shape':" ValueError: el tamaño total de la nueva matriz debe permanecer sin cambios ". Estoy ejecutando Python 2.6.2, con numpy 1.2.1. – perimosocordiae

1

He encontrado una manera de hacerlo:

from numpy import array 
a = array([(3,2),(6,2),(3,6),(3,4),(5,3)]) 
array(sorted(sorted(a,key=lambda e:e[1]),key=lambda e:e[0])) 

Es bastante terrible tener que ordenar dos veces (y el uso de la llanura función de Python sorted en lugar de una especie más rápido numpy), pero encaja muy bien en uno línea.

3

Puede usar np.complex_sort. Esto tiene el efecto secundario de cambiar sus datos de punto flotante, espero que eso no es un problema:

>>> a = np.array([[3, 2], [6, 2], [3, 6], [3, 4], [5, 3]]) 
>>> atmp = np.sort_complex(a[:,0] + a[:,1]*1j) 
>>> b = np.array([[np.real(x), np.imag(x)] for x in atmp]) 
>>> b 
array([[ 3., 2.], 
     [ 3., 4.], 
     [ 3., 6.], 
     [ 5., 3.], 
     [ 6., 2.]]) 
+1

Creo que ganas el premio de inteligencia; ¡No hubiera pensado en hacer las coordenadas y imaginarias! – perimosocordiae

+0

¡Pero perro lento! Lo siento, realmente no consideré el rendimiento cuando publiqué esto. – mtrw

3

he tenido problemas con la misma cosa y acabo de ayuda y resuelto el problema.Funciona sin problemas si la matriz tiene nombres de columna (array estructurado) y creo que esta es una manera muy simple para ordenar usando la misma lógica que lo hace sobresalir:

array_name[array_name[['colname1','colname2']].argsort()] 

Nota los dobles corchetes que encierran los criterios de clasificación. Y, por supuesto, puede usar más de 2 columnas como criterios de clasificación.

13

El título dice "ordenar matrices 2D". Aunque el interlocutor utiliza una matriz en forma de (N,2), es posible generalizar la solución de unutbu para trabajar con cualquier arreglo (N,M), ya que eso es lo que la gente podría estar buscando.

Uno podría transpose la matriz y el uso de la notación rebanada con negativo step para pasar todas las columnas a lexsort en orden inverso:

>>> import numpy as np 
>>> a = np.random.randint(1, 6, (10, 3)) 
>>> a 
array([[4, 2, 3], 
     [4, 2, 5], 
     [3, 5, 5], 
     [1, 5, 5], 
     [3, 2, 1], 
     [5, 2, 2], 
     [3, 2, 3], 
     [4, 3, 4], 
     [3, 4, 1], 
     [5, 3, 4]]) 

>>> a[np.lexsort(np.transpose(a)[::-1])] 
array([[1, 5, 5], 
     [3, 2, 1], 
     [3, 2, 3], 
     [3, 4, 1], 
     [3, 5, 5], 
     [4, 2, 3], 
     [4, 2, 5], 
     [4, 3, 4], 
     [5, 2, 2], 
     [5, 3, 4]]) 
3

El paquete numpy_indexed (exención de responsabilidad: yo soy su autor) se puede utilizar para resolver este tipo de problemas de procesamiento-on-nd-matriz de una manera totalmente vectorizado eficiente:

import numpy_indexed as npi 
npi.sort(a) # by default along axis=0, but configurable 
Cuestiones relacionadas