Tìm phần tử phổ biến nhất trong danh sách


174

Cách hiệu quả để tìm phần tử phổ biến nhất trong danh sách Python là gì?

Các mục trong danh sách của tôi có thể không được băm nên không thể sử dụng từ điển. Ngoài ra trong trường hợp rút ra, mục có chỉ số thấp nhất sẽ được trả lại. Thí dụ:

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

2
Nếu các mục trong danh sách không thể băm được, bạn sẽ xác định khi nào chúng bằng '? Mất hiệu quả trong việc xác định công bằng cho các mục không thể băm có thể sẽ phủ nhận bất kỳ hiệu quả nào bạn hy vọng đạt được với một thuật toán tốt :)
HS.

3
Tôi nghĩ rằng anh ta có nghĩa là các vật phẩm có thể thay đổi và do đó không thể trở thành chìa khóa trong một hashmap ...
fortran

1
vâng đó là những gì tôi muốn nói - đôi khi nó sẽ chứa các danh sách
hoju


Câu trả lời:


96

Với rất nhiều giải pháp được đề xuất, tôi ngạc nhiên không ai đề xuất những gì tôi coi là một giải pháp rõ ràng (đối với các yếu tố không thể băm nhưng có thể so sánh được) - [ itertools.groupby] [1]. itertoolscung cấp chức năng nhanh, có thể tái sử dụng và cho phép bạn ủy thác một số logic phức tạp cho các thành phần thư viện tiêu chuẩn được kiểm tra tốt. Xem xét ví dụ:

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]

Điều này có thể được viết chính xác hơn, tất nhiên, nhưng tôi đang hướng đến sự rõ ràng tối đa. Hai printtuyên bố có thể không được chú ý để thấy rõ hơn máy móc đang hoạt động; ví dụ, với các bản in không bị lỗi:

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

phát ra:

SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose

Như bạn thấy, SLlà một danh sách các cặp, mỗi cặp một mục theo sau là chỉ mục của mục trong danh sách ban đầu (để thực hiện điều kiện chính là, nếu các mục "phổ biến nhất" có cùng số lượng cao nhất là> 1, kết quả phải là người xuất hiện sớm nhất).

groupbynhóm theo mục chỉ (thông qua operator.itemgetter). Hàm phụ trợ, được gọi một lần cho mỗi nhóm trong quá trình maxtính toán, nhận và giải nén bên trong một nhóm - một tuple với hai mục (item, iterable)trong đó các mục của iterable cũng là các bộ dữ liệu hai mục, (item, original index)[[các mục của SL]].

Sau đó, hàm phụ trợ sử dụng một vòng lặp để xác định cả số lượng mục trong vòng lặp của nhóm chỉ mục gốc tối thiểu; nó trả về những cái đó là "khóa chất lượng" kết hợp, với chỉ số tối thiểu được thay đổi để maxhoạt động sẽ xem xét "tốt hơn" những mục đã xảy ra trước đó trong danh sách ban đầu.

Mã này có thể đơn giản hơn nhiều nếu nó lo lắng hơn một chút về các vấn đề lớn về thời gian và không gian, ví dụ ...

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]

cùng một ý tưởng cơ bản, chỉ được thể hiện đơn giản và gọn gàng hơn ... nhưng, than ôi, một không gian phụ O (N) bổ sung (để thể hiện các lần lặp của nhóm vào danh sách) và thời gian O (N bình phương) (để có được L.indexmọi mục) . Mặc dù tối ưu hóa sớm là gốc rễ của mọi tội lỗi trong lập trình, nhưng việc cố tình chọn cách tiếp cận O (N bình phương) khi phương pháp O (N log N) có sẵn chỉ là đi quá nhiều so với khả năng mở rộng! -)

Cuối cùng, đối với những người thích "oneliners" cho sự rõ ràng và hiệu suất, một phiên bản 1 phần thưởng có tên được xáo trộn phù hợp :-).

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]

