Menyortir daftar Python berdasarkan dua bidang


173

Saya memiliki daftar berikut yang dibuat dari csv yang diurutkan

list1 = sorted(csv1, key=operator.itemgetter(1))

Saya sebenarnya ingin menyortir daftar dengan dua kriteria: pertama dengan nilai di bidang 1 dan kemudian dengan nilai di bidang 2. Bagaimana saya melakukan ini?



Apakah kita membiarkan pertanyaan ini bertahan dan hanya membatasi ruang lingkupnya menjadi "daftar-daftar-panjang-dua-builtin-tipe (misalnya string / int / float)" . Atau apakah kami juga mengizinkan "daftar objek yang ditentukan pengguna" , karena judulnya juga diperbolehkan, dalam hal ini jawabannya adalah "Tentukan __lt__()metode pada kelas Anda atau warisi dari beberapa kelas yang melakukannya" ? Itu akan membuatnya menjadi kanonik yang jauh lebih baik.
smci

Jawaban:


158

seperti ini:

import operator
list1 = sorted(csv1, key=operator.itemgetter(1, 2))

1
+1: Lebih elegan dari milik saya. Saya lupa bahwa itemgetter dapat mengambil beberapa indeks.
dappawit

7
operatoradalah modul yang perlu diimpor.
trapicki

3
bagaimana saya akan melanjutkan jika saya ingin mengurutkan naik pada satu elemen dan turun pada yang lain, menggunakan itemgetter ??.
ashish

3
@ashish, lihat jawaban saya di bawah ini dengan fungsi lambda ini jelas, urutkan berdasarkan "-x [1]" atau bahkan "x [0] + x [1]" jika Anda mau
jaap

bagaimana jika satu kriteria dalam mode terbalik?
YaserKH

328

Tidak perlu mengimpor apa pun saat menggunakan fungsi lambda.
Berikut ini diurutkan listmenurut elemen pertama, kemudian oleh elemen kedua.

sorted(list, key=lambda x: (x[0], -x[1]))

12
Bagus. Seperti yang Anda catat dalam komentar untuk jawaban utama di atas, ini adalah cara terbaik (hanya?) Untuk melakukan berbagai macam dengan urutan berbeda. Mungkin sorot itu. Juga, teks Anda tidak menunjukkan bahwa Anda mengurutkan turun pada elemen kedua.
PeterVermont

2
@ user1700890 Saya berasumsi bidang sudah string. Seharusnya mengurutkan string dalam urutan abjad secara default. Anda harus memposting pertanyaan Anda sendiri secara terpisah pada SO jika tidak secara spesifik terkait dengan jawaban di sini atau pertanyaan awal OP.
pbible

5
untuk apa -in -x[1]berdiri?
Januari

7
@jan itu semacam mundur
jaap

3
Tidak akan bekerja dalam satu kasus khusus. Solusi yang diterima juga tidak akan berfungsi. Misalnya, kolom yang akan digunakan sebagai kunci adalah semua string yang tidak dapat dikonversi menjadi angka. Kedua, seseorang ingin mengurutkan dalam urutan naik dengan satu kolom dan urutan menurun dengan kolom lain.
coder.in.me

20

Python memiliki jenis yang stabil, jadi asalkan kinerja bukan masalah, cara paling sederhana adalah mengurutkannya berdasarkan bidang 2 dan kemudian mengurutkannya lagi berdasarkan bidang 1.

Itu akan memberi Anda hasil yang Anda inginkan, satu-satunya hasil adalah bahwa jika itu adalah daftar besar (atau Anda ingin sering mengurutkannya) memanggil semacam dua kali mungkin overhead yang tidak dapat diterima.

list1 = sorted(csv1, key=operator.itemgetter(2))
list1 = sorted(list1, key=operator.itemgetter(1))

Melakukannya dengan cara ini juga memudahkan untuk menangani situasi di mana Anda ingin beberapa kolom diurutkan mundur, cukup sertakan parameter 'reverse = True' bila perlu.

