在numpy数组上映射函数的最有效的方法

在numpy数组上映射函数的最有效方法是什么? 我在当前项目中一直这样做的方式如下:

import numpy as np x = np.array([1, 2, 3, 4, 5]) # Obtain array of square of each element in x squarer = lambda t: t ** 2 squares = np.array([squarer(xi) for xi in x]) 

然而,这看起来可能是非常低效的,因为我使用列表parsing来构造新的数组作为Python列表,然后再将其转换回numpy数组。

我们可以做得更好吗?

如何使用numpy.vectorize

 >>> import numpy as np >>> x = np.array([1, 2, 3, 4, 5]) >>> squarer = lambda t: t ** 2 >>> vfunc = np.vectorize(squarer) >>> vfunc(x) array([ 1, 4, 9, 16, 25]) 

http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.vectorize.html

TL; DR

根据我的经验,对于不同大小的数组,不同版本的Python和NumPy来自不同的编译器, np.fromiter通常是最快的。 这里是一个简短的例子:

 import numpy as np x = np.array([1, 2, 3, 4, 5]) f = lambda x: x ** 2 squares = np.fromiter((f(xi) for xi in x), x.dtype, count=len(x)) 

请注意, count是可选的。 对至less有数百个元素的数组使用np.vectorize可能是值得的(参见Nico答案中的图)。

方法的比较

下面是一些简单的testing来比较三种方法来映射一个函数,这个例子使用Python 2.7和NumPy 1.9。 首先,testing的设置function:

 import timeit import numpy as np f = lambda x: x ** 2 vf = np.vectorize(f) def test_array(x, n): t = timeit.timeit( 'np.array([f(xi) for xi in x])', 'from __main__ import np, x, f', number=n) print('array: ' + str(t)) def test_fromiter(x, n): t = timeit.timeit( 'np.fromiter((f(xi) for xi in x), x.dtype, count=len(x))', 'from __main__ import np, x, f', number=n) print('fromiter: ' + str(t)) def test_vectorized(x, n): t = timeit.timeit( 'vf(x)', 'from __main__ import x, vf', number=n) print('vectorized: ' + str(t)) 

有五个元素, np.fromiter是最快的, np.vectorize要慢得多(由于安装成本):

 x = np.array([1, 2, 3, 4, 5]) n = 100000 test_array(x, n) # 0.616514921188 test_fromiter(x, n) # 0.585698843002 test_vectorized(x, n) # 2.6228120327 

有了100多个元素,所有的方法都差不多:

 x = np.arange(100) n = 10000 test_array(x, n) # 0.519502162933 test_fromiter(x, n) # 0.500586986542 test_vectorized(x, n) # 0.525988101959 

但是对于1000个或更多的数组元素,vector化方法是最有效的:

 x = np.arange(1000) n = 1000 test_array(x, n) # 0.472352981567 test_fromiter(x, n) # 0.453316926956 test_vectorized(x, n) # 0.291934967041 

但是,不同版本的Python / NumPy和编译器优化会有不同的结果,因此请针对您的环境进行类似的testing。

我已经用perfplot (我的一个小项目np.array(map(f, x))testing了所有build议的方法加上np.array(map(f, x)) ,发现按照最高票数的答案,

 vf = np.vectorize(f) y = vf(x) 

是最快的解决scheme,即使只是一次执行vector化。 所有其他方法同样快速。

在这里输入图像说明


代码重现情节:

 import numpy as np import perfplot def f(x): return x**2 vf = np.vectorize(f) def array_for(x): return np.array([f(xi) for xi in x]) def array_map(x): return np.array(map(f, x)) def fromiter(x): return np.fromiter((f(xi) for xi in x), x.dtype) def vectorize(x): return np.vectorize(f)(x) def vectorize_without_init(x): return vf(x) perfplot.show( setup=lambda n: np.random.rand(n), n_range=[2**k for k in range(15)], kernels=[array_for, array_map, fromiter, vectorize, vectorize_without_init], logx=True, logy=True, xlabel='len(x)', ) 
 squares = squarer(x) 

数组上的算术运算是以元素的方式自动应用的,有效的C级循环可以避免所有解释器的开销,这些开销会被应用于Python级循环或理解。

大部分你想要应用于NumPy数组的元素都可以工作,尽pipe有些可能需要改变。 例如, if不起作用。 你会想要转换这些使用像numpy.where

 def using_if(x): if x < 5: return x else: return x**2 

 def using_where(x): return numpy.where(x < 5, x, x**2) 

我相信在新版本(我使用1.13)numpy,你可以简单地调用函数,通过将numpy数组传递给你为标量types编写的函数,它会自动将函数调用应用到numpy数组上的每个元素,并返回给你另一个numpy数组

 >>> import numpy as np >>> squarer = lambda t: t ** 2 >>> x = np.array([1, 2, 3, 4, 5]) >>> squarer(x) array([ 1, 4, 9, 16, 25]) 

正如在这篇文章中提到的 ,只需使用如下的生成器expression式:

 numpy.fromiter((<some_func>(x) for x in <something>),<dtype>,<size of something>) 

也许使用vector化更好

 def square(x): return x**2 vfunc=vectorize(square) vfunc([1,2,3,4,5]) output:array([ 1, 4, 9, 16, 25])