如何按给定索引处的元素对列表/元组的列表/元组进行排序

901

我有一些数据,可以是列表的列表或元组的列表,就像这样:

data = [[1,2,3], [4,5,6], [7,8,9]]
data = [(1,2,3), (4,5,6), (7,8,9)]

我想按子集中的第二个元素进行排序。也就是说,按2、5、8排序,其中2来自(1,2,3)5来自(4,5,6)。做到这一点的常见方法是什么?我应该在列表中存储元组还是列表?


80
关于“我应该在我的列表中存储元组还是列表?”的问题,一个经验法则是尽可能使事物不可变。如果你不需要就地修改子列表,请将它们创建为元组。 - Matthew Flaschen
11个回答

1489
sorted_by_second = sorted(data, key=lambda tup: tup[1])

或者:

data.sort(key=lambda tup: tup[1])  # sorts in place

默认的排序模式是升序。要按降序排序,请使用选项 reverse=True

sorted_by_second = sorted(data, key=lambda tup: tup[1], reverse=True)

或者:

data.sort(key=lambda tup: tup[1], reverse=True)  # sorts in place

16
怎样将它从大到小排序? - billwild
84
@billwild: 帮助(sorted). reverse=True. 这句话的意思是请求帮助对于Python内建函数sorted()的reverse参数,将其设为True时可以实现降序排列。 - Stephen
46
@Stephen 使用itemgetter更快更简单:key=itemgetter(1) 并在文件开头添加:from operator import itemgetter - Joschua
3
关于第二个例子,这里的sort是Python中List对象的一个方法,它接收一个lambda函数作为key参数。你可以将其命名为tupt或其他任何名称,它仍然可以工作。这里的tup指定了列表元组的索引,所以1意味着排序将按原始列表中元组的第二个值(2、5、8)进行排序。 - Neurotransmitter
5
我对“使用itemgetter更快更简单”的无依据声明持保留意见。虽然我主观上认为直观的lambda方法比难以理解的itemgetter类更简单,但是事实证明itemgetter确实似乎更快。我很好奇为什么会这样。我粗略的猜测是lambda会产生隐藏成本,将所有局部变量捕获到闭包上下文中,而itemgetter实例则不会。_总之:始终使用itemgetter,因为速度胜利。_ - Cecil Curry
显示剩余3条评论

307
from operator import itemgetter
data.sort(key=itemgetter(1))

56
这应该成为被采纳的答案。此外,Charlie发布时间也值得参考,它演示了使用itemgetter类进行排序比等效的lambda函数平均快**126%**。 - Cecil Curry
22
你还可以按多个指标进行分层排序,例如 data.sort(key=itemgetter(3,1)) - Michael Ohlrogge

91

要按多个条件排序,例如按元组中的第二个和第三个元素进行排序,请使用

data = [(1,2,3),(1,2,1),(1,1,4)]

因此,定义一个返回描述优先级的元组的lambda函数,例如

sorted(data, key=lambda tup: (tup[1],tup[2]) )
[(1, 1, 4), (1, 2, 1), (1, 2, 3)]

67
我想对Stephen的回答进行补充,如果您想将数组从大到小排序,除了上面的评论中提到的方法外,还有另一种方法,只需将以下内容添加到该行即可:
reverse = True

结果将如下所示:

data.sort(key=lambda tup: tup[1], reverse=True)

30

Stephen的回答是我会用的一个。为了完整起见,这里有一个使用列表推导式的DSU(装饰-排序-去装饰)模式:

decorated = [(tup[1], tup) for tup in data]
decorated.sort()
undecorated = [tup for second, tup in decorated]

或者更简洁地说:

[b for a,b in sorted((tup[1], tup) for tup in data)]

正如Python Sorting HowTo所指出的那样,自从Python 2.4之后,使用键函数已经不再需要了。


