在 numpy 中存在 NaN 的掩码位置设置值

Setting a value in masked location with NaNs present in numpy

我有一个包含 NaN 的数组,比方说

>>> a = np.random.randn(3, 3)
>>> a[1, 1] = a[2, 2] = np.nan
>>> a
array([[-1.68425874,  0.65435007,  0.55068277],
       [ 0.71726307,         nan, -0.09614409],
       [-1.45679335, -0.12772348,         nan]])

我想将此数组中的负数设置为 -1。以 "straightforward" 方式执行此操作会导致出现警告,而我正试图避免这种情况:

>>> a[a < 0] = -1
__main__:1: RuntimeWarning: invalid value encountered in less
>>> a
array([[-1.        ,  0.65435007,  0.55068277],
       [ 0.71726307,         nan, -1.        ],
       [-1.        , -1.        ,         nan]])

将 AND 应用于掩码会导致相同的警告,因为 a < 0 当然是作为单独的临时数组计算的:

>>> n = ~np.isnan(a)
>>> a[n & (a < 0)] = -1
__main__:1: RuntimeWarning: invalid value encountered in less

当我尝试对 a 中的 nans 应用掩码时,掩码部分未写回原始数组:

>>> n = ~np.isnan(a)
>>> a[n][a[n] < 0] = -1
>>> a
array([[-1.68425874,  0.65435007,  0.55068277],
       [ 0.71726307,         nan, -0.09614409],
       [-1.45679335, -0.12772348,         nan]])

我能想出解决这个问题的唯一方法是使用 a:

的无偿中间屏蔽版本
>>> n = ~np.isnan(a)
>>> b = a[n]
>>> b[b < 0] = -1
>>> a[n] = b
>>> a
array([[-1.        ,  0.65435007,  0.55068277],
       [ 0.71726307,         nan, -1.        ],
       [-1.        , -1.        ,         nan]])

在存在 NaN 的情况下,是否有更简单的方法来执行此掩码分配?如果可能的话,我想在不使用掩码数组的情况下解决这个问题。

注意

上面的片段最好 运行 和

import numpy as np
import warnings
np.seterr(all='warn')
warnings.simplefilter("always")

根据 .

如果您想避免在 a < 0 处出现 a 包含 NaNs 的警告,我认为替代方法包括使用 flattenedrow-column non-Nan 个位置的索引,然后执行比较。因此,我们将有两种采用该理念的方法。

具有扁平索引的一个 -

idx = np.flatnonzero(~np.isnan(a))
a.ravel()[idx[a.ravel()[idx] < 0]] = -1

另一个 下标指数 -

r,c = np.nonzero(~np.isnan(a))
mask = a[r,c] < 0
a[r[mask],c[mask]] = -1

您可以暂时取消警告,这是您想要的吗?

In [9]: a = np.random.randn(3, 3)

In [10]: a[1, 1] = a[2, 2] = np.nan

In [11]: with np.errstate(invalid='ignore'):
   ....:     a[a < 0] = -1
   ....:

查看我发现的 np.nan... 函数 np.nan_to_num

In [569]: a=np.arange(9.).reshape(3,3)-5
In [570]: a[[1,2],[1,2]]=np.nan
In [571]: a
Out[571]: 
array([[ -5.,  -4.,  -3.],
       [ -2.,  nan,   0.],
       [  1.,   2.,  nan]])
In [572]: np.nan_to_num(a)   # replace nan with 0
Out[572]: 
array([[-5., -4., -3.],
       [-2.,  0.,  0.],
       [ 1.,  2.,  0.]])
In [573]: np.nan_to_num(a)<0    # and safely do the <
Out[573]: 
array([[ True,  True,  True],
       [ True, False, False],
       [False, False, False]], dtype=bool)
In [574]: a[np.nan_to_num(a)<0]=-1
In [575]: a
Out[575]: 
array([[ -1.,  -1.,  -1.],
       [ -1.,  nan,   0.],
       [  1.,   2.,  nan]])

查看 nan_to_num 代码,它似乎使用了掩码 copyto:

In [577]: a1=a.copy(); np.copyto(a1, 0.0, where=np.isnan(a1))
In [578]: a1
Out[578]: 
array([[-1., -1., -1.],
       [-1.,  0.,  0.],
       [ 1.,  2.,  0.]])

所以它就像你的带有 'gratuitous' 掩码的版本,但它隐藏在函数中。

np.placenp.putmask 是其他使用掩码的函数。