如何加快numpy数组的枚举/如何有效地枚举numpy数组?

时间:2012-03-20 18:40:10

标签: python numpy

我需要生成大量随机数。我尝试过使用random.random,但这个功能很慢。因此我切换到numpy.random.random,这更快!到现在为止还挺好。生成的随机数实际上用于计算某些事物(基于数字)。因此,我enumerate超过每个数字并替换值。这似乎杀死了我之前获得的所有加速。以下是使用timeit()

生成的统计信息
test_random - no enumerate
0.133111953735
test_np_random - no enumerate
0.0177130699158


test_random - enumerate
0.269361019135
test_np_random - enumerate
1.22525310516

正如您所看到的,使用numpy生成数字的速度几乎快10倍,但枚举这些数字会让我的运行时间相等。

以下是我正在使用的代码:

import numpy as np
import timeit
import random

NBR_TIMES = 10
NBR_ELEMENTS = 100000

def test_random(do_enumerate=False):
    y = [random.random() for i in range(NBR_ELEMENTS)]
    if do_enumerate:
        for index, item in enumerate(y):
            # overwrite the y value, in reality this will be some function of 'item'
            y[index] = 1 + item

def test_np_random(do_enumerate=False):
    y = np.random.random(NBR_ELEMENTS)
    if do_enumerate:
        for index, item in enumerate(y):
            # overwrite the y value, in reality this will be some function of 'item'
            y[index] = 1 + item

if __name__ == '__main__':
    from timeit import Timer

    t = Timer("test_random()", "from __main__ import test_random")
    print "test_random - no enumerate"
    print t.timeit(NBR_TIMES)

    t = Timer("test_np_random()", "from __main__ import test_np_random")
    print "test_np_random - no enumerate"
    print t.timeit(NBR_TIMES)


    t = Timer("test_random(True)", "from __main__ import test_random")
    print "test_random - enumerate"
    print t.timeit(NBR_TIMES)

    t = Timer("test_np_random(True)", "from __main__ import test_np_random")
    print "test_np_random - enumerate"
    print t.timeit(NBR_TIMES)

加快这一过程的最佳方法是什么?为什么enumerate会大幅减缓这种情况?

编辑:我使用enumerate的原因是因为我需要索引和当前元素的值。

2 个答案:

答案 0 :(得分:6)

要充分利用numpy的速度,您希望尽可能创建ufuncs。将vectorize应用于函数mgibsonbr建议是一种方法,但如果可能的话,更好的方法是构造一个利用numpy的内置ufunc的函数。所以像这样:

>>> import numpy
>>> a = numpy.random.random(10)
>>> a + 1
array([ 1.29738145,  1.33004628,  1.45825441,  1.46171177,  1.56863326,
        1.58502855,  1.06693054,  1.93304272,  1.66056379,  1.91418473])
>>> (a + 1) * 0.25 / 4
array([ 0.08108634,  0.08312789,  0.0911409 ,  0.09135699,  0.09803958,
        0.09906428,  0.06668316,  0.12081517,  0.10378524,  0.11963655])

要在numpy数组中应用的函数的性质是什么?如果你告诉我们,也许我们可以帮你提出一个只使用numpy ufuncs的版本。

也可以在不使用enumerate的情况下生成索引数组。 Numpy提供ndenumerate,它是一个迭代器,可能更慢,但它也提供indices,这是一种非常快速的方法来生成与数组中的值对应的索引。所以......

>>> numpy.indices(a.shape)
array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])

因此,为了更明确,您可以使用上述内容并使用numpy.rec.fromarrays组合它们:

>>> a = numpy.random.random(10)
>>> ind = numpy.indices(a.shape)
>>> numpy.rec.fromarrays([ind[0], a])
rec.array([(0, 0.092473494150913438), (1, 0.20853257641948986),
       (2, 0.35141455604686067), (3, 0.12212258656960817),
       (4, 0.50986868372639049), (5, 0.0011439325711705139),
       (6, 0.50412473457942508), (7, 0.28973489788728601),
       (8, 0.20078799423168536), (9, 0.34527678271856999)], 
      dtype=[('f0', '<i8'), ('f1', '<f8')])

这开始听起来像你主要担心的是就地执行操作。使用vectorize更难做到,但使用ufunc方法很容易:

>>> def somefunc(a):
...     a += 1
...     a /= 15
... 
>>> a = numpy.random.random(10)
>>> b = a
>>> somefunc(a)
>>> a
array([ 0.07158446,  0.07052393,  0.07276768,  0.09813235,  0.09429439,
        0.08561703,  0.11204622,  0.10773558,  0.11878885,  0.10969279])
>>> b
array([ 0.07158446,  0.07052393,  0.07276768,  0.09813235,  0.09429439,
        0.08561703,  0.11204622,  0.10773558,  0.11878885,  0.10969279])

如您所见,numpy就地执行这些操作。

答案 1 :(得分:3)

检查numpy.vectorize,它应该允许你将任意函数应用于numpy数组。举个简单的例子,你可以这样做:

vecFunc = vectorize(lambda x: x + 1)
vecFunc(y)

但是,这将创建一个新的numpy数组,而不是就地修改它(在您的特定情况下可能会或可能不会出现问题)。

一般来说,使用numpy函数操作numpy结构总是比使用python函数迭代更好,因为前者不仅经过优化而且在C中实现,而后者将始终被解释。

相关问题