【问题标题】:What is the fastest way to insert elements diagonally in 2D numpy array?在二维 numpy 数组中对角插入元素的最快方法是什么?
【发布时间】:2016-01-06 18:29:02
【问题描述】:

假设我们有一个像这样的二维 numpy 数组:

matrix = [[1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
          [10, 11, 12]]

我想在对角线上插入一个值,比如 0,这样它就变成了:

matrix = [[0, 1, 2, 3],
          [4, 0, 5, 6],
          [7, 8, 0, 9],
          [10, 11, 12, 0]]

最快的方法是什么?

【问题讨论】:

  • 形状总是(4, 3)?有什么限制吗?如果您尝试在形状为(3, 4) 的数组中插入对角线,您想要什么?
  • 原始形状总是这样 (n+1,n) 其中 n > 0 但我希望答案也适用于 (n,n)。
  • 我会创建一个新数组(零)并使用np.tri... 函数将上下三角形从旧数组复制到新数组。

标签: python numpy matrix diagonal


【解决方案1】:

创建一个新的更大的矩阵,为零留出空间。将原始矩阵复制到子矩阵,裁剪和重塑:

matrix = numpy.array([[1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
          [10, 11, 12]])

matrix_new = numpy.zeros((4,5))
matrix_new[:-1,1:] = matrix.reshape(3,4)
matrix_new = matrix_new.reshape(-1)[:-4].reshape(4,4)

或更广义的形式:

matrix = numpy.array([[1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
          [10, 11, 12]])

d = matrix.shape[0]
assert matrix.shape[1] == d - 1
matrix_new = numpy.ndarray((d, d+1), dtype=matrix.dtype)
matrix_new[:,0] = 0
matrix_new[:-1,1:] = matrix.reshape((d-1, d))
matrix_new = matrix_new.reshape(-1)[:-d].reshape(d,d)

【讨论】:

    【解决方案2】:

    这是一种方法(但我不能保证它是最快的方法):

    In [62]: a
    Out[62]: 
    array([[ 1,  2,  3],
           [ 4,  5,  6],
           [ 7,  8,  9],
           [10, 11, 12]])
    
    In [63]: b = np.zeros((a.shape[0], a.shape[1]+1), dtype=a.dtype)
    
    In [64]: i = np.arange(b.shape[0])
    
    In [65]: j = np.arange(b.shape[1])
    
    In [66]: b[np.not_equal.outer(i, j)] = a.ravel()  # or a.flat, if a is C-contiguous
    
    In [67]: b
    Out[67]: 
    array([[ 0,  1,  2,  3],
           [ 4,  0,  5,  6],
           [ 7,  8,  0,  9],
           [10, 11, 12,  0]])
    

    它适用于任何二维数组a

    In [72]: a
    Out[72]: 
    array([[17, 18, 15, 19, 12],
           [16, 14, 11, 16, 17],
           [19, 11, 16, 11, 14]])
    
    In [73]: b = np.zeros((a.shape[0], a.shape[1]+1), dtype=a.dtype)
    
    In [74]: i = np.arange(b.shape[0])
    
    In [75]: j = np.arange(b.shape[1])
    
    In [76]: b[np.not_equal.outer(i, j)] = a.flat
    
    In [77]: b
    Out[77]: 
    array([[ 0, 17, 18, 15, 19, 12],
           [16,  0, 14, 11, 16, 17],
           [19, 11,  0, 16, 11, 14]])
    

    它有效,但我认为@Daniel 的答案是正确的方法。

    【讨论】:

    • 你可以用~np.eye(*b.shape, dtype=np.bool)替换np.not_equal.outer(i, j)
    【解决方案3】:

    另一种方法,可能更慢,使用追加和重塑

    import numpy as np
    
    mat = np.array(range(1,13)).reshape(4,3)
    mat
    
    array([[ 1,  2,  3],
           [ 4,  5,  6],
           [ 7,  8,  9],
           [10, 11, 12]])
    
    z=np.zeros((3,1), dtype=mat.dtype)
    m3=np.append(z,mat.reshape(3,4),1)
    np.append(m3,0).reshape(4,4)
    
    array([[ 0,  1,  2,  3],
           [ 4,  0,  5,  6],
           [ 7,  8,  0,  9],
           [10, 11, 12,  0]])
    

    【讨论】:

    • 正要发布一些非常接近这个的东西!这里的想法不错。另外,看看使用np.hstacknp.concatenate 是否有助于提高性能。
    【解决方案4】:

    看起来您正在使用下三角阵列和上三角阵列并用零对角线将它们分开。这个序列就是这样做的:

    In [54]: A=np.arange(1,13).reshape(4,3)
    

    目标数组,多一列

    In [55]: B=np.zeros((A.shape[0],A.shape[1]+1),dtype=A.dtype)
    

    复制下三角(不包括对角线)

    In [56]: B[:,:-1]+=np.tril(A,-1)
    

    复制上行程

    In [57]: B[:,1:]+=np.triu(A,0)
    
    In [58]: B
    Out[58]: 
    array([[ 0,  1,  2,  3],
           [ 4,  0,  5,  6],
           [ 7,  8,  0,  9],
           [10, 11, 12,  0]])
    

    有一些np.tril_indices... 函数,但它们只适用于方形数组。所以它们不能与A一起使用。

    【讨论】:

      【解决方案5】:

      假设您有一个 p x q numpy 2d 数组 A,这是一个 (p,q) 为 (3,4) 的示例:

      In []: A = np.arange(1,13).reshape(4,3)
      In []: A
      Out[]: 
      array([[ 1,  2,  3],
            [ 4,  5,  6],
            [ 7,  8,  9],
            [10, 11, 12]])
      

      Step 1:

      要插入零对角线,需要创建一个形状为 p x q+1 的新二维数组。

      在此之前,我们为新的二维数组创建一个二维数组,其中包含非对角元素的列索引值,如下所示

      In []: columnIndexArray = np.delete(np.meshgrid(np.arange(q+1), np.arange(p))[0], np.arange(0, p * (q+1), q+2)).reshape(p,q)
      

      上面的输出将如下所示:

      In []: columnIndexArray
      Out[]: 
      array([[1, 2, 3],
            [0, 2, 3],
            [0, 1, 3],
            [0, 1, 2]])
      

      Step 2:

      现在像这样构造 p x q+1 二维零数组

      In []: B = np.zeros((p,q+1))
      
      In []: B
      Out[]: 
      array([[ 0.,  0.,  0.,  0.],
             [ 0.,  0.,  0.,  0.],
             [ 0.,  0.,  0.,  0.],
             [ 0.,  0.,  0.,  0.]])
      

      Step 3:

      现在用 A 中的值分配非对角元素

      In []: B[np.arange(p)[:,None], columnIndexArray] = A
      
      In []: B
      Out[]: 
      array([[  0.,   1.,   2.,   3.],
            [  4.,   0.,   5.,   6.],
            [  7.,   8.,   0.,   9.],
            [ 10.,  11.,  12.,   0.]])
      

      Note: 为了使您的代码动态替换 p 与 A.shape[0] 和 q 分别与 A.shape[1]。

      【讨论】:

        【解决方案6】:

        同样,我不知道它有多快,但您可以尝试使用numpy.lib.stride_tricks.as_strided

        import numpy as np
        as_strided = np.lib.stride_tricks.as_strided
        
        matrix = (np.arange(12)+1).reshape((4,3))
        
        n, m = matrix.shape
        t = matrix.reshape((m, n))
        t = np.hstack((np.array([[0]*m]).T, t))
        t = np.vstack((t, [0]*(n+1)))
        q = as_strided(t, (n,n), (t.itemsize*n, 8))
        print(q)
        

        输出:

        [[ 0  1  2  3]
         [ 4  0  5  6]
         [ 7  8  0  9]
         [10 11 12  0]]
        

        也就是说,用零填充重构后的数组的左侧和底部,并跨步将左侧的零放在对角线上。不幸的是,在(n+1,n) 数组的情况下,您需要最后一行零来获得输出矩阵中的最后一个零(因为例如 5*3=15 比 4*4=16 小一)。如果你从方阵开始,你可以不这样做。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-01-26
          • 1970-01-01
          • 2015-06-16
          • 1970-01-01
          • 2010-10-27
          • 1970-01-01
          • 2011-01-31
          • 2023-03-29
          相关资源
          最近更新 更多