【问题标题】:slice a 3d numpy array using a 2d numpy array使用 2d numpy 数组切片 3d numpy 数组
【发布时间】:2016-03-01 19:57:19
【问题描述】:

是否可以使用 2d 数组对 3d 数组进行切片。我假设它可以完成,但需要您指定轴?

如果我有 3 个数组,那么:

A = [[1,2,3,4,5],
     [1,3,5,7,9],
     [5,4,3,2,1]] # shape (3,5)

B1 = [[1],
      [2],
      [3]] # shape (3, 1) 

B2 = [[4],
      [3],
      [4]] # shape (3,1)

是否可以使用 B1 和 B2 对 A 进行切片,例如:

Out = A[B1:B2]

这样它就会返回我:

Out = [[2,3,4,5],
       [5, 7],
       [2, 1]]

或者如果切片在Out 中创建不同长度的数组,这将不起作用?

【问题讨论】:

    标签: python arrays numpy slice


    【解决方案1】:

    Numpy 针对具有固定维度的同构数字数组进行了优化,因此它不支持不同的行或列大小。

    但是,您可以通过使用数组列表来实现您想要的:

    Out = [A[i, B1[i]:B2[i]+1] for i in range(len(B1))]
    

    【讨论】:

    • 这真的很整洁,但它并没有让我得到我想要的,它一直返回切片无效的错误。然而,它确实让我思考,虽然效率不高,但我可以使用 'for i in range(len(B1))' 后跟 out.append([A[i][B1[i]: [B2[i]]])。它可能效率不高,但可以满足我的需要。它似乎并不太慢
    • 我自己测试了代码,没有发现任何问题。你确定你在形状为 (3,5)、(3,1) 和 (3,1) 的 numpy 数组上尝试过这个吗?
    • 诚然,我的数组与此示例的形状不同。 A 是 (40000,432),B1 和 B2 是 (40000, 1)。我假设我提供的示例只是一个缩小版本,如果不是,请道歉。
    • 它应该可以工作,只要每个iB2[i] 大于或等于B1[i](当然两者都应该是整数)
    【解决方案2】:

    这是一个 vectorization -

    n_range = np.arange(A.shape[1])
    elems = A[(n_range >= B1) & (n_range <= B2)]      
    idx = (B2 - B1 + 1).ravel().cumsum()
    out = np.split(elems,idx)[:-1]
    

    诀窍是使用broadcasting 创建要为输出选择的元素掩码。然后,在指定位置拆分这些元素的数组以获得数组列表。

    样本输入、输出-

    In [37]: A
    Out[37]: 
    array([[1, 2, 3, 4, 5],
           [1, 3, 5, 7, 9],
           [5, 4, 3, 2, 1]])
    
    In [38]: B1
    Out[38]: 
    array([[1],
           [2],
           [3]])
    
    In [39]: B2
    Out[39]: 
    array([[4],
           [3],
           [4]])
    
    In [40]: out
    Out[40]: [array([2, 3, 4, 5]), array([5, 7]), array([2, 1])]
    # Please note that the o/p is a list of arrays
    

    【讨论】:

      【解决方案3】:

      您想要的结果在每行中具有不同数量的项 - 这是一个强有力的指标,表明完全矢量化的解决方案是不可能的。它不是对每一行或每一列做同样的事情。

      其次,n:m 转换为 slice(n,m)slice 只接受整数,不接受列表或数组。

      显而易见的解决方案是对行进行某种迭代:

      In [474]: A = np.array([[1,2,3,4,5],
                [1,3,5,7,9],
                [5,4,3,2,1]]) # shape (3,5)
      
      In [475]: B1=[1,2,3]  # no point in making these 2d
      
      In [476]: B2=[5,4,5]  # corrected values
      
      In [477]: [a[b1:b2] for a,b1,b2 in zip(A,B1,B2)]
      Out[477]: [array([2, 3, 4, 5]), array([5, 7]), array([2, 1])]
      

      如果A 是嵌套列表,此解决方案同样有效

      In [479]: [a[b1:b2] for a,b1,b2 in zip(A.tolist(),B1,B2)]
      Out[479]: [[2, 3, 4, 5], [5, 7], [2, 1]]
      

      这两个列表也可以转换为一维索引数组,然后从A.ravel() 中选择值。这将产生一个一维数组,例如

      array([2, 3, 4, 5, 5, 7, 2, 1]
      

      理论上可能是np.split - 但最近处理其他问题的经验表明这并不能节省太多时间。

      如果行选择的长度都相同,我们可以得到一个二维数组。每行采用 2 个元素的迭代版本:

      In [482]: np.array([a[b1:b1+2] for a,b1 in zip(A,B1)])
      Out[482]: 
      array([[2, 3],
             [5, 7],
             [2, 1]])
      

      我在前面的 SO 问题中讨论过如何通过一次索引操作产生这种结果。


      关于slice 接受的内容:

      In [486]: slice([1,2],[3,4]).indices(10)
      ---------------------------------------------------------------------------
      TypeError                                 Traceback (most recent call last)
      <ipython-input-486-0c3514e61cf6> in <module>()
      ----> 1 slice([1,2],[3,4]).indices(10)
      
      TypeError: slice indices must be integers or None or have an __index__ method
      

      “矢量化”拉威尔索引

      In [505]: B=np.array([B1,B2])    
      In [506]: bb=A.shape[1]*np.arange(3)+B
      In [508]: ri =np.r_[tuple([slice(i,j) for i,j in bb.T])]
      # or np.concatenate([np.arange(i,j) for i,j in bb.T])
      
      In [509]: ri
      Out[509]: array([ 1,  2,  3,  4,  7,  8, 13, 14])
      
      In [510]: A.ravel()[ri]
      Out[510]: array([2, 3, 4, 5, 5, 7, 2, 1])
      

      它仍然有一个迭代 - 生成进入 np.r_ 的切片(将它们扩展为单个索引数组)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-03
        • 2011-10-01
        • 2021-10-28
        • 2021-12-21
        • 1970-01-01
        相关资源
        最近更新 更多