【问题标题】:Find the most common element in a list查找列表中最常见的元素
【发布时间】:2010-12-03 20:41:10
【问题描述】:

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

我的列表项可能无法散列,因此无法使用字典。 此外,在绘制的情况下,应返回索引最低的项目。示例:

>>> most_common(['duck', 'duck', 'goose'])
'duck'
>>> most_common(['goose', 'duck', 'duck', 'goose'])
'goose'

【问题讨论】:

标签: python list


【解决方案1】:

如果它们不是可散列的,您可以对它们进行排序,并对结果进行一次循环计算项目(相同的项目将彼此相邻)。但是让它们可散列并使用字典可能会更快。

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

【讨论】:

  • 这是一个更简单的方法 ideone.com/Nq81vf ,与 Alex 的 Counter() 解决方案相比
【解决方案2】:

对列表的副本进行排序并找到最长的运行。您可以在使用每个元素的索引对其进行排序之前装饰列表,然后在平局的情况下选择以最低索引开始的运行。

【讨论】:

  • 这些项目可能没有可比性。
【解决方案3】:

如果排序和散列都不可行,但相等比较 (==) 可用,这显然是缓慢的解决方案 (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))。

【讨论】:

  • 致反对者:这个答案有什么问题?当排序和散列都不可行时,其他答案是否提供了解决方案?
【解决方案4】:

这里:

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

我有一种模糊的感觉,标准库中的某处有一种方法可以为您提供每个元素的计数,但我找不到。

【讨论】:

  • 'max' 是一种方法。你会更改变量的名称吗?
  • 请注意,set() 还需要可散列项,在这种情况下解决方案将不起作用。
  • 等等,我错过了不可哈希的那部分。但是,如果对象具有相等性,则应该很容易使它们可散列。
【解决方案5】:
>>> 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'

【讨论】:

  • 当 n 很大并且唯一元素的数量也很大时,这具有糟糕的性能特征:转换为集合的 O(n) 和 O(m*n)=O(n^ 2) 用于计数(其中 m 是唯一数)。排序和步行是 O(n log n) 的排序和 0(n) 的步行。
  • 是的,你是对的。现在我知道这是一个糟糕的解决方案以及为什么。谢谢评论!! :-)
【解决方案6】:

单行:

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

