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

Abe*_*Abe 210 python arrays performance 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]]
Run Code Online (Sandbox Code Playgroud)

(对你来说,R用户在那里,我基本上都在寻找这个table()功能)

jme*_*jme 443

从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
Run Code Online (Sandbox Code Playgroud)

这使:

 [[ 1  5]
  [ 2  3]
  [ 5  1]
  [25  1]]
Run Code Online (Sandbox Code Playgroud)

快速比较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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的更新!现在,这是IMO的正确答案. (18认同)
  • @NumesSanguis你使用的是什么版本的numpy?在v1.9之前,`return_counts`关键字参数不存在,这可能解释了异常.在这种情况下,[docs](http://docs.scipy.org/doc/numpy-1.8.0/reference/generated/numpy.unique.html#numpy.unique)建议`np.unique(x, True)`相当于`np.unique(x,return_index = True)`,它不返回计数. (3认同)
  • 砰!这就是我们更新的原因……当我们找到这些答案时。numpy 1.8 这么久。我们怎样才能把它放在列表的顶部? (2认同)

Jos*_*del 139

看看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]
Run Code Online (Sandbox Code Playgroud)

然后:

zip(ii,y[ii]) 
# [(1, 5), (2, 3), (5, 1), (25, 1)]
Run Code Online (Sandbox Code Playgroud)

要么:

np.vstack((ii,y[ii])).T
# array([[ 1,  5],
         [ 2,  3],
         [ 5,  1],
         [25,  1]])
Run Code Online (Sandbox Code Playgroud)

或者你想要结合计数和唯一值.

  • 嗨,如果x的元素有一个除int之外的dtype,这将不起作用. (35认同)
  • 如果它们不是非负的整数,它将无法工作,如果整数间隔,它将是非常低效的. (7认同)

McK*_*vin 127

更新:原始答案中提到的方法已弃用,我们应该使用新方法:

>>> 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]])
Run Code Online (Sandbox Code Playgroud)

原始答案:

你可以使用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.]])
Run Code Online (Sandbox Code Playgroud)

  • 似乎是迄今为止最Pythonic 的方法。另外,我在 100k x 100k 矩阵上遇到了 np.bincount 的“对象对于所需数组来说太深”的问题。 (2认同)
  • 我宁愿建议最初的问题提出者将接受的答案从第一个更改为这个,以增加其可见性 (2认同)

Nic*_*mer 37

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

 y = np.bincount(a)
 ii = np.nonzero(y)[0]
 out = np.vstack((ii, y[ii])).T
Run Code Online (Sandbox Code Playgroud)

是目前为止最快的.(注意日志缩放.)

在此输入图像描述


生成图的代码:

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)'
    )
Run Code Online (Sandbox Code Playgroud)

  • 感谢您发布生成绘图的代码。之前不知道 [perfplot](https://pypi.python.org/pypi/perfplot)。看起来很方便。 (2认同)

iva*_*ler 25

使用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
Run Code Online (Sandbox Code Playgroud)

dtype:int64

  • pd.Series()不是必需的.否则,很好的例子.Numpy也是.熊猫可以将一个简单的列表作为输入. (3认同)

Eel*_*orn 18

这是迄今为止最通用,最高效的解决方案; 惊讶它尚未发布.

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))
Run Code Online (Sandbox Code Playgroud)

与当前接受的答案不同,它适用于任何可排序的数据类型(不仅仅是正整数),并且具有最佳性能; 唯一重要的开支是np.unique完成的排序.


Bi *_*ico 14

numpy.bincount可能是最好的选择.如果你的数组包含除了小密集整数之外的任何东西,那么将它包装成这样的东西可能是有用的:

def count_unique(keys):
    uniq_keys = np.unique(keys)
    bins = uniq_keys.searchsorted(keys)
    return uniq_keys, np.bincount(bins)
Run Code Online (Sandbox Code Playgroud)

例如:

>>> 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]))
Run Code Online (Sandbox Code Playgroud)


Jir*_*Jir 8

虽然它已经得到了回答,但我建议采用一种不同的方法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.]))
Run Code Online (Sandbox Code Playgroud)


Rai*_*Lee 5

老问题,但我想提供我自己的解决方案,根据我的基准测试,该解决方案被证明是最快的,使用正常list而不是作为输入(或首先转移到列表)。np.array

\n\n

如果您也遇到了,请检查一下。

\n\n
def count(a):\n    results = {}\n    for x in a:\n        if x not in results:\n            results[x] = 1\n        else:\n            results[x] += 1\n    return results\n
Run Code Online (Sandbox Code Playgroud)\n\n

例如,

\n\n
>>>timeit count([1,1,1,2,2,2,5,25,1,1]) would return:\n
Run Code Online (Sandbox Code Playgroud)\n\n

100000 个循环,3 个循环中最好的:每个循环 2.26 \xc2\xb5s

\n\n
>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]))\n
Run Code Online (Sandbox Code Playgroud)\n\n

100000 个循环,3 个循环中最好的:每个循环 8.8 \xc2\xb5s

\n\n
>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]).tolist())\n
Run Code Online (Sandbox Code Playgroud)\n\n

100000 个循环,3 个循环中最好的:每个循环 5.85 \xc2\xb5s

\n\n

虽然接受的答案会更慢,而且scipy.stats.itemfreq解决方案更糟糕。

\n\n
\n\n

更深入的测试并未证实所制定的期望。

\n\n
from zmq import Stopwatch\naZmqSTOPWATCH = Stopwatch()\n\naDataSETasARRAY = ( 100 * abs( np.random.randn( 150000 ) ) ).astype( np.int )\naDataSETasLIST  = aDataSETasARRAY.tolist()\n\nimport numba\n@numba.jit\ndef numba_bincount( anObject ):\n    np.bincount(    anObject )\n    return\n\naZmqSTOPWATCH.start();np.bincount(    aDataSETasARRAY );aZmqSTOPWATCH.stop()\n14328L\n\naZmqSTOPWATCH.start();numba_bincount( aDataSETasARRAY );aZmqSTOPWATCH.stop()\n592L\n\naZmqSTOPWATCH.start();count(          aDataSETasLIST  );aZmqSTOPWATCH.stop()\n148609L\n
Run Code Online (Sandbox Code Playgroud)\n\n

参考号 下面评论了影响小数据集大量重复测试结果的缓存和其他 RAM 副作用。

\n


Ker*_*m T 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()))
Run Code Online (Sandbox Code Playgroud)

这给你:{1: 5, 2: 3, 5: 1, 25: 1}