2
这个答案对Python 2.3-有用吗?在更现代的Python版本中是否有任何有效的用途,您可以详细说明一下吗?如果没有,那就算了...只是路过,看到这个问题,脑子里想了一下。无论如何,感谢这次回到Python早期的经历。 - mechanical_meat

29

为了将一个元组列表 (<word>, <count>) 按照 count 降序和 word 字母顺序排序:

data = [
('betty', 1),
('bought', 1),
('a', 1),
('bit', 1),
('of', 1),
('butter', 2),
('but', 1),
('the', 1),
('was', 1),
('bitter', 1)]

我使用这种方法:

sorted(data, key=lambda tup:(-tup[1], tup[0]))

并且它给我了结果:

[('butter', 2),
('a', 1),
('betty', 1),
('bit', 1),
('bitter', 1),
('bought', 1),
('but', 1),
('of', 1),
('the', 1),
('was', 1)]

1
如果tup [1]是一个字符串呢? - eric

16

没有 lambda:

def sec_elem(s):
    return s[1]

sorted(data, key=sec_elem)

11

itemgetter()lambda tup: tup[1]略微更快,但其增速相对较小(大约为10%至25%)。

(IPython会话)

>>> from operator import itemgetter
>>> from numpy.random import randint
>>> values = randint(0, 9, 30000).reshape((10000,3))
>>> tpls = [tuple(values[i,:]) for i in range(len(values))]

>>> tpls[:5]    # display sample from list
[(1, 0, 0), 
 (8, 5, 5), 
 (5, 4, 0), 
 (5, 7, 7), 
 (4, 2, 1)]

>>> sorted(tpls[:5], key=itemgetter(1))    # example sort
[(1, 0, 0), 
 (4, 2, 1), 
 (5, 4, 0), 
 (8, 5, 5), 
 (5, 7, 7)]

>>> %timeit sorted(tpls, key=itemgetter(1))
100 loops, best of 3: 4.89 ms per loop

>>> %timeit sorted(tpls, key=lambda tup: tup[1])
100 loops, best of 3: 6.39 ms per loop

>>> %timeit sorted(tpls, key=(itemgetter(1,0)))
100 loops, best of 3: 16.1 ms per loop

>>> %timeit sorted(tpls, key=lambda tup: (tup[1], tup[0]))
100 loops, best of 3: 17.1 ms per loop

1
请看此处关于itemgetter排序解决多列不同反向参数的方案,您需要按顺序多步骤进行排序: https://dev59.com/7mYq5IYBdhLWcg3weAWj - questionto42

7

@Stephen的回答很到位!这里有一个例子可以更好地进行可视化,

向《头号玩家》的粉丝们大喊一声! =)

>>> gunters = [('2044-04-05', 'parzival'), ('2044-04-07', 'aech'), ('2044-04-06', 'art3mis')]
>>> gunters.sort(key=lambda tup: tup[0])
>>> print gunters
[('2044-04-05', 'parzival'), ('2044-04-06', 'art3mis'), ('2044-04-07', 'aech')]

key是一个函数,用于转换集合中的项目以进行比较。就像Java中的compareTo方法。

传递给key的参数必须是可调用的。在这里,使用lambda创建了一个匿名函数(它是可调用的)。
Lambda的语法是单词lambda,然后是一个可迭代的名称,然后是一块单独的代码。

下面的示例中,我们正在对包含某个事件和演员名称的时间信息元组列表进行排序。

我们正在按事件发生的时间(元组的第0个元素)对此列表进行排序。

注意 - s.sort([cmp[, key[, reverse]]])会就地对s的项目进行排序。


1
我在我的代码中使用这个:

#To sort the list based on each element's second integer (elem[1])
sorted(d2, key=lambda elem: elem[1])

根据您想要排序的元素,您可以将其放入

(elem[*insert the index of the element you are sorting it by*])

1
sorted creates new list. To do in-place sorting use .sort(key=...) - Shital Shah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接