【讨论】:

    【解决方案7】:

    更简单的单行:

    def most_common(lst):
        return max(set(lst), key=lst.count)
    

    【讨论】:

    • OP 声明 [..] 在绘制的情况下应返回具有最低索引的项目。 通常,此代码不满足该要求。
    • 另外,OP 声明元素必须是可散列的:集合必须包含可散列的对象。
    • 另外,这种方法在算法上很慢(对于set(lst) 中的每个元素,必须再次检查整个列表)......不过对于大多数用途来说可能足够快......
    • 您可以将set(lst) 替换为lst,它也适用于不可散列的元素;尽管速度较慢。
    • 这可能看起来有吸引力,但从算法的角度来看,这是一个糟糕的建议。 list.count() 必须完整地 遍历列表,并且您对列表中的每个唯一项 都这样做。这使得这是一个 O(NK) 解决方案(在最坏的情况下为 O(N^2))。使用 Counter() 只需要 O(N) 时间!
    【解决方案8】:
    # 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'
    

    【讨论】:

      【解决方案9】:

      这是一个 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
      

      (reversed 用于确保返回最低索引项)

      【讨论】:

        【解决方案10】:

        提出了这么多解决方案,我很惊讶没有人提出我认为显而易见的解决方案(对于不可散列但可比较的元素)——[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 语句以更好地查看运行中的机制;例如,with 打印未注释:

        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) [[@ 的项目987654333@]].

        然后辅助函数使用循环来确定组的可迭代项中的条目数,最小原始索引;它将这些作为组合的“质量键”返回,最小索引符号已更改,因此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 平方) 时间(以获得 @每个项目的987654336@)。虽然过早的优化是编程中万恶之源,但当 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]
        

        【讨论】:

        • 如果您的列表有不同的类型,这会在 Python3 上中断。
        • groupby 需要先排序 (O(NlogN));使用 Counter()most_common() 可以击败它,因为它使用 heapq 来查找频率最高的项目(对于 1 个项目,这是 O(N) 时间)。由于Counter() 现在经过高度优化(计数发生在 C 循环中),即使对于小列表,它也可以轻松击败此解决方案。对于大型列表,它会将其从水中吹出来。
        • 只有对关系的“最低索引”要求才能使这成为解决此问题的有效解决方案。对于更一般的情况,您绝对应该使用 Counter 方法。
        • @MartijnPieters 也许你错过了问题的一部分,它说项目可能是不可散列的。
        • @wim 对,如果项目是不可散列的。这使得 set 和 max 方法的投票更加不协调。
        【解决方案11】:

        您可能不再需要这个,但这是我为类似问题所做的。 (由于 cmets,它看起来比实际更长。)

        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
        

        【讨论】:

        • 你可以使用 counter[item] = counter.get(item, 0) + 1 来替换 try/except 部分
        【解决方案12】:

        借用here,这可用于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 提出的 one-liner 快 50 倍。

        在出现平局的情况下检索列表中第一个出现的元素:

        def most_common(lst):
            data = Counter(lst)
            return max(lst, key=data.get)
        

        【讨论】:

        • 这可能对某些人有用,但......不幸的是 Counter 是一个 dict 子类,并且 OP 说他不能使用字典(因为项目可能不可散列)。
        • 喜欢这个。上面@newacct 的单线可能很简单,但它运行在 O(n^2);也就是说,其中 n 是列表的长度。这个解决方案是 O(n)。
        • 喜欢简单和速度……也许不适合 OP。但非常适合我!
        • 不返回最低索引项。 most_common 返回一个无序列表,抓取 (1) 只会返回它想要的任何内容。
        • @AgentBawls: most_common 是按计数排序的,而不是无序的。也就是说,如果有关系,它不会选择第一个元素;我添加了另一种方法来使用选择第一个元素的计数器。
        【解决方案13】:

        你想要的在统计学中被称为模式,Python当然有一个内置函数可以为你做这件事:

        >>> from statistics import mode
        >>> mode([1, 2, 2, 3, 3, 3, 3, 3, 4, 5, 6, 6, 6])
        3
        

        请注意,如果没有“最常见的元素”,例如前两个并列的情况,这将引发StatisticsError,因为从统计上讲,没有模式 在这种情况下。

        【讨论】:

        • 这不满足 OP 的要求,即当有多个最常见的值时返回什么 - 引发了 statistics.StatisticsError
        • 糟糕,阅读时错过了要求。我仍然相信这个答案是有价值的,因为没有人在这个问题中提出这个建议,对于限制性要求最低的人来说,这是一个很好的解决方案。这是“列表 python 中最常见的项目”的最佳结果之一
        • 在这种情况下使用 pandas DataFrames 中的 mode 函数。
        • 赞成,这个应该更高。并且通过简单的 try-except 来满足 OP 的要求并不难(见我的stackoverflow.com/a/52952300/6646912
        • 粗体字不再正确。这在 3.8 中已更改:现在通过返回遇到的第一个模式来处理多模式数据集。现在 statistics.multimode(data) 可用
        【解决方案14】:
        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)
        

        【讨论】:

          【解决方案15】:
           def most_common(lst):
              if max([lst.count(i)for i in lst]) == 1:
                  return False
              else:
                  return max(set(lst), key=lst.count)
          

          【讨论】:

          • 请提供一些有关您的代码的信息,仅发布代码不是完整的答案
          • 有人应该在其他 15 个答案中使用它吗?
          【解决方案16】:

          我需要在最近的一个程序中这样做。我承认,我听不懂亚历克斯的回答,所以这就是我的结局。

          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%。

          【讨论】:

            【解决方案17】:

            嗨,这是一个非常简单的解决方案,O(n) 很大

            L = [1, 4, 7, 5, 5, 4, 5]
            
            def mode_f(L):
            # your code here
                counter = 0
                number = L[0]
                for i in L:
                    amount_times = L.count(i)
                    if amount_times > counter:
                        counter = amount_times
                        number = i
            
                return number
            

            列表中重复次数最多的元素的编号

            【讨论】:

              【解决方案18】:

              Luiz's answer 为基础,但满足“在绘制时应返回索引最低的项目”条件:

              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
              

              【讨论】:

                【解决方案19】:

                简单的一条线解决方案

                moc= max([(lst.count(chr),chr) for chr in set(lst)])
                

                它将返回最频繁出现的元素及其频率。

                【讨论】:

                  【解决方案20】:
                  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
                  

                  最常见元素(["a","b","a","c"]) -> "a"

                  【讨论】:

                  • 所有其他答案。你想让我把它们链接起来吗?
                  【解决方案21】:

                  没有最低索引的要求,您可以使用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'
                  

                  【讨论】:

                  • 这个答案需要更多的支持,因为它解决了使用标准模块和 2 行代码计算列表中元素出现次数的一般任务
                  【解决方案22】:

                  我正在使用 scipy stat 模块和 lambda:

                  import scipy.stats
                  lst = [1,2,3,4,5,6,7,5]
                  most_freq_val = lambda x: scipy.stats.mode(x)[0][0]
                  print(most_freq_val(lst))
                  

                  结果:

                   most_freq_val = 5
                  

                  【讨论】:

                    【解决方案23】:
                    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

                    【讨论】:

                      【解决方案24】:

                      最常见的元素应该是在数组中出现超过N/2 次的元素,其中Nlen(array)。以下技术将以O(n) 的时间复杂度完成此操作,仅消耗O(1) 辅助空间。

                      from collections import Counter
                      
                      def majorityElement(arr):        
                          majority_elem = Counter(arr)
                          size = len(arr)
                          for key, val in majority_elem.items():
                              if val > size/2:
                                  return key
                          return -1
                      

                      【讨论】:

                        【解决方案25】:
                        #This will return the list sorted by frequency:
                        
                        def orderByFrequency(list):
                        
                            listUniqueValues = np.unique(list)
                            listQty = []
                            listOrderedByFrequency = []
                            
                            for i in range(len(listUniqueValues)):
                                listQty.append(list.count(listUniqueValues[i]))
                            for i in range(len(listQty)):
                                index_bigger = np.argmax(listQty)
                                for j in range(listQty[index_bigger]):
                                    listOrderedByFrequency.append(listUniqueValues[index_bigger])
                                listQty[index_bigger] = -1
                            return listOrderedByFrequency
                        
                        #And this will return a list with the most frequent values in a list:
                        
                        def getMostFrequentValues(list):
                            
                            if (len(list) <= 1):
                                return list
                            
                            list_most_frequent = []
                            list_ordered_by_frequency = orderByFrequency(list)
                            
                            list_most_frequent.append(list_ordered_by_frequency[0])
                            frequency = list_ordered_by_frequency.count(list_ordered_by_frequency[0])
                            
                            index = 0
                            while(index < len(list_ordered_by_frequency)):
                                index = index + frequency
                                
                                if(index < len(list_ordered_by_frequency)):
                                    testValue = list_ordered_by_frequency[index]
                                    testValueFrequency = list_ordered_by_frequency.count(testValue)
                                    
                                    if (testValueFrequency == frequency):
                                        list_most_frequent.append(testValue)
                                    else:
                                        break    
                            
                            return list_most_frequent
                        
                        #tests:
                        print(getMostFrequentValues([]))
                        print(getMostFrequentValues([1]))
                        print(getMostFrequentValues([1,1]))
                        print(getMostFrequentValues([2,1]))
                        print(getMostFrequentValues([2,2,1]))
                        print(getMostFrequentValues([1,2,1,2]))
                        print(getMostFrequentValues([1,2,1,2,2]))
                        print(getMostFrequentValues([3,2,3,5,6,3,2,2]))
                        print(getMostFrequentValues([1,2,2,60,50,3,3,50,3,4,50,4,4,60,60]))
                        
                        Results:
                        []
                        [1]
                        [1]
                        [1, 2]
                        [2]
                        [1, 2]
                        [2]
                        [2, 3]
                        [3, 4, 50, 60]
                        

                        【讨论】:

                          【解决方案26】:
                          def most_frequent(List):
                          
                              counter = 0
                          
                              num = List[0]
                          
                           
                          
                              for i in List:
                          
                                  curr_frequency = List.count(i)
                          
                                  if(curr_frequency> counter):
                          
                                      counter = curr_frequency
                          
                                      num = i
                          
                          
                              return num
                          
                          
                          List = [2, 1, 2, 2, 1, 3]
                          
                          print(most_frequent(List))
                          

                          【讨论】:

                          • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
                          猜你喜欢
                          • 1970-01-01
                          • 2018-09-11
                          • 2017-06-14
                          • 2012-07-06
                          • 1970-01-01
                          相关资源
                          最近更新 更多