3
Điều này phá vỡ trên Python3 nếu danh sách của bạn có các loại khác nhau.
AlexLordThorsen

2
groupbyyêu cầu sắp xếp trước (O (NlogN)); sử dụng a Counter()with most_common()có thể đánh bại điều đó bởi vì nó sử dụng heapq để tìm mục tần số cao nhất (chỉ với 1 mục, đó là thời gian O (N)). Vì Counter()bây giờ được tối ưu hóa rất nhiều (đếm diễn ra trong một vòng lặp C), nó có thể dễ dàng đánh bại giải pháp này ngay cả đối với các danh sách nhỏ. Nó thổi nó ra khỏi nước cho danh sách lớn.
Martijn Pieters

Chỉ yêu cầu 'chỉ số thấp nhất' cho các mối quan hệ làm cho điều này trở thành một giải pháp hợp lệ cho vấn đề này. Đối với trường hợp tổng quát hơn, bạn chắc chắn nên sử dụng phương pháp Counter.
Martijn Pieters

@MartijnPieters Có lẽ bạn đã bỏ lỡ một phần của câu hỏi mà nó nói các mục có thể không thể hiểu được.
wim

@wim đúng, và nếu các mặt hàng là không thể. Điều này làm cho số phiếu trên tập hợp và cách tiếp cận tối đa càng trở nên phi lý hơn.
Martijn Pieters

442

Một lớp lót đơn giản hơn:

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

24
OP tuyên bố rằng [..] trong trường hợp rút ra vật phẩm có chỉ số thấp nhất sẽ được trả lại. Mã này, nói chung, không đáp ứng yêu cầu đó.
Stephan202

2
Thêm vào đó, OP tuyên bố rằng các yếu tố phải có khả năng băm: các bộ phải chứa các đối tượng có thể băm.
Eric O Lebigot

2
Thêm vào đó, cách tiếp cận này chậm về mặt thuật toán (đối với từng yếu tố trong set(lst), toàn bộ danh sách phải được kiểm tra lại) Có lẽ đủ nhanh cho hầu hết các mục đích sử dụng, mặc dù
giật

9
Bạn có thể thay thế set(lst)bằng lstvà nó cũng sẽ hoạt động với các yếu tố không thể băm; mặc dù chậm hơn
newacct

24
Điều này có thể trông hấp dẫn nhưng từ quan điểm thuật toán, đây là lời khuyên khủng khiếp. list.count()phải duyệt qua danh sách đầy đủ và bạn làm như vậy cho mỗi mục duy nhất trong danh sách. Điều này làm cho đây là một giải pháp O (NK) (O (N ^ 2) trong trường hợp xấu nhất). Sử dụng Counter()chỉ mất thời gian O (N)!
Martijn Pieters

185

Mượn từ đây , điều này có thể được sử dụng với Python 2.7:

from collections import Counter

def Most_Common(lst):
    data = Counter(lst)
    return data.most_common(1)[0][0]

Hoạt động nhanh hơn khoảng 4 - 6 lần so với các giải pháp của Alex và nhanh hơn 50 lần so với giải pháp một lớp do newacct đề xuất.

Để lấy phần tử xuất hiện đầu tiên trong danh sách trong trường hợp quan hệ:

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

3
Điều này có thể hữu ích với một số người nhưng ... thật không may, Counter là một lớp con chính tả và OP cho biết anh ta không thể sử dụng từ điển (vì các mục có thể không thể băm được).
Danimal

13
Thích cái này. Một-liner của @newacct ở trên có thể đơn giản, nhưng nó chạy trong O (n ^ 2); đó là, trong đó n là độ dài của danh sách. Giải pháp này là O (n).
BoltzmannBrain

5
Giống như sự đơn giản và tốc độ ... có lẽ không lý tưởng cho OP. Nhưng rất hợp với tôi!
Thơm

không trả về mục được lập chỉ mục thấp nhất. most_common trả về một danh sách không có thứ tự và lấy (1) chỉ trả về bất cứ thứ gì nó muốn.
AgentBawls

