numpy 벡터에서 가장 빈번한 숫자 찾기


123

파이썬에 다음 목록이 있다고 가정합니다.

a = [1,2,3,1,2,1,1,1,3,2,2,1]

이 목록에서 가장 빈번한 번호를 깔끔하게 찾는 방법은 무엇입니까?

답변:


193

목록에 음수가 아닌 정수가 모두 포함 된 경우 numpy.bincounts를 살펴 봐야합니다.

http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html

그리고 아마도 np.argmax를 사용하십시오.

a = np.array([1,2,3,1,2,1,1,1,3,2,2,1])
counts = np.bincount(a)
print(np.argmax(counts))

더 복잡한 목록 (음수 또는 정수가 아닌 값 포함)의 np.histogram경우 비슷한 방식으로 사용할 수 있습니다 . 또는 numpy를 사용하지 않고 파이썬으로 작업하고 싶다면 collections.Counter이런 종류의 데이터를 처리하는 좋은 방법입니다.

from collections import Counter
a = [1,2,3,1,2,1,1,1,3,2,2,1]
b = Counter(a)
print(b.most_common(1))

58
+1. 그냥 될 수 있습니다np.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()
Nikolai Fetissov 2011-06-06

1
+1. scipy.stats.mode덜 일반적이긴하지만 이것은 적어도 10 배 더 빠릅니다 .
Fred Foo

좋은 대답입니다! 그러나 누군가 Python 2.6을 사용하는 경우 collections.Counter를 사용할 수 없습니다. 이 경우 아래 내 대답을 참조하십시오.
JJC

19
2016 년 이후에 방문하는 사람들에게 : bincount (arr)는 arr에서 가장 큰 요소만큼 큰 배열을 반환하므로 범위가 큰 작은 배열은 지나치게 큰 배열을 생성하므로이 답변이 마음에 들지 않습니다. 아래 Apoengtus의 답변은 훨씬 낫지 만이 답변이 생성 된 2011 년에 numpy.unique ()가 존재하지 않았다고 생각합니다.
Wehrdo

2
Python 3 :Counter(array).most_common(1)[0][0]
diralik

80

당신은 사용할 수 있습니다

(values,counts) = np.unique(a,return_counts=True)
ind=np.argmax(counts)
print values[ind]  # prints the most frequent element

일부 요소가 다른 요소만큼 자주 발생하는 경우이 코드는 첫 번째 요소 만 반환합니다.


4
이것은 일반적이고 짧으며 일부 파생 인덱스에 의해 값 또는 개수에서 요소를 가져올 수 있기 때문에 가장 유용합니다.
ryanjdillon 2015 년

2
가장 빈번한 값이 여러 개인 경우 values[counts.argmax()]첫 번째 값을 반환합니다. 모두 가져 오기 위해 values[counts == counts.max()].
W. Zhu

44

SciPy 를 사용하려는 경우 :

>>> from scipy.stats import mode
>>> mode([1,2,3,1,2,1,1,1,3,2,2,1])
(array([ 1.]), array([ 6.]))
>>> most_frequent = mode([1,2,3,1,2,1,1,1,3,2,2,1])[0][0]
>>> most_frequent
1.0

30

여기에서 찾을 수있는 일부 솔루션에 대한 성능 (iPython 사용) :

>>> # small array
>>> a = [12,3,65,33,12,3,123,888000]
>>> 
>>> import collections
>>> collections.Counter(a).most_common()[0][0]
3
>>> %timeit collections.Counter(a).most_common()[0][0]
100000 loops, best of 3: 11.3 µs per loop
>>> 
>>> import numpy
>>> numpy.bincount(a).argmax()
3
>>> %timeit numpy.bincount(a).argmax()
100 loops, best of 3: 2.84 ms per loop
>>> 
>>> import scipy.stats
>>> scipy.stats.mode(a)[0][0]
3.0
>>> %timeit scipy.stats.mode(a)[0][0]
10000 loops, best of 3: 172 µs per loop
>>> 
>>> from collections import defaultdict
>>> def jjc(l):
...     d = defaultdict(int)
...     for i in a:
...         d[i] += 1
...     return sorted(d.iteritems(), key=lambda x: x[1], reverse=True)[0]
... 
>>> jjc(a)[0]
3
>>> %timeit jjc(a)[0]
100000 loops, best of 3: 5.58 µs per loop
>>> 
>>> max(map(lambda val: (a.count(val), val), set(a)))[1]
12
>>> %timeit max(map(lambda val: (a.count(val), val), set(a)))[1]
100000 loops, best of 3: 4.11 µs per loop
>>> 

