找到列表中最常见的元素

hoj*_*oju 158 python list

查找Python列表中最常见元素的有效方法是什么?

我的列表项可能不具有哈希值,因此无法使用字典.同样在绘制的情况下,应返回具有最低索引的项目.例:

>>> most_common(['duck', 'duck', 'goose'])
'duck'
>>> most_common(['goose', 'duck', 'duck', 'goose'])
'goose'
Run Code Online (Sandbox Code Playgroud)

new*_*cct 419

一个更简单的单线程:

def most_common(lst):
    return max(set(lst), key=lst.count)
Run Code Online (Sandbox Code Playgroud)

  • OP表示,在绘制具有最低索引的项目的情况下,应返回*[..].*此代码通常不符合该要求. (23认同)
  • 这可能看起来很吸引人,但从算法的角度来看,这是一个糟糕的建议.`list.count()`必须遍历整个*中的列表*,并且对列表中的每个唯一项*进行遍历.这使得这成为O(NK)溶液(在最坏的情况下为O(N ^ 2)).使用`Counter()`只需要O(N)时间! (22认同)
  • 您可以将`set(lst)`替换为`lst`,它也适用于不可散列的元素; 虽然比较慢. (9认同)
  • 您可以使用min()而不是max()来获得最少的频率.. (4认同)
  • 此外,OP声明元素必须是可清除的:集合必须包含可清除的对象. (2认同)
  • 另外,这种方法在算法上很慢(对于`set(lst)`中的每个元素,必须再次检查整个列表)......对于大多数用途来说可能足够快,但是...... (2认同)

Ale*_*lex 172

借用这里,这可以用于Python 2.7:

from collections import Counter

def Most_Common(lst):
    data = Counter(lst)
    return data.most_common(1)[0][0]
Run Code Online (Sandbox Code Playgroud)

工作速度比Alex的解决方案快4-6倍,比newacct提出的单线程快50倍.

在绑定的情况下检索列表中首先出现的元素:

def most_common(lst):
    data = Counter(lst)
    return max(lst, key=data.get)
Run Code Online (Sandbox Code Playgroud)

  • 喜欢这个.上面的@newacct单行可能很简单,但它运行在O(n ^ 2); 也就是说,其中n是列表的长度.该解决方案是O(n). (12认同)
  • 就像简单和速度一样......也许不适合OP.但是非常适合我! (4认同)
  • 这可能对某些人有用,但是......不幸的是Counter是一个dict子类,OP说他不能使用字典(因为项目可能不是可以清除的). (3认同)

Ale*_*lli 93

有了这么多解决方案,我很惊讶没有人提出我认为是明显的解决方案(对于不可拆解但可比较的元素) - [ 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]
Run Code Online (Sandbox Code Playgroud)

当然,这可以写得更简洁,但我的目标是最大限度地提高清晰度.这两个print陈述可以被取消评论,以便更好地了解行动中的机制; 例如,打印未注释:

print most_common(['goose', 'duck', 'duck', 'goose'])
Run Code Online (Sandbox Code Playgroud)

发出:

SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
Run Code Online (Sandbox Code Playgroud)

如您所见,SL是一对对的列表,每一对都是一个项目,后跟原始列表中的项目索引(为了实现关键条件,如果具有相同最高计数的"最常见"项目> 1,则结果必须是最早发生的一个).

groupby仅按项目分组(通过operator.itemgetter).在max计算过程中每个分组调用一次的辅助函数接收并在内部解包一个组 - 一个包含两个项的元组,(item, iterable)其中iterable的项也是两项元组,(item, original index)[[items of 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]
Run Code Online (Sandbox Code Playgroud)

同样的基本想法,只是简单而紧凑地表达......但是,唉,额外的O(N)辅助空间(将群体的迭代体现为列表)和O(N平方)时间(以获得L.index每个项目) .虽然过早的优化是编程中所有邪恶的根源,但是当O(N log N)可用时故意选择O(N平方)方法对于可扩展性的粒度而言太过分了! - )

最后,对于那些喜欢"oneliners"以获得清晰度和表现的人来说,这是一个奖励的1-liner版本,其中包含适当的错误名称:-).

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]
Run Code Online (Sandbox Code Playgroud)

  • 如果您的列表具有不同的类型,则这会在Python3上中断。 (3认同)
  • `groupby`需要先排序(O(NlogN)); 将`Counter()`与`most_common()`一起使用可以克服这一点,因为它使用heapq查找频率最高的项(仅一项,即O(N)时间)。由于现在对Counter()进行了高度优化(计数在C循环中进行),因此即使对于较小的列表,它也可以轻松击败该解决方案。它把它从水里吹出来,列出了很多。 (2认同)

Lui*_*rti 55

你想要的东西在统计学中称为模式,Python当然有一个内置函数来完成你的工作:

>>> from statistics import mode
>>> mode([1, 2, 2, 3, 3, 3, 3, 3, 4, 5, 6, 6, 6])
3
Run Code Online (Sandbox Code Playgroud)

请注意,如果没有"最常见的元素",例如前两个被绑定的情况,这将会提高StatisticsError,因为从统计上讲,在这种情况下没有模式.

  • 当不止一个最常见的值时,这不满足OP对返回内容的要求 - 统计数据.引发了统计信息错误 (8认同)
  • 糟糕,在阅读时错过了要求.我仍然认为这个答案具有价值,因为在这个问题中没有人提出这个问题,对于需求限制最少的人来说,这是一个很好的解决方案.这是"列表python中最常见的项目"的最佳结果之一 (4认同)
  • 粗体文本不再正确。这在 3.8 中已更改:现在通过返回遇到的第一个模式来处理多模式数据集。现在 stats.multimode(data) 可用 (3认同)

The*_*her 31

没有关于最低索引的要求,您可以使用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'
Run Code Online (Sandbox Code Playgroud)

  • 这个答案需要更多的赞成票,因为它解决了使用标准模块和 2 行代码计算列表中元素出现次数的一般任务 (3认同)

Luk*_*ský 9

如果它们不可清洗,您可以对它们进行排序,并对计算项目的结果进行一次循环(相同的项目将彼此相邻).但它可以更快地使它们可以使用并使用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
Run Code Online (Sandbox Code Playgroud)


Thi*_*ony 6

这是一个O(n)解决方案.

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
Run Code Online (Sandbox Code Playgroud)

(反向用于确保它返回最低的索引项)


Boo*_*jum 5

对列表的副本进行排序,找到最长的运行时间.您可以在使用每个元素的索引对其进行排序之前对列表进行装饰,然后在绑定的情况下选择以最低索引开头的运行.


wil*_*urd 5

单行:

def most_common (lst):
    return max(((item, lst.count(item)) for item in set(lst)), key=lambda a: a[1])[0]
Run Code Online (Sandbox Code Playgroud)