Jika tidak, Anda dapat meneruskan beberapa parameter ke itemgetter atau membuat tuple secara manual. Itu mungkin akan lebih cepat, tetapi memiliki masalah bahwa itu tidak menggeneralisasi dengan baik jika beberapa kolom ingin diurutkan terbalik (kolom numerik masih dapat dibalik dengan meniadakannya tetapi itu menghentikan pengurutan menjadi stabil).

Jadi jika Anda tidak memerlukan kolom yang disortir terbalik, pilih beberapa argumen untuk itemgetter, jika Anda mau, dan kolom tidak numerik atau Anda ingin menjaga penyortiran stabil untuk beberapa jenis berturut-turut.

Sunting: Untuk komentator yang memiliki masalah dalam memahami bagaimana ini menjawab pertanyaan asli, berikut adalah contoh yang menunjukkan dengan tepat bagaimana sifat stabil penyortiran memastikan kami dapat melakukan pengurutan terpisah pada setiap kunci dan berakhir dengan data yang diurutkan pada beberapa kriteria:

DATA = [
    ('Jones', 'Jane', 58),
    ('Smith', 'Anne', 30),
    ('Jones', 'Fred', 30),
    ('Smith', 'John', 60),
    ('Smith', 'Fred', 30),
    ('Jones', 'Anne', 30),
    ('Smith', 'Jane', 58),
    ('Smith', 'Twin2', 3),
    ('Jones', 'John', 60),
    ('Smith', 'Twin1', 3),
    ('Jones', 'Twin1', 3),
    ('Jones', 'Twin2', 3)
]