문제와 같은 작은 배열경우 '설정'이있는 '최대'가 가장 좋습니다.

@David Sanders에 따르면 배열 크기를 100,000 개의 요소와 같은 크기로 늘리면 "max w / set"알고리즘은 결국 최악이되는 반면 "numpy bincount"방법이 가장 좋습니다.


1
@IuliusCurt는 여러 사례에 대해 테스트해야하는 최상의 접근 방식을 지적하기 위해 작은 배열, 큰 배열, 임의 배열, 실제 배열 (예 : 정렬을 위해 timsort 가 수행하는 작업), ...하지만 동의합니다
iuridiniz

3
접근 방식에서와 같이 작은 배열 만 사용하면 서로 다른 알고리즘을 잘 구별하지 못합니다.
David Sanders

10
테스트 목록 크기를 100000 ( a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a))으로 늘리면 "max w / set"알고리즘이 최악이되는 반면 "numpy bincount"방법이 가장 좋습니다. 이 테스트 a_list는 네이티브 파이썬 코드와 anumpy 코드를 사용하여 결과를 망치는 마샬링 비용을 피하기 위해 수행했습니다.
데이비드 샌더스

4

또한 모듈을로드하지 않고 가장 빈번한 값 (양수 또는 음수)을 얻으려면 다음 코드를 사용할 수 있습니다.

lVals = [1,2,3,1,2,1,1,1,3,2,2,1]
print max(map(lambda val: (lVals.count(val), val), set(lVals)))

1
이것은 얼마 전의 것이지만 후대에 대한 것입니다. 이것은 읽기 쉬운 것과 동일 하며 대략 O (n ^ 2)에 max(set(lVals), key=lVals.count)대해 각 고유 요소에 lVals대해 O (n) 카운트를 수행합니다 (O (n) 고유하다고 가정). 집단). JoshAdel이 제안한collections.Counter(lVals).most_common(1)[0][0] 대로 표준 라이브러리에서 사용 하는 것은 O (n)뿐입니다.
Dougal

3

위의 답변 대부분이 유용하지만, 1) 양수가 아닌 정수 값 (예 : 부동 소수점 또는 음의 정수 ;-))을 지원하기 위해 필요하고 2) Python 2.7 (컬렉션)에 없습니다. 필요), 3) 코드에 scipy (또는 numpy)의 종속성을 추가하지 않는 것을 선호하는 경우 O (nlogn) (즉, 효율적) 인 순수 파이썬 2.6 솔루션은 다음과 같습니다.

from collections import defaultdict

a = [1,2,3,1,2,1,1,1,3,2,2,1]

d = defaultdict(int)
for i in a:
  d[i] += 1
most_frequent = sorted(d.iteritems(), key=lambda x: x[1], reverse=True)[0]

2

JoshAdel의 솔루션이 마음에 듭니다.

하지만 한 가지 문제가 있습니다.

np.bincount()솔루션은 숫자에만 적용됩니다.

문자열이 있으면 collections.Counter솔루션이 작동합니다.


1

이 방법을 확장 하면 값이 분포의 중심에서 얼마나 멀리 떨어져 있는지 확인하기 위해 실제 배열의 인덱스가 필요할 수있는 데이터 모드를 찾는 데 적용됩니다.

(_, idx, counts) = np.unique(a, return_index=True, return_counts=True)
index = idx[np.argmax(counts)]
mode = a[index]

len (np.argmax (counts))> 1 인 경우 모드를 폐기해야합니다.



1

에서 시작 Python 3.4하는 표준 라이브러리에는 statistics.mode가장 일반적인 단일 데이터 포인트를 반환하는 함수가 포함되어 있습니다.

from statistics import mode

mode([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1])
# 1

주파수가 동일한 모드가 여러 개있는 경우 statistics.mode처음 발견 된 모드를 반환합니다.


에서 시작 Python 3.8하면이 statistics.multimode함수는 가장 자주 발생하는 값의 목록을 처음 발견 한 순서대로 반환합니다.

