Numpy:向量化双分支测试(类似三元运算符)

Numpy: vectorizing two-branch test (ternary-operator like)

我正在 Numpy 中对一个测试进行矢量化以实现以下想法:执行 elementwise 一些测试并选择 expr1 expr2 根据测试。这就像 C 中的 三元运算符 : test?expr1:expr2

我看到了两种主要的执行方式;我想知道是否有充分的理由选择一个而不是另一个;也许还有其他技巧可用,我很乐意了解它们。主要目标是速度;因此,我不想将 np.vectorizeif-else 语句一起使用。

对于我的示例,我将重新构建 min 函数;请不要告诉我一些用于计算的 Numpy 函数;这只是一个例子!

思路一:在乘法中使用布尔的算术值:

# a and b have similar shape
test = a < b
ntest = np.logical_not(test)
out = test*a + ntest*b

想法 2:或多或少遵循 APL/J 编码风格(通过使用条件表达式作为数组的索引,该数组的维度比初始维度大数组)。

# a and b have similar shape
np.choose(a<b, np.array([b,a]))

这是更好的使用方法 choose

np.choose(a<b, [b,a])

在我的小时间里它更快。 choose 文档还说 Ifchoicesis itself an array (not recommended), ....

(a<b).choose([b,a])

节省一级函数重定向。

另一种选择:

out = b.copy(); out[test] = a[test]

在快速测试中,这实际上更快。 masked.filled 使用 np.copyto 进行这种 'where' 复制,尽管它似乎并没有更快。

choose 的变体是 where:

np.where(test,a,b)

或使用where(或np.nonzero)将布尔索引转换为数字索引:

I = np.where(test); out = b.copy(); out[I] = a[I]

出于某种原因,这个时间比一件式快得多。

我过去使用过乘法方法;如果我没记错的话,即使是 APL(尽管那是几十年前)。避免除以 0 的一个老技巧是添加 n==0a/(b+(b==0))。但它并不普遍适用。 a*0a*1要有道理。

choose 看起来不错,但是使用 mode 参数可能会更强大(因此更复杂)。

我不确定是否有 'best' 方法。时序测试可以评估某些情况,但我不知道它们在哪里可以推广到所有情况。