Numpy select 惰性版本

Numpy select lazy version

考虑下面的代码

 >>> x = np.array([0, 0, 1, 1])
 >>> np.select([x==0, True], [x+1, 1/x])
 array([ 1.,  1.,  1.,  1.])

它有两个问题。

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

其次,每次代码为运行

时,numpy都会发出警告
RuntimeWarning: divide by zero encountered in true_divide

这与上一点有些相关,因为即使在最终答案中不需要计算 1/x,它也会尝试求值。

有没有select的版本比较懒,不会出现上述问题?

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

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

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

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

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])]

这个想法是通过将 1/x 的值隐藏在 lambda 定义之后来保护它们不被计算。辅助数组 xfun1xfun2x 的每个值定义了一个虚拟 lambda;第一个 returning x+1,第二个 returning 1/x。但是,在您将元素称为 xfun2[2]().

之前,不会评估这些元素

所以我们使用 select 调用从两个函数数组中选择元素,但随后我们获得了一个函数列表。为了获得数字 return 值,我们需要使用列表理解来评估每个 lambda.