在Python中,我有一个列表:
L = [1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 56, 6, 7, 67]
我想找出发生最多次的项目。我知道如何解决它,但我需要最快的方法。我知道有一种很好的Pythonic答案。
我很惊讶没有人提到最简单的解决方案,即使用max()
函数和键值list.count
:
max(lst,key=lst.count)
例子:
>>> lst = [1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 56, 6, 7, 67]
>>> max(lst,key=lst.count)
4
这个方法适用于Python 2或3,但请注意它只返回最常见的项目而不是频率。另外,如果存在并列(即最常见的项目相同),则只返回一个项目。
虽然使用max()
的时间复杂度比使用Counter.most_common(1)
更差,正如PM 2Ring所评论的那样,但该方法有着快速的C
实现,并且我发现该方法对于短列表来说速度最快,但对于较长列表则速度较慢(在IPython 5.3中显示Python 3.6的计时情况):
In [1]: from collections import Counter
...:
...: def f1(lst):
...: return max(lst, key = lst.count)
...:
...: def f2(lst):
...: return Counter(lst).most_common(1)
...:
...: lst0 = [1,2,3,4,3]
...: lst1 = lst0[:] * 100
...:
In [2]: %timeit -n 10 f1(lst0)
10 loops, best of 3: 3.32 us per loop
In [3]: %timeit -n 10 f2(lst0)
10 loops, best of 3: 26 us per loop
In [4]: %timeit -n 10 f1(lst1)
10 loops, best of 3: 4.04 ms per loop
In [5]: %timeit -n 10 f2(lst1)
10 loops, best of 3: 75.6 us per loop
import sys
from collections import Counter, defaultdict
from itertools import groupby
from operator import itemgetter
from timeit import timeit
L = [1,2,45,55,5,4,4,4,4,4,4,5456,56,6,7,67]
def max_occurrences_1a(seq=L):
"dict iteritems"
c = dict()
for item in seq:
c[item] = c.get(item, 0) + 1
return max(c.iteritems(), key=itemgetter(1))
def max_occurrences_1b(seq=L):
"dict items"
c = dict()
for item in seq:
c[item] = c.get(item, 0) + 1
return max(c.items(), key=itemgetter(1))
def max_occurrences_2(seq=L):
"defaultdict iteritems"
c = defaultdict(int)
for item in seq:
c[item] += 1
return max(c.iteritems(), key=itemgetter(1))
def max_occurrences_3a(seq=L):
"sort groupby generator expression"
return max(((k, sum(1 for i in g)) for k, g in groupby(sorted(seq))), key=itemgetter(1))
def max_occurrences_3b(seq=L):
"sort groupby list comprehension"
return max([(k, sum(1 for i in g)) for k, g in groupby(sorted(seq))], key=itemgetter(1))
def max_occurrences_4(seq=L):
"counter"
return Counter(L).most_common(1)[0]
versions = [max_occurrences_1a, max_occurrences_1b, max_occurrences_2, max_occurrences_3a, max_occurrences_3b, max_occurrences_4]
print sys.version, "\n"
for vers in versions:
print vers.__doc__, vers(), timeit(vers, number=20000)
我的机器上的结果:
2.7.2 (v2.7.2:8527427914a2, Jun 11 2011, 15:22:34)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
dict iteritems (4, 6) 0.202214956284
dict items (4, 6) 0.208412885666
defaultdict iteritems (4, 6) 0.221301078796
sort groupby generator expression (4, 6) 0.383440971375
sort groupby list comprehension (4, 6) 0.402786016464
counter (4, 6) 0.564319133759
看起来 Counter
的解决方案不是最快的。而且,在这种情况下,groupby
更快。使用 defaultdict
是不错的选择,但你需要为其方便付出一点代价;用带有 get
的普通字典会稍微快一些。
如果列表很大会发生什么?在上面的测试中添加 L *= 10000
并将重复计数减少到 200:
dict iteritems (4, 60000) 10.3451900482
dict items (4, 60000) 10.2988479137
defaultdict iteritems (4, 60000) 5.52838587761
sort groupby generator expression (4, 60000) 11.9538850784
sort groupby list comprehension (4, 60000) 12.1327362061
counter (4, 60000) 14.7495789528
现在defaultdict
显然是最佳选择。因此,也许'get'方法的成本和原地添加的损失会相加(生成的代码的检查留作练习)。
但是使用修改后的测试数据时,唯一项值的数量没有改变,因此dict
和defaultdict
在这方面比其他实现更具优势。那么如果我们使用更大的列表,但大幅增加唯一项的数量会发生什么?将L的初始化替换为:
LL = [1,2,45,55,5,4,4,4,4,4,4,5456,56,6,7,67]
L = []
for i in xrange(1,10001):
L.extend(l * i for l in LL)
dict iteritems (2520, 13) 17.9935798645
dict items (2520, 13) 21.8974409103
defaultdict iteritems (2520, 13) 16.8289561272
sort groupby generator expression (2520, 13) 33.853593111
sort groupby list comprehension (2520, 13) 36.1303369999
counter (2520, 13) 22.626899004
现在Counter
显然比groupby
的解决方案更快,但仍比dict
和defaultdict
的iteritems
版本慢。
这些示例的重点不是产生最佳解决方案,而是通常不存在一个最佳的通用解决方案。此外,还有其他性能指标。这些解决方案之间的内存需求会有很大差异,并且随着输入大小的增加,内存需求可能成为算法选择中的主要因素。
总之:一切取决于具体情况,需要进行测量。
int
和str
无害,但对其他类型来说代价高昂);在此之前是纯Python。 Counter
始终是最简单的,而且有了加速器后也是最快的。 - ShadowRangerCounter
将优于所有这些(它可以在不急切地在内存中实现整个输入的迭代器上工作,而sorted
需要;峰值内存最终与唯一项目数成比例,而不是总数)。对于小输入,#4输给#1/2(击败其他人),但是将most_common
内联为return max(Counter(L).items(), key=itemgetter(1))[0]
即可解决;对于较大的输入,它的竞争对手的表现提高了2倍以上。 - ShadowRanger这里是一个使用defaultdict
的解决方案,适用于Python 2.5及以上版本:
from collections import defaultdict
L = [1,2,45,55,5,4,4,4,4,4,4,5456,56,6,7,67]
d = defaultdict(int)
for i in L:
d[i] += 1
result = max(d.iteritems(), key=lambda x: x[1])
print result
# (4, 6)
# The number 4 occurs 6 times
请注意,如果 L = [1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 7, 7, 7, 7, 7, 56, 6, 7, 67]
,
那么列表中有六个4和六个7。然而,结果将是(4, 6)
,即六个4。
itemgetter(1)
可能比使用lambda x: x[1]
更好。这只是一个很小的建议。具体可以参考http://docs.python.org/howto/sorting.html#operator-module-functions。 - Darren Yin如果您使用的是Python 3.8或更高版本,则可以使用statistics.mode()
返回第一个遇到的众数,或者statistics.multimode()
返回所有众数。
>>> import statistics
>>> data = [1, 2, 2, 3, 3, 4]
>>> statistics.mode(data)
2
>>> statistics.multimode(data)
[2, 3]
statistics.mode()
会抛出一个statistics.StatisticsError
异常,而statistics.multimode()
则返回一个空列表。statistics.mode()
(在3.4中引入)还会在没有恰好一个最常见值的情况下抛出一个statistics.StatisticsError
异常。一种简单的方法,不需要任何库或集合
def mcount(l):
n = [] #To store count of each elements
for x in l:
count = 0
for i in range(len(l)):
if x == l[i]:
count+=1
n.append(count)
a = max(n) #largest in counts list
for i in range(len(n)):
if n[i] == a:
return(l[i],a) #element,frequency
return #if something goes wrong
def max_occ(lst,x):
count=0
for i in lst:
if (i==x):
count=count+1
return count
lst=[1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 56, 6, 7, 67]
x=max(lst,key=lst.count)
print(x,"occurs ",max_occ(lst,x),"times")
输出:4 出现了 6 次
我使用Python 3.5.2版本,结合itertools
模块中的groupby
函数,取得了最佳的结果:
from itertools import groupby
a = [1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 56, 6, 7, 67]
def occurrence():
occurrence, num_times = 0, 0
for key, values in groupby(a, lambda x : x):
val = len(list(values))
if val >= occurrence:
occurrence, num_times = key, val
return occurrence, num_times
occurrence, num_times = occurrence()
print("%d occurred %d times which is the highest number of times" % (occurrence, num_times))
输出:
4 occurred 6 times which is the highest number of times
使用timeit
模块进行测试。
我在测试中使用了以下脚本,number= 20000
:
from itertools import groupby
def occurrence():
a = [1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 56, 6, 7, 67]
occurrence, num_times = 0, 0
for key, values in groupby(a, lambda x : x):
val = len(list(values))
if val >= occurrence:
occurrence, num_times = key, val
return occurrence, num_times
if __name__ == '__main__':
from timeit import timeit
print(timeit("occurrence()", setup = "from __main__ import occurrence", number = 20000))
输出(最佳):
0.1893607140000313
我的(简单的)代码(学习Python三个月):
def more_frequent_item(lst):
new_lst = []
times = 0
for item in lst:
count_num = lst.count(item)
new_lst.append(count_num)
times = max(new_lst)
key = max(lst, key=lst.count)
print("In the list: ")
print(lst)
print("The most frequent item is " + str(key) + ". Appears " + str(times) + " times in this list.")
more_frequent_item([1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 56, 6, 7, 67])
输出将为:
In the list:
[1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 56, 6, 7, 67]
The most frequent item is 4. Appears 6 times in this list.
max
如何与key=
一起使用的解释。 - Asara.count
需要扫描整个列表以查找每个项,使其成为O(n²)。 - PM 2Ringset()
运算符。我想知道为什么这会起作用:我的意思是,我从列表中删除了所有重复项,然后再使用key=list.count
..这对我来说没有意义。你明白这个吗? - Luk