Liste birden çok özelliğe göre sıralansın mı?


457

Listelerin bir listesi var:

[[12, 'tall', 'blue', 1],
[2, 'short', 'red', 9],
[4, 'tall', 'blue', 13]]

Uzun boylu / kısa element gibi bir öğeye göre sıralamak istersem, bunu yapabilirdim s = sorted(s, key = itemgetter(1)).

Ben göre sıralamak isteseydim hem uzun boylu / kısa ve renk, ben her element için bir kez, sıralama iki kez yapabilirdi, ama daha hızlı bir yolu var mı?



8
Eğer kullanırsanız dizilerini girdileri tarafından yerine listeleri, piton emir türlü çalıştırdığınızda soldan sağa sort. Yani sorted([(4, 2), (0, 3), (0, 1)]) == [(0, 1), (0, 3), (4, 2)],.
Mateen Ulhaq

Yanıtlar:


773

Anahtar, bir demet döndüren bir işlev olabilir:

s = sorted(s, key = lambda x: (x[1], x[2]))

Ya da aynı şeyi itemgetter(daha hızlı ve Python işlev çağrısından kaçınır) kullanarak elde edebilirsiniz :

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

Burada, yeniden atamak sortyerine kullanabileceğinizi unutmayın sorted:

s.sort(key = operator.itemgetter(1, 2))

20
Timeit'ten tamlık için: benim için önce döngü başına 6 bize ve döngü başına ikinci 4.4 bize verdi
Brian Larsen

10
Birincisini artan ve ikincisini azalan şekilde sıralamanın bir yolu var mı? (Her iki özelliğin de dize olduğunu varsayalım, bu nedenle -tamsayı eklemek gibi bir kesmek yok )
Martin Thoma

73
revrse=Truesadece başvurmak isteyip istemediğime ne dersin x[1]?
Amyth

28
@moose, @Amyth, yalnızca bir niteliğe geri dönmek için iki kez sıralayabilirsiniz: önce ikincil, s = sorted(s, key = operator.itemgetter(2))sonra birincil tarafından s = sorted(s, key = operator.itemgetter(1), reverse=True)İdeal değil, ama çalışıyor.
tomcounsell

52
@Amyth veya başka bir seçenek, anahtar sayı ise, tersine çevirmek için birden çok kez yapabilirsiniz -1.
Serge

37

Bu en pitonik yöntem olup olmadığından emin değilim ... Tamsayı değerlerini ve 2. alfabetik olarak azalan 1. sıralama gereken tuples bir listesi vardı. Bu, tamsayı sıralamanın tersine çevrilmesini gerektiriyordu, ancak alfabetik sıralamanın tersine çevrilmesini gerektiriyordu. İşte benim çözümüm: (bir sınavda anında, sıralı işlevleri 'iç ​​içe' yapabileceğinizin farkında bile değildim)

a = [('Al', 2),('Bill', 1),('Carol', 2), ('Abel', 3), ('Zeke', 2), ('Chris', 1)]  
b = sorted(sorted(a, key = lambda x : x[0]), key = lambda x : x[1], reverse = True)  
print(b)  
[('Abel', 3), ('Al', 2), ('Carol', 2), ('Zeke', 2), ('Bill', 1), ('Chris', 1)]

13
2. sayı bir sayı b = sorted(a, key = lambda x: (-x[1], x[0]))olduğu için, ilk olarak hangi ölçütlerin uygulandığı daha görünür olanı gibi çalışır . Verimlilik konusunda emin değilim, birinin zamana ihtiyacı var.
Andrei-Niculae Petre

5

Partiye birkaç yıl geç kaldı ama ben hem 2 kriter sıralamak ve kullanmak istiyorum reverse=True. Başka birinin nasıl olduğunu bilmek istediği takdirde, ölçütlerinizi (işlevlerinizi) parantez içine alabilirsiniz:

s = sorted(my_list, key=lambda i: ( criteria_1(i), criteria_2(i) ), reverse=True)

5

Bir listyerine kullanabilirsiniz tuple. Bir liste / grubun 'sihirli dizinleri' yerine nitelikleri kaparken bu daha önemli hale gelir.

Benim durumumda, gelen anahtarların dizeler olduğu bir sınıfın birden çok özelliğine göre sıralamak istedim. Farklı yerlerde farklı sıralama gerekiyordu ve ben istemciler ile etkileşim üst sınıf için ortak bir varsayılan sıralama istedim; sadece 'gerçekten gerekliyken' sıralama anahtarlarını geçersiz kılmak zorunda kaldım, aynı zamanda bunları sınıfın paylaşabileceği listeler olarak depolayabileceğim şekilde

İlk önce bir yardımcı yöntem tanımladım

