목록에서 가장 일반적인 요소 찾기


174

파이썬 목록에서 가장 일반적인 요소를 찾는 효율적인 방법은 무엇입니까?

내 목록 항목을 해시 할 수 없으므로 사전을 사용할 수 없습니다. 또한 추첨의 경우 가장 낮은 색인을 가진 항목을 반환해야합니다. 예:

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

2
목록의 항목이 해시 가능하지 않은 경우 '동일한'시기를 어떻게 결정 하시겠습니까? 해시 불가능 항목에 대한 동등성을 결정하는 효율성 손실은 아마도 좋은 알고리즘으로 얻을 수있는 효율성을 무효화 할 것입니다
.

3
그는 아이템이 변경 가능하여 해시 맵의 키가 될 수 없다는 것을 의미한다고 생각합니다 ...
fortran

1
네, 이것이 제가 의미하는
바입니다.


답변:


96

제안 된 솔루션이 너무 많아서 아무도 해쉬 할 수 없지만 비교할 수없는 요소 인 것으로 생각되는 것을 제안한 사람이 아무도 없습니다 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조작은 원래 목록에서 이전에 발생한 항목을 "더 나은"것으로 간주합니다.

이 코드는 시간과 공간의 큰 문제에 대해 조금 덜 걱정한다면 훨씬 간단 할 수 있습니다 .

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 제곱) 접근 방식을 선택하면 확장 성 수준에 비해 너무 많이 진행됩니다!-)

마지막으로, 명확성과 성능보다 "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]

3
목록에 다른 유형이 있으면 Python3에서 중단됩니다.
AlexLordThorsen

2
groupby먼저 정렬 필요 (O (NlogN)); 사용 Counter()most_common()캔 비트를가 (O (N) 시간 그냥 한 항목에 대해) 가장 높은 주파수 항목을 찾기 위해 heapq을 사용하기 때문입니다. 마찬가지로 Counter()지금 많이 (계산은 C 루프에서 발생) 최적화, 쉽게 아주 작은 목록이 솔루션을 이길 수 있습니다. 큰 목록을 위해 물에서 그것을 날려 버립니다.
Martijn Pieters

관계에 대한 '가장 낮은 인덱스'요구 사항 만이 문제에 대한 올바른 솔루션입니다. 보다 일반적인 경우에는 카운터 접근 방식을 사용해야합니다.
Martijn Pieters

@MartijnPieters 아마도 당신은 그 항목이 해싱 불가능할 수 있다고 말한 부분을 놓쳤을 것입니다.
wim

@wim이 맞고 아이템이 해싱 가능하지 않은 경우. 세트 및 최대 접근 방식에 대한 투표가 더 부적합합니다.
Martijn Pieters

442

더 간단한 원 라이너 :

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

24
OP는 [..] 추첨시 지수가 가장 낮은 품목을 반환해야한다고 명시했습니다. 이 코드는 일반적으로 해당 요구 사항을 충족하지 않습니다.
Stephan202

2
또한 OP는 요소가 해시 가능해야한다고 명시했습니다. 세트에는 해시 가능 객체가 포함되어야합니다.
Eric O Lebigot

2
또한이 방법은 알고리즘 적으로 느립니다 (의 각 요소에 set(lst)대해 전체 목록을 다시 확인해야합니다)… 그러나 대부분의 용도로는 충분히 빠를 것입니다.
Eric O Lebigot

9
당신은 대체 할 수 set(lst)와 함께 lst하고 너무 비 해쉬 요소와 함께 작동합니다; 느리지 만.
newacct

24
이것은 매력적으로 보일지 모르지만 알고리즘 관점에서 이것은 끔찍한 조언입니다. list.count()전체 목록을 순회해야하며 목록의 모든 고유 항목마다 수행해야 합니다. 이것은 이것을 O (NK) 솔루션으로 만듭니다 (최악의 경우 O (N ^ 2)). Counter()O (N) 시간 만 사용하십시오 !
Martijn Pieters

185

에서 빌리기 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에서 제안한 1- 라이너보다 50 배 더 빠릅니다.

관계가있는 경우 목록에서 처음 나타나는 요소를 검색하려면 다음을 수행하십시오.

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

3
이것은 일부에게는 유용하지만 ... 불행히도 Counter는 dict 하위 클래스이며 OP는 사전을 사용할 수 없습니다 (항목을 해시 할 수 없기 때문에).
Danimal

13
이거 너무 좋아. 위의 @newacct에 의한 one-liner는 간단하지만 O (n ^ 2)로 실행됩니다. 즉, 여기서 n은 목록의 길이입니다. 이 솔루션은 O (n)입니다.
BoltzmannBrain

5
단순성과 속도와 마찬가지로 OP에는 적합하지 않을 수 있습니다. 그러나 나에게 적합합니다!
Thom

가장 낮은 인덱스 항목을 반환하지 않습니다. most_common은 순서가없는 목록을 반환하고, 잡아 당기면 (1) 원하는대로 반환합니다.
AgentBawls

@AgentBawls : most_common순서가 아닌 카운트로 정렬됩니다. 그것은 관계의 경우 첫 번째 요소를 선택하지 않을 것입니다; 첫 번째 요소를 선택하는 카운터를 사용하는 다른 방법을 추가했습니다.
user2357112는 Monica

