【问题标题】:Numpy multi-dimensional slicing with multiple boolean arrays具有多个布尔数组的 Numpy 多维切片
【发布时间】:2016-04-11 08:28:07
【问题描述】:

我正在尝试使用单独的一维布尔数组对多维数组进行切片。由于某种原因,此代码不起作用:

>>> a = np.ones((100, 200, 300, 2))
>>> a.shape
(100, 200, 300, 2)
>>> m1 = np.asarray([True]*200)
>>> m2 = np.asarray([True]*300)
>>> m2[-1] = False
>>> a[:,m1,m2,:]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (200,) (299,) 
>>> m2 = np.asarray([True]*300) # try again with all 300 dimensions True
>>> a[:,m1,m2,:]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (200,) (300,) 

但这很好用:

>>> a = np.asarray([[[1, 2], [3, 4], [5, 6]], [[11, 12], [13, 14], [15, 16]]])
>>> a.shape
(2, 3, 2)
>>> m1 = np.asarray([True, False, True])
>>> m2 = np.asarray([True, False])
>>> a[:,m1,m2]
array([[ 1,  5],
       [11, 15]])

知道我在第一个示例中可能做错了什么吗?

【问题讨论】:

    标签: python arrays numpy slice


    【解决方案1】:

    简答:m1m2 中的 True 元素的数量必须匹配,除非其中一个只有一个 True 项。

    还要区分“对角线”索引和“矩形”索引。这是关于索引,而不是切片。带有: 的尺寸只是为了方便。

    初步构想

    我可以处理您的第一个案例:

    In [137]: a=np.ones((100,200,300,2))
    
    In [138]: m1=np.ones((200,),bool)    
    In [139]: m2=np.ones((300,),bool)
    In [140]: m2[-1]=False
    
    In [141]: I,J=np.ix_(m1,m2)
    
    In [142]: a[:,I,J,:].shape
    Out[142]: (100, 200, 299, 2)
    

    np.ix_ 将 2 个布尔数组转换为可广播的索引数组

    In [143]: I.shape
    Out[143]: (200, 1)
    In [144]: J.shape
    Out[144]: (1, 299)
    

    请注意,这会在一个维度中选择 200 个“行”,在另一个维度中选择 299 个。

    我不确定为什么在这种情况下需要对数组进行这种返工,但在第二次中不需要

    In [154]: b=np.arange(2*3*2).reshape((2,3,2))
    
    In [155]: n1=np.array([True,False,True])
    In [156]: n2=np.array([True,False])
    
    In [157]: b[:,n1,n2]
    Out[157]: 
    array([[ 0,  4],      # shape (2,2)
           [ 6, 10]])
    

    采用相同的ix_ 策略会产生相同的值但形状不同:

    In [164]: b[np.ix_(np.arange(b.shape[0]),n1,n2)]
    # or I,J=np.ix_(n1,n2);b[:,I,J]
    Out[164]: 
    array([[[ 0],
            [ 4]],
    
           [[ 6],
            [10]]])
    
    In [165]: _.shape
    Out[165]: (2, 2, 1)
    

    这两种情况都使用第一维的所有行。 ix 选择第二个暗淡的 2 个“行”和最后一个暗淡的 1 列,形成 (2,2,1) 形状。其他选择 b[:,0,0]b[0,2,0] 术语,得到 (2,2) 形状。 (请参阅我的附录,了解为什么两者都只是广播)。 这些都是高级索引的例子,带有布尔和数字索引。一个人可以研究文档,或者一个人可以玩。有时做后者更有趣。 :)

    (我知道ix_ 非常适合将必要的np.newaxis 添加到数组中,因此可以一起广播,但没有意识到它也适用于布尔数组 - 它使用np.nonzero() 将布尔值转换为索引.)

    分辨率

    我认为,这是对 2 种索引模式的混淆。这可能称为“对角线”和“矩形”(或逐个元素选择与块选择)。为了说明看一个小的二维数组

    In [73]: M=np.arange(6).reshape(2,3)
    In [74]: M
    Out[74]: 
    array([[0, 1, 2],
           [3, 4, 5]])
    

    和 2 个简单的数字索引

    In [75]: m1=np.arange(2); m2=np.arange(2)
    

    它们有两种使用方式:

    In [76]: M[m1,m2]
    Out[76]: array([0, 4])
    

    In [77]: M[m1[:,None],m2]
    Out[77]: 
    array([[0, 1],
           [3, 4]])
    

    第一个选择 2 分,M[0,0]M[1,1]。这种索引可以让我们挑选出数组的对角线。

    第二个选择 2 行并从 2 列中选择。这是np.ix_ 产生的索引类型。第一个选择 2 分,M[0,0]M[1,1]。这是一种“矩形”形式的索引。

    m2 更改为 3 个值:

    In [78]: m2=np.arange(3)
    In [79]: M[m1[:,None],m2]   # returns a 2x3
    Out[79]: 
    array([[0, 1, 2],
           [3, 4, 5]])
    In [80]: M[m1,m2]   # produces an error
    ...
    ValueError: shape mismatch: objects cannot be broadcast to a single shape
    

    但是如果m2只有一个元素,我们不会收到广播错误——因为在广播过程中可以扩展size 1维度:

    In [81]: m2=np.arange(1)
    In [82]: M[m1,m2]
    Out[82]: array([0, 3])
    

    现在将索引数组更改为布尔值,每个数组都匹配各自维度 2 和 3 的长度。

    In [91]: m1=np.ones(2,bool); m2=np.ones(3,bool)
    In [92]: M[m1,m2]
    ...
    ValueError: shape mismatch: objects cannot be broadcast to a single shape
    In [93]: m2[2]=False  # m1 and m2 each have 2 True elements
    In [94]: M[m1,m2]
    Out[94]: array([0, 4])
    In [95]: m2[0]=False   # m2 has 1 True element
    In [96]: M[m1,m2]
    Out[96]: array([1, 4])
    

    使用 2 和 3 True 术语我们会得到一个错误,但是使用 2 和 2 或 2 和 1 它会运行 - 就像我们使用 True 元素的索引一样:np.nonzero(m2)

    将此应用于您的示例。首先,m1m2 有 200 和 299 个 True 元素。 a[:,m1,m2,:] 失败,因为 True 术语的数量不匹配。

    在第二个中,它们有 2 个和 1 个 True 项,具有非零索引 [0,2][0],可以广播到 [0,0]。所以它运行。

    http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html 根据 nonzeroix_ 解释布尔数组索引。

    使用 obj.nonzero() 类比可以最好地理解组合多个布尔索引数组或布尔值与整数索引数组。函数 ix_ 还支持布尔数组,并且可以毫无意外地工作。

    附录

    进一步思考'对角线'和'块/矩形'索引之间的区别可能更像是numpys的我的心理结构。两者的基础是广播的概念。

    获取n1n2 布尔值,并获得它们的nonzero 等价物:

    In [107]: n1
    Out[107]: array([ True, False,  True], dtype=bool)
    In [108]: np.nonzero(n1)
    Out[108]: (array([0, 2], dtype=int32),)
    In [109]: n2
    Out[109]: array([ True, False], dtype=bool)
    In [110]: np.nonzero(n2)
    Out[110]: (array([0], dtype=int32),)
    

    现在尝试以“对角线”和“矩形”模式进行广播:

    In [105]: np.broadcast_arrays(np.array([0,2]),np.array([0]))
    Out[105]: [array([0, 2]), 
               array([0, 0])]
    
    In [106]: np.broadcast_arrays(np.array([0,2])[:,None],np.array([0]))
    Out[106]: 
    [array([[0],
            [2]]), 
     array([[0],
            [0]])]
    

    一个产生(2,) 数组,另一个产生(2,1)

    【讨论】:

    • 有趣的分析并感谢您的解决方法。仍然很好奇为什么在一种情况下需要这样做。
    • 我发现当使用“对角线”索引样式时,2 个布尔数组中 True 元素的数量非常匹配(或为 1)。查看我的编辑。
    • 我仍在努力掌握这个逻辑。我需要一段时间才能正确消化它,但感谢您解决这个问题。
    【解决方案2】:

    这可能是一个简单的解决方法:

    a[:,m1,:,:][:,:,m2,:]
    

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 2021-11-23
      • 1970-01-01
      • 2015-10-23
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多