如何确定是否需要numpy.vectorize()?

时间:2014-01-30 23:49:23

标签: python numpy

有没有办法在运行时确定函数是否需要numpy.vectorize()按预期运行?

对于背景,我问这是因为我在一个程序中使用Numpy来计算文献中可用的热力学函数的相图(基于CALPHAD)。对于给定温度,评估自由能函数并确定接触凹入的公共正切曲线(二阶导数> 0)以定义相位共存的组成范围。为此,直接定义二阶导数函数很好。所有这些都与真正的自由能函数(不难得到衍生物)相得益彰,直到我尝试使用简单的抛物线自由函数进行测试,该函数具有恒定的二阶导数。这搞砸了我的算法,因为我没想到numpy广播会查看函数并决定它不需要广播。

困难归结为这种行为:

import numpy as np
def f(x):
   return(x*x)
def g(x):
   return(3.0)
def h(x):
   return(0*x+3.0)
def i(x):
   return(x-x+3.0)

x = np.linspace(1.0, 5.0, 5)

在IPython 3.3.2中运行会产生以下输出:

  
    
      

f(x) - >       数组([1.,4.,9.,16.,25。]) - 我的预期

             

g(x) - >       3.0(注意只有1个元素,浮点数,而不是ndarray) - 不是天真的预期

             

h(x) - >       数组([3.,3.,3.,3.,3。]) - 好吧,通过让x做某事来欺骗广播

             

i(x) - >       数组([3.,3.,3.,3.,3。]) - 与h(x)相同但避免乘法但有舍入问题

    
  

现在我可以使用

gv = np.vectorize(g)

并获取

  
    
      

gv(x) - > array([3.,3.,3.,3.,3。]) - 预期的行为

    
  

如果我的程序要(最终)接受任意用户输入的自由能量函数,这将导致问题,除非所有用户都理解numpy内部广播魔法。 或者,我可以反复地np.vectorize一切,以防止这种情况。如果函数在numpy中“正常工作”,问题就是成本。

也就是说,在IPython中使用%timeit,

h(x) -> 100000 loops, best of 3: 3.45 µs per loop

如果我不必要地向量化h(x)(即hv = np.vectorize(h)),我得

hv(x) -> 10000 loops, best of 3: 43.2 µs per loop

因此,不必要的矢量化是一个巨大的惩罚(5个函数的证据为40微秒)。

我想我可以对一个小的ndarray上的函数返回进行初始测试,看看返回类型是数组还是浮点数,然后定义一个新函数,如果它是float,如:

def gv(x):
   return(g(x)+0.0*x)

这看起来像是一个可怕的kludge。

那么 - 在这种情况下,是否有更好的方法可以“愚弄”numpy进行有效广播?

2 个答案:

答案 0 :(得分:2)

解决所示问题。如果你想要一个新阵列:

def g(x):
    return np.ones_like(x)*3

或者如果要将数组中的所有元素设置为3:

def g(x):
    x[:] = 3

注意这里没有return语句,因为你只是更新数组x,所以所有元素都是3。

如图所示def g(x): return(3)的问题是函数内部没有引用numpy。您声明任何给定输入返回3.说明x=3会遇到类似的问题,因为您要更新指针x以指向3而不是numpy阵列。虽然语句x[:]=3numpy.ndarray类访问一个称为视图的内部函数,而不是通常使用只更新指针的=语句。

答案 1 :(得分:0)

正如其他人所建议的那样,您可以包装用户提供的函数以确保输出形状正确。例如:

def wrap_user_function(func, x):
   out = func(x)
   if np.isscalar(out):
     return np.zeros_like(x) + out
   return out

这只是专门处理标量输出的情况,但它至少应该处理你的g(x)问题,而不会造成很大的性能损失。