有没有办法在运行时确定函数是否需要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进行有效广播?
答案 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[:]=3
从numpy.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)
问题,而不会造成很大的性能损失。