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 2017年

1
あなたが今あなたの質問に正しいものとしてこの答えをスタックするなら、それが良いと思います:stackoverflow.com/a/25943480/9024698
2018年

Collections.counterはかなり遅いです。私のポストを参照してください。stackoverflow.com/questions/41594940/...
せんべい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 14

7
非負の整数以外の場合は機能せず、整数が離れているとスペース効率が非常に悪くなります。
エリック

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
更新してくれてありがとう!これがIMOの正解です。
Erve1879 2014

1
バム!これが更新の理由です...このような答えを見つけたとき。とても長い派手な1.8。これをリストのトップにするにはどうすればよいですか?
user1269942 14年

エラーが発生した場合: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_counts5倍以上高速にクロックを使用していました。
ハイメ

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
これまでで最もパイソンのようなアプローチのようです。また、100k x 100kマトリックスのnp.bincountで「目的の配列に対してオブジェクトが深すぎる」​​問題が発生しました。
メタセコイア2014年

1
元の質問の持ち主に、最初の回答からこの回答に変更して、その視認性を高めることをお勧めします
wiswit

ただし、0.14より前のバージョンでは遅くなります。
Jason S

配列が文字列でいっぱいの場合、返される各項目の両方の要素も文字列であることに注意してください。
user1269942

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 2018年

にオプションequality_check=array_sorteqを追加することで、コードを実行できましたperfplot.show()。エラーを引き起こしていたのはpd.value_counts(Python 2で)(sort = Falseを使用した場合でも)です。
user2314737

33

pandasモジュールの使用:

>>> 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()は必要ありません。そうでなければ、良い例です。Numpyも。パンダは単純なリストを入力として受け取ることができます。
Yohan Obadia 2017

1
@YohanObadia-配列のサイズに応じて、最初にそれをシリーズに変換すると、最終的な操作が速くなります。約50,000の値のマークで推測します。
n1k31t4 2018年

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ます。シーケンスが指定されたこのような関数は、ビンにグループ化された要素の頻度を返します

ただし注意してください:数値は整数であるため、この例では機能します。それらが実数の場合、このソリューションはうまく適用されません。

>>> 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 2017年

pd.Series(x).reshape(-1)多次元配列の場合は転送する必要があることに注意してください。
natsuapo

4

Eelco Hoogendoornの回答に似ていますが、かなり高速(私のマシンでは5の因数)である一意の非整数をカウントするために、以前は少しのcコードweave.inlineと組み合わせnumpy.uniqueていました。

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:ループあたり8.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個のランダムアイテムを1回の実行で少し正確に測定するとしますか?
user3666197

いくつかのテストを行いましたが、実際のデータセットのパフォーマンスには大きな違いがあります。テストでは、ブルートフォースでスケーリングされたループを実行し、非現実的なin vitroナノ秒を引用するよりも、Pythonの内部メカニズムについてもう少し洞察が必要です。試験した- np.bincount()内150.000アレイ処理するようにすることができる未満600 [US]ながら上記DEF -ed カウント()変換前リスト表現にそれら取っ以上122.000 [US]
user3666197

ええ、私の経験則は、少量のレイテンシを処理できるが、レイテンシが重要な小規模データセットのリスト、およびもちろん実際のベンチマーク FTWである可能性のあるものについては何もかもがっしりしています
David

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

また、ユニークな要素効率的にカウントすることに関するこの以前の投稿は 、私が何かを見逃していない限り、あなたの質問にかなり似ているようです。


リンクされた質問はちょっと似ていますが、彼はより複雑なデータ型を扱っているようです。
安倍首相

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.