输入是未排序的元组列表:
x = [('herr', 1),
('dapao', 1),
('cino', 1),
('o', 38),
('tiao', 2),
('tut', 1),
('poh', 6),
('micheal', 1),
('orh', 1),
('horlick', 3),
('si', 1),
('tai', 1),
('titlo', 1),
('siew', 17),
('da', 1),
('halia', 2)]
目标是找到计数最少的最后n
个键,即所需的输出:
['orh', 'si', 'tai', 'titlo', 'da']
我试过这样做:
[-n:]
Counter.most_common()
元组列表
[-n:]
转换为dict 即。
n = 5
list(dict(Counter(dict(x)).most_common()[-n:]).keys())
是否有一种不那么复杂的方法来获得相同的输出?
我也可以这样做:
from operator import itemgetter
output, *_ = zip(*sorted(x, key=itemgetter(1))[n:])
list(output)
但现在我只是用Counter.most_common
和sorted
替换了itemgetter
。然后我仍然需要zip(*list)
通过从zip后的每个元组列表中解压缩第一个值来提取密钥。
必须有一种更简单的方法。
请注意,问题不在于要求排序,而是在原始列表中提取列表中的第一个元素。并且提取的标准基于第二个元素中具有最低值的最后第n个项目。
answers from the possible duplicate linked仍需要解压缩已排序元组列表的步骤,并提取第一个元素列表的前n个。
答案 0 :(得分:5)
目标是找到计数最少的最后
n
个键
鉴于这个目标的定义,你的两个解决方案都不合适。在使用Counter
的{{1}}中,您将使用dict
,这将使键的顺序未定义,您将无法获得最后一个键,但一些n
键的值最小。第二个解决方案有不正确的切片,如果它已修复,它将返回值最小的第一个n
个键。
考虑到sorted
实施是stable,可以像这样重写以符合目标:
def author_2():
output, *_ = zip(*sorted(reversed(l), key=lambda v: v[1])[:n])
return list(reversed(output))
但最好使用heapq
,这是stdlib工具,用于解答“来自可迭代的n个最小/最大值”的问题(正如Martijn Pieters指出的那样,nlargest
和{{1也是稳定的,文档真的这么说,但是以隐式的方式)。特别是如果您必须处理的真实列表很大(对于小nsmallest
,它应该比n
docs describe更快。
sorted
您可以进一步提高性能,但代价是订单(排序稳定性),即一些def prop_1():
rev_result = heapq.nsmallest(n, reversed(l), key=lambda v: v[1])
return [item[0] for item in rev_result][::-1]
个键值最小而不是最后值n
的{{1}}个键。要做到这一点,你需要保留一个“堆化”列表并将其用作内部数据结构而不是普通n
(如果你不更改列表并且只需要一次底部n,它就不会给出绩效福利)。您可以从列表中进行推送和弹出,例如:
list
以下是可用于对解决方案进行基准测试的完整模块。
_p2_heap = None
def prop_2():
global _p2_heap
if not _p2_heap:
_p2_heap = []
for item in l:
heapq.heappush(_p2_heap, item[::-1])
return [item[1] for item in heapq.nsmallest(n, _p2_heap)]
以下是import heapq
from collections import Counter
l = [
('herr', 1), ('dapao', 1),
('cino', 1), ('o', 38),
('tiao', 2), ('tut', 1),
('poh', 6), ('micheal', 1),
('orh', 1), ('horlick', 3),
('si', 1), ('tai', 1),
('titlo', 1), ('siew', 17),
('da', 1), ('halia', 2)
]
n = 5
def author_1():
return list(dict(Counter(dict(l)).most_common()[-n:]).keys())
def author_2():
output, *_ = zip(*sorted(reversed(l), key=lambda v: v[1])[:n])
return list(reversed(output))
def prop_1():
rev_result = heapq.nsmallest(n, reversed(l), key=lambda v: v[1])
return [item[0] for item in rev_result][::-1]
_p2_heap = None
def prop_2():
global _p2_heap
if not _p2_heap:
_p2_heap = []
for item in l:
heapq.heappush(_p2_heap, item[::-1])
return [item[1] for item in heapq.nsmallest(n, _p2_heap)][::-1]
结果:
timeit
但如果我们制作$ python -m timeit -s "import tst" "tst.author_1()"
100000 loops, best of 3: 7.72 usec per loop
$ python -m timeit -s "import tst" "tst.author_2()"
100000 loops, best of 3: 3.7 usec per loop
$ python -m timeit -s "import tst" "tst.prop_1()"
100000 loops, best of 3: 5.51 usec per loop
$ python -m timeit -s "import tst" "tst.prop_2()"
100000 loops, best of 3: 3.96 usec per loop
,差异就会变得明显:
l = l * 1000
答案 1 :(得分:2)
只需使用堆,它就会为您提供所需的输出。
import heapq
x = [('herr', 1),
('dapao', 1),
('cino', 1),
('o', 38),
('tiao', 2),
('tut', 1),
('poh', 6),
('micheal', 1),
('orh', 1),
('horlick', 3),
('si', 1),
('tai', 1),
('titlo', 1),
('siew', 17),
('da', 1),
('halia', 2)]
heap = [(item[1],-index,item[0]) for index, item in enumerate(x)]
heapq.heapify(heap)
print(list(map(lambda item : item[2], heapq.nsmallest(5, heap))))
heapq.nsmallest(n, iterable, key=None)
有一个关键参数,您可以像我一样使用-index
。
答案 2 :(得分:1)
编辑 @alvas:
mi = min(x, key =lambda x:x[1])[1]
r = [a[0] for a in x if a[1] == mi][-5:]
将生成您想要的输出
您可以使用:
sorted(x, key=lambda x: x[1])
请参阅此(可能重复)
答案 3 :(得分:1)
这是我的建议:
n = 5
output=[]
# Search and store the n least numbers
leastNbs = [a[1] for a in sorted(x, key=lambda x: x[1])[:n]]
# Iterate over the list of tuples starting from the end
# in order to find the tuples including one of the n least numbers
for x,nb in reversed(x):
if nb in leastNbs:
output.append(x) # Store the string in output
print(x)
# Keep only the n last strings (starting from the end)
output = list(reversed(output[:n]))
print(output)
答案 4 :(得分:1)
您不需要为此任务执行任何导入,您也可以通过以下方式执行此操作:
x = [('herr', 1),
('dapao', 1),
('cino', 1),
('o', 38),
('tiao', 2),
('tut', 1),
('poh', 6),
('micheal', 1),
('orh', 1),
('horlick', 3),
('si', 1),
('tai', 1),
('titlo', 1),
('siew', 17),
('da', 1),
('halia', 2)]
n = 5
result = [name[0] for name in sorted(x, key=lambda i: i[1], reverse=True)[-n:]]
print(result)
输出:
['orh', 'si', 'tai', 'titlo', 'da']
答案 5 :(得分:1)
如果您不想重新发明轮子,可以使用pandas。性能应该很好,因为它基于NumPy,它使用C而不是纯Python。
df = pd.DataFrame(x, columns=['name', 'count'])
df = df.sort_values(by='count', kind='mergesort', ascending=False).tail(n)
print df['name'].tolist()
['orh', 'si', 'tai', 'titlo', 'da']
import pandas as pd
n = 5
x = [('herr', 1),
('dapao', 1),
('cino', 1),
('o', 38),
('tiao', 2),
('tut', 1),
('poh', 6),
('micheal', 1),
('orh', 1),
('horlick', 3),
('si', 1),
('tai', 1),
('titlo', 1),
('siew', 17),
('da', 1),
('halia', 2)]
# Put the data in a dataframe.
df = pd.DataFrame(x, columns=['name', 'count'])
# Get the last n rows having the smallest 'count'.
# Mergesort is used instead of quicksort (default) since a stable sort is needed
# to get the *last* n smallest items instead of just *any* n smallest items.
df = df.sort_values(by='count', kind='mergesort', ascending=False).tail(n)
# Print the 'name' column as a list (since a list is what you asked for).
print df['name'].tolist()
答案 6 :(得分:1)
这是一种干净,简单的方法,不使用python习语:
m = x[0][1]
l = []
for elem in x:
if m > elem[1]:
l = [elem[0]]
m = elem[1]
elif m == elem[1]:
l.append(elem[0])
print(l[-5:])
有点像最小值搜索和过滤的融合。 m
存储最小值到目前为止,l
存储具有该最小计数的元素列表。当您找到较低的值时重置它们。
这可以修改为只容纳5个元素,因此最后不需要拼接。
答案 7 :(得分:1)
[k for k,v in sorted(x, key=lambda x: x[1])[:n]]
其中x
是键列表,计数元组和n
是所需的键数。
您也可以调整排序条件以包含密钥本身 - 如果他们的订单很重要
[k for k,v in sorted(x, key=lambda x: (x[1], x[0]))[:n]]
答案 8 :(得分:1)
[i [0] for i in sorted(x .__ reverse __(),key = lambda x:x [1])[:n]]
与@Stacksonstacks答案差不多完全一样,只是这实际上给你'所需的输出'(如果你把n = 5)
答案 9 :(得分:0)
纯Python解决方案
由于我们试图按照从最小到最大的顺序找到n
元素,我们不能简单地过滤掉那些没有最小第二元素的元素。我们还有第二个尝试维护顺序的目的 - 这只消除了每个元组的第二个元素上的排序。
我的解决方案具有复杂性O(n)
- 这是您在此处可以做的最好的,因为我们正在创建一个依赖于预先存在的列表的新列表。
它的工作原理是在set
转换n
之后,在x
中创建每个元组的第一个x
元素[::-1]
(无序)(set
)然后根据第二个元素进行排序。这有一个巧妙的技巧,因为我们在转换为集合之前进行切片,这些元组中仍然存在具有等效第二元素的顺序。
现在,使用O(1)
的整洁性是查找是hashes
(即时),因为元素按其__contains__
的顺序存储,因此调用list
是比使用list-comprehension
要快得多。
我们最后需要使用x
来对>>> n = 5
>>> s = {i[0] for i in sorted(x[::-1], key=lambda t: t[1])[:n]}
>>> [i for i, _ in x if i in s]
['orh', 'si', 'tai', 'titlo', 'da']
进行最终过滤:
n = 11
也是一项测试,表明它适用于['herr', 'dapao', 'cino', 'tut', 'micheal', 'orh', 'si', 'tai', 'titlo', 'da', 'halia']
py::vectorize()
答案 10 :(得分:0)
[key for key,value in sorted(x, key=lambda y: y[1], reverse=True)][-n:]
或
[key for key,value in sorted(reversed(x), key=lambda y: y[1])][:n][::-1]
其中n
是结果中所需的键数。请注意,使用后者[::-1]
会更加昂贵,因为它会再次对列表进行切片以将其反转。
from timeit import default_timer
def timeit(method, *args, **kwargs):
start = default_timer()
result = method(*args, **kwargs)
end = default_timer()
print('%s:\n(timing: %fs)\n%s\n' % (method.__name__, (end - start), result))
def with_copy(x, n):
return [key for key,value in sorted(reversed(x), key=lambda y: y[1])][:n][::-1]
def without_copy(x, n):
return [key for key,value in sorted(x, key=lambda y: y[1], reverse=True)][-n:]
x = [('herr', 1), ('dapao', 1), ('cino', 1), ('o', 38), ('tiao', 2),
('tut', 1), ('poh', 6), ('micheal', 1), ('orh', 1), ('horlick', 3),
('si', 1), ('tai', 1), ('titlo', 1), ('siew', 17), ('da', 1),
('halia', 2)]
n = 5
timeit(with_copy, x, n)
timeit(without_copy, x, n)
n = 11
timeit(with_copy, x, n)
timeit(without_copy, x, n)
n = 5
的结果:with_copy:
(timing: 0.000026s)
['orh', 'si', 'tai', 'titlo', 'da']
without_copy:
(timing: 0.000018s)
['orh', 'si', 'tai', 'titlo', 'da']
n = 11
的结果:with_copy:
(timing: 0.000019s)
['halia', 'herr', 'dapao', 'cino', 'tut', 'micheal', 'orh', 'si', 'tai', 'titlo', 'da']
without_copy:
(timing: 0.000013s)
['halia', 'herr', 'dapao', 'cino', 'tut', 'micheal', 'orh', 'si', 'tai', 'titlo', 'da']
答案 11 :(得分:0)
小解决方案:
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<form name="TestList" id="InsertForm" ng-submit="insert(Test);">
<div>
<input ng-model="Test.Pack" >
</div>
<div>
<input ng-model="Test.LM" ng-required="{{Test.Pack.substring(0,2) != 'RR'}}">
</div>
<div>
<button ng-disabled="TestList.$invalid">Test Button</button>
</div>
</form>
</div>
</body>
</html>
<script src="angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
app.controller('myCtrl', function($scope) {
});
</script>
上述解决方案的输出:
import numpy as np
n = 5
x = [('herr', 1),
('dapao', 1),
('cino', 1),
('o', 38),
('tiao', 2),
('tut', 1),
('poh', 6),
('micheal', 1),
('orh', 1),
('horlick', 3),
('si', 1),
('tai', 1),
('titlo', 1),
('siew', 17),
('da', 1),
('halia', 2)]
x = np.array(x) # make the list a numpy array
names = x[:, 0]
numbers = x[:, 1].astype(int)
least_count = np.take(names, np.where(numbers == np.min(numbers)))[0][-n:]
print(least_count)
带注释的解决方案说明
['orh', 'si', 'tai', 'titlo', 'da']
以上解释的输出:
import numpy as np
x = [('herr', 1),
('dapao', 1),
('cino', 1),
('o', 38),
('tiao', 2),
('tut', 1),
('poh', 6),
('micheal', 1),
('orh', 1),
('horlick', 3),
('si', 1),
('tai', 1),
('titlo', 1),
('siew', 17),
('da', 1),
('halia', 2)]
x = np.array(x) # make the list a numpy array
# ==========================================
# split the array into names and numbers
# ==========================================
names = x[:, 0]
numbers = x[:, 1].astype(int)
mini = np.min(numbers) # find the minimum in the numbers array
idx = np.where(numbers == mini) # Find the indices where minimum occurs in the numbers array
least_count = np.take(names, idx)[0] # Use the indices found from numbers array in the above line to access names array
print(least_count)
least_count = least_count.tolist() # to convert the numpy array to list
n = 5 # say n is 5
print(least_count[-n:]) # now you can do simple slicing to extract the last n element