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

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

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

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



답변

제안 된 솔루션이 너무 많아서 아무도 해쉬 할 수 없지만 비교할 수없는 요소 인 것으로 생각되는 것을 제안한 사람이 아무도 없습니다 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]


답변

더 간단한 원 라이너 :

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


답변

에서 빌리기 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)


답변

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

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

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


답변

그것들이 해시 가능하지 않다면, 그것들을 정렬하고 항목을 세는 결과에 대해 단일 루프를 수행 할 수 있습니다 (동일한 항목은 나란히 있습니다). 그러나 해시 가능하고 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


답변

이것은 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

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


답변

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