numpy : 배열에서 고유 한 값에 대한 가장 효율적인 빈도 수


244

numpy / scipy,가 효율적으로 배열에서 고유 값에 대한 주파수 카운트를 얻을 수있는 방법은?

이 라인을 따라 뭔가 :

x = array( [1,1,1,2,2,2,5,25,1,1] )
y = freq_count( x )
print y

>> [[1, 5], [2,3], [5,1], [25,1]]

(당신을 위해, R 사용자는 기본적으로 table() 함수를 있습니다)


5
collections.Counter(x)충분?
pylang

1
이 답변이 귀하의 질문에 맞는 것으로 선택하면 stackoverflow.com/a/25943480/9024698으로하는 것이 좋습니다.
추방

Collections.counter는 매우 느립니다. 내 게시물 참조 : stackoverflow.com/questions/41594940/…
Sembei Norimaki

답변:


161

살펴보십시오 np.bincount:

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

import numpy as np
x = np.array([1,1,1,2,2,2,5,25,1,1])
y = np.bincount(x)
ii = np.nonzero(y)[0]

그리고:

zip(ii,y[ii]) 
# [(1, 5), (2, 3), (5, 1), (25, 1)]

또는:

np.vstack((ii,y[ii])).T
# array([[ 1,  5],
         [ 2,  3],
         [ 5,  1],
         [25,  1]])

또는 카운트와 고유 값을 결합하려고합니다.


42
안녕하세요, x의 요소에 int 이외의 dtype이 있으면 작동하지 않습니다.
Manoj

7
음수가 아닌 정수 이외의 것이면 작동하지 않으며 정수가 간격을두면 공간이 비효율적입니다.
Erik

numpy 버전 1.10에서는 정수를 계산할 때 np.unique보다 약 6 배 빠릅니다. 또한 올바른 매개 변수가 제공되면 음수 정수도 계산합니다.
Jihun

@Manoj : 내 요소 x는 배열입니다. jme의 솔루션을 테스트하고 있습니다.
Catalina Chircu

508

Numpy 1.9부터 가장 쉽고 빠른 방법은 단순히 키워드 인수를 numpy.unique갖는을 사용하는 것입니다 return_counts.

import numpy as np

x = np.array([1,1,1,2,2,2,5,25,1,1])
unique, counts = np.unique(x, return_counts=True)

print np.asarray((unique, counts)).T

다음을 제공합니다.

 [[ 1  5]
  [ 2  3]
  [ 5  1]
  [25  1]]

다음과의 빠른 비교 scipy.stats.itemfreq:

In [4]: x = np.random.random_integers(0,100,1e6)

In [5]: %timeit unique, counts = np.unique(x, return_counts=True)
10 loops, best of 3: 31.5 ms per loop

In [6]: %timeit scipy.stats.itemfreq(x)
10 loops, best of 3: 170 ms per loop

22
업데이트 해 주셔서 감사합니다! 이것이 바로 정답입니다.
Erve1879

1
밤! 이것이 우리가 업데이트하는 이유입니다 ... 우리가 이와 같은 답변을 찾으면. 너무 긴 숫자 1.8. 우리는 어떻게 이것을 목록의 최상위에 올릴 수 있습니까?
user1269942

오류가 발생하면 : TypeError : unique () 예기치 않은 키워드 인수 'return_counts'를 얻었습니다. unique, counts = np.unique (x, True)
NumesSanguis

3
@NumesSanguis 어떤 버전의 numpy를 사용하고 있습니까? v1.9 이전에는 return_counts키워드 인수가 존재하지 않아 예외를 설명 할 수 있습니다. 이 경우 문서 에서는 np.unique(x, True)에 해당하며 np.unique(x, return_index=True)카운트를 반환하지 않습니다.
jme

1
오래된 numpy 버전에서는 같은 것을 얻는 전형적인 관용구가있었습니다 unique, idx = np.unique(x, return_inverse=True); counts = np.bincount(idx). 이 기능이 추가되면 ( 여기 참조 ) 일부 비공식 테스트 return_counts에서 5 배 이상 빠른 클럭킹을 사용했습니다 .
Jaime

133

업데이트 : 원래 답변에 언급 된 방법은 더 이상 사용되지 않으므로 대신 새로운 방법을 사용해야합니다.

