如何在Numpy 3D数组中根据2D索引和1D值向量替换沿z轴的值

6

我发现自己对数组索引还不太理解。

这是给出的信息:

我有一个三维数组,如下所示:

a_3d = np.zeros((3,3,3))

一个二维索引数组:
a_2d_index = np.array([[0,0,1], [0,0,0], [0,1,1]]).astype('bool')

需要在x,y位置放置到3D数组中的值:

a_1d_fill = np.array([10,20,30])

现在,我想使用a_2d_index在a_3d中查找位置,并在该x,y位置垂直放置a_1d_fill... 最终结果应如下所示:
a_3d := [[[0,0, 10],
          [0,0,  0],
          [0,10,10]],
         [[0,0, 20],
          [0,0,  0],
          [0,20,20]],
         [[0,0, 30],
          [0,0,  0],
          [0,30,30]]]

这将用于非常大的数组,因此内存效率和速度至关重要...(尽可能少的复制,最好是原地修改)

2个回答

7
In [26]: a_3d = np.zeros((3,3,3), dtype=int)

In [27]: a_2d_index = np.array([[0,0,1], [0,0,0], [0,1,1]]).astype('bool')

In [28]: a_1d_fill = np.array([10,20,30])

In [29]: a_3d[:,a_2d_index] = a_1d_fill[:,np.newaxis]

In [30]: a_3d
Out[30]:
array([[[ 0,  0, 10],
        [ 0,  0,  0],
        [ 0, 10, 10]],

       [[ 0,  0, 20],
        [ 0,  0,  0],
        [ 0, 20, 20]],

       [[ 0,  0, 30],
        [ 0,  0,  0],
        [ 0, 30, 30]]])

谢谢,看起来很干净!还有一个问题。如果我有一个4D数组,并且我想将其应用于4D数组的切片(形状为(10,3,3,3)),那么这个方法就不起作用。假设我想在索引2的切片上执行此操作:a_4d_array[2, :, a_2d_index] = a_1d_fill[:, np.newaxis]在我的脚本中失败了,因为形状不匹配。 - Christian
我承认我也对为什么这两行代码不等价感到困惑。我本以为它们会做同样的事情,但发现它们并不相同。 - Stuart Berg
a_4d_array[2, :, idx]是(棘手的)基本和高级索引混合的情况,https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing。它的形状与您预期的相反,首先是`idx`形状,最后是切片形状的转置。 - hpaulj

5
也许一点解释和直觉会帮助未来读者更好地理解切片。我(无耻地)使用 Stuart Berg的解决方案 来解释用户如何直观地感受切片。
数组和掩码定义:
In [57]: a_3d        # array with unique numbers for better understanding.
Out[57]: 
array([[[13, 14, 15],
        [23, 24, 25],
        [33, 34, 35]],

       [[ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]],

       [[ 0,  1,  2],
        [30, 31, 32],
        [40, 41, 42]]], dtype=uint8)

将上述三维数组看作是三个大小为3x3的数组,其中最上面的数组索引为0,中间的数组索引为1,最下面的数组索引为2
布尔掩码:
In [58]: a_2d_index
Out[58]: 
array([[False, False,  True],
       [False, False, False],
       [False,  True,  True]], dtype=bool)

现在,让我们使用布尔掩码a_2d_index来切片数组a_3d
In [68]: a_3d[:, a_2d_index]   # : means apply the mask to all the slices(here 3)
Out[68]: 
array([[15, 34, 35],
       [ 5, 10, 11],
       [ 2, 41, 42]], dtype=uint8)

好的,现在我们得到了上面的结果。为什么和如何呢?想象一下:我们将布尔掩码数组放在数组a_3d的每个切片上,并进行叠加。
现在,无论我们在掩码中有哪些True值,在数组a_3d中对应的元素都会发光并对结果数组做出贡献。当我们将掩码放在每个切片上时,这种情况会发生(因为我们使用了:,所以我们对数组a_3d中的所有切片都进行了操作)。
所以,切片完成了!现在,我们想要分配新值(其中掩码具有布尔值True)。
分配新值:
In [69]: a_1d_fill
Out[69]: array([10, 20, 30])

这是我们拥有的一维数组。但是,掩码是二维数组。因此,我们使用np.newaxis将其转换为二维数组。
In [70]: a_2d_fill = a_1d_fill[:, np.newaxis]

In [71]: a_2d_fill
Out[71]: 
array([[10],
       [20],
       [30]])


In [73]: a_3d[:, a_2d_index] = a_2d_fill

现在,此任务将把值10复制到第一个切片中的布尔掩码为True的索引处,然后将值20复制到第二个切片中的布尔掩码为True的索引处,最后将值30复制到第三个切片中的布尔掩码为True的索引处。

最终结果如下:

In [74]: a_3d
Out[74]: 
array([[[13, 14, 10],
        [23, 24, 25],
        [33, 10, 10]],

       [[ 3,  4, 20],
        [ 6,  7,  8],
        [ 9, 20, 20]],

       [[ 0,  1, 30],
        [30, 31, 32],
        [40, 30, 30]]], dtype=uint8)

哎呀,这真的很长。简而言之,结果数组应该在第一、第二和第三个切片中具有值102030(其中布尔掩码的值为True)。


P.S.:切片提供了原始数组的视图或引用。因此,当我们使用切片更改值时,这将影响原始数组。因此,这是一种原地修改。

1
谢谢你的解释,帮了我大忙! - Guillaume

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