# Sort by Surname, Age DESCENDING, Firstname
print("Initial data in random order")
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
First we sort by first name, after this pass all
Twin1 come before Twin2 and Anne comes before Fred''')
DATA.sort(key=lambda row: row[1])

for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
Second pass: sort by age in descending order.
Note that after this pass rows are sorted by age but
Twin1/Twin2 and Anne/Fred pairs are still in correct
firstname order.''')
DATA.sort(key=lambda row: row[2], reverse=True)
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
Final pass sorts the Jones from the Smiths.
Within each family members are sorted by age but equal
age members are sorted by first name.
''')
DATA.sort(key=lambda row: row[0])
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

Ini adalah contoh runnable, tetapi untuk menyelamatkan orang yang menjalankannya, hasilnya adalah:

Initial data in random order
Jones      Jane       58
Smith      Anne       30
Jones      Fred       30
Smith      John       60
Smith      Fred       30
Jones      Anne       30
Smith      Jane       58
Smith      Twin2      3
Jones      John       60
Smith      Twin1      3
Jones      Twin1      3
Jones      Twin2      3

First we sort by first name, after this pass all
Twin1 come before Twin2 and Anne comes before Fred
Smith      Anne       30
Jones      Anne       30
Jones      Fred       30
Smith      Fred       30
Jones      Jane       58
Smith      Jane       58
Smith      John       60
Jones      John       60
Smith      Twin1      3
Jones      Twin1      3
Smith      Twin2      3
Jones      Twin2      3

Second pass: sort by age in descending order.
Note that after this pass rows are sorted by age but
Twin1/Twin2 and Anne/Fred pairs are still in correct
firstname order.
Smith      John       60
Jones      John       60
Jones      Jane       58
Smith      Jane       58
Smith      Anne       30
Jones      Anne       30
Jones      Fred       30
Smith      Fred       30
Smith      Twin1      3
Jones      Twin1      3
Smith      Twin2      3
Jones      Twin2      3

Final pass sorts the Jones from the Smiths.
Within each family members are sorted by age but equal
age members are sorted by first name.

Jones      John       60
Jones      Jane       58
Jones      Anne       30
Jones      Fred       30
Jones      Twin1      3
Jones      Twin2      3
Smith      John       60
Smith      Jane       58
Smith      Anne       30
Smith      Fred       30
Smith      Twin1      3
Smith      Twin2      3

Perhatikan secara khusus bagaimana pada langkah kedua reverse=Trueparameter menjaga nama depan tetap berurutan sedangkan hanya mengurutkan lalu membalikkan daftar akan kehilangan urutan yang diinginkan untuk kunci pengurutan ketiga.


1
Penyortiran yang stabil tidak berarti penyortiran Anda tidak akan lupa sebelumnya. Jawaban ini salah.
Mike Axiak

7
Pengurutan yang stabil berarti Anda dapat mengurutkan berdasarkan kolom a, b, c cukup dengan mengurutkan berdasarkan kolom c lalu b lalu a. Kecuali Anda peduli untuk memperluas komentar Anda, saya pikir Andalah yang salah.
Duncan

7
Jawaban ini pasti benar, meskipun untuk daftar yang lebih besar itu tidak ideal: jika daftar itu sudah diurutkan sebagian, maka Anda akan kehilangan sebagian besar optimasi penyortiran Python dengan mengocok daftar itu lebih banyak lagi. @ Mike, Anda salah; Saya menyarankan untuk benar-benar menguji jawaban sebelum menyatakannya salah.
Glenn Maynard

6
@MikeAxiak: docs.python.org/2/library/stdtypes.html#index-29 menyatakan dalam komentar 9: Dimulai dengan Python 2.3, metode sort () dijamin stabil. Penyortiran stabil jika menjamin tidak mengubah urutan relatif elemen yang membandingkan sama - ini berguna untuk menyortir beberapa lintasan (misalnya, urutkan berdasarkan departemen, lalu menurut tingkat gaji).
trapicki

Ini tidak benar karena ini tidak menjawab pertanyaan yang diajukannya. dia ingin daftar diurutkan berdasarkan indeks pertama dan dalam kasus di mana ada ikatan dalam indeks pertama, dia ingin menggunakan indeks kedua sebagai kriteria penyortiran. Sortir yang stabil hanya menjamin bahwa semua hal sama, urutan asli yang dilewati akan menjadi urutan item yang muncul.
Jon

14
list1 = sorted(csv1, key=lambda x: (x[1], x[2]) )

4
Saya tidak berpikir tuple()dapat menerima dua argumen (atau lebih tepatnya, tiga, jika Anda hitung dengan self)
Filipe Correia

3
tuple take hanya dapat mengambil satu argumen
ada

1
returnpernyataan harus return tuple((x[1], x[2]))atau sederhana return x[1], x[2]. Lihat @jaap jawaban di bawah ini jika Anda mencari pengurutan ke arah yang berbeda
Jo Kachikaran

... atau tuple(x[1:3]), jika Anda ingin menggunakan konstruktor tuple untuk beberapa alasan, bukan hanya daftar tampilan tuple x[1], x[2]. Atau keyfunc = operator.itemgetter(1, 2)dan bahkan tidak menulis fungsi sendiri.
abarnert

3
employees.sort(key = lambda x:x[1])
employees.sort(key = lambda x:x[0])

Kita juga dapat menggunakan .sort dengan lambda 2 kali karena semacam python sudah ada dan stabil. Ini pertama-tama akan mengurutkan daftar berdasarkan elemen kedua, x [1]. Kemudian, itu akan mengurutkan elemen pertama, x [0] (prioritas tertinggi).

employees[0] = Employee's Name
employees[1] = Employee's Salary

Ini sama dengan melakukan yang berikut ini: employee.sort (key = lambda x: (x [0], x [1]))


1
tidak, aturan penyortiran ini harus diutamakan kemudian kedua.
CodeFarmer

1

Dalam urutan menaik, Anda dapat menggunakan:

sorted_data= sorted(non_sorted_data, key=lambda k: (k[1],k[0]))

atau dalam urutan menurun Anda dapat menggunakan:

sorted_data= sorted(non_sorted_data, key=lambda k: (k[1],k[0]),reverse=True)

0

Daftar sortir dicts menggunakan di bawah ini akan mengurutkan daftar dalam urutan menurun pada kolom pertama sebagai gaji dan kolom kedua sesuai usia

d=[{'salary':123,'age':23},{'salary':123,'age':25}]
d=sorted(d, key=lambda i: (i['salary'], i['age']),reverse=True)

Output: [{'gaji': 123, 'usia': 25}, {'gaji': 123, 'usia': 23}]

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.