Sortowanie tablic w NumPy według kolumny

336

Jak mogę posortować tablicę w NumPy według n-tej kolumny?

Na przykład,

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

Chciałbym posortować wiersze według drugiej kolumny, tak aby uzyskać:

array([[7, 0, 5],
       [9, 2, 3],
       [4, 5, 6]])
Paul Wintz
źródło
8
To naprawdę zły przykład, ponieważ np.sort(a, axis=0)byłoby zadowalającym rozwiązaniem dla danej matrycy. Zasugerowałem edycję z lepszym przykładem, ale został odrzucony, chociaż w rzeczywistości pytanie byłoby o wiele bardziej jasne. Przykładem powinno być coś a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]])z pożądaną wydajnościąarray([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
David
29
David, nie rozumiesz sedna pytania. Chce utrzymać porządek w każdym rzędzie bez zmian.
marcorossi,
@marcorossi Dostałem punkt, ale przykład został bardzo źle sformułowany, ponieważ, jak powiedziałem, istnieje wiele możliwych odpowiedzi (które jednak nie spełniłyby żądania OP). Późniejsza edycja oparta na moim komentarzu rzeczywiście została zatwierdzona (zabawne, że moja została odrzucona). Więc teraz wszystko jest w porządku.
David

Odpowiedzi:

141

@steve „s odpowiedź jest rzeczywiście najbardziej elegancki sposób to zrobić.

Aby uzyskać „prawidłowy” sposób, zobacz argument słowa kluczowego rzędu numpy.ndarray.sort

Musisz jednak zobaczyć tablicę jako tablicę z polami (tablica strukturalna).

„Właściwy” sposób jest dość brzydki, jeśli początkowo nie zdefiniowałeś swojej tablicy za pomocą pól ...

Jako szybki przykład posortuj go i zwróć kopię:

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

Aby posortować to na miejscu:

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's jest naprawdę najbardziej eleganckim sposobem na zrobienie tego, o ile wiem ...

Jedyną zaletą tej metody jest to, że argument „kolejność” to lista pól, według których należy uporządkować wyszukiwanie. Na przykład można sortować według drugiej kolumny, następnie trzeciej kolumny, a następnie pierwszej kolumny, podając kolejność = [„f1”, „f2”, „f0”].

Joe Kington
źródło
3
W moim numpy 1.6.1rc1 podnosiValueError: new type not compatible with array.
Clippit
9
Czy sensowne byłoby złożenie wniosku o funkcję, aby „poprawny” sposób stał się mniej brzydki?
endolith,
4
Co jeśli wartości w tablicy są float? Czy powinienem coś zmienić?
Marco
1
A w przypadku typu hybrydowego, a = np.array([['a',1,2,3],['b',4,5,6],['c',0,0,1]])jakim podejściu powinienem się kierować?
ePascoal
10
Jedną z głównych zalet tej metody w porównaniu z metodą Steve'a jest to, że umożliwia sortowanie bardzo dużych tablic na miejscu. W przypadku wystarczająco dużej tablicy zwracane przez nią indeksy np.argsortmogą zajmować całkiem sporo pamięci, a ponadto indeksowanie tablicą wygeneruje również kopię sortowanej tablicy.
ali_m
737

Przypuszczam, że to działa: a[a[:,1].argsort()]

Oznacza to drugą kolumnę ai odpowiednio ją posortuj.

Steve Tjoa
źródło
2
Nie jest jasne, co 1tu jest? indeks do posortowania?
orezvani
29
[:,1]wskazuje drugą kolumnę a.
Steve Tjoa,
60
Jeśli chcesz sortować w odwrotnej kolejności, zmień to naa[a[:,1].argsort()[::-1]]
Steven C. Howell
1
Wygląda prosto i działa! Czy to jest szybsze niż np.sortnie?
Václav Pavlík
14
Uważam to za łatwiejsze do odczytania:ind = np.argsort( a[:,1] ); a = a[ind]
poppie
32

Możesz sortować według wielu kolumn, zgodnie z metodą Steve'a Tjoa, stosując sortowanie stabilne, takie jak scalanie i sortowanie indeksów od kolumn od najmniej znaczących do najbardziej znaczących:

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

Sortuje to według kolumny 0, następnie 1, a następnie 2.

JJ
źródło
4
Dlaczego First Sort nie musi być stabilny?
Little Bobby Tables
10
Dobre pytanie - stabilne oznacza, że ​​w przypadku remisu zachowujesz oryginalną kolejność, a pierwotna kolejność nieposortowanego pliku jest nieistotna.
JJ
To wydaje się być naprawdę bardzo ważnym punktem. posiadanie listy, która cicho się nie sortuje, byłoby złe.
Niezdarny kot
19

W przypadku, gdy ktoś chce skorzystać z sortowania w krytycznej części swoich programów, oto porównanie wydajności różnych propozycji:

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

Wygląda na to, że indeksowanie za pomocą argsort jest najszybszą jak dotąd metodą ...

prl900
źródło
19

Z wiki dokumentacji Pythona myślę, że możesz:

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

Dane wyjściowe to:

[[[0, 0, 1], [1, 2, 3], [4, 5, 6]]]
użytkownik541064
źródło
21
Dzięki temu rozwiązaniu zamiast tablicy NumPy dostaje się listę, więc nie zawsze może to być wygodne (zajmuje więcej pamięci, jest prawdopodobnie wolniejsze itp.).
Eric O Lebigot,
to „rozwiązanie” jest wolniejsze od najbardziej uprzywilejowanej odpowiedzi przez czynnik ... no cóż, w rzeczywistości blisko nieskończoności
Jivan
16

Z listy mailingowej NumPy , oto inne rozwiązanie:

>>> 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
źródło
3
Prawidłowe uogólnienie to a[np.lexsort(a.T[cols])]. gdzie cols=[1]w pierwotnym pytaniu.
Kontrolowany radiowo
5

Miałem podobny problem.

Mój problem:

Chcę obliczyć SVD i muszę posortować moje wartości własne w kolejności malejącej. Ale chcę zachować mapowanie między wartościami własnymi a wektorami własnymi. Moje wartości własne znajdowały się w pierwszym rzędzie, a odpowiadający mu wektor własny pod nim w tej samej kolumnie.

Chcę więc posortować tablicę dwuwymiarową pod względem kolumny według pierwszego rzędu w kolejności malejącej.

Moje rozwiązanie

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

Jak to działa?

a[0,] to tylko pierwszy wiersz, według którego chcę posortować.

Teraz używam argsort, aby uzyskać porządek indeksów.

Używam, [::-1]ponieważ potrzebuję porządku malejącego.

Wreszcie używam, a[::, ...]aby uzyskać widok z kolumnami we właściwej kolejności.

xuma202
źródło
1

Trochę bardziej skomplikowany lexsortprzykład - zejście na pierwszą kolumnę, a następnie wejście na drugą. Sztuczki lexsortpolegają na tym, że sortuje według rzędów (stąd .T) i daje pierwszeństwo ostatnim.

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
źródło
0

Oto inne rozwiązanie uwzględniające wszystkie kolumny (bardziej zwarty sposób odpowiedzi 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]])

Sortuj za pomocą lexsort,

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

Wynik:

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
źródło
0

Po prostu używając sortowania, użyj numeru koloru, na podstawie którego chcesz sortować.

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
źródło
0

To stare pytanie, ale jeśli chcesz je uogólnić na tablice o wymiarach większych niż 2, oto rozwiązanie, które można łatwo uogólnić:

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

Jest to przesada dla dwóch wymiarów i a[a[:,1].argsort()]wystarczyłaby na odpowiedź @ Steve'a, jednak odpowiedzi tej nie można uogólnić na wyższe wymiary. Możesz znaleźć tym pytaniu przykład tablicy 3D.

Wynik:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
Ehsan
źródło