from statistics import multimode

multimode([1, 2, 3, 1, 2])
# [1, 2]

0

다음은 순전히 numpy를 사용하여 값에 관계없이 축을 따라 적용될 수있는 일반적인 솔루션입니다. 또한 고유 한 값이 많으면 scipy.stats.mode보다 훨씬 빠릅니다.

import numpy

def mode(ndarray, axis=0):
    # Check inputs
    ndarray = numpy.asarray(ndarray)
    ndim = ndarray.ndim
    if ndarray.size == 1:
        return (ndarray[0], 1)
    elif ndarray.size == 0:
        raise Exception('Cannot compute mode on empty array')
    try:
        axis = range(ndarray.ndim)[axis]
    except:
        raise Exception('Axis "{}" incompatible with the {}-dimension array'.format(axis, ndim))

    # If array is 1-D and numpy version is > 1.9 numpy.unique will suffice
    if all([ndim == 1,
            int(numpy.__version__.split('.')[0]) >= 1,
            int(numpy.__version__.split('.')[1]) >= 9]):
        modals, counts = numpy.unique(ndarray, return_counts=True)
        index = numpy.argmax(counts)
        return modals[index], counts[index]

    # Sort array
    sort = numpy.sort(ndarray, axis=axis)
    # Create array to transpose along the axis and get padding shape
    transpose = numpy.roll(numpy.arange(ndim)[::-1], axis)
    shape = list(sort.shape)
    shape[axis] = 1
    # Create a boolean array along strides of unique values
    strides = numpy.concatenate([numpy.zeros(shape=shape, dtype='bool'),
                                 numpy.diff(sort, axis=axis) == 0,
                                 numpy.zeros(shape=shape, dtype='bool')],
                                axis=axis).transpose(transpose).ravel()
    # Count the stride lengths
    counts = numpy.cumsum(strides)
    counts[~strides] = numpy.concatenate([[0], numpy.diff(counts[~strides])])
    counts[strides] = 0
    # Get shape of padded counts and slice to return to the original shape
    shape = numpy.array(sort.shape)
    shape[axis] += 1
    shape = shape[transpose]
    slices = [slice(None)] * ndim
    slices[axis] = slice(1, None)
    # Reshape and compute final counts
    counts = counts.reshape(shape).transpose(transpose)[slices] + 1

    # Find maximum counts and return modals/counts
    slices = [slice(None, i) for i in sort.shape]
    del slices[axis]
    index = numpy.ogrid[slices]
    index.insert(axis, numpy.argmax(counts, axis=axis))
    return sort[index], counts[index]

-1

나는 최근에 프로젝트를하고 collections.Counter. (나를 고문 한)를 사용하고 있습니다.

컬렉션의 카운터는 제 생각에 매우 나쁜 성능을 가지고 있습니다. dict ()를 래핑하는 클래스 일뿐입니다.

더 나쁜 것은 cProfile을 사용하여 메서드를 프로파일 링하는 경우 전체 시간을 낭비하는 '__missing__'및 '__instancecheck__'항목을 많이 볼 수 있습니다.

가장 느린 정렬을 호출 할 때마다 most_common () 사용에주의하십시오. 그리고 most_common (x)를 사용하면 힙 정렬을 호출하는데,이 역시 느립니다.

Btw, numpy의 bincount에도 문제가 있습니다. np.bincount ([1,2,4000000])를 사용하면 4000000 요소가있는 배열을 얻을 수 있습니다.


3
dict는 Python에서 가장 미세하게 조정 된 데이터 구조이며 임의의 객체를 계산하는 데 이상적입니다. 반대로 비닝은 숫자 값에 대해서만 작동하며 밀접하게 배치 된 이산 값 간의 앨리어싱을 방지 할 수 없습니다. Counter의 경우 __missing__ 메서드는 요소가 처음 나타날 때만 호출됩니다. 그렇지 않으면 비용이 들지 않습니다. 참고는 most_common () 힙이 전체 데이터 세트에 비해 매우 작기 때문에 방법은 빠른 대부분의 경우 엄청나게입니다. 대부분의 경우 most_common () 메서드는 min () 보다 약간 더 많은 비교를 수행 합니다.
Raymond Hettinger 2013 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.