频率对NumPy数组中的唯一值进行计数

我如何有效地获得一个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)]
463159 次浏览

像这样的东西应该做到:

#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

此外,之前关于有效地计算独特元素的这篇文章似乎与你的问题非常相似,除非我遗漏了什么。

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

用这个:

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

这是迄今为止最通用和性能最好的解决方案;很惊讶它还没有发布。

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所做的排序。

为了计算独特的非整数——类似于Eelco Hoogendoorn的答案,但要快得多(在我的机器上是5倍),我使用weave.inlinenumpy.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-code循环中来进一步优化代码。

使用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

老问题,但我想提供我自己的解决方案,这是最快的,根据我的台架测试,使用正常的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.uniquereturn_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

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

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

是目前为止最快的。(请注意对数缩放。)

enter image description here


代码生成的情节:

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

import pandas as pd
import numpy as np


print(pd.Series(name_of_array).value_counts())

多维频率计数,即计数数组。

>>> 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,
from collections import Counter
x = array( [1,1,1,2,2,2,5,25,1,1] )
mode = counter.most_common(1)[0][0]

大多数简单的问题变得复杂,因为像R中的order()这样的简单函数在各种python库中都没有以降序和正反两种顺序给出统计结果。但是,如果我们设想,python中所有这些统计顺序和参数都很容易在熊猫中找到,我们就可以比在100个不同的地方寻找更快地得出结果。此外,R和熊猫的发展是齐头并进的,因为它们是为同一个目的而创造的。为了解决这个问题,我使用以下代码,让我得到任何地方:

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

你可以这样写freq_count:

def freq_count(data):
mp = dict();
for i in data:
if i in mp:
mp[i] = mp[i]+1
else:
mp[i] = 1
return mp