输入是未排序的元组列表:
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个。
目标是找到计数最少的最后一个
n
键
鉴于这个目标的定义,你的两个解决方案都不合适。在使用Counter
的一个中,你使用dict
,这将使键的顺序未定义,你将不会获得最后的键,但一些n
键具有最小值。第二个解决方案有不正确的切片,如果它被修复,它将返回具有最小值的第一个n
键。
考虑到sorted
的实现是stable,它可以像这样重写以适应目标:
def author_2():
output, *_ = zip(*sorted(reversed(l), key=lambda v: v[1])[:n])
return list(reversed(output))
但是使用heapq
是一个更好的主意,nlargest
是stdlib工具,用于解答诸如“来自可迭代的n个最小/最大值”之类的问题(正如Martijn Pieters所指出的那样,nsmallest
和n
也是稳定的,而且文档真的这么说,但是隐含办法)。特别是如果您必须处理的真实列表很大(对于小sorted
它应该更快docs describe作为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
您可以进一步提高性能,但是以订单为代价(排序稳定性),即一些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)]
(如果你不更改列表并且只需要一次底部n,它将不会带来性能优势) 。您可以从列表中推送和弹出,例如:
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
但如果我们制作$ python -m timeit -s "import tst" "tst.author_1()"
1000 loops, best of 3: 263 usec per loop
$ python -m timeit -s "import tst" "tst.author_2()"
100 loops, best of 3: 2.72 msec per loop
$ python -m timeit -s "import tst" "tst.prop_1()"
1000 loops, best of 3: 1.65 msec per loop
$ python -m timeit -s "import tst" "tst.prop_2()"
1000 loops, best of 3: 767 usec per loop
,差异就会变得明显:
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))))
Appe Py Pyung So Chi
由于我们试图按照从最小到最大的顺序找到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']
一起使用
list comprehension
使用sorted和[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']
: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
['herr' 'dapao' 'cino' 'tut' 'micheal' 'orh' 'si' 'tai' 'titlo' 'da']
['orh', 'si', 'tai', 'titlo', 'da']
只需使用堆,它将为您提供所需的输出。
heapq.nsmallest(n, iterable, key=None)
-index
有一个关键的论点,你可以像我一样使用[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]]
是所需的密钥数。
您还可以调整排序条件以包括密钥本身 - 如果它们的顺序很重要
mi = min(x, key =lambda x:x[1])[1]
r = [a[0] for a in x if a[1] == mi][-5:]
编辑@alvas:
sorted(x, key=lambda x: x[1])
会产生你想要的输出
你可以用这个:
Sort a list of tuples by 2nd item (integer value)
请参考此(可能重复)
如果您不想重新发明轮子,可以使用df = pd.DataFrame(x, columns=['name', 'count'])
df = df.sort_values(by='count', kind='mergesort', ascending=False).tail(n)
print df['name'].tolist()
。性能应该很好,因为它基于NumPy,它使用C而不是纯Python。
['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()
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)
[i [0] for i in sorted(x .__ reverse __(),key = lambda x:x [1])[:n]]
与@Stacksonstacks几乎完全一样的答案,只是这实际上给你'期望的输出'(如果你把n = 5)
您不需要为此任务执行任何导入,您也可以通过以下方式执行此操作:
['orh', 'si', 'tai', 'titlo', 'da']
输出:
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)
这是我的建议:
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:])
这是一个干净,简单的方法,不使用python习语:
m
它有点像最小值搜索和过滤的融合。 l
存储最小值到目前为止,n
存储具有最小计数的元素列表。找到较低的值时重置它们。
这可以修改为仅容纳5个元素,因此最终不需要拼接。