2010-04-24 12 views
23

Mam dwuwymiarową macierz kształtu (N, 2), która trzyma N punktów (współrzędne x i y). Na przykład:Sortowanie dwuwymiarowej macierzy 2D przez wiele osi

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

Chciałbym uporządkować to taki, że moje punkty są sortowane według współrzędna x, a następnie przez Y w przypadkach, gdy współrzędna X jest taka sama. Więc tablica powyżej powinien wyglądać następująco:

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

Jeśli to był normalny lista Python, chciałbym po prostu zdefiniować komparator robić to, co chcę, ale o ile mogę powiedzieć, funkcja sortowania numpy nie robi akceptuj kompilatory zdefiniowane przez użytkownika. Jakieś pomysły?


EDYCJA: Dzięki za pomysły! Przygotowałem szybki test z 1000000 losowymi liczbami całkowitymi i przetestowałem te, które mogłem uruchomić (przykro mi, nie można uaktualnić numpy w tej chwili).

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

Odpowiedz

39

Korzystanie 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() zwraca widok jeśli a jest C_CONTIGUOUS. Jeśli to prawda, @ars's method, lekko zmodyfikowaliśmy za pomocą ravel zamiast flatten, daje piękny sposób sortowania aw miejscu:

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

Od b jest widok a, sortowania b rodzaju a jako dobrze:

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

Ah, widziałem lexsort w dokumentach, ale nie mogłem dowiedzieć się, jak to będzie miało zastosowanie do tego problemu. Dzięki! – perimosocordiae

+3

Tak, często mam trudności ze zrozumieniem dokumentacji. Przykłady wydają się być o wiele bardziej pouczające. Problem polega na tym, że po odtworzeniu przykładów ponownie przeczytałem dokumenty i stwierdziłem, że dokumenty były całkowicie jasne ... :-) – unutbu

+0

To robi kopię tablicy, nie? – g33kz0r

2

EDYCJA: usunięto złą odpowiedź.

Oto jeden ze sposobów, aby to zrobić za pomocą pośrednią zorganizowanego tablicy:

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 

co daje pożądany wynik:

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

Nie wiem, czy to jest dość najlepszym sposobem, aby przejść o nim choć .

+0

To nie działa, ponieważ traci powiązania między X i Y dla moich punktów. – perimosocordiae

+0

Och, masz rację; Przepraszam. Zaktualizowałem moją odpowiedź. – ars

+0

Hm. Po uruchomieniu tego, pojawia się błąd w linii 'b.shape = a.shape':" ValueError: całkowity rozmiar nowej tablicy musi pozostać niezmieniony ". Używam Python 2.6.2, z numpy 1.2.1. – perimosocordiae

1

Znalazłem jeden sposób, aby to zrobić:

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

To dość straszne mieć do sortowania dwa razy (i użyć zwykłego python sorted funkcję zamiast szybciej numpy rodzaju), ale to pasuje ładnie na jednej linia.

3

Możesz użyć np.complex_sort. Ma to efekt uboczny zmieniając dane zmiennoprzecinkowe, mam nadzieję, że nie jest to problem:

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

Myślę, że wygrywasz nagrodę sprytu; Nie pomyślałbym o wyimaginowaniu współrzędnych y! – perimosocordiae

+0

Ale pies wolny! Przepraszam, naprawdę nie brałem pod uwagę wydajności, kiedy to opublikowałem. – mtrw

3

Miałem problemy z tej samej rzeczy i po prostu dostał pomoc i rozwiązać problem.To działa płynnie, jeśli tablica ma nazwy kolumn (Structured tablicy) i myślę, że jest to bardzo prosty sposób uporządkować stosując tę ​​samą logikę, że program Excel robi:

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

Zanotuj podwójne nawiasy zamykające kryteria sortowania. I oczywiście można użyć więcej niż 2 kolumn jako kryteriów sortowania.

13

Tytuł mówi "sortowanie tablic 2D". Mimo, że pytający używa tablicy w kształcie (N,2), możliwe jest uogólnienie rozwiązania unutbu do pracy z każdą tablicą (N,M), ponieważ tego właśnie ludzie mogą faktycznie szukać.

Jeden mógłby transpose tablicy i użyć plastra notacji z ujemnym step przekazać wszystkie kolumny do lexsort w odwrotnej kolejności:

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

Pakiet numpy_indexed (Zastrzeżenie: Jestem jego autorem) mogą być wykorzystywane do rozwiązywania ten rodzaj problemów z przetwarzaniem na macierzy w efektywnym w pełni wektoryzowanym sposobie:

import numpy_indexed as npi 
npi.sort(a) # by default along axis=0, but configurable 
Powiązane problemy