Mengurutkan array di NumPy berdasarkan kolom


336

Bagaimana saya bisa mengurutkan array di NumPy berdasarkan kolom ke-n?

Sebagai contoh,

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

Saya ingin mengurutkan baris menurut kolom kedua, sehingga saya dapat kembali:

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

8
Ini adalah contoh yang sangat buruk karena np.sort(a, axis=0)akan menjadi solusi yang memuaskan untuk matriks yang diberikan. Saya menyarankan edit dengan contoh yang lebih baik tetapi ditolak, meskipun sebenarnya pertanyaannya akan jauh lebih jelas. Contohnya harus seperti a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]])dengan keluaran yang diinginkanarray([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
David

29
David, kamu tidak mengerti maksud dari pertanyaan itu. Dia ingin menjaga agar urutan dalam setiap baris tetap sama.
marcorossi

@marcorossi saya mengerti maksudnya, tetapi contohnya dirumuskan dengan sangat buruk karena, seperti yang saya katakan, ada beberapa kemungkinan jawaban (yang, bagaimanapun, tidak akan memenuhi permintaan OP). Sunting kemudian berdasarkan komentar saya memang telah disetujui (lucu bahwa saya ditolak, meskipun). Jadi sekarang semuanya baik-baik saja.
David

Jawaban:


141

@ Steve 's jawabannya sebenarnya adalah cara yang paling elegan untuk melakukannya.

Untuk cara yang "benar" lihat argumen kata kunci pesanan dari numpy.ndarray.sort

Namun, Anda harus melihat array Anda sebagai array dengan bidang (array terstruktur).

Cara "benar" cukup jelek jika Anda awalnya tidak mendefinisikan array dengan bidang ...

Sebagai contoh cepat, untuk mengurutkannya dan mengembalikan salinan:

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

Untuk mengurutkannya di tempat:

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 benar-benar adalah cara paling elegan untuk melakukannya, sejauh yang saya tahu ...

Satu-satunya keuntungan dari metode ini adalah bahwa argumen "urutan" adalah daftar bidang untuk memesan pencarian. Misalnya, Anda bisa mengurutkan berdasarkan kolom kedua, lalu kolom ketiga, lalu kolom pertama dengan memasok order = ['f1', 'f2', 'f0'].


3
Dalam 1.6.1rc1 numpy saya, ia memunculkanValueError: new type not compatible with array.
Clippit

9
Apakah masuk akal untuk mengajukan permintaan fitur bahwa cara "benar" dibuat kurang jelek?
endolith

4
Bagaimana jika nilai dalam array float? Haruskah saya mengubah sesuatu?
Marco

1
Dan untuk tipe hybrid seperti a = np.array([['a',1,2,3],['b',4,5,6],['c',0,0,1]])apa pendekatan yang harus saya ikuti?
ePascoal

10
Salah satu keuntungan utama dari metode ini daripada Steve adalah memungkinkan array yang sangat besar untuk diurutkan di tempat. Untuk array yang cukup besar, indeks yang dikembalikan oleh np.argsortmereka sendiri mungkin mengambil cukup banyak memori, dan di atas itu, pengindeksan dengan array juga akan menghasilkan salinan array yang sedang diurutkan.
ali_m

737

Saya kira ini bekerja: a[a[:,1].argsort()]

Ini menunjukkan kolom kedua adan mengurutkannya berdasarkan itu.


2
Ini tidak jelas, apa yang ada 1di sini? indeks yang akan disortir?
orezvani

29
[:,1]menunjukkan kolom kedua a.
Steve Tjoa

60
Jika Anda ingin jenis yang sebaliknya, ubah ini menjadia[a[:,1].argsort()[::-1]]
Steven C. Howell

1
Terlihat sederhana dan berfungsi! Apakah lebih cepat np.sortatau tidak?
Václav Pavlík

14
Saya menemukan ini lebih mudah dibaca:ind = np.argsort( a[:,1] ); a = a[ind]
poppie

32

Anda dapat mengurutkan pada beberapa kolom sesuai metode Steve Tjoa dengan menggunakan pengurutan stabil seperti mergesort dan mengurutkan indeks dari kolom yang paling signifikan ke yang paling signifikan:

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

Ini mengurutkan berdasarkan kolom 0, lalu 1, lalu 2.


4
Mengapa First Sort tidak perlu stabil?
Little Bobby Tables

10
Pertanyaan bagus - stable berarti bahwa ketika ada dasi Anda mempertahankan urutan asli, dan urutan asli dari file yang tidak disortir tidak relevan.
JJ

Ini sepertinya poin yang sangat penting. memiliki daftar yang diam-diam tidak mengurutkan akan menjadi buruk.
Kucing kikuk

19

Jika seseorang ingin memanfaatkan penyortiran pada bagian penting dari program mereka di sini adalah perbandingan kinerja untuk berbagai proposal

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

Jadi, sepertinya pengindeksan dengan argsort adalah metode tercepat sejauh ini ...


19

Dari wiki dokumentasi Python , saya pikir Anda dapat melakukannya:

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

Outputnya adalah:

[[[0, 0, 1], [1, 2, 3], [4, 5, 6]]]

21
Dengan solusi ini, seseorang mendapat daftar alih-alih array NumPy, jadi ini mungkin tidak selalu nyaman (membutuhkan lebih banyak memori, mungkin lebih lambat, dll.).
Eric O Lebigot

"solusi" ini lebih lambat oleh jawaban yang paling banyak dipilih oleh faktor ... well, hampir tak terhingga sebenarnya
Jivan

16

Dari milis NumPy , berikut adalah solusi lain:

>>> 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
Generalisasi yang benar adalah a[np.lexsort(a.T[cols])]. dimana cols=[1]dalam pertanyaan aslinya.
Radio Terkendali

5

Saya punya masalah serupa.

Masalahku:

Saya ingin menghitung SVD dan perlu mengurutkan nilai eigen saya dalam urutan menurun. Tapi saya ingin menjaga pemetaan antara nilai eigen dan vektor eigen. Nilai eigen saya ada di baris pertama dan vektor eigen yang sesuai di bawahnya di kolom yang sama.

Jadi saya ingin mengurutkan array dua dimensi kolom-bijaksana dengan baris pertama dalam urutan menurun.

Solusi saya

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

Jadi bagaimana cara kerjanya?

a[0,] hanya baris pertama yang ingin saya urutkan berdasarkan.

Sekarang saya menggunakan argsort untuk mendapatkan urutan indeks.

Saya menggunakan [::-1]karena saya perlu memesan.

Terakhir saya gunakan a[::, ...]untuk mendapatkan tampilan dengan kolom dalam urutan yang benar.


1

Contoh yang sedikit lebih rumit lexsort- turun pada kolom 1, naik kedua pada kolom ke-2. Trik dengan itu lexsortadalah jenis pada baris (karenanya .T), dan memberikan prioritas ke yang terakhir.

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

Berikut adalah solusi lain yang mempertimbangkan semua kolom (cara JJ yang lebih ringkas );

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

Sortir dengan lexsort,

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

Keluaran:

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

Cukup menggunakan sortir, gunakan nomor kolom berdasarkan yang ingin Anda sortir.

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

Ini adalah pertanyaan lama tetapi jika Anda perlu menggeneralisasi ini ke array yang lebih tinggi dari 2 dimensi, inilah solusinya daripada yang dapat digeneralisasikan dengan mudah:

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

Ini adalah kerja keras yang berlebihan untuk dua dimensi dan a[a[:,1].argsort()]akan cukup untuk setiap jawaban, tetapi jawaban itu tidak dapat digeneralisasikan ke dimensi yang lebih tinggi. Anda dapat menemukan contoh array 3D dalam pertanyaan ini.

Keluaran:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.