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),这意味着代码可能会进一步优化,把unique
function放在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}