在numpy数组

时间:2016-01-07 21:27:41

标签: python numpy

我有一个执行此操作的函数:它接受给定的numpy数组A和给定的函数func,并将该函数应用于数组的每个元素。

def transform(A, func):
    return func(A)

Afunc是从外部提供的,我无法控制它们。如果它们是transform(A, np.sin)这样的矢量化函数,我希望这些函数能够工作,但我也希望能够接受正常的numpy函数,例如像transform(A, lambda x: x^2 if x > 5 else 0)这样的lambdas。当然第二个没有矢量化,所以我需要在应用之前调用np.vectorize()。像这样:transform(A, np.vectorize(lambda x: x^2 if x > 5 else 0)) ...但我不想把这个负担强加给用户。我想对所有功能采用统一的方法。我只是从外面获得一个功能并应用它。

有没有一种方法可以决定哪种功能需要矢量化,哪种功能不需要?类似的东西:

def transform(A, func):
    if requires_vectorization(func):  # how to do this???
        func = np.vectorize(func)
    return func(A)   

或者我应该在默认情况下全部矢量化

def transform(A, func):
    func = np.vectorize(func)  # is this correct and efficient?
    return func(A)   

这个解决方案好吗?换句话说,在已经向量化的函数上调用np.vectorize是否有害?或者有其他选择吗?

1 个答案:

答案 0 :(得分:2)

EAFP principle之后,您可以先尝试直接在A上调用该函数,看看是否会引发异常:

import numpy as np

def transform(A, func):
    try:
        return func(A)
    except TypeError:
        return np.vectorize(func)(A)

例如:

import math

A = np.linspace(0, np.pi, 5)

print(transform(A, np.sin))     # vectorized function
# [  0.00000000e+00   7.07106781e-01   1.00000000e+00   7.07106781e-01
#    1.22464680e-16]

print(transform(A, math.sin))   # non-vectorized function
# [  0.00000000e+00   7.07106781e-01   1.00000000e+00   7.07106781e-01
#    1.22464680e-16]
  

在已经向量化的函数上调用np.vectorize是否有害?

是的,绝对的。当你将np.vectorize应用于一个函数时,所有循环输入数组元素都是在Python中完成的,这与在C中循环的“适当的”向量化numpy函数不同。来自the documentation

  

提供矢量化功能主要是为了方便,而不是为了提高性能。 实施基本上是for循环。

我觉得这句话应该用粗体全文写成。

案例:

In [1]: vecsin = np.vectorize(np.sin)

In [2]: %%timeit A = np.random.randn(10000);
np.sin(A)
   ....: 
1000 loops, best of 3: 243 µs per loop

In [3]: %%timeit A = np.random.randn(10000);
vecsin(A)
   ....: 
100 loops, best of 3: 11.7 ms per loop

In [4]: %%timeit A = np.random.randn(10000);
[np.sin(a) for a in A]
   ....: 
100 loops, best of 3: 12.5 ms per loop

在此示例中,将np.vectorize应用于np.sin可将其减慢约50倍,使其与常规Python列表理解速度一样慢。

编辑:

为了完整性,这里是“转型”版本。如您所见,try / except块对性能的影响可以忽略不计:

In [5]: %%timeit A = np.random.randn(10000);
transform(A, np.sin)
   ...: 
1000 loops, best of 3: 241 µs per loop