从NumPy数组中删除NaN(或其他值)对的元素(Python实现)

11

我有一个包含两列的numpy数组。例如:

a = array([[1, 5, nan, 6],
           [10, 6, 6, nan]])
a = transpose(a)

我希望高效地遍历两列a[:, 0]和a[:, 1],并删除满足特定条件的任何一对值,例如如果它们是NaN。我能想到的明显方法是:

new_a = []
for val1, val2 in a:
  if val2 == nan or val2 == nan:
    new_a.append([val1, val2])

但这看起来很笨重。有没有更符合Python风格的numpy方法可以实现这个功能?

谢谢。

4个回答

31
如果你想要仅获取没有NAN值的行,那么这就是你需要的表达式:
>>> import numpy as np
>>> a[~np.isnan(a).any(1)]
array([[  1.,  10.],
       [  5.,   6.]])

如果你想要筛选出某个元素不包含特定数字的行,比如5:

>>> a[~(a == 5).any(1)]
array([[  1.,  10.],
       [ NaN,   6.],
       [  6.,  NaN]])

后者显然等价于

>>> a[(a != 5).all(1)]
array([[  1.,  10.],
       [ NaN,   6.],
       [  6.,  NaN]])

说明: 首先让我们创建您的示例输入。

>>> import numpy as np
>>> a = np.array([[1, 5, np.nan, 6],
...               [10, 6, 6, np.nan]]).transpose()
>>> a
array([[  1.,  10.],
       [  5.,   6.],
       [ NaN,   6.],
       [  6.,  NaN]])

这确定哪些元素为 NaN。

>>> np.isnan(a)
array([[False, False],
       [False, False],
       [ True, False],
       [False,  True]], dtype=bool)

这将确定哪些行具有任何为True的元素。

>>> np.isnan(a).any(1)
array([False, False,  True,  True], dtype=bool)

由于我们不想要这些内容,因此我们否定了最后一个表达式:

>>> ~np.isnan(a).any(1)
array([ True,  True, False, False], dtype=bool)

最后,我们使用布尔数组来选择我们想要的行:

>>> a[~np.isnan(a).any(1)]
array([[  1.,  10.],
       [  5.,   6.]])

2
+1:超级清晰和有用的解释,我喜欢 ~np.isnan,因为它能明确说明你正在做什么。 - tom10
多棒的答案。 - Tjorriemorrie

3
您可以将数组转换为掩码数组,然后使用“压缩行”方法: masked array,并使用compress_rows方法
import numpy as np
a = np.array([[1, 5, np.nan, 6],
           [10, 6, 6, np.nan]])
a = np.transpose(a)
print(a)
# [[  1.  10.]
#  [  5.   6.]
#  [ NaN   6.]
#  [  6.  NaN]]
b=np.ma.compress_rows(np.ma.fix_invalid(a))
print(b)
# [[  1.  10.]
#  [  5.   6.]]

2
+1:哇!我很少看到使用和建议掩码数组!太棒了! - Eric O. Lebigot

3

不是要贬低ig0774的答案,他的回答完全正确、符合Python规范,事实上在纯Python中这也是常规的做法,但是:numpy支持布尔索引系统,也可以完成这个任务。

new_a = a[(a==a).all(1)]

我不确定哪种方法更高效(或执行速度更快)。

如果你想使用不同的条件选择行,这将需要进行更改,具体取决于条件。如果它是可以独立评估每个数组元素的内容,你只需用适当的测试替换 a==a 即可。例如,要消除所有数字大于100的行,您可以进行如下操作:

new_a = a[(a<=100).all(1)]

但是,如果你想做一些复杂的事情,涉及到一行中的所有元素(比如消除所有总和大于100的行),这可能会更加复杂。如果是这种情况,如果你愿意分享你的确切条件,我可以尝试编辑一个更具体的答案。


+1:对于向量化方法。这几乎总是更快的,并且是使用numpy的主要原因之一。 - tom10

2
我认为列表推导式可以做到这一点。例如,
new_a = [(val1, val2) for (val1, val2) in a if math.isnan(val1) or math.isnan(val2)]

2
你实际上需要将测试设置为 val1 != val1,因为 nan == nan 返回 false。但是加一分。 - David Z
我编辑了上面的内容...老实说,我从来没有需要使用NaN,所以我主要是在处理构建列表的形式。根据这里的文档:http://docs.python.org/library/math.html#math.isnan,我认为这应该可以工作... - ig0774
2
应该是:...在where子句中不是math.isnan(val1)和not math.isnan(val2)吗? - user248237
你已经写了太多的SQL语句了。 :) 用“if”代替“where”。 - Mark Dickinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接