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

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


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.


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.


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


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

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

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.


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

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

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)

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]]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.