按多个属性对列表进行排序?

时间:2010-11-20 15:30:54

标签: python sorting

我有一份清单清单:

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

如果我想按一个元素排序,比如说高/短元素,我可以通过s = sorted(s, key = itemgetter(1))来完成。

如果我想按两者高/短和颜色排序,我可以对每个元素进行两次排序,但是有更快的方法吗?

7 个答案:

答案 0 :(得分:585)

键可以是返回元组的函数:

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

或者你可以使用itemgetter实现相同的功能(更快,避免Python函数调用):

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

请注意,您可以在此使用sort代替sorted,然后重新分配:

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

答案 1 :(得分:29)

我不确定这是否是最pythonic的方法...... 我有一个元组列表,需要按降序整数值排序第一,按字母顺序排序第二。这需要反转整数排序,但不是按字母顺序排序。这是我的解决方案:(在考试顺便说一下,我甚至不知道你可以'嵌套'排序功能)

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

答案 2 :(得分:3)

您似乎可以使用list代替tuple。 当你抓住属性而不是魔术索引时,我认为这变得更加重要。列表/元组。

在我的情况下,我想按类的多个属性排序,其中传入的键是字符串。我需要在不同的地方进行不同的排序,我希望客户端与之交互的父类有一个共同的默认排序;只需要覆盖'排序键'当我真的需要'时,我也可以将它们存储为班级可以共享的列表

首先我定义了一个辅助方法

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 ]

然后使用它

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

这将使用生成的lambda函数按object.attrA对列表进行排序,然后object.attrB假设object具有与提供的字符串名称对应的getter。第二种情况将按object.attrC然后object.attrA排序。

这也允许你潜在地将外向排序选择暴露给消费者,单元测试,或者他们可能告诉你他们希望如何为api中的某些操作完成排序只需要给你一个列出并且不将它们耦合到您的后端实现。

答案 3 :(得分:1)

这里有一种方法:您基本上重新编写排序函数以获取排序函数列表,每个排序函数比较您要测试的属性,在每个排序测试中,您查看并查看cmp如果是,则函数返回非零返回并发送返回值。 您可以通过调用Lambda列表函数的Lambda来调用它。

它的优点是它可以单独传递数据,而不是像其他方法那样的前一种数据。另一件事是它在适当的位置排序,而排序似乎是复制。

我用它来编写一个排名函数,它对每个对象在一个组中的类列表进行排名,并且有一个分数函数,但是你可以添加任何属性列表。 注意非lambda式,虽然hackish使用lambda来调用setter。 排名部分不会为一系列列表工作,但排序会。

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

这是一种对对象列表进行排名的方法

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

答案 4 :(得分:0)

聚会晚了几年,但我想两者对2个条件进行排序使用reverse=True。如果其他人想知道如何做,则可以将条件(函数)括在括号中:

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

答案 5 :(得分:0)

将列表列表转换为元组列表,然后按多个字段对元组进行排序。

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

 data=[tuple(x) for x in data]
 result = sorted(data, key = lambda x: (x[1], x[2]))
 print(result)

输出:

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

答案 6 :(得分:-1)

列表之间有一个运算符<,例如:

[12, 'tall', 'blue', 1] < [4, 'tall', 'blue', 13]

会给予

False