python numpy获取蒙版数据而不展平

时间:2015-12-26 04:13:12

标签: python arrays numpy mask

如何在不将数据展平为一维数组的情况下获取屏蔽数据?也就是说,假设我有一个numpy数组

a = np.array([[0, 1, 2, 3],
              [0, 1, 2, 3],
              [0, 1, 2, 3]])

我屏蔽了大于1的所有元素,

b = ma.masked_greater(a, 1)

masked_array(data =
 [[0 1 -- --]
 [0 1 -- --]
 [0 1 -- --]],
             mask =
 [[False False  True  True]
 [False False  True  True]
 [False False  True  True]],
       fill_value = 999999)

如何在不展平输出的情况下仅获取蒙版元素?也就是说,我需要得到

array([[ 2, 3],
       [2, 3],
       [2, 3]])

3 个答案:

答案 0 :(得分:2)

让我们尝试一个产生不规则结果的例子 - 每行中有不同数量的'蒙面'值。

In [292]: a=np.arange(12).reshape(3,4)
In [293]: a
Out[293]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
In [294]: a<6
Out[294]: 
array([[ True,  True,  True,  True],
       [ True,  True, False, False],
       [False, False, False, False]], dtype=bool)

符合此条件的展平值列表。它无法返回常规的2d数组,因此必须恢复为扁平数组。

In [295]: a[a<6]
Out[295]: array([0, 1, 2, 3, 4, 5])

做同样的事情,但是逐行迭代

In [296]: [a1[a1<6] for a1 in a]
Out[296]: [array([0, 1, 2, 3]), array([4, 5]), array([], dtype=int32)]

尝试生成结果数组会生成一个对象类型数组,它只不过是数组包装器中的列表:

In [297]: np.array([a1[a1<6] for a1 in a])
Out[297]: array([array([0, 1, 2, 3]), array([4, 5]), array([], dtype=int32)], dtype=object)

结果粗糙的事实是一个很好的指标,即如果不是不可能的话,用一个矢量化操作执行该动作是很困难的。

这是生成数组列表的另一种方法。使用sum,我会在每行中找到有多少元素,然后将其用于split展平数组到子列表中。

In [320]: idx=(a<6).sum(1).cumsum()[:-1]
In [321]: idx
Out[321]: array([4, 6], dtype=int32)
In [322]: np.split(a[a<6], idx)
Out[322]: [array([0, 1, 2, 3]), array([4, 5]), array([], dtype=float64)]

它确实使用'展平'。对于这些小例子,它比行迭代慢。 (不要担心空的浮点数组,split必须构造一些东西并使用默认的dtype。)

另一个没有空行的掩码清楚地显示了两种方法的等价性。

In [344]: mask=np.tri(3,4,dtype=bool)  # lower tri
In [345]: mask
Out[345]: 
array([[ True, False, False, False],
       [ True,  True, False, False],
       [ True,  True,  True, False]], dtype=bool)
In [346]: idx=mask.sum(1).cumsum()[:-1]
In [347]: idx
Out[347]: array([1, 3], dtype=int32)
In [348]: [a1[m] for a1,m in zip(a,mask)]
Out[348]: [array([0]), array([4, 5]), array([ 8,  9, 10])]
In [349]: np.split(a[mask],idx)
Out[349]: [array([0]), array([4, 5]), array([ 8,  9, 10])]

答案 1 :(得分:1)

将两个列表压缩在一起,然后将其过滤掉:

data = [[0, 1, 1, 1], [0, 1, 1, 1], [0, 1, 1, 1]]

mask = [[False, False,  True,  True],
 [False, False,  True,  True],
 [False, False,  True,  True]]

zipped = zip(data, mask) # [([0, 1, 1, 1], [False, False, True, True]), ([0, 1, 1, 1], [False, False, True, True]), ([0, 1, 1, 1], [False, False, True, True])]

masked = []
for lst, mask in zipped:
    pairs = zip(lst, mask)  # [(0, False), (1, False), (1, True), (1, True)]
    masked.append([num for num, b in pairs if b])

print(masked)  # [[1, 1], [1, 1], [1, 1]]

或更简洁:

zipped = [...]
masked = [[num for num, b in zip(lst, mask) if b] for lst, mask in zipped]
print(masked)  # [[1, 1], [1, 1], [1, 1]]

答案 2 :(得分:0)

由于numpy中的矢量化,您可以使用np.where从第一个数组中选择项目,并使用None(或某个任意值)来指示值已被屏蔽掉的位置。请注意,这意味着您必须对数组使用不太紧凑的表示,因此可能需要使用-1或某些特殊值。

import numpy as np

a = np.array([
    [0, 1, 2, 3],
    [0, 1, 2, 3],
    [0, 1, 2, 3]])

mask = np.array([[ True,  True,  True,  True],
    [ True, False,  True,  True],
    [False,  True,  True, False]])

np.where(a, np.array, None)

这会产生

array([[0, 1, 2, 3],
   [0, None, 2, 3],
   [None, 1, 2, None]], dtype=object)