ChatGPT解决这个技术问题 Extra ChatGPT

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)]
collections.Counter(x) 是否足够?
我认为,如果您现在将这个答案打勾为您的问题的正确答案会更好:stackoverflow.com/a/25943480/9024698
Collections.counter 很慢。请参阅我的帖子:stackoverflow.com/questions/41594940/…

M
Mateen Ulhaq

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

感谢更新!这是现在,IMO,正确的答案。
砰!这就是我们更新的原因……当我们找到这样的答案时。这么长的numpy 1.8。我们怎样才能把它放在列表的顶部?
如果您收到错误:TypeError: unique() got an unexpected keyword argument 'return_counts',只需执行:unique, counts = np.unique(x, True)
@NumesSanguis 您使用的是什么版本的 numpy?在 v1.9 之前,不存在 return_counts 关键字参数,这可能解释了异常。在这种情况下,the docs 建议 np.unique(x, True) 等价于不返回计数的 np.unique(x, return_index=True)
在较旧的 numpy 版本中,获得相同内容的典型习惯用法是 unique, idx = np.unique(x, return_inverse=True); counts = np.bincount(idx)。添加此功能后(请参阅 here),一些非正式测试使用 return_counts 的时钟速度提高了 5 倍。
J
JoshAdel

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

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


嗨,如果 x 的元素具有非 int 的 dtype,这将不起作用。
如果它们不是非负整数,它将不起作用,如果整数被隔开,空间效率将非常低。
使用 numpy 1.10 版,我发现对于整数计数,它比 np.unique 快大约 6 倍。另外,请注意,如果给出正确的参数,它也会计算负整数。
@Manoj:我的元素 x 是数组。我正在测试jme的解决方案。
那么对于此处的 return_inverse 选项,什么是好的模拟?
M
Mateen Ulhaq

用这个:

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

似乎是迄今为止最pythonic的方法。此外,我在 100k x 100k 矩阵上遇到了 np.bincount 的“对象对于所需数组而言太深”的问题。
我宁愿建议原始问题提出者将接受的答案从第一个更改为这个,以增加其可见性
不过,对于 0.14 之前的版本,它的速度很慢。
请注意,如果数组中充满了字符串,则返回的每个项目中的两个元素也是字符串。
看起来 itemfreq 已被弃用
N
Nico Schlömer

我也对此感兴趣,所以我做了一些性能比较(使用 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。看起来很方便。
通过在 perfplot.show() 中添加选项 equality_check=array_sorteq,我能够运行您的代码。导致错误的原因(在 Python 2 中)是 pd.value_counts(即使 sort=False)。
i
ivankeller

使用熊猫模块:

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

pd.Series() 不是必需的。否则,很好的例子。麻木也一样。 Pandas 可以将一个简单的列表作为输入。
@YohanObadia - 根据数组的大小,首先将其转换为系列使我的最终操作更快。我猜大概有 50,000 个值。
我编辑了我的答案以考虑@YohanObadia 的相关评论
df = pd.DataFrame(x) df = df.astype('category') print(df.describe()) 将提供类似 count 10 unique 4 top 1 freq 5 的信息,这可能很有用
E
Eelco Hoogendoorn

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

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)
B
Bi Rico

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

J
Jir

即使已经回答了,我还是建议使用 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.]))

u
user3666197

老问题,但我想提供自己的解决方案,结果证明是最快的,使用普通 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 不一定是要走的路。
@Rain Lee 很有趣。您是否也在某些不可缓存的数据集大小上交叉验证了列表假设?让我们假设在任一表示中都有 150.000 个随机项目,并且在单次运行中测量得更准确,例如 aZmqStopwatch.start();count(aRepresentation);aZmqStopwatch.stop() ?
做了一些测试,是的,实际数据集性能存在巨大差异。测试需要对 python 内部机制有更多的了解,而不是只运行一个蛮力缩放的循环并引用不现实的体外纳秒。经测试 - 可以使 np.bincount() 在不到 600 [us] 的时间内处理 150.000 个数组,而在其预转换列表表示上的上述定义 count() 花费超过 122.000 [us]
是的,我的经验法则是任何可以处理少量延迟但有可能非常大的东西,列出延迟关键的较小数据集,当然还有真正的基准测试 FTW :)
K
Kerem T
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)
j
jmetz

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


v
vishal

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

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

A
Andrew Regan
import pandas as pd
import numpy as np

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

Y
Yichang Wu
from collections import Counter
x = array( [1,1,1,2,2,2,5,25,1,1] )
mode = counter.most_common(1)[0][0]

P
Prem Kumar Tiwari

大多数简单的问题变得复杂,因为在各种 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

C
Community

像这样的事情应该这样做:

#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 上的这篇文章似乎与您的问题非常相似,除非我遗漏了什么。


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