@AgentBawls: most_commonđược sắp xếp theo số lượng, không theo thứ tự. Điều đó nói rằng, nó sẽ không chọn yếu tố đầu tiên trong trường hợp quan hệ; Tôi đã thêm một cách khác để sử dụng bộ đếm chọn phần tử đầu tiên.
user2357112 hỗ trợ Monica

58

Những gì bạn muốn được biết đến trong thống kê là chế độ và tất nhiên Python có chức năng tích hợp sẵn để thực hiện chính xác điều đó cho bạn:

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

Lưu ý rằng nếu không có "yếu tố phổ biến nhất", chẳng hạn như các trường hợp hai phần trên cùng bị ràng buộc , thì điều này sẽ tăng lên StatisticsError, vì nói theo thống kê, không có chế độ nào trong trường hợp này.


8
điều này không đáp ứng yêu cầu của OP về những gì sẽ trả lại khi có nhiều hơn một giá trị phổ biến nhất - một số liệu thống kê .StatisticError được nêu ra
Keith Hall

5
Rất tiếc, đã bỏ lỡ yêu cầu khi đọc nó. Tôi vẫn tin rằng câu trả lời này có giá trị mặc dù, vì không ai đề xuất nó trong câu hỏi này, và nó là một giải pháp tốt cho vấn đề cho những người có yêu cầu ít hạn chế nhất. Đây là một trong những kết quả hàng đầu cho "mục phổ biến nhất trong danh sách trăn"
Luiz Berti

1
Trong trường hợp đó, sử dụng hàm chế độ trong DataFrames của gấu trúc.
Elmex80s

1
Up-vote, cái này nên cao hơn. Và không khó để đáp ứng yêu cầu của OP chỉ với thử đơn giản (xem stackoverflow.com/a/52952300/6646912 )
krassowski

1
@BreakBadSP câu trả lời của bạn sử dụng nhiều bộ nhớ hơn vì bổ sung setvà rất hợp lý O(n^3).
Luiz Berti

9

Nếu chúng không thể băm được, bạn có thể sắp xếp chúng và thực hiện một vòng lặp trên kết quả đếm các mục (các mục giống hệt nhau sẽ nằm cạnh nhau). Nhưng nó có thể nhanh hơn để làm cho chúng có thể băm và sử dụng một lệnh.

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

Đây là một cách đơn giản hơn ideone.com/Nq81vf , so sánh với Counter()giải pháp của Alex
Miguel

6

Đây là một giải pháp 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

(đảo ngược được sử dụng để đảm bảo rằng nó trả về mục chỉ mục thấp nhất)


5

Sắp xếp một bản sao của danh sách và tìm ra lâu nhất. Bạn có thể trang trí danh sách trước khi sắp xếp nó với chỉ mục của từng yếu tố, sau đó chọn chạy bắt đầu với chỉ số thấp nhất trong trường hợp hòa.


Các mặt hàng có thể không thể so sánh.
Pawel Furmaniak

5

Nếu không có yêu cầu về chỉ số thấp nhất, bạn có thể sử dụng collections.Countercho điều này:

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'

Dễ dàng và nhanh chóng. Bạn là Bố già của tôi
chainstair

câu trả lời này cần nhiều sự nâng cao hơn vì nó giải quyết nhiệm vụ chung là đếm số lần xuất hiện của phần tử trong danh sách bằng mô-đun chuẩn và 2 dòng mã
pcko1

4

Một lớp lót:

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

3
# 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'

3

Giải pháp một dòng đơn giản

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

Nó sẽ trả về phần tử thường xuyên nhất với tần số của nó.


2

Bạn có thể không cần điều này nữa, nhưng đây là những gì tôi đã làm cho một vấn đề tương tự. (Có vẻ lâu hơn là vì các ý kiến.)

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

1
bạn có thể sử dụng bộ đếm [item] = counter.get (item, 0) + 1 để thay thế phần thử / ngoại trừ
XueYu

