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?

setengah penuh
sumber
3
Kemungkinan rangkap dari Urutkan daftar berdasarkan banyak atribut?
Chris_Rands
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))
mouad
sumber
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]))
Jaap
sumber
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.

Duncan
sumber
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]) )
dappawit
sumber
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]))

Deepak Yadav
sumber
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)
Majid Arasteh
sumber
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}]

Saurabh
sumber