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

Posted

技术标签:

【中文标题】按两个字段对 Python 列表进行排序【英文标题】:Sorting a Python list by two fields 【发布时间】:2011-07-09 22:20:45 【问题描述】:

我从排序的 csv 创建了以下列表

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

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

【问题讨论】:

Sort a list by multiple attributes?的可能重复 我们是否让这个问题保持不变,并将其范围限制在 "list-of-lists-of-length-two-builtin-types (eg string/int/float)"我>。或者我们是否也允许"list-of-user-defined-object",正如标题所暗示的那样也是允许的,在这种情况下,答案是"Define __lt__() method on your class或从某个类继承“?这将使它成为一个更好的规范。 【参考方案1】:

使用 lambda 函数时无需导入任何内容。 下面按第一个元素对list 进行排序,然后按第二个元素排序。您还可以按一个字段升序和另一个降序排序,例如:

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

【讨论】:

不错。正如您在对上述主要答案的评论中指出的那样,这是使用不同排序顺序进行多种排序的最佳(唯一?)方法。也许强调这一点。此外,您的文本并不表示您按第二个元素降序排序。 @user1700890 我假设该字段已经是字符串。默认情况下,它应该按字母顺序对字符串进行排序。如果与此处的答案或 OP 的原始问题没有特别相关,您应该在 SO 上单独发布您自己的问题。 -x[1] 中的- 代表什么? @jan 这是反向排序 在一种特定情况下不起作用。接受的解决方案也不起作用。例如,要用作键的列都是不能转换为数字的字符串。其次,要按一列升序排序,另一列降序排序。【参考方案2】:

在阅读了该线程中的答案后,我编写了一个适用于任意数量列的通用解决方案:

def sort_array(array, *columns):
    for col in columns:
        array.sort(key = lambda x:x[col])

OP 会这样称呼它:

sort_array(list1, 2, 1)

首先按第 2 列排序,然后按第 1 列排序。 (最重要的列放在最后)

【讨论】:

【参考方案3】:

蟒蛇3 https://docs.python.org/3.5/howto/sorting.html#the-old-way-using-the-cmp-parameter

from functools import cmp_to_key

def custom_compare(x, y):
    # custom comparsion of x[0], x[1] with y[0], y[1]
    return 0

sorted(entries, key=lambda e: (cmp_to_key(custom_compare)(e[0]), e[1]))

【讨论】:

【参考方案4】:
list1 = sorted(csv1, key=lambda x: (x[1], x[2]) )

【讨论】:

我认为tuple() 不能接收两个参数(或者更确切地说,三个参数,如果你用self 计算的话) 元组只接受一个参数 return 语句应该是 return tuple((x[1], x[2])) 或简单的 return x[1], x[2]。 Refer@jaap 如果您正在寻找不同方向的排序,请在下面回答 ... 或tuple(x[1:3]),如果您出于某种原因想要使用元组构造函数,而不仅仅是一个元组显示列表x[1], x[2]。或者keyfunc = operator.itemgetter(1, 2) 甚至不要自己编写函数。 我可以这样做吗,list1 = sorted(csv1, key=lambda x: x[1] and x[2] )?如果不是,在这种情况下会是什么行为?【参考方案5】:
employees.sort(key = lambda x:x[1])
employees.sort(key = lambda x:x[0])

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

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

这相当于执行以下操作: employees.sort(key = lambda x:(x[0], x[1]))

【讨论】:

不,这个排序规则需要优先于第二。【参考方案6】:

您可以按升序使用:

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

或者你可以按降序使用:

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

【讨论】:

【参考方案7】:

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

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]

【讨论】:

【参考方案8】:

像这样:

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

【讨论】:

+1:比我的更优雅。我忘记了 itemgetter 可以采用多个索引。 operator是需要导入的模块。 如果我想使用 itemgetter 对一个元素进行升序排序并在另一个元素上降序排序,我将如何进行?。 @ashish,请参阅下面的 lambda 函数答案,这很清楚,如果您愿意,可以按“-x[1]”甚至“x[0]+x[1]”排序 如果一个条件处于反转模式会怎样?【参考方案9】:

Python 有一个稳定的排序,所以如果性能不是问题,最简单的方法是按字段 2 排序,然后按字段 1 再次排序。

这将为您提供您想要的结果,唯一的问题是,如果它是一个大列表(或者您想经常对其进行排序),则调用两次排序可能是不可接受的开销。

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 参数如何保持名字的顺序,而简单地排序然后反转列表会丢失第三个排序键的所需顺序。

【讨论】:

稳定排序并不意味着它不会忘记你之前的排序是什么。这个答案是错误的。 稳定排序意味着你可以通过a、b、c列排序,只需按c列排序,b列排序,a列排序。除非您愿意扩展您的评论,否则我认为是您错了。 这个答案绝对是正确的,尽管对于较大的列表它是不理想的:如果列表已经部分排序,那么您将失去 Python 排序的大部分优化,因为要对列表进行更多的洗牌。 @Mike,你错了;我建议在宣布错误之前实际测试答案。 @MikeAxiak: docs.python.org/2/library/stdtypes.html#index-29 在评论 9 中声明:从 Python 2.3 开始,sort() 方法保证是稳定的。如果保证不改变比较相等的元素的相对顺序,则排序是稳定的 - 这有助于多次排序(例如,按部门排序,然后按薪级排序)。我> 这是不正确的,因为这并不能回答他提出的问题。他想要一个按第一个索引排序的列表,并且在第一个索引中有关系的情况下,他想使用第二个索引作为排序标准。稳定的排序只保证所有的东西都是平等的,原来传递的顺序就是项目出现的顺序。

以上是关于按两个字段对 Python 列表进行排序的主要内容,如果未能解决你的问题,请参考以下文章

使用mysql按两个字段对结果顺序进行排序

如何按两个元素对元组列表进行排序?

同张表中同时查询两个字段显示一个字段,对两个字段进行按时间排序

如何按两个字段对 Firebase 记录进行排序(Android)[重复]

如何按两个字段对 Firebase 记录进行排序(Android)[重复]

如何按两个字段对 Firebase 记录进行排序(Android)[重复]