>>> import numpy as np
>>> x = [1,1,1,2,2,2,5,25,1,1]
>>> np.array(np.unique(x, return_counts=True)).T
    array([[ 1,  5],
           [ 2,  3],
           [ 5,  1],
           [25,  1]])

원래 답변 :

scipy.stats.itemfreq 를 사용할 수 있습니다

>>> from scipy.stats import itemfreq
>>> x = [1,1,1,2,2,2,5,25,1,1]
>>> itemfreq(x)
/usr/local/bin/python:1: DeprecationWarning: `itemfreq` is deprecated! `itemfreq` is deprecated and will be removed in a future version. Use instead `np.unique(..., return_counts=True)`
array([[  1.,   5.],
       [  2.,   3.],
       [  5.,   1.],
       [ 25.,   1.]])

1
훨씬 가장 pythonic 접근법처럼 보입니다. 또한 100k x 100k 행렬에서 np.bincount의 "원하는 배열에 비해 너무 깊은 개체"문제가 발생했습니다.
metasequoia

1
차라리 원래 질문 제기자가 답답한 답변을 첫 번째 질문에서이 질문으로 변경하여 가시성을 높이
도록 제안

그러나 0.14 이전의 버전에서는 느립니다.
Jason S

배열에 문자열이 가득 찬 경우 반환되는 각 항목의 두 요소도 모두 문자열입니다.
user1269942 2016 년

itemfreq 사용 중단 된 것 같습니다
Terence Parr

48

나는 이것에 관심이 있었기 때문에 약간의 성능 비교 ( perfplot , 내 애완 동물 프로젝트 사용 )를 수행했습니다. 결과:

y = np.bincount(a)
ii = np.nonzero(y)[0]
out = np.vstack((ii, y[ii])).T

훨씬 빠릅니다. (로그 스케일링에 유의하십시오.)

여기에 이미지 설명을 입력하십시오


플롯을 생성하는 코드 :

import numpy as np
import pandas as pd
import perfplot
from scipy.stats import itemfreq


def bincount(a):
    y = np.bincount(a)
    ii = np.nonzero(y)[0]
    return np.vstack((ii, y[ii])).T


def unique(a):
    unique, counts = np.unique(a, return_counts=True)
    return np.asarray((unique, counts)).T


def unique_count(a):
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)
    return np.vstack((unique, count)).T


def pandas_value_counts(a):
    out = pd.value_counts(pd.Series(a))
    out.sort_index(inplace=True)
    out = np.stack([out.keys().values, out.values]).T
    return out


perfplot.show(
    setup=lambda n: np.random.randint(0, 1000, n),
    kernels=[bincount, unique, itemfreq, unique_count, pandas_value_counts],
    n_range=[2 ** k for k in range(26)],
    logx=True,
    logy=True,
    xlabel="len(a)",
)

1
줄거리를 생성하는 코드를 게시 해 주셔서 감사합니다. 지금까지 perfplot 에 대해 몰랐습니다 . 편리해 보입니다.
ruffsl

에 옵션 equality_check=array_sorteq을 추가하여 코드를 실행할 수있었습니다 perfplot.show(). 오류를 발생시킨 원인은 (파이썬 2에서) pd.value_counts(정렬 = 거짓으로도)였습니다.
user2314737

33

팬더 모듈 사용 :

>>> import pandas as pd
>>> import numpy as np
>>> x = np.array([1,1,1,2,2,2,5,25,1,1])
>>> pd.value_counts(x)
1     5
2     3
25    1
5     1
dtype: int64

5
pd.Series ()는 필요하지 않습니다. 그렇지 않으면 좋은 예입니다. 장난 꾸러기 팬더는 간단한 목록을 입력으로 사용할 수 있습니다.
요한 오바 디아

1
@ YohanObadia-배열의 크기에 따라 먼저 시리즈로 변환하면 최종 작업이 더 빨라졌습니다. 나는 약 50,000 개의 값으로 추측합니다.
n1k31t4

1
나는 계정으로 @YohanObadia에서 관련 의견을 내 대답을 편집
ivankeller

19

이것은 가장 일반적이고 성능이 뛰어난 솔루션입니다. 아직 게시되지 않았습니다.

import numpy as np

def unique_count(a):
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)
    return np.vstack(( unique, count)).T

print unique_count(np.random.randint(-10,10,100))

