我一直在尝试升级一个库,其中包含许多针对标量的几何操作,使它们也能与numpy数组一起使用。在这个过程中,我注意到了一些关于numpy除法的奇怪行为。
在原始代码中,检查两个变量之间的归一化差异,如果两个变量都不为零,则交换到numpy后看起来像这样:
import numpy as np
a = np.array([0, 1, 2, 3, 4])
b = np.array([1, 2, 3, 0, 4])
o = np.zeros(len(a))
o = np.divide(np.subtract(a, b), b, out=o, where=np.logical_and(a != 0, b != 0))
print(f'First implementation: {o}')
当我传入一个初始化为零的输出缓冲区来处理无法计算的情况时,它会返回:
First implementation: [ 0. -0.5 -0.33333333 0. 0. ]
对于标量,我不得不稍微修改一下,因为out
需要一个数组,但看起来还不错。
a = 0
b = 4
o = None if np.isscalar(a) else np.zeros(len(a))
o = np.divide(np.subtract(a, b), b, out=o, where=np.logical_and(b != 0, a != 0))
print(f'Modified out for scalar: {o}')
返回值
Modified out for scalar: 0.0.
接着我通过一些测试函数运行了这段代码,结果发现其中很多函数都失败了。深入研究后,我发现当我第一次调用带有 where
设置为 False
的除法函数时,它会返回零值,但如果我再次调用该函数,第二次它将返回不可预测的结果。
a = 0
b = 4
print(f'First divide: {np.divide(b, a, where=False)}')
print(f'Second divide: {np.divide(b, a, where=False)}')
返回值
First divide: 0.0
Second divide: 4.0
看文档上说,“其中条件为 False 的位置将保持未初始化”,所以我猜 numpy 有一些内部缓冲区,最初被设置为零,然后随后它就会携带早期的中间值。
我很难看出如何使用 divide
,无论是否使用 where
子句; 如果我使用 where
,我会得到一个不可预测的输出,如果我不使用,则无法防止被零除。我是错过了什么,还是我只需要在这些情况下具有不同的代码路径?我意识到我已经有了一个不同的代码路径与 out
变量。
我会很感激任何建议。
where
时,必须使用out
参数。否则非where值将是未指定的(不可预测的)。 - hpauljout
必须是ndarray
、None
或ndarray
和None
的元组。如果传入标量将会失败。 - John M.np.zeros
)的正确方法这一事实。至于使用标量进行np.divide
- 为什么?您可以将其设置为out=np.array(0)
,结果将是一个类似大小的数组,而不是“真正”的标量。 - hpauljnp.empty
相同。有时你会看到一些奇怪的数字,其他时候则是类似于先前缓冲区的值。这完全取决于内存如何被使用或重用。对于out=None
,where=False
的值是不可预测的。 - hpaulj