如何使用掩码仅替换numpy数组的零

时间:2016-03-23 12:47:02

标签: python numpy

考虑2D阵列:

arr = np.zeros((10,10))
arr[3:7,3:7] = 1

现在我想用掩码用其他值替换它的一部分:

mask = np.ones((5,5)).astype(bool)
arr[5:,5:][mask] = 2

是否可以将非零元素保留在原始arr中并使用掩码仅替换零元素?我想通过平面索引来避免这样做,因为我处理的数组是大型3D数组(大约1000x1000x1000)。

编辑:其他一些信息:

我想避免更改掩码,这包括将数组设置为False,其中数组非零以及调整大小。原因是该掩模位于阵列的不同区域需要多次重复该操作。由于数组非常大,因此避免复制数据也是很好的。

5 个答案:

答案 0 :(得分:1)

使用np.logical_and

arr = np.zeros((10,10))
arr[3:7,3:7] = 1
mask = np.ones((10,10)).astype(bool) #same shape as the array
mask = np.logical_and(mask, arr == 0)
arr[mask] = 2 # replace 0's with whatever value

答案 1 :(得分:1)

如果你想应用一个滑动窗口方法,你可以使用扩展@Thiru的方法让它工作:

>>> arr = np.zeros((10,10))
>>> arr[3:7,3:7] = 1
>>> mask = np.ones((5,5)).astype(bool)

相应地更新阵列:

>>> CONSTANT = 2
>>> arr[5:,5:] += np.logical_and(mask, arr[5:, 5:] == 0) * CONSTANT
array([[ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  1.,  1.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  1.,  1.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  1.,  1.,  1.,  2.,  2.,  2.],
       [ 0.,  0.,  0.,  1.,  1.,  1.,  1.,  2.,  2.,  2.],
       [ 0.,  0.,  0.,  0.,  0.,  2.,  2.,  2.,  2.,  2.],
       [ 0.,  0.,  0.,  0.,  0.,  2.,  2.,  2.,  2.,  2.],
       [ 0.,  0.,  0.,  0.,  0.,  2.,  2.,  2.,  2.,  2.]])

添加将保持非零元素,logical_and将创建一个掩码,乘以常量将添加0值,其中数组为非零且{{1}否则。

答案 2 :(得分:1)

其他人建议logical_and,但你反对说它涉及太多的复制。但首先让我们设置一个可以做到这一点的交互式案例

In [353]: arr=np.zeros((10,10))
In [354]: arr[3:7,3:7]=1

In [355]: tups=[(slice(5),slice(5)),
                (slice(0,5),slice(3,8)),
                (slice(4,9),slice(1,6))]

In [356]: for i,tup in enumerate(tups):
    mask1=np.logical_and(mask,arr[tup]==0)
    arr[tup][mask1]=i+1
   .....:     

In [357]: arr
Out[357]: 
array([[ 1.,  1.,  1.,  1.,  1.,  2.,  2.,  2.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.,  2.,  2.,  2.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.,  2.,  2.,  2.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  2.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  2.,  0.,  0.],
       [ 0.,  3.,  3.,  1.,  1.,  1.,  1.,  0.,  0.,  0.],
       [ 0.,  3.,  3.,  1.,  1.,  1.,  1.,  0.,  0.,  0.],
       [ 0.,  3.,  3.,  3.,  3.,  3.,  0.,  0.,  0.,  0.],
       [ 0.,  3.,  3.,  3.,  3.,  3.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])

arr[tup]==0是另一个面具。这是你告诉numpy你只想改变0的唯一方法。它不会自动处理0与1或3不同。我没有看到任何使用logical_and在每一步创建新掩码的方法。

布尔掩码的应用确实涉及平面索引 - 也就是说,结果是1d数组(无论是在右侧还是左侧)

查看从最后一次迭代中应用蒙版的结果

In [360]: arr[tup][mask]
Out[360]: 
array([ 1.,  1.,  1.,  1.,  1.,  3.,  3.,  1.,  1.,  1.,  3.,  3.,  1.,
        1.,  1.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.])

In [361]: arr[tup][mask1]
Out[361]: array([ 3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.,  3.])

以下是使用np.where的替代方法:

for i,tup in enumerate(tups):
    arr[tup]=np.where(arr[tup]==0,i+1,arr[tup])

这更简洁,但每次都要编写整个arr[tup]切片。

In [374]: %%timeit arr=np.zeros((10,10),int);arr[3:7,3:7]=1
   .....: for i,tup in enumerate(tups):
    arr[tup]=np.where(arr[tup]==0,i+1,arr[tup])
   .....: 
1000 loops, best of 3: 134 us per loop

In [375]: %%timeit arr=np.zeros((10,10),int);arr[3:7,3:7]=1
   .....: for i,tup in enumerate(tups):
    mask1=np.logical_and(mask,arr[tup]==0)
    arr[tup][mask1]=i+1p
   .....: 
10000 loops, best of 3: 64.9 us per loop

警告,使用arr[tup][mask]=...时, arr[tup]必须是视图,例如由切片生成。其他索引会生成一个副本,该副本会阻止对原始数组的更改。

答案 3 :(得分:0)

您可以使用pandas轻松完成此操作。要转换为3d数组,您需要在pandas中使用多索引。

import pandas as pd
import numpy as np

arr = np.zeros((10,10))
arr[3:7,3:7] = 1    

df = pd.DataFrame(arr)
df.loc[5:,5:] = df.loc[5:,5:].replace(0,2)

答案 4 :(得分:0)

这样的本地问题使用花式索引(真/假掩码),由于阵列上的多次传递,这通常很昂贵。

在这种情况下,Numba(或cython)通常是一个很好的改进来源:

def s1(a):
    a[N//2:,N//2:][N//2:, N//2:] == 0] = 30 

from numba import jit
@jit(nopython=True)
def s2(a):
    for i in range(N//2,N):
        for j in range(N//2,N):
            if a[i,j]==0 : a[i,j]=30

测试100x100阵列:

In [8]: %timeit s1(a)
10000 loops, best of 3: 65.5 µs per loop

In [9]: %timeit s2(a)
100000 loops, best of 3: 10.5 µs per loop