使用自定义比较功能对列表列表进行排序
Posted
技术标签:
【中文标题】使用自定义比较功能对列表列表进行排序【英文标题】:Sort a list of lists with a custom compare function 【发布时间】:2011-07-09 22:34:36 【问题描述】:我知道有几个这样命名的问题,但它们似乎对我不起作用。
我有一个列表,50 乘以 5 个元素。我想通过对每个元素应用自定义比较函数来对该列表进行排序。此函数计算元素排序所依据的列表的适应度。我创建了两个函数,比较和适应度:
def compare(item1, item2):
return (fitness(item1) < fitness(item2))
和
def fitness(item):
return item[0]+item[1]+item[2]+item[3]+item[4]
然后我尝试通过以下方式给他们打电话:
sorted(mylist, cmp=compare)
或
sorted(mylist, key=fitness)
或
sorted(mylist, cmp=compare, key=fitness)
或
sorted(mylist, cmp=lambda x,y: compare(x,y))
我还尝试了 list.sort() 使用相同的参数。但在任何情况下,函数都不会将列表作为参数,而是None
。我不知道为什么会这样,主要来自 C++,这与我对回调函数的任何想法相矛盾。如何使用自定义函数对列表进行排序?
编辑 我发现了我的错误。在创建原始列表的链中,一个函数没有返回任何内容,但使用了返回值。抱歉打扰了
【问题讨论】:
显示代码,你期望什么,你得到什么。 请注意您的compare
函数不正确,因为它只返回True 或False,并且不区分item1
和item2
是否相等和item1
大于@987654332 @。编写compare
的正确方法是返回cmp(fitness(item1), fitness(item2))
。但是使用key
更好。
cmp
关键字已在 Python 3 中删除。您现在可以在导入 functools
后使用 key=functools.cmp_to_key(<function>)
。
【参考方案1】:
另外,您的比较功能不正确。它需要返回 -1、0 或 1,而不是你所拥有的布尔值。正确的比较函数是:
def compare(item1, item2):
if fitness(item1) < fitness(item2):
return -1
elif fitness(item1) > fitness(item2):
return 1
else:
return 0
# Calling
list.sort(key=compare)
【讨论】:
或者只是return fitness(item1) - fitness(item2)
。比较函数不必返回 -1 或 1,而只需返回负数或正数(或零)。参考:docs.python.org/2/library/stdtypes.html#mutable-sequence-types
sorted(myList, key=lambda x: -fitness(x))
或sorted(myList, key=fitness, reverse=True)
对于 py3,使用key=functools.cmp_to_key(compare)
。查看 JustAC0der 的回答
@LarsH,对不起。你说的对。我一定很累,看不到那个。【参考方案2】:
由于 OP 要求使用自定义比较功能(这也是我提出这个问题的原因),我想在这里给出一个可靠的答案:
通常,您希望使用内置的sorted()
函数,该函数将自定义比较器作为其参数。我们需要注意的是,在 Python 3 中参数名称和语义发生了变化。
自定义比较器的工作原理
在提供自定义比较器时,它通常应返回遵循以下模式的整数/浮点值(与大多数其他编程语言和框架一样):
返回负值 (< 0
) 当左侧项目应排在右侧项目之前 右侧项目
当左侧项目应排在右侧项目之后 右侧项目时,返回一个正值 (> 0
)
返回0
,当左右物品重量相同且应“平等”订购时,无先后顺序
在 OP 问题的特定情况下,可以使用以下自定义比较功能:
def compare(item1, item2):
return fitness(item1) - fitness(item2)
使用减法运算是一个绝妙的技巧,因为当左侧item1
的权重大于右侧item2
的权重时,它会产生正值。因此item1
将被排序在之后 item2
。
如果要反转排序顺序,只需反转减法即可:return fitness(item2) - fitness(item1)
在 Python 2 中调用 sorted()
sorted(mylist, cmp=compare)
或:
sorted(mylist, cmp=lambda item1, item2: fitness(item1) - fitness(item2))
在 Python 3 中调用 sorted()
from functools import cmp_to_key
sorted(mylist, key=cmp_to_key(compare))
或:
from functools import cmp_to_key
sorted(mylist, key=cmp_to_key(lambda item1, item2: fitness(item1) - fitness(item2)))
【讨论】:
请注意,cmp_to_key
用于将 Python 2 样式 compare()
函数更改为 Python 3 所需的样式。
它不仅用于替换 Python 2 样式,cmp_to_key
还允许提供一个比较器,可以直接比较两个元素,这对于那些你不能或不能的情况很方便想要直接将列表元素映射到一个值。
这是最完整的,应该被接受的答案。【参考方案3】:
您需要稍微修改您的compare
函数并使用functools.cmp_to_key
将其传递给sorted
。示例代码:
import functools
lst = [list(range(i, i+5)) for i in range(5, 1, -1)]
def fitness(item):
return item[0]+item[1]+item[2]+item[3]+item[4]
def compare(item1, item2):
return fitness(item1) - fitness(item2)
sorted(lst, key=functools.cmp_to_key(compare))
输出:
[[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9]]
作品:)
【讨论】:
【参考方案4】:>>> l = [list(range(i, i+4)) for i in range(10,1,-1)]
>>> l
[[10, 11, 12, 13], [9, 10, 11, 12], [8, 9, 10, 11], [7, 8, 9, 10], [6, 7, 8, 9], [5, 6, 7, 8], [4, 5, 6, 7], [3, 4, 5, 6], [2, 3, 4, 5]]
>>> sorted(l, key=sum)
[[2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8], [6, 7, 8, 9], [7, 8, 9, 10], [8, 9, 10, 11], [9, 10, 11, 12], [10, 11, 12, 13]]
以上工作。你在做不同的事情吗?
请注意,您的关键功能只是sum
;不需要明确写出来。
【讨论】:
你说得对,是其他代码导致了这个错误,谢谢。再次感谢您,至少现在我有一个使用函数而不是 lambda 表达式作为键值的示例。之前找解决方案的时候没找到。 不回答问题。问题要求自定义比较器,没有显示。【参考方案5】:我偶然发现了这个线程,以通过比较器函数对列表列表进行排序。对于 Python 新手或具有 C++ 背景的任何人。我们想复制到这里使用回调函数,如 c++。我用 sorted() 函数试过这个。
例如:如果我们想根据标记(升序)对这个列表进行排序,如果标记相等,则名称(升序)
students= [['Harry', 37.21], ['Berry', 37.21], ['Tina', 37.2], ['Akriti', 41.0], ['Harsh', 39.0]]
def compare(e):
return (e[1],e[0])
students = sorted(students,key=compare)
排序后:
[['Tina', 37.2], ['Berry', 37.21], ['Harry', 37.21], ['Harsh', 39.0], ['Akriti', 41.0]]
【讨论】:
【参考方案6】:查看它的一种简单方法是 Python 中的 sorted()
(或 list.sort()
)函数一次只对一个键进行操作。它通过列表元素一次构建一个键列表。然后,它会确定哪个键更大或更小,并将它们按正确的顺序排列。
因此,正如我发现的那样,解决方案是制作一个提供正确顺序的密钥。在这里,Python 可以将密钥用作str
或tuple
。这不需要像其他示例中的 functools
模块:
# task: sort the list of strings, such that items listed as '_fw' come before '_bw'
foolist = ['Goo_fw', 'Goo_bw', 'Foo_fw', 'Foo_bw', 'Boo_fw', 'Boo_bw']
def sortfoo(s):
s1, s2 = s.split('_')
r = 1 if s2 == 'fw' else 2 # forces 'fw' to come before 'bw'
return (r, s1) # order first by 'fw'/'bw', then by name
foolist.sort(key=sortfoo) # sorts foolist inplace
print(foolist)
# prints:
# ['Boo_fw', 'Foo_fw', 'Goo_fw', 'Boo_bw', 'Foo_bw', 'Goo_bw']
这是有效的,因为元组是用于排序的合法键。这可以根据您的需要进行自定义,其中不同的排序元素按照排序的重要性顺序简单地堆叠到这个元组中。
【讨论】:
以上是关于使用自定义比较功能对列表列表进行排序的主要内容,如果未能解决你的问题,请参考以下文章
Android - 是不是可以使用 Comparator.comparing 而不是 API < 24 上的自定义比较器对列表进行排序?