Python listesini iki alana göre sıralama


173

Sıralı bir csv oluşturulan aşağıdaki liste var

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

Aslında listeyi iki kritere göre sıralamak istiyorum: önce alan 1'deki değere ve sonra alan 2'deki değere göre. Bunu nasıl yapabilirim?



Bu sorunun durmasına izin veriyor ve kapsamını "uzunluk-iki-yerleşik-uzunluk listesi (ör. String / int / float)" ile sınırlıyor muyuz ? Ya da başlığın da önerdiği gibi "kullanıcı-tanımlı-nesne listesi" ne izin veriyoruz , bu durumda cevap " __lt__()sınıfınızda yöntemi tanımlayın veya bunu yapan bir sınıftan miras alınsın" mı? Bu onu çok daha iyi bir kanonik yapardı.
smci

Yanıtlar:


158

bunun gibi:

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

1
+1: Benimkinden daha zarif. İtemgetter'ın birden fazla endeks alabileceğini unuttum.
dappawit

7
operatoriçe aktarılması gereken bir modüldür.
trapicki

3
itemgetter kullanarak bir öğenin artan ve diğer azalan sıralamak istiyorsanız nasıl devam edeceğim ??.
Ashish

3
@ashish, lambda fonksiyonlarıyla aşağıdaki cevabımı görün, bu açıktır, "-x [1]" veya hatta "x [0] + x [1]" 'e göre
sıralayın

ters modda bir kriter varsa ne olur?
YaserKH

328

Lambda işlevlerini kullanırken herhangi bir şey içe aktarmaya gerek yoktur.
Aşağıdaki listilk öğeye, sonra ikinci öğeye göre sıralar .

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

12
Güzel. Yukarıdaki ana cevaba yorumda belirttiğiniz gibi, bu, farklı sıralama düzenleri ile birden çok tür yapmanın en iyi (tek?) Yoludur. Belki de bunu vurgulayın. Ayrıca, metniniz ikinci öğeye göre azalan sıralama yaptığınızı göstermez.
PeterVermont

2
@ user1700890 Alanın zaten dize olduğunu varsayıyordum. Dizeleri varsayılan olarak alfabetik sırayla sıralamalıdır. Buradaki yanıtla veya OP'nin orijinal sorusuyla ilgili değilse, kendi sorunuzu SO'ya ayrı olarak göndermelisiniz.
pbible

5
yapar -içinde -x[1]standı için?
Ocak

7
@jan ters sıralama
jaap

3
Belirli bir durumda çalışmayacak. Kabul edilen çözüm de çalışmaz. Örneğin, anahtar olarak kullanılacak sütunların tümü sayıya dönüştürülemeyen dizelerdir. İkincisi, kişi bir sütuna göre artan ve bir sütuna göre azalan düzende sıralamak ister.
coder.in.me

20

Python'un kararlı bir türü vardır, bu nedenle performansın bir sorun olmaması şartıyla, en basit yol onu alan 2'ye göre sıralamak ve daha sonra tekrar alan 1'e göre sıralamaktır.

Bu size istediğiniz sonucu verecektir, tek yakalama büyük bir liste (veya sık sık sıralamak istiyorsanız) iki kez sıralama çağırmak kabul edilemez bir yük olabilir olmasıdır.

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

Bu şekilde yapmak, bazı sütunların tersine sıralanmasını istediğiniz durumun işlenmesini de kolaylaştırır, sadece gerektiğinde 'reverse = True' parametresini ekleyin.

Aksi takdirde, itemgetter'a birden fazla parametre iletebilir veya manuel olarak bir demet oluşturabilirsiniz. Bu muhtemelen daha hızlı olacak, ancak bazı sütunların tersine sıralanması isteniyorsa iyi bir şekilde genelleştirilmemesi sorunu var (sayısal sütunlar yine de reddedilerek tersine çevrilebilir, ancak sıralama sabit kalır).

Dolayısıyla, ters sıralanmış sütunlara ihtiyacınız yoksa, itemgetter için birden fazla bağımsız değişkene gidin, eğer mümkünse ve sütunlar sayısal değilse veya sıralamayı birden çok ardışık sıralama için sabit tutmak istiyorsanız.