1

Dựa trên câu trả lời của Luiz , nhưng đáp ứng điều kiện " trong trường hợp rút ra vật phẩm có chỉ số thấp nhất sẽ được trả lại ":

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

Thí dụ:

>>> most_common(['a', 'b', 'b'])
'b'
>>> most_common([1, 2])
1
>>> most_common([])
StatisticsError: no mode for empty data

0

Đây:

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

Tôi có một cảm giác mơ hồ có một phương pháp ở đâu đó trong thư viện tiêu chuẩn sẽ cung cấp cho bạn số lượng của từng yếu tố, nhưng tôi không thể tìm thấy nó.


3
"Tối đa" là một phương pháp. Bạn sẽ thay đổi tên của biến?
Pratik Deoghare

1
Lưu ý rằng set () cũng yêu cầu các mục có thể băm, để giải pháp không hoạt động trong trường hợp này.
Lukáš Lalinský

Đợi đã, tôi đã bỏ lỡ một phần của việc không thể băm được. Nhưng nếu các đối tượng có sự bình đẳng thì sẽ dễ dàng làm cho chúng có thể băm được.
Lennart Regebro

0

Đây là giải pháp chậm rõ ràng (O (n ^ 2)) nếu không sắp xếp hay băm là khả thi, nhưng so sánh bằng ( ==) có sẵn:

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]

Nhưng làm cho các mục của bạn có thể băm hoặc sắp xếp được (như được đề xuất bởi các câu trả lời khác) hầu như luôn luôn giúp việc tìm kiếm phần tử phổ biến nhất nhanh hơn nếu độ dài của danh sách của bạn (n) lớn. Trung bình O (n) với băm và O (n * log (n)) tệ nhất để sắp xếp.


Đối với downvoter: câu trả lời này có gì sai? Có bất kỳ câu trả lời nào khác cung cấp một giải pháp khi không sắp xếp hay băm là khả thi không?
pts

0
>>> 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'

Điều này có đặc tính hiệu suất khủng khi n lớn và số phần tử duy nhất cũng lớn: O (n) để chuyển đổi thành tập hợp và O (m * n) = O (n ^ 2) cho số đếm (trong đó m là số lượng đơn vị). Sắp xếp và đi bộ là O (n log n) cho sắp xếp và 0 (n) cho đi bộ.
jmucchiello

1
Vâng, bạn đúng. Bây giờ tôi biết đây là một giải pháp khủng khiếp và tại sao. Cảm ơn vì đã bình luận!! :-)
Pratik Deoghare

0

Tôi cần phải làm điều này trong một chương trình gần đây. Tôi sẽ thừa nhận nó, tôi không thể hiểu câu trả lời của Alex, vì vậy đây là những gì tôi đã kết thúc.

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

Tôi đã hẹn giờ với giải pháp của Alex và nó nhanh hơn khoảng 10 - 15% cho các danh sách ngắn, nhưng một khi bạn vượt qua 100 yếu tố trở lên (đã thử nghiệm tới 200000) thì chậm hơn khoảng 20%.


-1

Xin chào, đây là một giải pháp rất đơn giản với chữ O lớn (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

Trường hợp đánh số phần tử trong danh sách lặp lại hầu hết thời gian


-2
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

mostCommonEuity (["a", "b", "a", "c"]) -> "a"


Tất cả các câu trả lời khác. Bạn có muốn tôi liên kết chúng?
12 rhombi trong lưới w không có góc

-3
 def most_common(lst):
    if max([lst.count(i)for i in lst]) == 1:
        return False
    else:
        return max(set(lst), key=lst.count)

6
Vui lòng cung cấp một số thông tin về mã của bạn, chỉ cần đăng mã không phải là câu trả lời hoàn chỉnh
jhhoff02

1
Có một lý do ai đó nên sử dụng điều này hơn 15 câu trả lời khác?
Tất cả công nhân là cần thiết

-5
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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.