[英]numpy fastest way to transform an array's elements to their frequency
正如标题所说,我正在寻找一种方法来转换数组,因此它将是其适当元素的频率数组。
我找到了np.count
和np.histogram
但它不是我想要的
就像是:
从:
array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0])
至:
array_ = np.array([8,8,8,2,8,8,2,8,8,2,2,8])
提前致谢!
如果数组中的值是非负整数,而不是太大,则可以使用np.bincount
。 使用原始数组作为bincount
结果的索引, bincount
得到所需的输出。
>>> array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0])
>>> np.bincount(array_)
array([8, 2, 2])
>>> np.bincount(array_)[array_]
array([8, 8, 8, 2, 8, 8, 2, 8, 8, 2, 2, 8])
请记住, np.bincount
的结果的大小为max(array_) + 1
,所以如果你的数组值很大,这种方法效率很低:你最终会创建一个非常大的中间结果。
即使对于大输入或负输入也应该有效的替代方法是将np.unique
与return_inverse
和return_counts
参数一起使用,如下所示:
>>> array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0])
>>> _, inv, counts = np.unique(array_, return_inverse=True, return_counts=True)
>>> counts[inv]
array([8, 8, 8, 2, 8, 8, 2, 8, 8, 2, 2, 8])
请注意, return_counts
参数是NumPy 1.9.0中的新参数,因此您需要一个NumPy的最新版本。 如果你没有NumPy 1.9.0,一切都不会丢失! 你仍然可以使用return_inverse
的说法np.unique
,它给你回小整数的相同排列的数组作为您的原始之一。 这个新阵列现在处于完美状态,以便bincount
地处理它:
>>> array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0])
>>> _, inverse = np.unique(array_, return_inverse=True)
>>> np.bincount(inverse)[inverse]
array([8, 8, 8, 2, 8, 8, 2, 8, 8, 2, 2, 8])
另一个例子,有更大的array_
内容:
>>> array_ = np.array([0, 71, 598, 71, 0, 0, 243])
>>> _, inverse = np.unique(array_, return_inverse=True)
>>> inverse
array([0, 1, 3, 1, 0, 0, 2])
>>> np.bincount(inverse)[inverse]
array([3, 2, 1, 2, 3, 3, 1])
所有这些解决方案都在纯NumPy中工作,因此它们应该比通过Python Counter
或dict
的解决方案更有效。 但是,与往常一样,如果效率是一个问题,那么您应该剖析以找出最合适的效率。 特别要注意的是, np.unique
正在进行排序,因此其理论复杂度高于纯np.bincount
解决方案。 如果没有时间安排,这在实践中是否有所作为是不可能的。 所以让我们做一些时间,使用IPython的timeit
(这是在Python 3.4上)。 首先,我们将为我们需要的操作定义函数:
In [1]: import numpy as np; from collections import Counter
In [2]: def freq_bincount(array):
...: return np.bincount(array)[array]
...:
In [3]: def freq_unique(array):
...: _, inverse, counts = np.unique(array, return_inverse=True, return_counts=True)
...: return counts[inverse]
...:
In [4]: def freq_counter(array):
...: c = Counter(array)
...: return np.array(list(map(c.get, array)))
...:
现在我们创建一个测试数组:
In [5]: test_array = np.random.randint(100, size=10**6)
然后我们做一些时间安排。 以下是我机器上的结果:
In [6]: %timeit freq_bincount(test_array)
100 loops, best of 3: 2.69 ms per loop
In [7]: %timeit freq_unique(test_array)
10 loops, best of 3: 166 ms per loop
In [8]: %timeit freq_counter(test_array)
1 loops, best of 3: 317 ms per loop
np.bincount
方法和np.unique
方法之间存在一个数量级的差异。 来自@ Kasramvd解决方案的Counter
方法比np.unique
方法慢一些,但是可以在不同的机器上或使用不同版本的Python和NumPy进行更改:您应该使用适合您的用例的数据进行测试。
作为一种快速方法,您可以使用colections.Counter
,这是获得可迭代项目频率的更加pythonic方式:
>>> import numpy as np
>>> array_ = np.array([0,0,0,1,0,0,2,0,0,1,2,0])
>>> from collections import Counter
>>> c=Counter(array_)
>>> np.array(map(c.get,array_))
array([8, 8, 8, 2, 8, 8, 2, 8, 8, 2, 2, 8])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.