Düzenleme: Bu orijinal soruya nasıl cevap anlamakta sorun yaşayan yorumcular için, burada tam olarak sıralama istikrarlı doğası nasıl her anahtar ayrı sıralar yapmak ve birden çok kritere göre sıralanmış verilerle sonuçlanmasını sağlar bir örnek:

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

Bu çalıştırılabilir bir örnektir, ancak çalıştıran kişileri kaydetmek için çıktı:

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

Özellikle ikinci adımda reverse=Trueparametrenin adların sırasını nasıl koruduğuna dikkat edin, listeyi sıralamak ve tersine çevirmek üçüncü sıralama anahtarı için istenen sırayı kaybedecektir.


1
Kararlı sıralama, önceki sıralamanızın ne olduğunu unutmayacağı anlamına gelmez. Bu cevap yanlış.
Mike Axiak

7
Kararlı sıralama, a, b, c sütunlarına göre yalnızca c sonra b sonra a sütunlarına göre sıralayabileceğiniz anlamına gelir. Yorumunuzu genişletmediğiniz sürece, yanılmış olduğunuzu düşünüyorum.
Duncan

7
Bu cevap kesinlikle doğrudur, ancak daha büyük listeler için unideal: liste zaten kısmen sıralandıysa, listeyi çok daha fazla karıştırarak Python'un sıralama optimizasyonunun çoğunu kaybedersiniz. @Mike, yanılıyorsun; Yanıtları yanlış beyan etmeden önce test etmenizi öneririm.
Glenn Maynard

6
@MikeAxiak: docs.python.org/2/library/stdtypes.html#index-29 durum 9'daki yorum: Python 2.3 ile başlayarak, sort () yönteminin kararlı olduğu garanti edilmektedir. Bir sıralama, eşit olan öğelerin göreli sırasını değiştirmemeyi garanti ederse kararlıdır - bu, çoklu geçişlerde sıralama için yararlıdır (örneğin, departmana göre, sonra maaş derecesine göre sırala).
trapicki

Bu doğru değil çünkü sorduğu soruya cevap vermiyor. ilk dizine göre sıralanmış bir liste istiyor ve ilk dizinde bağların olduğu durumlarda ikinci dizini sıralama ölçütü olarak kullanmak istiyor. Kararlı bir sıralama sadece her şeyin eşit olduğunu garanti eder, geçen orijinal sipariş öğelerin görünme sırası olacaktır.
Jon

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

4
Sanmıyorum tuple()(eğer birlikte sayarsanız, daha doğrusu, üç iki argüman alabilir self)
Filipe Correia

3
tuple sadece bir argüman alabilir
therealprashant

1
returnifadesi return tuple((x[1], x[2]))ya da basit olmalıdır return x[1], x[2]. Bakın size farklı yönlerde sıralama arıyorsanız aşağıda cevabını @jaap
Jo Kachikaran

… Ya tuple(x[1:3])da tuple yapıcısını sadece bir tuple görüntüleme listesi yerine herhangi bir nedenle kullanmak istiyorsanız x[1], x[2]. Ya da keyfunc = operator.itemgetter(1, 2)kendiniz bir işlev bile yazmayın.
abarnert

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

Ayrıca python sıralaması yerinde ve kararlı olduğu için .sort'u lambda ile 2 kez kullanabiliriz. Bu önce listeyi ikinci öğeye (x [1]) göre sıralar. Ardından, ilk öğeyi (x [0] (en yüksek öncelik)) sıralar.

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

Bu, aşağıdakileri yapmaya eşdeğerdir: workers.sort (anahtar = lambda x: (x [0], x [1]))


1
Hayır, bu sıralama kuralının ikinci sırada olması gerekir.
CodeFarmer

1

Artan sırada şunları kullanabilirsiniz:

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

veya azalan sırada şunları kullanabilirsiniz:

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

0

Aşağıdakileri kullanarak dikte listesinin sıralanması, listeyi ilk sütunda maaş, ikinci sütunda yaş olarak azalan sırada sıralayacaktır.

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

Çıktı: [{'maaş': 123, 'yaş': 25}, {'maaş': 123, 'yaş': 23}]

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.