>>> lst
[('BFD', 0), ('NORTHLANDER', 3), ('HP', 23), ('VOLT', 3)]
>>> min([x for x in lst if x[1]!=0], key=lambda x: x[1])
('NORTHLANDER', 3)
>>>
这里 min() 只返回一组。它实际上应该返回:
[('NORTHLANDER', 3), ('VOLT', 3)]
有什么内置功能可以达到这种效果吗?
这是一个简单的两步解决方案,首先计算最小值,然后收集所有具有最小值的元组,因此请编写自己的函数来执行此操作。这是一个相当专业的操作,而不是通用min()
功能所期望的。
找到具有最小值的元素:
>>> lstm = min([x for x in lst if x[1] > 0], key = lambda x: x[1])
>>> lstm
('NORTHLANDER', 3)
然后只需形成一个新列表,list
其中的值是 of 的元素lstm
:
>>> [y for y in lst if y[1] == lstm[1]]
[('NORTHLANDER', 3), ('VOLT', 3)]
使用collections.defaultdict
:
d=collections.defaultdict(list)
for item in lst:
d[item[1]].append(item)
d[min(key for key in d.keys() if key!=0)]
出去:
[('NORTHLANDER', 3), ('VOLT', 3)]
测试:
#unwind's solution
def f(lst):
return [y for y in lst if y[1] == min([x for x in lst if x[1] > 0],
key = lambda x: x[1])[1]]
def f2(lst):
d=collections.defaultdict(list)
for item in lst:
d[item[1]].append(item)
return d[min(key for key in d.keys() if key!=0)]
%timeit f(lst)
100000 loops, best of 3: 12.1 us per loop
%timeit f2(lst)
100000 loops, best of 3: 5.42 us per loop
所以,defaultdict
似乎快了两倍多。
编辑 @martineau 优化:
def f3(lst):
lstm = min((x for x in lst if x[1]), key = lambda x: x[1])[1]
return [y for y in lst if y[1] == lstm]
%timeit f3(lst)
100000 loops, best of 3: 4.19 us per loop
另一个dict
基于解决方案的使用set.default
甚至更快:
def f4(lst):
d={}
for item in lst:
if item[1] != 0:
d.setdefault(item[1],{})[item]=0
return d[min(d.keys())].keys()
%timeit f4(lst)
100000 loops, best of 3: 3.76 us per loop
您可以将自己的 multimin 函数编写为:
def multimin(seq, key=None):
if key is None:
key = lambda x: x
min_e = min(seq, key=key)
return filter((lambda x: key(x) == key(min_e)), seq)
例如:
>>> lst = [('BFD', 0), ('NORTHLANDER', 3), ('HP', 23), ('VOLT', 3)]
>>> print multimin([x for x in lst if x[1]!=0], key=lambda x: x[1])
[('NORTHLANDER', 3), ('VOLT', 3)]
集合。计数器?它专为处理类似数据而设计:
# Instantiate excluding zero length items
>>> c = collections.Counter({k: v for (k, v) in lst if v != 0})
>>> c
Counter({'HP': 23, 'NORTHLANDER': 3, 'VOLT': 3})
# Retrieve most common then reverse it
>>> least_common = c.most_common()[::-1]
[('VOLT', 3), ('NORTHLANDER', 3), ('HP', 23)]
>>> [(k,v) for (k,v) in least_common if v == least_common[0][1]]
[('VOLT', 3), ('NORTHLANDER', 3)]
(这只是一个想法,并不打算高效)