查找列表中出现次数最多的项

在 Python 中,我有一个列表:

L = [1, 2, 45, 55, 5, 4, 4, 4, 4, 4, 4, 5456, 56, 6, 7, 67]

我要确定出现次数最多的项目。我能够解决它,但我需要最快的方式来做到这一点。我知道有一个很好的 Python 式的答案。

186138 次浏览
from collections import Counter
most_common,num_most_common = Counter(L).most_common(1)[0] # 4, 6 times

对于较老的 Python 版本(< 2.7) ,可以使用 这个食谱创建 Counter类。

也许是 Most _ common ()方法

下面是一个 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] 然后是六个4s 和六个7s,但结果是 (4, 6),即六个4s。

在你的问题中,你问了最快的方法。正如已经反复证明的那样,特别是在使用 Python 时,直觉并不是一个可靠的指南: 您需要度量。

下面是几种不同实现的简单测试:

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的常规 dict会稍微快一些。

如果名单更长怎么办?在上面的测试中加入 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”方法的成本和 inplace 的损失加起来是合理的(对生成代码的检查留作练习)。

但是对于修改后的测试数据,惟一项值的数量没有改变,因此推测 dictdefaultdict在这方面比其他实现有优势。那么,如果我们使用更大的列表,但大大增加了独特项的数量,会发生什么情况呢?将 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解决方案快,但仍然慢于 dictdefaultdictiteritems版本。

这些例子的重点不是产生一个最优解。问题的关键在于通常没有 最优的一般解。而且还有其他的表现标准。在不同的解决方案之间,内存需求将大不相同,随着输入大小的增加,内存需求可能成为算法选择中的首要因素。

底线: 这完全取决于您需要测量的。

我很惊讶没有人提到最简单的解决方案,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

这在 Python3或2中可以工作,但是请注意,它只返回最频繁的项,而不返回频率。此外,在 拔枪(即联合最常见的项目)的情况下,只返回一个项目。

尽管使用 max()的时间复杂性比使用 Counter.most_common(1)作为 PM 2 Ring注释要差,但是这种方法得益于快速的 C实现,我发现这种方法对于短列表来说是最快的,但是对于大列表来说则更慢(Python 3.6计时显示在 IPython 5.3中) :

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

通过使用 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模块使用 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

我想扔在另一个解决方案,看起来不错,是快速的 太短了列表。

def mc(seq=L):
"max/count"
max_element = max(seq, key=seq.count)
return (max_element, seq.count(max_element))

您可以使用 Ned Deily 提供的代码对此进行基准测试,这些代码将为您提供最小测试用例的结果:

3.5.2 (default, Nov  7 2016, 11:31:36)
[GCC 6.2.1 20160830]


dict iteritems (4, 6) 0.2069783889998289
dict items (4, 6) 0.20462976200065896
defaultdict iteritems (4, 6) 0.2095775119996688
sort groupby generator expression (4, 6) 0.4473949929997616
sort groupby list comprehension (4, 6) 0.4367636879997008
counter (4, 6) 0.3618192010007988
max/count (4, 6) 0.20328268999946886

但是要注意,它是低效的,因此得到的 真的缓慢的大型列表!

下面是我想出来的解决方案,如果有多个字符在字符串都具有最高的频率。

mystr = input("enter string: ")
#define dictionary to store characters and their frequencies
mydict = {}
#get the unique characters
unique_chars = sorted(set(mystr),key = mystr.index)
#store the characters and their respective frequencies in the dictionary
for c in unique_chars:
ctr = 0
for d in mystr:
if d != " " and d == c:
ctr = ctr + 1
mydict[c] = ctr
print(mydict)
#store the maximum frequency
max_freq = max(mydict.values())
print("the highest frequency of occurence: ",max_freq)
#print all characters with highest frequency
print("the characters are:")
for k,v in mydict.items():
if v == max_freq:
print(k)

输入: “大家好”

产出:

{'o': 2, 'p': 2, 'h': 1, ' ': 0, 'e': 3, 'l': 3}

发生频率最高: 3

角色是:

e


l

可能会发生这样的事情:

TestList = [1,2,3,4,2,2,1,4,4] Print (max (set (testList) ,key = testList.count))

一种没有任何库或集的简单方法

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 学习) :

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.

如果使用 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()返回一个空列表。

注意,在 Python 3.8之前,如果没有一个最常见的值,statistics.mode()(在3.4中引入)将另外抛出 statistics.StatisticsError

如果您在解决方案中使用 numpy 以提高计算速度,请使用以下命令:

import numpy as np
x = np.array([2,5,77,77,77,77,77,77,77,9,0,3,3,3,3,3])
y = np.bincount(x,minlength = max(x))
y = np.argmax(y)
print(y)  #outputs 77