def attr_sort(self, attrs=['someAttributeString']:
  '''helper to sort by the attributes named by strings of attrs in order'''
  return lambda k: [ getattr(k, attr) for attr in attrs ]

sonra kullanmak için

# would defined elsewhere but showing here for consiseness
self.SortListA = ['attrA', 'attrB']
self.SortListB = ['attrC', 'attrA']
records = .... #list of my objects to sort
records.sort(key=self.attr_sort(attrs=self.SortListA))
# perhaps later nearby or in another function
more_records = .... #another list
more_records.sort(key=self.attr_sort(attrs=self.SortListB))

Bu, listeyi sıralamak için oluşturulan lambda işlevini kullanır object.attrAve daha sonra sağlanan dize adlarına karşılık gelen bir alıcı olduğunu object.attrBvarsayar object. Ve ikinci dava object.attrCo zamana kadar sıralama yapardı object.attrA.

Bu aynı zamanda, tüketici, birim testi ya da api'nizdeki bazı işlemler için sıralama yapmanın size sadece bir liste vermek zorunda kalmadan nasıl sıralamak istediklerini anlatmaları için potansiyel olarak dışa dönük sıralama seçeneklerini ortaya çıkarmanıza izin verir. bunları arka uç uygulamanıza bağlayabilirsiniz.


İyi iş. Öznitelikler farklı siparişlerde sıralanırsa ne olur? AttrA'nın artan ve attrB azalan olarak sıralanması gerektiğini varsayalım? Bunun üzerinde hızlı bir çözüm var mı? Teşekkürler!
mhn_namak

1

İşte bir yol: Temelde sıralama işlevlerinin bir listesini almak için sıralama işlevinizi yeniden yazıyorsunuz, her sıralama işlevi sınamak istediğiniz öznitelikleri karşılaştırıyor, her sıralama testinde cmp işlevinin sıfırdan farklı bir dönüş döndürüp döndürmediğini görüyorsunuz eğer öyleyse mola verin ve dönüş değerini gönderin. Buna Lambdas listesinin bir fonksiyonunun Lambda'sını çağırarak adını veriyorsunuz.

Avantajı, diğer yöntemlerde olduğu gibi önceki türden değil, verilerden tek geçiş yapmasıdır. Başka bir şey, yerinde sıralanması, sıralı bir kopya oluşturması gibi görünüyor.

Her nesnenin bir grupta olduğu ve bir puan işlevine sahip olduğu sınıfların bir listesini sıralayan bir sıralama işlevi yazmak için kullandım, ancak herhangi bir öznitelik listesi ekleyebilirsiniz. Unter lambda benzeri, ancak bir pasör çağırmak için bir lambda hackish kullanımı dikkat edin. Sıralama bölümü bir dizi liste için çalışmaz, ancak sıralama çalışır.

#First, here's  a pure list version
my_sortLambdaLst = [lambda x,y:cmp(x[0], y[0]), lambda x,y:cmp(x[1], y[1])]
def multi_attribute_sort(x,y):
    r = 0
    for l in my_sortLambdaLst:
        r = l(x,y)
        if r!=0: return r #keep looping till you see a difference
    return r

Lst = [(4, 2.0), (4, 0.01), (4, 0.9), (4, 0.999),(4, 0.2), (1, 2.0), (1, 0.01), (1, 0.9), (1, 0.999), (1, 0.2) ]
Lst.sort(lambda x,y:multi_attribute_sort(x,y)) #The Lambda of the Lambda
for rec in Lst: print str(rec)

İşte bir nesne listesini sıralamanın bir yolu

class probe:
    def __init__(self, group, score):
        self.group = group
        self.score = score
        self.rank =-1
    def set_rank(self, r):
        self.rank = r
    def __str__(self):
        return '\t'.join([str(self.group), str(self.score), str(self.rank)]) 


def RankLst(inLst, group_lambda= lambda x:x.group, sortLambdaLst = [lambda x,y:cmp(x.group, y.group), lambda x,y:cmp(x.score, y.score)], SetRank_Lambda = lambda x, rank:x.set_rank(rank)):
    #Inner function is the only way (I could think of) to pass the sortLambdaLst into a sort function
    def multi_attribute_sort(x,y):
        r = 0
        for l in sortLambdaLst:
            r = l(x,y)
            if r!=0: return r #keep looping till you see a difference
        return r

    inLst.sort(lambda x,y:multi_attribute_sort(x,y))
    #Now Rank your probes
    rank = 0
    last_group = group_lambda(inLst[0])
    for i in range(len(inLst)):
        rec = inLst[i]
        group = group_lambda(rec)
        if last_group == group: 
            rank+=1
        else:
            rank=1
            last_group = group
        SetRank_Lambda(inLst[i], rank) #This is pure evil!! The lambda purists are gnashing their teeth

Lst = [probe(4, 2.0), probe(4, 0.01), probe(4, 0.9), probe(4, 0.999), probe(4, 0.2), probe(1, 2.0), probe(1, 0.01), probe(1, 0.9), probe(1, 0.999), probe(1, 0.2) ]

RankLst(Lst, group_lambda= lambda x:x.group, sortLambdaLst = [lambda x,y:cmp(x.group, y.group), lambda x,y:cmp(x.score, y.score)], SetRank_Lambda = lambda x, rank:x.set_rank(rank))
print '\t'.join(['group', 'score', 'rank']) 
for r in Lst: print r
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.