Python中最常见的元素列表
什么是在Python列表中find最常见元素的有效方法?
我的列表项可能不可散列,所以不能使用字典。 同样在绘制的情况下,应返回索引最低的项目。 例:
>>> most_common(['duck', 'duck', 'goose']) 'duck' >>> most_common(['goose', 'duck', 'duck', 'goose']) 'goose'
有这么多的解决scheme提出,我很惊讶没有人提出了我认为明显的(不可排除但可比的元素) – [ itertools.groupby
] [1]。 itertools
提供快速,可重用的function,并允许您将一些棘手的逻辑委托给经过良好testing的标准库组件。 考虑例如:
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)
的元组,这里iterable的项目也是两个项目元组(item, original index)
[[the项目的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]
相同的基本思想,只是expression得更加简单紧凑…但是,可惜的是,一个额外的O(N)辅助空间(用来体现组的迭代列表)和O(N平方)时间(得到每个项目)。 虽然不成熟的优化是编程中所有邪恶的根源,但是当O(N log N)可用时,故意selectO(N平方)的方法对于可扩展性来说太过分了!
最后,对于那些喜欢“清除”和“性能”的人来说,奖励一行内容的版本应该有适当的错误名称:-)。
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]
简单的一行:
def most_common(lst): return max(set(lst), key=lst.count)
从这里借用,这可以用于Python 2.7:
from collections import Counter def Most_Common(lst): data = Counter(lst) return data.most_common(1)[0][0]
比Alex的解决scheme快4-6倍,比newacct提出的单线快50倍。
要检索列表中第一个出现在元素中的元素,请执行以下操作:
def most_common(lst): data = Counter(lst) return max(lst, key=data.get)
你想要什么在统计模式中是已知的,Python当然有一个内置的函数来完成你的工作:
>>> from statistics import mode >>> mode([1, 2, 2, 3, 3, 3, 3, 3, 4, 5, 6, 6, 6]) 3
如果它们不可排除,则可以对它们进行sorting并对结果进行计数(相同的项目将相邻)。 但是使它们可以被哈希和使用字典可能会更快。
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
sorting列表的副本,并find最长的运行。 您可以在使用每个元素的索引进行sorting之前修饰列表,然后select以平局的情况下以最低索引开始的运行。
这是一个O(n)解决scheme。
mydict = {} cnt, itm = 0, '' for item in reversed(lst): mydict[item] = mydict.get(item, 0) + 1 if mydict[item] >= cnt : cnt, itm = mydict[item], item print itm
(反转是用来确保它返回最低的索引项目)
你可能不需要这个了,但这是我为类似的问题所做的。 (由于评论,看起来比它长。)
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
单线:
def most_common (lst): return max(((item, lst.count(item)) for item in set(lst)), key=lambda a: a[1])[0]
# 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'
这是明显缓慢的解决scheme(O(n ^ 2)),如果既不sorting也不哈希是可行的,但是等式比较( ==
)是可用的:
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)的长度很大,使得你的项目可sorting或sorting(如其他答案所推荐的)几乎总是能够更快find最常见的元素。 O(n)平均为散列,O(n * log(n))为最差sorting。
这里:
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
我有一个模糊的感觉,在标准库中有一个方法会给你每个元素的数量,但我找不到它。
>>> 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 mostCommon(lst): # Finds the element of highest value & occurrence table = {} # Counts the number of occurences for each number for ele in lst: if ele in table: table[ele] = table[ele] + 1 else: table.update( {ele : 1} ) # Inverts the keys & values invert = lambda mydict: {v:k for k, v in mydict.items()} table = invert(table) # Inverting is necessary to access values # Returns highest value in dictionary return table[ max(table.keys()) ]
我需要在最近的一个项目中这样做。 我承认,我不明白阿历克斯的回答,所以这就是我最终的结果。
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的解决scheme进行了计时,对于短名单,速度大约快10-15%,但是一旦超过100个元素或更多(testing到200000),速度会降低大约20%。
def most_common(lst): if max([lst.count(i)for i in lst]) == 1: return False else: return max(set(lst), key=lst.count)
def popular(L): C={} for a in L: C[a]=L.count(a) for b in C.keys(): if C[b]==max(C.values()): return b L=[2,3,5,3,6,3,6,3,6,3,7,467,4,7,4] print popular(L)