【问题标题】:Add two matrices containing NaN in python在python中添加两个包含NaN的矩阵
【发布时间】:2018-07-25 07:59:06
【问题描述】:

我有两个要按元素添加的 5D 矩阵。这些矩阵具有完全相同的维度和元素数量,但它们都包含随机分布的 NaN 值。

我想以一种有效的方式逐元素添加这两个矩阵。我目前正在通过逐元素循环来添加它们,但是这个循环大约需要 40 分钟,我只是认为必须有一种更有效的方法。

我认为一种有效的方法是,如果可以使用 numpy.nansum 来添加它们,但据我所知,numpy.nansum 仅适用于一维数组。

如果添加像 numpy.nansum (https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.nansum.html) 那样下降,我会更喜欢它。即,(1)如果添加两个值,我希望总和是一个值,(2)如果添加一个值和一个 NaN,我希望总和是值,(3)如果添加两个 NaN,我想要总和为 NaN。

下面是一个示例代码:

import numpy as np

# Creating fake data
A = np.arange(0,720,1).reshape(2,3,4,5,6)
B = np.arange(720,1440,1).reshape(2,3,4,5,6)

# Assigning some elements as NaN
A[0,1,2,3,4] = np.nan
A[1,2,3,4,5] = np.nan
B[1,2,3,4,5] = np.nan

所以,如果我现在添加 A 和 B(假设 C = A + B),我希望元素 C[0,1,2,3,4] 成为 B[0,1,2, 3,4],元素C[1,2,3,4,5]为NaN,C中所有其他元素为A和B中分别添加的元素之和。

有人对此添加有有效的解决方案吗?

【问题讨论】:

  • 你试过屏蔽吗?也就是说,进行通常的求和,然后使用掩码赋值(布尔索引)将不需要的 nan 替换为正确的值。可能需要两个这样的步骤。
  • @hpaulj 感谢您的回复!我没有尝试过,但我确实考虑过屏蔽。但是当您(i)添加一个掩码值和一个实际值以及(ii)两个掩码值时会发生什么。 (i) 和 (ii) 返回什么结果(总和)?
  • 我认为@hpaulj 的意思是先添加,然后使用掩码替换错误的nans
  • 对,好吧,我想这会起作用,但在我看来,这也需要循环遍历大矩阵,这就是我试图避免的,因为我现在已经在这样做了,而且非常耗时(实际矩阵的大小为 [2000,200,400])。

标签: python python-2.7 numpy


【解决方案1】:
np.where(np.isnan(A), B, A + np.nan_to_num(B))    

我们通过两个部分来了解它的工作原理:

  1. 对于A的nan部分,我们填写B中的值。

如果BA 同时是nan,则存储的值将是nan。如果B 中的值不是nanA 中的值是nan,则将采用B 的值。

  1. A中非nan的部分,我们填写A + np.nan_to_num(B)

np.nan_to_num(B) 会将B 的 nan 部分变为 0。因此,当 Bnan 时,A + np.nan_to_num(B) 将不是 nan

感谢 Paul Panzer 的更正。

【讨论】:

  • copy=False 不起作用,因为在调用 where 之前修改了 B,因此您将在输出中得到 0,其中 AB 都是 @ 987654347@ - 除此之外还有一个不错的解决方案
  • @PaulPanzer 感谢您的更正。欣赏它。
  • 感谢 PaulPanzer 和 Tai!这似乎也是一个有效的解决方案!非常感谢!
【解决方案2】:

我在想一些更平淡的东西

In [22]: A=np.arange(10.)         # make sure A is float
In [23]: B=np.arange(100,110.)
In [24]: A[[1,3,9]]=np.nan
In [25]: B[[2,5,9]]=np.nan

In [26]: A
Out[26]: array([ 0., nan,  2., nan,  4.,  5.,  6.,  7.,  8., nan])
In [27]: B
Out[27]: array([100., 101.,  nan, 103., 104.,  nan, 106., 107., 108.,  nan])
In [29]: C=A+B
In [30]: C
Out[30]: array([100.,  nan,  nan,  nan, 108.,  nan, 112., 114., 116.,  nan])

In [31]: mask1 = np.isnan(A) & ~np.isnan(B)
In [32]: C[mask1] = B[mask1]
In [33]: mask2 = np.isnan(B) & ~np.isnan(A)
In [34]: C[mask2] = A[mask2]
In [35]: C
Out[35]: array([100., 101.,   2., 103., 108.,   5., 112., 114., 116.,  nan])

我喜欢 stacknansum 方法,但我不确定它是否更快:

In [36]: s=np.stack((A,B))
In [37]: C1 = np.nansum(s, axis=0)
In [38]: C1
Out[38]: array([100., 101.,   2., 103., 108.,   5., 112., 114., 116.,   0.])
In [40]: C1[np.all(np.isnan(s), axis=0)] = np.nan
In [41]: C1
Out[41]: array([100., 101.,   2., 103., 108.,   5., 112., 114., 116.,  nan])

看看s这个方法是否令人费解:

In [42]: s
Out[42]: 
array([[  0.,  nan,   2.,  nan,   4.,   5.,   6.,   7.,   8.,  nan],
       [100., 101.,  nan, 103., 104.,  nan, 106., 107., 108.,  nan]])

s 是一个新数组,具有新的 0 维。该维度上的sumA+B 相同。这种堆叠让我们可以利用nansum。不幸的是你仍然想保留一些nan,所以我们仍然需要做一个掩码分配来处理那个细节。

【讨论】:

  • 此外,由于 AB 显然很大,因此可能不希望创建堆叠的中间体。
  • 非常感谢您阐明您的意思!您刚刚演示的这几行代码看起来非常高效!最后一个问题,在您上面的示例中,A、B、C、C1 是一维数组,但是使用您的方法添加多维数组时,编码没有区别,对吗?
【解决方案3】:
s = np.stack((A, B))
C = np.nansum(s, axis=0)
C[np.all(np.isnan(s), axis=0)] = np.nan

这会将np.nan 视为0.0 用于求和,然后最后一行添加回np.nan 存在的位置,用于跨越A 和@ 的新“深度”轴上的所有条目987654327@.

请注意,对于 NumPy 版本 > 1.8,最后一个操作是必需的,正如 the documentation 中所说:

在 NumPy 版本

如果您可以保证 NumPy 版本 nansum 部分就足够了。

【讨论】:

    【解决方案4】:

    在求和之前添加一个新的斧头:

    np.nansum(np.concatenate((A[None,:],B[None,:])),axis=0)
    

    【讨论】:

    • 注意 NumPy 版本 > 1.8,您需要设置 np.nan 值,这些值将由沿轴对所有 nan 求和产生,因为 nansum 将它们视为零。当它们都是nan 时,OP 希望输出为nan。否则,为忽略nan 值的总和。
    猜你喜欢
    • 1970-01-01
    • 2023-03-20
    • 2012-07-02
    • 1970-01-01
    • 2021-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多