Numpy 选择惰性版本

dzh*_*lil 5 python select numpy lazy-evaluation

考虑下面的代码

 >>> x = np.array([0, 0, 1, 1])
 >>> np.select([x==0, True], [x+1, 1/x])
 array([ 1.,  1.,  1.,  1.])
Run Code Online (Sandbox Code Playgroud)

它有两个问题。

首先,它并不懒惰。它会急切地计算 x+1 和 1/x,即使最终结果中不需要某些计算值。

其次,numpy 每次运行代码时都会发出警告

RuntimeWarning: divide by zero encountered in true_divide
Run Code Online (Sandbox Code Playgroud)

这与上一点有些相关,因为它试图评估 1/x,即使最终答案中不需要这样做。

是否有一个 select 版本是惰性的并且不会遇到上述问题?

And*_*eak 5

您可以通过显式屏蔽两种情况的输出向量来避免计算:

y = x.copy()
mask = (x == 0)  # parentheses only necessary for readability
y[mask] = x[mask] + 1
y[~mask] = 1 / x[~mask]
Run Code Online (Sandbox Code Playgroud)

以上是我强烈建议您做的事情,因此您应该只继续阅读一个毫无意义的设计解决方案,该解决方案实际上解决了问题的“惰性评估”部分。我不建议在实践中使用下面的代码片段!你被警告了。

我终于可以实现实际的惰性评估,尽管它有点混乱并且造成了不必要的情况复杂化(好吧,至少在这种情况下;我可以想象在某些情况下这可能会派上用场)。本着“任何值得做的事情都值得过度去做”的精神:

xfun1 = [lambda xval=xval: xval + 1 for xval in x]
xfun2 = [lambda xval=xval: 1 / xval for xval in x]
[fun() for fun in np.select([x == 0, True], [xfun1, xfun2])]
Run Code Online (Sandbox Code Playgroud)

这个想法是通过将它们隐藏在定义后面来保护 的值1/x不被评估lambda。辅助数组xfun1和为 的每个值xfun2定义一个虚拟值;第一次回来,第二次回来。然而,直到您将元素称为 时,才会对这些进行求值。lambdaxx+11/xxfun2[2]()

因此,我们使用select调用从两个函数数组中选择元素,然后我们获得一个函数列表。为了获得数字返回值,我们需要使用列表理解来评估每个lambda