在Python列表中查找最常见元素的有效方法是什么?
我的列表项可能无法散列,因此不能使用字典。同样在绘制时,应返回索引最低的项目。例:
>>> most_common(['duck', 'duck', 'goose'])
'duck'
>>> most_common(['goose', 'duck', 'duck', 'goose'])
'goose'
在Python列表中查找最常见元素的有效方法是什么?
我的列表项可能无法散列,因此不能使用字典。同样在绘制时,应返回索引最低的项目。例:
>>> most_common(['duck', 'duck', 'goose'])
'duck'
>>> most_common(['goose', 'duck', 'duck', 'goose'])
'goose'
Answers:
提出了这么多解决方案,我很惊讶没有人提出我认为是显而易见的解决方案(对于不可哈希但可比较的元素)-[ itertools.groupby
] [1]。 itertools
提供快速,可重用的功能,并允许您将一些棘手的逻辑委托给经过良好测试的标准库组件。考虑例如:
import itertools
import operator
def most_common(L):
# get an iterable of (item, iterable) pairs
SL = sorted((x, i) for i, x in enumerate(L))
# print 'SL:', SL
groups = itertools.groupby(SL, key=operator.itemgetter(0))
# auxiliary function to get "quality" for an item
def _auxfun(g):
item, iterable = g
count = 0
min_index = len(L)
for _, where in iterable:
count += 1
min_index = min(min_index, where)
# print 'item %r, count %r, minind %r' % (item, count, min_index)
return count, -min_index
# pick the highest-count/earliest item
return max(groups, key=_auxfun)[0]
当然,这可以写得更简洁一些,但我的目标是最大程度地清晰。print
可以不加注释这两个语句,以更好地了解运行中的机制。例如,带有未注释的打印:
print most_common(['goose', 'duck', 'duck', 'goose'])
发出:
SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
如您所见,SL
是一个成对的列表,每对一个项目,后跟原始列表中的项目索引(以实现关键条件,即,如果具有相同最高计数的“最常见”项目> 1,则结果必须是最早出现的一个)。
groupby
仅按项目分组(通过operator.itemgetter
)。辅助功能在max
计算过程中每分组一次调用,它接收并在内部解压缩一个组-具有两个项目的元组,(item, iterable)
其中可迭代的项目也是两个项目元组(item, original index)
[[ SL
]的项目]。
然后,辅助功能使用循环来确定组可迭代项中的条目数和最小原始索引。它将返回那些作为组合的“质量关键字”,并且最小索引符号已更改,因此该max
操作将考虑“更好”那些在原始列表中较早出现的项目。
此代码可能是更简单的,如果它担心一点点时间和空间,少谈大O问题,如...:
def most_common(L):
groups = itertools.groupby(sorted(L))
def _auxfun((item, iterable)):
return len(list(iterable)), -L.index(item)
return max(groups, key=_auxfun)[0]
相同的基本思想,只是表达得更简单,紧凑...但是,可惜的是,额外的O(N)辅助空间(将组的可迭代对象体现到列表中)和O(N平方)时间(获取L.index
每个项目的总和) 。尽管过早的优化是编程中所有弊端的根源,但在O(N log N)可用时刻意选择O(N平方)方法与可扩展性背道而驰!
最后,对于那些更喜欢“单线”而不是清晰度和性能的人,可以使用名称经过适当修饰的1线附加版本:-)。
from itertools import groupby as g
def most_common_oneliner(L):
return max(g(sorted(L)), key=lambda(x, v):(len(list(v)),-L.index(x)))[0]
groupby
需要首先排序(O(NlogN)); 使用Counter()
withmost_common()
可以解决这个问题,因为它使用heapq来查找频率最高的项(仅一项,即O(N)时间)。由于Counter()
现在已进行了高度优化(计数在C循环中进行),因此即使对于较小的列表,它也可以轻松击败该解决方案。它把它从水里吹出来,列出了很多。
更简单的单行代码:
def most_common(lst):
return max(set(lst), key=lst.count)
set(lst)
,都必须再次检查整个列表)…虽然对于大多数用途来说可能足够快…
set(lst)
为lst
,它也可以与非哈希元素一起使用;虽然慢。
list.count()
必须完整遍历该列表,而您需要对列表中的每个唯一项进行遍历。这使其成为O(NK)解(在最坏的情况下为O(N ^ 2))。Counter()
仅使用O(N)时间!
从这里借用,可以与Python 2.7一起使用:
from collections import Counter
def Most_Common(lst):
data = Counter(lst)
return data.most_common(1)[0][0]
它的工作速度比Alex的解决方案快4到6倍,比newacct建议的单线快50倍。
要检索在出现联系时首先出现在列表中的元素:
def most_common(lst):
data = Counter(lst)
return max(lst, key=data.get)
most_common
按计数排序,而不是无序。就是说,如果出现联系,它将不会选择第一个元素;我添加了另一种使用计数器的方法,该计数器可以选择第一个元素。
您想要的在统计中称为模式,Python当然具有内置功能可以为您完全做到这一点:
>>> from statistics import mode
>>> mode([1, 2, 2, 3, 3, 3, 3, 3, 4, 5, 6, 6, 6])
3
请注意,如果没有“最常见的元素”(例如,前两个元素并列的情况),则会上升StatisticsError
,因为从统计学上讲,在这种情况下没有模式。
set
似乎是合理的O(n^3)
。
无需最低索引,您可以使用collections.Counter
此方法:
from collections import Counter
a = [1936, 2401, 2916, 4761, 9216, 9216, 9604, 9801]
c = Counter(a)
print(c.most_common(1)) # the one most common element... 2 would mean the 2 most common
[(9216, 2)] # a set containing the element, and it's count in 'a'
如果它们不可散列,则可以对它们进行排序,并对计数项目的结果进行单个循环(相同的项目将彼此相邻)。但是使它们可散列并使用dict可能更快。
def most_common(lst):
cur_length = 0
max_length = 0
cur_i = 0
max_i = 0
cur_item = None
max_item = None
for i, item in sorted(enumerate(lst), key=lambda x: x[1]):
if cur_item is None or cur_item != item:
if cur_length > max_length or (cur_length == max_length and cur_i < max_i):
max_length = cur_length
max_i = cur_i
max_item = cur_item
cur_length = 1
cur_i = i
cur_item = item
else:
cur_length += 1
if cur_length > max_length or (cur_length == max_length and cur_i < max_i):
return cur_item
return max_item
Counter()
解决方案进行比较
对列表的副本进行排序,并找到最长的运行时间。您可以在使用每个元素的索引对列表进行排序之前对列表进行修饰,然后在出现平局的情况下选择从最低索引开始的运行。
# use Decorate, Sort, Undecorate to solve the problem
def most_common(iterable):
# Make a list with tuples: (item, index)
# The index will be used later to break ties for most common item.
lst = [(x, i) for i, x in enumerate(iterable)]
lst.sort()
# lst_final will also be a list of tuples: (count, index, item)
# Sorting on this list will find us the most common item, and the index
# will break ties so the one listed first wins. Count is negative so
# largest count will have lowest value and sort first.
lst_final = []
# Get an iterator for our new list...
itr = iter(lst)
# ...and pop the first tuple off. Setup current state vars for loop.
count = 1
tup = next(itr)
x_cur, i_cur = tup
# Loop over sorted list of tuples, counting occurrences of item.
for tup in itr:
# Same item again?
if x_cur == tup[0]:
# Yes, same item; increment count
count += 1
else:
# No, new item, so write previous current item to lst_final...
t = (-count, i_cur, x_cur)
lst_final.append(t)
# ...and reset current state vars for loop.
x_cur, i_cur = tup
count = 1
# Write final item after loop ends
t = (-count, i_cur, x_cur)
lst_final.append(t)
lst_final.sort()
answer = lst_final[0][2]
return answer
print most_common(['x', 'e', 'a', 'e', 'a', 'e', 'e']) # prints 'e'
print most_common(['goose', 'duck', 'duck', 'goose']) # prints 'goose'
简单的一线解决方案
moc= max([(lst.count(chr),chr) for chr in set(lst)])
它将返回其频率最频繁的元素。
您可能不再需要它了,但这就是我为类似问题所做的。(由于注释,它看起来比它更长。)
itemList = ['hi', 'hi', 'hello', 'bye']
counter = {}
maxItemCount = 0
for item in itemList:
try:
# Referencing this will cause a KeyError exception
# if it doesn't already exist
counter[item]
# ... meaning if we get this far it didn't happen so
# we'll increment
counter[item] += 1
except KeyError:
# If we got a KeyError we need to create the
# dictionary key
counter[item] = 1
# Keep overwriting maxItemCount with the latest number,
# if it's higher than the existing itemCount
if counter[item] > maxItemCount:
maxItemCount = counter[item]
mostPopularItem = item
print mostPopularItem
以Luiz的答案为基础,但满足“如果绘制具有最低索引的项目应返回”条件:
from statistics import mode, StatisticsError
def most_common(l):
try:
return mode(l)
except StatisticsError as e:
# will only return the first element if no unique mode found
if 'no unique mode' in e.args[0]:
return l[0]
# this is for "StatisticsError: no mode for empty data"
# after calling mode([])
raise
例:
>>> most_common(['a', 'b', 'b'])
'b'
>>> most_common([1, 2])
1
>>> most_common([])
StatisticsError: no mode for empty data
这里:
def most_common(l):
max = 0
maxitem = None
for x in set(l):
count = l.count(x)
if count > max:
max = count
maxitem = x
return maxitem
我有一种模糊的感觉,在标准库中某处有一个方法可以为您提供每个元素的计数,但是我找不到它。
如果排序和哈希都不可行,但是相等比较(==
)可用,这是很明显的慢速解决方案(O(n ^ 2)):
def most_common(items):
if not items:
raise ValueError
fitems = []
best_idx = 0
for item in items:
item_missing = True
i = 0
for fitem in fitems:
if fitem[0] == item:
fitem[1] += 1
d = fitem[1] - fitems[best_idx][1]
if d > 0 or (d == 0 and fitems[best_idx][2] > fitem[2]):
best_idx = i
item_missing = False
break
i += 1
if item_missing:
fitems.append([item, 1, i])
return items[best_idx]
但是,如果列表(n)的长度很大,则使您的项目可散列或可排序(如其他答案所建议)几乎总是会使查找最常见的元素更快。O(n)平均带有散列,最差的O(n * log(n))用于排序。
>>> li = ['goose', 'duck', 'duck']
>>> def foo(li):
st = set(li)
mx = -1
for each in st:
temp = li.count(each):
if mx < temp:
mx = temp
h = each
return h
>>> foo(li)
'duck'
我需要在最近的程序中执行此操作。我承认,我听不懂亚历克斯的回答,所以这就是我的最终选择。
def mostPopular(l):
mpEl=None
mpIndex=0
mpCount=0
curEl=None
curCount=0
for i, el in sorted(enumerate(l), key=lambda x: (x[1], x[0]), reverse=True):
curCount=curCount+1 if el==curEl else 1
curEl=el
if curCount>mpCount \
or (curCount==mpCount and i<mpIndex):
mpEl=curEl
mpIndex=i
mpCount=curCount
return mpEl, mpCount, mpIndex
我将其与Alex的解决方案进行了计时,对于短列表,它的速度大约提高了10-15%,但是一旦超过100个元素或更多(测试到200000),它的速度就会降低20%。
ans = [1, 1, 0, 0, 1, 1]
all_ans = {ans.count(ans[i]): ans[i] for i in range(len(ans))}
print(all_ans)
all_ans={4: 1, 2: 0}
max_key = max(all_ans.keys())
4
print(all_ans[max_key])
1个
def mostCommonElement(list):
count = {} // dict holder
max = 0 // keep track of the count by key
result = None // holder when count is greater than max
for i in list:
if i not in count:
count[i] = 1
else:
count[i] += 1
if count[i] > max:
max = count[i]
result = i
return result
mostCommonElement([[“ a”,“ b”,“ a”,“ c”])->“ a”