如何高效地获取NumPy数组中每个唯一值的频率计数?
>>> x = np.array([1,1,1,2,2,2,5,25,1,1])
>>> freq_count(x)
[(1, 5), (2, 3), (5, 1), (25, 1)]
如何高效地获取NumPy数组中每个唯一值的频率计数?
>>> x = np.array([1,1,1,2,2,2,5,25,1,1])
>>> freq_count(x)
[(1, 5), (2, 3), (5, 1), (25, 1)]
使用 numpy.unique
并加上 return_counts=True
(适用于 NumPy 1.9+):
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
return_counts
关键字参数不存在,这可能解释了异常情况。如果是这种情况,文档建议使用np.unique(x, True)
代替np.unique(x, return_index=True)
,其中不会返回计数。 - jmeunique, idx = np.unique(x, return_inverse=True); counts = np.bincount(idx)
。当这个特性被添加时(参见此处),一些非正式测试表明使用return_counts
的速度比原来快了5倍以上。 - Jaimenp.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]])
>>> 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.]])
我也对此很感兴趣,所以我进行了一些性能比较(使用我的一个宠物项目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), dtype=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
b = perfplot.bench(
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)],
xlabel="len(a)",
)
b.save("out.png")
b.show()
perfplot.show()
中添加选项equality_check=array_sorteq
来运行您的代码。导致错误(在Python 2中)的是pd.value_counts
(即使sort=False也是如此)。 - user2314737使用 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
df = df.astype('category')
print(df.describe()) ```将提供信息,例如```count 10
unique 4
top 1
freq 5 ```,这可能会很有用。
- Subhamimport 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))
AttributeError: 'numpy.ufunc'对象没有'at'属性
。 - P.R.np.bincount(inverse)
。 - ali_mnumpy.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]))
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.]))
虽然这是一个老问题,但我想提供自己的解决方案,这个方法是最快的,使用常规的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微秒
>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]))
100000次循环,3次中的最佳结果:每次循环8.8微秒
>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]).tolist())
100000次循环,最佳3次: 每次5.85微秒
尽管被接受的答案较慢,但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
不一定是最佳选择。 - Mahdiimport 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()))
collections.Counter(x)
也会给出相同的结果。我认为OP想要一个类似于R中table
函数的输出。保留Series
可能更有用。 - pylangpd.Series(x).reshape(-1)
。 - natsuapo
collections.Counter(x)
是否足够? - pylang