현재 허용되는 답변과 달리 긍정적 인 정수뿐만 아니라 정렬 가능한 모든 데이터 유형에서 작동하며 최적의 성능을 제공합니다. 유일한 중요한 비용은 np.unique에 의한 정렬에 있습니다.


작동하지 않습니다 :AttributeError: 'numpy.ufunc' object has no attribute 'at'
PR

가장 간단한 방법은 다음과 같습니다.np.bincount(inverse)
ali_m

15

numpy.bincount아마도 최선의 선택 일 것입니다. 배열에 작은 고밀도 정수 이외의 것이 포함되어 있으면 다음과 같이 감싸는 것이 유용 할 수 있습니다.

def count_unique(keys):
    uniq_keys = np.unique(keys)
    bins = uniq_keys.searchsorted(keys)
    return uniq_keys, np.bincount(bins)

예를 들면 다음과 같습니다.

>>> x = array([1,1,1,2,2,2,5,25,1,1])
>>> count_unique(x)
(array([ 1,  2,  5, 25]), array([5, 3, 1, 1]))

8

이미 답변을 받았지만을 사용하는 다른 접근법을 제안합니다 numpy.histogram. 이러한 함수는 시퀀스가 ​​주어지면 bin으로 그룹화 된 요소의 빈도를 반환합니다 .

그럼에도 불구하고 : 숫자가 정수이기 때문에이 예제에서 작동합니다. 그들이 실제 숫자라면,이 솔루션은 멋지게 적용되지 않을 것입니다.

>>> from numpy import histogram
>>> y = histogram (x, bins=x.max()-1)
>>> y
(array([5, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1]),
 array([  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.,
        12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,  20.,  21.,  22.,
        23.,  24.,  25.]))

5
import pandas as pd
import numpy as np
x = np.array( [1,1,1,2,2,2,5,25,1,1] )
print(dict(pd.Series(x).value_counts()))

{1 : 5, 2 : 3, 5 : 1, 25 : 1}


1
collections.Counter(x)또한 동일한 결과를 제공합니다. OP가 R table함수 와 유사한 출력을 원한다고 생각합니다 . 유지하는 Series것이 더 유용 할 수 있습니다.
pylang

pd.Series(x).reshape(-1)다차원 배열 인 경우 전송해야 합니다.
natsuapo

4

Eelco Hoogendoorn의 답변과 비슷하지만 상당히 빠른 (내 컴퓨터의 5 요소) 고유 한 비 정수 를 계산 weave.inline하기 위해 numpy.unique약간의 c 코드와 결합 했습니다.

import numpy as np
from scipy import weave

def count_unique(datain):
  """
  Similar to numpy.unique function for returning unique members of
  data, but also returns their counts
  """
  data = np.sort(datain)
  uniq = np.unique(data)
  nums = np.zeros(uniq.shape, dtype='int')

  code="""
  int i,count,j;
  j=0;
  count=0;
  for(i=1; i<Ndata[0]; i++){
      count++;
      if(data(i) > data(i-1)){
          nums(j) = count;
          count = 0;
          j++;
      }
  }
  // Handle last value
  nums(j) = count+1;
  """
  weave.inline(code,
      ['data', 'nums'],
      extra_compile_args=['-O2'],
      type_converters=weave.converters.blitz)
  return uniq, nums

프로필 정보

> %timeit count_unique(data)
> 10000 loops, best of 3: 55.1 µs per loop

Eelco의 순수한 numpy버전 :

> %timeit unique_count(data)
> 1000 loops, best of 3: 284 µs per loop

노트

여기에 중복성이 있습니다 ( unique정렬도 수행 함) unique.c 코드 루프 안에 기능 을 넣어 코드를 더 최적화 할 수 있음을 의미합니다 .


4

오래된 질문이지만 벤치 테스트를 기반으로 가장 빠른 것으로 내 자신의 솔루션을 제공하고 싶습니다. 입력 list대신 표준으로 사용하십시오 np.array(또는 먼저 목록으로 전송).

당신도 그것을 발견하면 그것을 확인하십시오 .

def count(a):
    results = {}
    for x in a:
        if x not in results:
            results[x] = 1
        else:
            results[x] += 1
    return results

예를 들어

>>>timeit count([1,1,1,2,2,2,5,25,1,1]) would return:

100000 루프, 루프 당 최고 3 : 2.26 µs

