有效地使用Numpy将函数值分配给数组

时间:2017-08-16 08:08:18

标签: python arrays python-3.x numpy

我有兴趣找到使用Numpy在Python3.6中进行简单操作的最快方法。我希望创建一个函数,从给定的数组到函数值数组。以下是使用map执行此操作的简化代码:

import numpy as np
def func(x):
    return x**2
xRange = np.arange(0,1,0.01)
arr_func = np.array(list(map(func, xRange)))

但是,当我使用复杂的函数运行它并使用大型数组时,运行时速度对我来说非常重要。有更快的方法吗?

编辑我的问题与this不一样,因为我问的是从函数分配而不是生成器。

2 个答案:

答案 0 :(得分:2)

检查相关的How do I build a numpy array from a generator?,其中最引人注目的选项似乎是预先分配numpy数组和设置值,而不是创建一次性中间列表。

arr_func = np.empty(len(xRange))
for i in range(len(xRange)):
  arr_func[i] = func(xRange[i])

答案 1 :(得分:1)

使用无法使用编译的numpy函数重写的复杂函数,我们无法在速度方面做出重大改进。

使用需要标量的math方法定义函数,例如:

def func(x):
    return math.sin(x)**2 + math.cos(x)**2

In [868]: x = np.linspace(0,np.pi,10000)

供参考做一个直接列表理解:

In [869]: np.array([func(i) for i in x])
Out[869]: array([ 1.,  1.,  1., ...,  1.,  1.,  1.])

In [870]: timeit np.array([func(i) for i in x])
13.4 ms ± 211 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

您的列表地图稍快一些:

In [871]: timeit np.array(list(map(func, x)))
12.6 ms ± 12.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

对于像这样的1d数组,np.array可以替换为np.fromiter。它也适用于生成器,包括Py3 map

In [875]: timeit np.fromiter(map(func, x),float)
13.1 ms ± 176 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

这样可以绕过创建整个列表的可能时间损失。但在这种情况下它没有帮助。

另一个迭代器是np.frompyfunc。它由np.vectorize使用,但通常更快,开销更少。它返回一个dtype对象数组:

In [876]: f = np.frompyfunc(func, 1, 1)
In [877]: f(x)
Out[877]: array([1.0, 1.0, 1.0, ..., 1.0, 1.0, 1.0], dtype=object)
In [878]: timeit f(x)
11.1 ms ± 298 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [879]: timeit f(x).astype(float)
11.2 ms ± 85.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

略微提速。我注意到1000项x有了更多改进。如果你的问题需要几个可以相互广播的数组,那就更好了。

分配给预先分配的out数组可以节省内存,并且通常建议作为列表追加迭代的替代方案。但这里并没有提高速度:

In [882]: %%timeit 
     ...: out = np.empty_like(x)
     ...: for i,j in enumerate(x): out[i]=func(j)
16.1 ms ± 308 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

(使用enumerate稍微快于range次迭代。)