如何使用两个键对列表进行排序,但其中一个键的顺序相反?
How to sort a list with two keys but one in reverse order?
我想知道什么是通过两个键对元组列表进行排序的 Pythonic 方式,其中使用一个(且只有一个)键排序将以相反的顺序进行,而使用另一个键排序将不区分大小写。
更具体地说,我有一个包含如下元组的列表:
myList = [(ele1A, ele2A),(ele1B, ele2B),(ele1C, ele2C)]
我可以用下面的代码用两个键对它进行排序:
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))
要倒序排序我可以使用
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]), reverse = True)
但是这会用两个键以相反的顺序排序。
当我们需要对具有两个约束的列表进行排序时,将使用两个键:一个按升序排列,另一个按降序排列,在同一列表或任何
在你的例子中,
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))
您只能按一个顺序对整个列表进行排序。
您可以尝试这些并检查发生了什么:
sortedList = sorted(myList, key = lambda y: (y[0].lower(), -y[1]))
sortedList = sorted(myList, key = lambda y: (-y[0].lower(), y[1]))
sortedList = sorted(myList, key = lambda y: (-y[0].lower(), -y[1]))
有时别无选择,只能使用比较器函数。 sorted
在 2.4 的介绍中有一个 cmp
参数,但它已从 Python 3 中删除,以支持更高效的 key
函数。 3.2中,cmp_to_key
加入functools
;它通过将原始对象包装在一个对象中来创建键,该对象的比较功能基于 cmp
函数。 (您可以在 end of the Sorting How-To
处看到 cmp_to_key
的简单定义
在你的情况下,由于小写字母相对昂贵,你可能想做一个组合:
class case_insensitive_and_2nd_reversed:
def __init__(self, obj, *args):
self.first = obj[0].lower()
self.second = obj[1]
def __lt__(self, other):
return self.first < other.first or self.first == other.first and other.second < self.second
def __gt__(self, other):
return self.first > other.first or self.first == other.first and other.second > self.second
def __le__(self, other):
return self.first < other.first or self.first == other.first and other.second <= self.second
def __ge__(self, other):
return self.first > other.first or self.first == other.first and other.second >= self.second
def __eq__(self, other):
return self.first == other.first and self.second == other.second
def __ne__(self, other):
return self.first != other.first and self.second != other.second
sortedList = sorted(myList, key = case_insensitive_and_2nd_reversed)
方法一
一个简单但可能不是最有效的解决方案是排序两次:第一次使用第二个元素,第二次使用第一个元素:
sortedList = sorted(sorted(myList, key=lambda (a,b):b, reverse=True), key=lambda(a,b):a)
或细分:
tempList = sorted(myList, key=lambda (a,b):b, reverse=True)
sortedList = sorted(tempList, key=lambda(a,b):a))
方法二
如果你的元素是数字,你可以稍微作弊:
sorted(myList, key=lambda(a,b):(a,1.0/b))
方法三
我建议不要使用这种方法,因为它很乱,而且 cmp
关键字在 Python 3.
中不可用
另一种方法是在比较元素时交换元素:
def compare_func(x, y):
tup1 = (x[0], y[1])
tup2 = (x[1], y[0])
if tup1 == tup2:
return 0
elif tup1 > tup2:
return 1
else:
return -1
sortedList = sorted(myList, cmp=compare_func)
或者,使用 lambda 来避免写函数:
sortedList = sorted(
myList,
cmp=lambda (a1, b1), (a2, b2): 0 if (a1, b2) == (a2, b1) else 1 if (a1, b2) > (a2, b1) else -1
)
您可以创建一个反向器 class 并用它来装饰有问题的密钥。此 class 可用于反转任何可比较的字段。
class reversor:
def __init__(self, obj):
self.obj = obj
def __eq__(self, other):
return other.obj == self.obj
def __lt__(self, other):
return other.obj < self.obj
像这样使用它:
sortedList = sorted(myList, key=lambda y: (y[0].lower(), reversor(y[1])))
可能是优雅但效率不高的方式:
reverse_key = functools.cmp_to_key(lambda a, b: (a < b) - (a > b))
sortedList = sorted(myList, key = lambda y: (reverse_key(y[0].lower()), y[1]))
当使用Python 3 时,@KellyBundy made an excellent observation that the multisort method listed in the current python docs 非常快,用于完成 multi-colum 离散排序排序。这是一个 NoneType
安全版本:
students = [
{'idx': 0, 'name': 'john', 'grade': 'A', 'attend': 100}
,{'idx': 1, 'name': 'jane', 'grade': 'B', 'attend': 80}
,{'idx': 2, 'name': 'dave', 'grade': 'B', 'attend': 85}
,{'idx': 3, 'name': 'stu' , 'grade': None, 'attend': 85}
]
def key_grade(student):
grade = student['grade']
return grade is None, grade
def key_attend(student):
attend = student['attend']
return attend is None, attend
students_sorted = sorted(students, key=key_attend)
students_sorted.sort(key=key_grade, reverse=True)
备注:
是 None,检查是防御性检查,因此搜索不会在 None 值 上失败
- 尽管这会进行多次排序调用,但它无疑是最快的 multi-sort 方法!
我创建了一个名为 multisort 的新 Python 项目,它公开了三种方法:
Method
Descr
Notes
speed
multisort
Simple one-liner designed after multisort
example from python docs
Second fastest of the bunch but most configurable and easy to read.
0.0035
cmp_func
Multi column sorting in the model java.util.Comparator
Reasonable speed
0.0138
reversor
Implementation of reversor - See 's answer
Pretty slow methodology
0.0370
供参考:
Method
speed
KellyBundy's Multisort
0.0005
pandas
0.0079
注意:速度是 1000 行 4 列的 10 次运行的平均值。
来自 multisort
library 的 multisort
示例:
from multisort import multisort
rows_sorted = multisort(rows_dict, [
('grade', True, lambda s:None if s is None else s.upper()),
'attend',
], reverse=True)
但是,对于来自 Java 的开发人员,这里有一个类似于 java.util.Comparator
的示例,用于 Python 3:
from multisort import cmp_func
def cmp_student(a,b):
k='grade'; va=a[k]; vb=b[k]
if va != vb:
if va is None: return -1
if vb is None: return 1
return -1 if va > vb else 1
k='attend'; va=a[k]; vb=b[k];
if va != vb:
return -1 if va < vb else 1
return 0
students_sorted = sorted(students, key=cmp_func(cmp_student))
我想知道什么是通过两个键对元组列表进行排序的 Pythonic 方式,其中使用一个(且只有一个)键排序将以相反的顺序进行,而使用另一个键排序将不区分大小写。 更具体地说,我有一个包含如下元组的列表:
myList = [(ele1A, ele2A),(ele1B, ele2B),(ele1C, ele2C)]
我可以用下面的代码用两个键对它进行排序:
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))
要倒序排序我可以使用
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]), reverse = True)
但是这会用两个键以相反的顺序排序。
当我们需要对具有两个约束的列表进行排序时,将使用两个键:一个按升序排列,另一个按降序排列,在同一列表或任何
在你的例子中,
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))
您只能按一个顺序对整个列表进行排序。
您可以尝试这些并检查发生了什么:
sortedList = sorted(myList, key = lambda y: (y[0].lower(), -y[1]))
sortedList = sorted(myList, key = lambda y: (-y[0].lower(), y[1]))
sortedList = sorted(myList, key = lambda y: (-y[0].lower(), -y[1]))
有时别无选择,只能使用比较器函数。 sorted
在 2.4 的介绍中有一个 cmp
参数,但它已从 Python 3 中删除,以支持更高效的 key
函数。 3.2中,cmp_to_key
加入functools
;它通过将原始对象包装在一个对象中来创建键,该对象的比较功能基于 cmp
函数。 (您可以在 end of the Sorting How-To
cmp_to_key
的简单定义
在你的情况下,由于小写字母相对昂贵,你可能想做一个组合:
class case_insensitive_and_2nd_reversed:
def __init__(self, obj, *args):
self.first = obj[0].lower()
self.second = obj[1]
def __lt__(self, other):
return self.first < other.first or self.first == other.first and other.second < self.second
def __gt__(self, other):
return self.first > other.first or self.first == other.first and other.second > self.second
def __le__(self, other):
return self.first < other.first or self.first == other.first and other.second <= self.second
def __ge__(self, other):
return self.first > other.first or self.first == other.first and other.second >= self.second
def __eq__(self, other):
return self.first == other.first and self.second == other.second
def __ne__(self, other):
return self.first != other.first and self.second != other.second
sortedList = sorted(myList, key = case_insensitive_and_2nd_reversed)
方法一
一个简单但可能不是最有效的解决方案是排序两次:第一次使用第二个元素,第二次使用第一个元素:
sortedList = sorted(sorted(myList, key=lambda (a,b):b, reverse=True), key=lambda(a,b):a)
或细分:
tempList = sorted(myList, key=lambda (a,b):b, reverse=True)
sortedList = sorted(tempList, key=lambda(a,b):a))
方法二
如果你的元素是数字,你可以稍微作弊:
sorted(myList, key=lambda(a,b):(a,1.0/b))
方法三
我建议不要使用这种方法,因为它很乱,而且 cmp
关键字在 Python 3.
另一种方法是在比较元素时交换元素:
def compare_func(x, y):
tup1 = (x[0], y[1])
tup2 = (x[1], y[0])
if tup1 == tup2:
return 0
elif tup1 > tup2:
return 1
else:
return -1
sortedList = sorted(myList, cmp=compare_func)
或者,使用 lambda 来避免写函数:
sortedList = sorted(
myList,
cmp=lambda (a1, b1), (a2, b2): 0 if (a1, b2) == (a2, b1) else 1 if (a1, b2) > (a2, b1) else -1
)
您可以创建一个反向器 class 并用它来装饰有问题的密钥。此 class 可用于反转任何可比较的字段。
class reversor:
def __init__(self, obj):
self.obj = obj
def __eq__(self, other):
return other.obj == self.obj
def __lt__(self, other):
return other.obj < self.obj
像这样使用它:
sortedList = sorted(myList, key=lambda y: (y[0].lower(), reversor(y[1])))
可能是优雅但效率不高的方式:
reverse_key = functools.cmp_to_key(lambda a, b: (a < b) - (a > b))
sortedList = sorted(myList, key = lambda y: (reverse_key(y[0].lower()), y[1]))
当使用Python 3 时,@KellyBundy made an excellent observation that the multisort method listed in the current python docs 非常快,用于完成 multi-colum 离散排序排序。这是一个 NoneType
安全版本:
students = [
{'idx': 0, 'name': 'john', 'grade': 'A', 'attend': 100}
,{'idx': 1, 'name': 'jane', 'grade': 'B', 'attend': 80}
,{'idx': 2, 'name': 'dave', 'grade': 'B', 'attend': 85}
,{'idx': 3, 'name': 'stu' , 'grade': None, 'attend': 85}
]
def key_grade(student):
grade = student['grade']
return grade is None, grade
def key_attend(student):
attend = student['attend']
return attend is None, attend
students_sorted = sorted(students, key=key_attend)
students_sorted.sort(key=key_grade, reverse=True)
备注:
是 None,检查是防御性检查,因此搜索不会在 None 值 上失败
- 尽管这会进行多次排序调用,但它无疑是最快的 multi-sort 方法!
我创建了一个名为 multisort 的新 Python 项目,它公开了三种方法:
Method | Descr | Notes | speed |
---|---|---|---|
multisort | Simple one-liner designed after multisort example from python docs |
Second fastest of the bunch but most configurable and easy to read. | 0.0035 |
cmp_func | Multi column sorting in the model java.util.Comparator |
Reasonable speed | 0.0138 |
reversor | Implementation of reversor - See |
Pretty slow methodology | 0.0370 |
供参考:
Method | speed |
---|---|
KellyBundy's Multisort | 0.0005 |
pandas | 0.0079 |
注意:速度是 1000 行 4 列的 10 次运行的平均值。
来自 multisort
library 的 multisort
示例:
from multisort import multisort
rows_sorted = multisort(rows_dict, [
('grade', True, lambda s:None if s is None else s.upper()),
'attend',
], reverse=True)
但是,对于来自 Java 的开发人员,这里有一个类似于 java.util.Comparator
的示例,用于 Python 3:
from multisort import cmp_func
def cmp_student(a,b):
k='grade'; va=a[k]; vb=b[k]
if va != vb:
if va is None: return -1
if vb is None: return 1
return -1 if va > vb else 1
k='attend'; va=a[k]; vb=b[k];
if va != vb:
return -1 if va < vb else 1
return 0
students_sorted = sorted(students, key=cmp_func(cmp_student))