如何有效地获取 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)]
collections.Counter(x)
是否足够?
将 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
看看 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]])
或者您想结合计数和唯一值。
return_inverse
选项,什么是好的模拟?
用这个:
>>> 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
是迄今为止最快的。 (注意对数缩放。)
https://i.stack.imgur.com/mjDiR.png
生成绘图的代码:
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)。
使用熊猫模块:
>>> 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 = pd.DataFrame(x) df = df.astype('category') print(df.describe())
将提供类似 count 10 unique 4 top 1 freq 5
的信息,这可能很有用
这是迄今为止最通用和最高效的解决方案;很惊讶它还没有发布。
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'
np.bincount(inverse)
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]))
即使已经回答了,我还是建议使用 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 µ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
参考。下面评论缓存和其他影响小数据集大量重复测试结果的内存中副作用。
numpy
不一定是要走的路。
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}
collections.Counter(x)
也给出相同的结果。我相信 OP 想要一个类似于 R table
函数的输出。保留 Series
可能更有用。
pd.Series(x).reshape(-1)
。
为了计算唯一的非整数 - 类似于 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 代码循环中来进一步优化代码。
多维频率计数,即计数数组。
>>> 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,
import pandas as pd
import numpy as np
print(pd.Series(name_of_array).value_counts())
from collections import Counter
x = array( [1,1,1,2,2,2,5,25,1,1] )
mode = counter.most_common(1)[0][0]
大多数简单的问题变得复杂,因为在各种 python 库中都缺少像 R 中的 order() 这样的简单功能,它可以同时给出统计结果和降序。但是,如果我们设想一下,python 中的所有这些统计排序和参数都可以在 pandas 中轻松找到,我们可以比查看 100 个不同的地方更快地得出结果。此外,R 和 pandas 的开发齐头并进,因为它们的创建目的相同。为了解决这个问题,我使用下面的代码让我在任何地方都能找到:
unique, counts = np.unique(x, return_counts=True)
d = {'unique':unique, 'counts':count} # pass the list to a dictionary
df = pd.DataFrame(d) #dictionary object can be easily passed to make a dataframe
df.sort_values(by = 'count', ascending=False, inplace = True)
df = df.reset_index(drop=True) #optional only if you want to use it further
像这样的事情应该这样做:
#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
此外,Efficiently counting unique elements 上的这篇文章似乎与您的问题非常相似,除非我遗漏了什么。
return_counts
关键字参数,这可能解释了异常。在这种情况下,the docs 建议np.unique(x, True)
等价于不返回计数的np.unique(x, return_index=True)
。unique, idx = np.unique(x, return_inverse=True); counts = np.bincount(idx)
。添加此功能后(请参阅 here),一些非正式测试使用return_counts
的时钟速度提高了 5 倍。