>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]))

100000 루프, 루프 당 3 : 3.8 µs 최고

>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]).tolist())

100000 루프, 루프 당 최고 3 : 5.85 µs

허용 된 답변은 느려질 수 있으며 scipy.stats.itemfreq솔루션은 훨씬 나쁩니다.


보다 심도있는 테스트는 공식화 된 기대치를 확인하지 못했습니다 .

from zmq import Stopwatch
aZmqSTOPWATCH = Stopwatch()

aDataSETasARRAY = ( 100 * abs( np.random.randn( 150000 ) ) ).astype( np.int )
aDataSETasLIST  = aDataSETasARRAY.tolist()

import numba
@numba.jit
def numba_bincount( anObject ):
    np.bincount(    anObject )
    return

aZmqSTOPWATCH.start();np.bincount(    aDataSETasARRAY );aZmqSTOPWATCH.stop()
14328L

aZmqSTOPWATCH.start();numba_bincount( aDataSETasARRAY );aZmqSTOPWATCH.stop()
592L

aZmqSTOPWATCH.start();count(          aDataSETasLIST  );aZmqSTOPWATCH.stop()
148609L

참조 작은 데이터 세트에 영향을 미치는 캐시 및 기타 RAM 내 부작용에 대한 아래의 의견은 대규모 반복 테스트 결과입니다.


이 답변은 numpy반드시 갈 길은 아니기 때문에 정말 좋습니다 .
Mahdi

@Rain Lee가 흥미 롭습니다. 캐시 불가능한 데이터 세트 크기에 대해서도 목록 가설을 교차 검증 했습니까? aZmqStopwatch.start (); count (aRepresentation); aZmqStopwatch.stop () 의 예에서와 같이 어느 하나의 표현에서 150.000 개의 임의 항목을 가정하고 단일 실행에서 조금 더 정확하게 측정 할 수 있습니다 .
user3666197

몇 가지 테스트를 수행했지만 실제 데이터 세트 성능 에는 큰 차이 가 있습니다. 테스트에는 무차별 적으로 스케일링 된 루프를 실행하는 것보다 비현실적인 생체 외 나노초를 인용하는 것보다 파이썬 내부 메커니즘에 대한 통찰력이 필요합니다 . 테스트 한대로 np.bincount ()600 [us] 미만 에서 150.000 배열을 처리하도록 만들 수 있으며 사전 변환 된 목록 표현 에서 위의 def- ed count ()122.000
user3666197

그래, 내 경험 법칙이다 NumPy와 매우 크게 지연의 적은 양을 처리하지만 잠재력을 가지고 수있는 일에 대한 목록 대기 시간이 중요한 작은 데이터 세트에 대한, 그리고 물론 실제 벤치마킹 : FTW
데이비드

1

이와 같은 일이해야합니다.

#create 100 random numbers
arr = numpy.random.random_integers(0,50,100)

#create a dictionary of the unique values
d = dict([(i,0) for i in numpy.unique(arr)])
for number in arr:
    d[j]+=1   #increment when that value is found

또한 독특한 요소효율적으로 계산하는 이전 게시물은 내가 빠진 것이 아니라면 귀하의 질문과 매우 유사합니다.


연결된 질문은 다소 비슷하지만 더 복잡한 데이터 형식으로 작업하는 것처럼 보입니다.
Abe

1

다차원 주파수 카운트, 즉 카운트 어레이.

>>> print(color_array    )
  array([[255, 128, 128],
   [255, 128, 128],
   [255, 128, 128],
   ...,
   [255, 128, 128],
   [255, 128, 128],
   [255, 128, 128]], dtype=uint8)


>>> np.unique(color_array,return_counts=True,axis=0)
  (array([[ 60, 151, 161],
    [ 60, 155, 162],
    [ 60, 159, 163],
    [ 61, 143, 162],
    [ 61, 147, 162],
    [ 61, 162, 163],
    [ 62, 166, 164],
    [ 63, 137, 162],
    [ 63, 169, 164],
   array([     1,      2,      2,      1,      4,      1,      1,      2,
         3,      1,      1,      1,      2,      5,      2,      2,
       898,      1,      1,  


0
from collections import Counter
x = array( [1,1,1,2,2,2,5,25,1,1] )
mode = counter.most_common(1)[0][0]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.