numpy:数组中唯一值的最有效的频率计数

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()函数)

看看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]]) 

或者你想要结合计数和独特的价值。

从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 

你可以使用scipy.stats.itemfreq

 >>> from scipy.stats import itemfreq >>> x = [1,1,1,2,2,2,5,25,1,1] >>> itemfreq(x) array([[ 1., 5.], [ 2., 3.], [ 5., 1.], [ 25., 1.]]) 

这是迄今为止最普遍和最高效的解决scheme; 惊讶它尚未发布。

 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)) 

与当前接受的答案不同,它适用于任何可sorting的数据types(不仅仅是正整数),而且具有最佳性能; 唯一重要的花费是由np.unique完成的sorting。

使用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(pd.Series(x)) 1 5 2 3 25 1 5 1 

dtype:int64

numpy.bincount可能是最好的select。 如果你的数组除了小的密集整数外还包含任何东西,把它包装起来可能是有用的:

 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])) 

我也对此感兴趣,所以我做了一些性能比较(使用perfplot ,我的一个宠物项目)。 结果:

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

是最快的。

在这里输入图像描述

看起来像np.bincount是要走的路。


代码生成的情节:

 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(22)], logx=True, logy=True, xlabel='len(a)' ) 

即使它已经被回答,我build议使用numpy.histogram一个不同的方法。 这样的函数给出了一个序列,它返回其元素分组的频率。

但要小心 :它在这个例子中起作用,因为数字是整数。 如果他们在哪里真实的数字,那么这个解决scheme将不适用于很好。

 >>> 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.])) 

老问题,但我想提供我自己的解决scheme,结果是最快的,使用正常list而不是np.array作为input(或转移到列表首先),基于我的台架testing。

检查出来,如果你遇到它。

 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解决scheme更糟糕。


更深入的testing并没有证实所制定的期望。

 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 

参考。 以下评论caching和其他RAM中的副作用,影响一个小的数据集大量重复的testing结果。

有些事情应该这样做:

 #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 

此外,这个有效计算独特元素的post看起来和你的问题非常相似,除非我错过了一些东西。

要计算独特的非整数 – 类似于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的也是sorting),这意味着代码可能会进一步优化,把uniquefunction放在C代码循环。

 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}