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


244

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()功能)


5
是否collections.Counter(x)足够?
pylang

1
我认为,如果您现在勾选此答案对您的问题是正确的,那就更好了:stackoverflow.com/a/25943480/9024698
弃儿

Collections.counter相当慢。请参阅我的文章:stackoverflow.com/questions/41594940/...
Sembei Norimaki

Answers:


161

看一下np.bincount

http://docs.scipy.org/doc/numpy/reference/generation/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]])

或者您想将计数和唯一值结合起来。


42
嗨,如果x的元素具有dtype而不是int,那么这将不起作用。
Manoj 2014年

7
如果它们不是非负整数,则将无法正常工作;如果将整数间隔开,则空间效率将非常低。
Erik 2014年

在numpy版本1.10中,我发现,对整数进行计数时,它比np.unique快6倍。另外,请注意,如果给出了正确的参数,它也算负整数。
Jihun 2013年

@Manoj:我的元素x是数组。我正在测试jme的解决方案。
卡塔琳娜·奇尔库

508

从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

22
感谢您的更新!IMO,这是正确的答案。
Erve1879

1
AM!这就是我们更新...的原因,当我们找到此类答案时。这么长的numpy 1.8。我们如何才能将其放在列表的顶部?
user1269942 2014年

如果您收到错误消息:TypeError:unique()得到了一个意外的关键字参数'return_counts',请这样做:unique,counts = np.unique(x,True)
NumesSanguis 2014年

3
@NumesSanguis您使用的是什么版本的numpy?在v1.9之前,return_counts关键字参数不存在,这可能解释了该异常。在这种情况下,文档建议该np.unique(x, True)值等于np.unique(x, return_index=True),它不会返回计数。
jme 2014年

1
在较早的numpy版本中,获得相同结果的典型习语是unique, idx = np.unique(x, return_inverse=True); counts = np.bincount(idx)。添加此功能后(请参阅此处),一些非正式测试使用的return_counts时钟速度提高了5倍。
Jaime 2015年

133

更新:不建议使用原始答案中提到的方法,而应使用新方法:

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

1
似乎是迄今为止最pythonic的方法。另外,在100k x 100k矩阵上,np.bincount遇到“对象太深,无法放入所需数组”的问题。
水杉2014年

1
我宁愿建议原始问题提出者将所回答的答案从第一个更改为该答案,以提高其可见度
wiswit 2014年

不过,对于0.14之前的版本,它的速度很慢。
杰森S

请注意,如果数组中充满了字符串,则返回的每个项目中的两个元素也都是字符串。
user1269942 2015年

似乎itemfreq已被弃用
Terence Parr

48

我对此也很感兴趣,因此我做了一些性能比较(使用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), 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(26)],
    logx=True,
    logy=True,
    xlabel="len(a)",
)

1
感谢您发布代码以生成图。以前不了解perfplot。看起来很方便。
ruffsl

通过在中添加选项equality_check=array_sorteq,我能够运行您的代码perfplot.show()。导致错误的原因(在Python 2中)是pd.value_counts(即使使用sort = False)。
user2314737

33

使用熊猫模块:

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

5
pd.Series()是不必要的。否则,很好的例子。脾气暴躁。熊猫可以将简单列表作为输入。
Yohan Obadia

1
@YohanObadia-根据数组的大小,首先将其转换为序列,这对我来说使最终操作更快。我猜大概在50,000个值左右。
n1k31t4

1
我修改了答案,以考虑@YohanObadia的相关评论
ivankeller

19

这是迄今为止最通用,最有效的解决方案。惊讶的是它还没有发布。

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'
PR

一个更简单的方法是致电np.bincount(inverse)
ali_m,2015年

15

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

8

即使已经回答过,我还是建议使用一种不同的方法numpy.histogram。给定一个序列的此类函数,它返回归类为bin的元素的频率。

请注意:由于数字是整数,因此在此示例中有效。如果它们是实数,则此解决方案将不太适用。

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

5
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}


1
collections.Counter(x)也给出相同的结果。我相信OP希望提供类似于R table函数的输出。保留Series可能会更有用。
pylang

请注意,pd.Series(x).reshape(-1)如果它是多维数组,则有必要转移到。
natsuapo

4

为了计算唯一的非整数 -与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代码循环中来进一步优化代码。


4

有一个老问题,但是我想提供自己的解决方案,该解决方案是最快的,根据我的基准测试,使用常规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不一定要走。
马赫迪

@雨李有趣。您是否还在某些不可缓存的数据集大小上交叉验证了列表假设?让我们假设两种表示形式都包含150.000个随机项目,并通过aZmqStopwatch.start(); count(aRepresentation); aZmqStopwatch.stop()的示例在一次运行中测得的精度更高一些
user3666197 2015年

做了一些测试,是的,实际数据集性能存在巨大差异。测试仅需要运行蛮力缩放的循环并引用不真实的体外纳秒级,就需要对python内部机制有更多的了解。作为测试-一个np.bincount() ,可向处理内150.000阵列小于600 [US]尽管以上DEF -ed 计数()上的预转换列表表示其需要超过122.000 [US]
user3666197

是的,我的经验法则对于任何可以处理少量延迟但潜力很大的事物都是麻木的,列出了对延迟至关重要的较小数据集的列表,当然还有真正的基准测试 FTW :)
David

1

这样的事情应该做到:

#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

另外,除非我缺少某些内容,否则上一篇有关 有效计数唯一元素的文章似乎与您的问题非常相似。


链接的问题有点类似,但是看起来他正在处理更复杂的数据类型。
倍晋三

1

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

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


0
from collections import Counter
x = array( [1,1,1,2,2,2,5,25,1,1] )
mode = counter.most_common(1)[0][0]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.