58

원하는 것은 통계에서 모드로 알려져 있으며, 파이썬에는 당연히이를 위해 내장 함수가 있습니다 :

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

상위 2 개가 묶인 경우와 같이 "가장 일반적인 요소"StatisticsError 가 없으면 통계적으로 말하면 이 경우 모드 가 없기 때문에이 값 이 증가 합니다.


8
이것은 가장 일반적인 값이 둘 이상일 때 반환 할 항목에 대한 OP의 요구 사항을 충족하지 않습니다. 통계. 통계 오류가 발생합니다.
Keith Hall

5
죄송합니다. 읽을 때 요구 사항이 누락되었습니다. 나는 여전히이 질문에 아무도 제안하지 않았기 때문에이 대답은 가치가 있다고 믿습니다. 이것은 "목록 파이썬에서 가장 일반적인 항목"에 대한 최고 결과 중 하나입니다.
Luiz Berti

1
이 경우 pandas DataFrames의 모드 기능을 사용하십시오.
Elmex80s

1
찬성 투표, 이보다 높아야합니다. 간단한 try-except를 사용하여 OP의 요구 사항을 충족시키는 것은 그리 어렵지 않습니다 (내 stackoverflow.com/a/52952300/6646912 참조 )
krassowski

1
@BreakBadSP 귀하의 답변은 추가로 인해 더 많은 메모리를 사용하며 set그럴듯 O(n^3)합니다.
Luiz Berti

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

여기에 간단한 방법이다 ideone.com/Nq81vf는 알렉스와 비교 Counter()솔루션
미구엘

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

(역순은 가장 낮은 인덱스 항목을 반환하도록하는 데 사용됩니다)


6

가장 낮은 인덱스에 대한 요구 사항이 없으면 다음을 사용할 수 있습니다 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'

쉽고 빠릅니다. 당신은 내 대부 😏✌ r에
chainstair을

1
이 표준 모듈과 코드의 2 개 라인을 사용하여 목록에서 요소의 발생을 계산의 일반적인 작업을 주소로이 답변이 더 upvotes 필요
pcko1을

5

리스트의 사본을 정렬하고 가장 긴 실행을 찾으십시오. 각 요소의 색인으로 정렬하기 전에 목록을 장식 한 다음 동점 인 경우 가장 낮은 색인으로 시작하는 런을 선택할 수 있습니다.


항목이 비교되지 않을 수 있습니다.
Pawel Furmaniak 2016 년

4

원 라이너 :

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

간단한 원 라인 솔루션

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

빈도가 가장 높은 요소를 반환합니다.


2

더 이상 필요하지 않을 수도 있지만 이것이 비슷한 문제에 대해 한 것입니다. (댓글로 인해보다 오래 보입니다.)

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
시도 / 제외 부분을 대체하기 위해 counter [item] = counter.get (item, 0) + 1을 사용할 수 있습니다
XueYu

1

바탕 루이스의 대답은 하지만 "만족 가장 낮은 인덱스 항목을 그리는 경우에 반환해야한다 "조건을 :

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

0

여기:

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

표준 라이브러리 어딘가에 각 요소의 수를 알려주는 방법이 있지만 모호한 느낌이 들지만 찾을 수 없습니다.


3
'max'는 방법입니다. 변수 이름을 바꾸시겠습니까?
Pratik Deoghare

1
set ()에는 해시 가능 항목이 필요하지만이 경우 솔루션이 작동하지 않습니다.
Lukáš Lalinský

잠깐, 해쉬 가능하지 않은 부분을 놓쳤다. 그러나 객체가 동등하면 해시 가능하게 만들어야합니다.
Lennart Regebro 08 년

0

정렬이나 해싱이 가능하지 않지만 동등 비교 ( ==)를 사용할 수있는 경우 이는 명백한 느린 해결책 (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)).


downvoter에게 :이 답변에 어떤 문제가 있습니까? 정렬이나 해싱이 불가능할 때 다른 답변 중 하나라도 해결책을 제공합니까?
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'

이것은 n이 크고 고유 한 요소의 수가 많을 때 끔찍한 성능 특성을 갖습니다. 집합으로 변환하기위한 O (n) 및 개수 (m 고유 수)입니다. 정렬 및 보행은 정렬에 대해 O (n log n)이고 보행에 대해 0 (n)입니다.
jmucchiello

1
그래 니가 맞아. 이제 이것이 끔찍한 해결책이라는 것을 알고 있습니다. 의견 주셔서 감사합니다 !! :-)
Pratik Deoghare

0

최근 프로그램에서이 작업을 수행해야했습니다. 나는 그것을 인정할 것이다, 나는 Alex의 대답을 이해할 수 없었다. 그래서 이것은 내가 끝내었던 것이다.

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 % 느립니다.


-1

안녕 이것은 큰 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

대부분의 시간에 반복되는 목록의 요소 번호


-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

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


다른 모든 답변. 연결시켜 주실 래요?
그리드에 12 개의 마름모꼴 코너 없음

-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
코드를 게시하는 것이 완전한 답이 아닌 코드에 대한 정보를 입력하십시오
jhhoff02

1
다른 15 가지 답변을 사용해야하는 이유가 있습니까?
모든 근로자는 필수

-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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.