按两个字段对Python列表进行排序


172

我有一个从排序的csv创建的以下列表

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

我实际上想按两个条件对列表进行排序:首先按字段1中的值,然后按字段2中的值。我该怎么做?



我们是否让这个问题站起来,而仅将其范围限制为“长度为两个内建类型的列表列表(例如,string / int / float)”。还是我们也允许“用户定义的对象列表”(如标题所建议的那样),在这种情况下,答案是__lt__()在您的类上定义方法或从某个类定义的类继承”?那将使其成为更好的规范。
smci

Answers:


157

像这样:

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

1
+1:比我的优雅。我忘了itemgetter可以采用多个索引。
dappawit 2011年

7
operator是需要导入的模块。
trapicki

3
如果要使用itemgetter在一个元素上升序排序而在另一个元素上降序排序,我将如何进行?
ashish 2013年

3
@ashish,请参阅下面关于lambda函数的答案,这很清楚,如果需要,可以按“ -x [1]”甚至“ x [0] + x [1]”进行
排序

如果一个条件处于反向模式怎么办?
YaserKH

328

使用lambda函数时无需导入任何内容。
以下list按第一个元素排序,然后按第二个元素排序。

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

12
真好 正如您在上面的主要答案的注释中指出的那样,这是对不同排序顺序进行多种排序的最佳方法(唯一?)。也许要强调这一点。另外,您的文字并不表示您在第二个元素上按降序排列。
PeterVermont 2015年

2
@ user1700890我以为该字段已经是字符串。默认情况下,它应该按字母顺序对字符串排序。如果与此处的答案或OP的原始问题没有特别关系,则应在SO上单独发布自己的问题。
可能

5
什么是--x[1]立场?
2016年1

7
@jan是一种反向排序
jaap,2016年

3
在特定情况下不起作用。接受的解决方案也不起作用。例如,用作键的列是所有不能转换为数字的字符串。其次,一个人想按升序排列,一列按降序排列。
coder.in.me,2016年

20

Python具有稳定的排序方式,因此,只要性能不成问题,最简单的方法就是按字段2对其进行排序,然后再次按字段1对其进行排序。

这将为您提供所需的结果,唯一的陷阱是,如果列表很大(或者您希望经常对其进行排序),则两次调用sort可能是不可接受的开销。

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

这样一来,还可以轻松处理需要对某些列进行反向排序的情况,只需在必要时添加'reverse = True'参数即可。

否则,您可以将多个参数传递给itemgetter或手动构建一个元组。这可能会更快一些,但是有一个问题,就是如果某些列想要反向排序,它不能很好地推广(数字列仍然可以通过取反来反转,但是这会使排序保持稳定)。

因此,如果您不需要对任何列进行反向排序,则可以向itemgetter输入多个参数(如果可能),并且这些列不是数字的,或者您希望保持排序稳定以进行多个连续排序。

编辑:对于在理解此答案的原始方式时遇到问题的评论者,以下示例准确显示了排序的稳定性,从而确保了我们可以对每个键进行单独的排序并最终对多个条件下的数据进行排序:

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

这是一个可运行的示例,但是为了节省运行它的人员,输出为:

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

特别要注意的是,在第二步中,reverse=True参数如何按顺序保留名字,而仅对列表进行排序然后反转,则会丢失第三个排序键的期望顺序。


1
稳定的排序并不意味着它不会忘记您之前的排序。这个答案是错误的。
Mike Axiak 2011年

7
稳定的排序意味着您可以简单地按a,b,c列进行排序,只需按c列然后按b然后再按a进行排序即可。除非您想扩大评论范围,否则我认为是您误会了。
邓肯

7
这个答案绝对是正确的,尽管对于较大的列表而言,这是不理想的:如果列表已经部分排序,那么通过将列表改组更多,您将失去Python排序的大部分优化。@Mike,你不正确;我建议在声明错误之前先实际测试答案。
Glenn Maynard'3

6
@MikeAxiak:docs.python.org/2/library/stdtypes.html#index-29在注释9中指出:从Python 2.3开始,sort()方法保证是稳定的。如果可以保证不更改比较相等的元素的相对顺序,则排序是稳定的-这有助于多次通过排序(例如,按部门排序,然后按薪级等级排序)。
trapicki

这是不正确的,因为这不能回答他提出的问题。他想要一个按第一个索引排序的列表,并且在第一个索引中存在联系的情况下,他想使用第二个索引作为排序标准。稳定的排序只能保证所有事物都是平等的,传递的原始顺序将是商品出现的顺序。
2015年

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

4
我认为不会tuple()收到两个论点(或者,如果您self
用来

3
元组只接受一个论点
realprashant 2015年

1
return声明应该是return tuple((x[1], x[2]))或简单return x[1], x[2]。 如果您正在寻找不同方向的排序,请参考下面的@jaap答案
Jo Kachikaran

…或者tuple(x[1:3]),如果出于某种原因要使用元组构造函数,而不仅仅是元组显示列表x[1], x[2]。或者keyfunc = operator.itemgetter(1, 2)甚至不要自己编写函数。
abarnert

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

我们也可以将.sort与lambda一起使用2次,因为python sort到位且稳定。这将首先根据第二个元素x [1]对列表进行排序。然后,它将对第一个元素x [0](最高优先级)进行排序。

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

这等效于执行以下操作:employee.sort(key = lambda x:(x [0],x [1]))


1
不,此排序规则需要优先于第二。
CodeFarmer


0

使用下面的字典排序列表将以降序对列表进行排序,第一列为薪水,第二列为年龄

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

输出:[{'salary':123,'age':25},{'salary':123,'age':23}]

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.