【问题标题】:Getting sum of adjacent elements of a matrix获取矩阵的相邻元素的总和
【发布时间】:2018-01-27 09:13:50
【问题描述】:

我有一个矩阵 例如。

[[4,5,0,0,0],
 [5,1,2,1,0],
 [0,2,3,2,0],
 [0,1,2,1,0],
 [0,0,0,0,0]]

对于矩阵中的每个元素,我试图获取其相邻对角线元素的总和及其相邻水平和垂直元素的总和。

以矩阵中间的 3 为例,我试图计算对角相邻元素(1)和水平和垂直相邻元素(2)的总和。 对于角落和边缘的情况,我想忽略没有元素的区域,例如(对于左上角的 4,我想得到对角相邻 1 的总和以及水平和垂直相邻的总和5个。

在 python 中最有效的方法是什么?

到目前为止,我想出了

diagonals = lambda x,y:[(x-1, y-1), (x-1,y+1), (x+1,y-1), (x+1,y+1)]
horiz_vert= lambda x,y:[(x,y+1), (x,y-1), (x+1,y), (x-1,y)]

获取索引,但这些没有考虑边缘和角落的情况。

【问题讨论】:

  • 另外如果不使用错误的算法,性能应该不会太差。您是否尝试过基准测试?

标签: python matrix


【解决方案1】:

解决此任务的正确工具似乎是convolution。您只需要定义将应用于每个位置的过滤器(“对角线邻居的总和”或“垂直/水平邻居的总和”),您就完成了。

import numpy as np
import scipy.signal

D = np.array([[4,5,0,0,0],
              [5,1,2,1,0],
              [0,2,3,2,0],
              [0,1,2,1,0],
              [0,0,0,0,0]])

h_diag = np.array([[1,0,1], [0,0,0], [1,0,1]])
h_hv   = np.array([[0,1,0], [1,0,1], [0,1,0]])

过滤器的外观如下:

>>> h_diag
array([[1, 0, 1],
       [0, 0, 0],
       [1, 0, 1]])
>>> h_hv
array([[0, 1, 0],
       [1, 0, 1],
       [0, 1, 0]])

您可以将 2D 卷积视为在矩阵中移动过滤器并在每个位置计算元素乘法的总和。严格来说,过滤器需要镜像,但在您的情况下,它们无论如何都是对称的。这是在网上找到的原理的随机说明:

与您的情况的唯一区别是我们希望将过滤器放置在任何地方,包括“边界”或“角落”位置。这可以通过对原始矩阵D 进行零填充来实现,这样可以放置滤波器的中心,例如在(0,0)的位置。

好消息!它已经在 Numpy/Scipy 中实现了,这是一个非常有效的实现!现在您只需将D 与过滤器h 进行卷积即可构造一个矩阵。

>>> scipy.signal.convolve2d(D, h_diag, mode='same')
array([[1, 7, 2, 2, 1],
       [7, 7, 9, 3, 2],
       [2, 9, 4, 4, 2],
       [2, 3, 4, 3, 2],
       [1, 2, 2, 2, 1]])

>>> scipy.signal.convolve2d(D, h_hv, mode='same')
array([[10,  5,  7,  1,  0],
       [ 5, 14,  5,  4,  1],
       [ 7,  5,  8,  5,  2],
       [ 1,  4,  5,  4,  1],
       [ 0,  1,  2,  1,  0]])

从这些矩阵中,您可以读取每个位置所需的总和。例如。原始矩阵 D 中的中心元素,即 D[2,2] 被对角线上的 4 个 1 和水平/垂直邻接上的 4 个 2 包围。因此,卷积输出中(2,2) 位置的条目是48。位置D[0,0] 只有一个对角线邻居(1)和两个水平/垂直邻居(55)。卷积输出矩阵中的条目如预期的110

【讨论】:

    【解决方案2】:
    def is_inbounds(x, y, length):
        return (0 <= x < length and 0 <= y < length)
    
    
    def total_of_diags(m, x, y):
        size = len(m)
        return sum([(m[x - 1][y - 1]) * is_inbounds(x, y, size),
                    (m[x - 1][y + 1] ) * is_inbounds(x, y, size),
                    (m[x + 1][y - 1] ) * is_inbounds(x, y, size),
                    (m[x + 1][y + 1] ) * is_inbounds(x, y, size)])
    

    现在对相邻数字之和应用相同的原理。

    【讨论】:

      【解决方案3】:
      def is_inside(x, y):
          return 0 <= x < len(m[0]) and 0 <= y < len(m) 
      
      def diagonal_sum(x, y):
          return sum([m[x - 1][y - 1] if is_inside(x - 1, y - 1) else 0,
                      m[x - 1][y + 1] if is_inside(x - 1, y + 1) else 0,
                      m[x + 1][y - 1] if is_inside(x + 1, y - 1) else 0,
                      m[x + 1][y + 1] if is_inside(x + 1, y + 1) else 0])
      
      def vertical_sum(x, y):
          return sum([m[x][y - 1] if is_inside(x, y - 1) else 0,
                      m[x][y + 1] if is_inside(x, y + 1) else 0,
                      m[x - 1][y] if is_inside(x - 1, y) else 0,
                      m[x + 1][y] if is_inside(x + 1, y) else 0])
      
      m = [[4,5,0,0,0],
           [5,1,2,1,0],
           [0,2,3,2,0],
           [0,1,2,1,0],
           [0,0,0,0,0]]
      
      diagonal = []
      vertical = []
      for i in range(len(m)):
          diagonal.append([])
          vertical.append([])
          for j in range(len(m[0])):
              diagonal[i].append(diagonal_sum(i, j))
              vertical[i].append(vertical_sum(i, j))
      
      print(diagonal)
      # prints:
      #
      # [[1, 7, 2, 2, 1],
      #  [7, 7, 9, 3, 2],
      #  [2, 9, 4, 4, 2],
      #  [2, 3, 4, 3, 2],
      #  [1, 2, 2, 2, 1]]
      
      print(vertical)
      # prints:
      #
      # [[10, 5,  7, 1, 0],
      #  [5, 14,  5, 4, 1],
      #  [7,  5,  8, 5, 2],
      #  [1,  4,  5, 4, 1],
      #  [0,  1,  2, 1, 0]]
      

      如果你需要对角线和垂直线的总和:

      def is_inside(x, y):
          return 0 <= x < len(m[0]) and 0 <= y < len(m) 
      
      def neighbour_sum(x, y):
          r = 0
          for i in range(y - 1, y + 2):
              for j in range(x - 1, x + 2):
                  if is_inside(i, j):
                      r += m[i][j]
          return r
      
      m = [[4,5,0,0,0],
           [5,1,2,1,0],
           [0,2,3,2,0],
           [0,1,2,1,0],
           [0,0,0,0,0]]
      
      result = []
      for i in range(len(m)):
          result.append([])
          for j in range(len(m[0])):
              result[i].append(neighbour_sum(i, j))
      
      print(result)
      # prints:
      #
      # [[15, 17,  9,  3,  1],
      #  [17, 22, 16,  8,  3],
      #  [9,  16, 15, 11,  4],
      #  [3,   8, 11,  8,  3],
      #  [1,   3,  4,  3,  1]]
      

      【讨论】:

        【解决方案4】:

        暂时忽略极端情况,一种方法是将矩阵的移位版本相互叠加。

        例如,给定一个矩阵m,我们对求和:

        def collapse_rows(m):
            # Select odd/even rows and then
            # pad top and bottom with row of zeroes
            e = np.pad(m[::2],  [(1,1), (0,0)], 'constant')
            o = np.pad(m[1::2], [(1,1), (0,0)], 'constant')
        
            # Sum together adjacent odd/even rows
            E = (e[:-1] + e[1:])[1:-1]
            O = (o[:-1] + o[1:])[1:-1]
        
            # Interweave rows
            m2 = np.empty((E.shape[0] + O.shape[0], m.shape[1]), dtype=m.dtype)
            m2[0::2] = E
            m2[1::2] = O
        
            return m2
        

        在你的矩阵上运行这个,我们得到:

        [[4, 7, 3, 2, 0],
         [5, 2, 4, 2, 0],
         [0, 2, 3, 2, 0]]
        

        然后我们可以为 做类似的事情。为了速度,我建议你专门写我为行写的代码,但我会偷懒,在转置上做:

        def collapse_columns(m):
            return collapse_rows(m.T).T
        

        在你的矩阵上运行这个,我们得到:

        [[4, 5, 0],
         [7, 2, 2],
         [3, 4, 3],
         [2, 2, 2],
         [0, 0, 0]]
        

        现在我们需要将这些矩阵组合在一起。让我们去掉多余的行/列以生成 3x3 矩阵,然后将它们相加:

        result = collapse_rows(m)[:, 1:-1] + collapse_columns(m)[1:-1, :]
        
        [[14,  5,  4],
         [ 5,  8,  5],
         [ 4,  5,  4]])
        

        如愿以偿!

        ...但是我们如何解释边界呢?只需用零填充矩阵!

        m_pad = np.pad(m, 1, 'constant')
        result = collapse_rows(m_pad)[:, 1:-1] + collapse_columns(m_pad)[1:-1, :]
        

        这给出了:

        [[10,  5,  7,  1,  0],
         [ 5, 14,  5,  4,  1],
         [ 7,  5,  8,  5,  2],
         [ 1,  4,  5,  4,  1],
         [ 0,  1,  2,  1,  0]]
        

        对角线求和,很简单:

        m_pad = np.pad(m, 1, 'constant')
        result = collapse_columns(collapse_rows(m_pad))
        

        产生:

        [[1, 7, 2, 2, 1],
         [7, 7, 9, 3, 2],
         [2, 9, 4, 4, 2],
         [2, 3, 4, 3, 2],
         [1, 2, 2, 2, 1]]
        

        【讨论】:

          【解决方案5】:

          事实证明,我意识到有一种比previous answer 中描述的更简单的方法。

          def get_adj(m):
              m_pad = np.pad(m, 1, 'constant')
          
              x = m_pad[:, :-2] + m_pad[:, 2:]
              y = m_pad[:-2] + m_pad[2:]
          
              hv = x[1:-1] + y[1:-1]
              diag = y[:, :-2] + y[:, 2:]
          
              return hv, diag
          
          print(*get_adj(
              [[4,5,0,0,0],
               [5,1,2,1,0],
               [0,2,3,2,0],
               [0,1,2,1,0],
               [0,0,0,0,0]]))
          

          结果:

          [[10,  5,  7,  1,  0],
           [ 5, 14,  5,  4,  1],
           [ 7,  5,  8,  5,  2],
           [ 1,  4,  5,  4,  1],
           [ 0,  1,  2,  1,  0]]
          
          [[1, 7, 2, 2, 1],
           [7, 7, 9, 3, 2],
           [2, 9, 4, 4, 2],
           [2, 3, 4, 3, 2],
           [1, 2, 2, 2, 1]]
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-07-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-09-10
            相关资源
            最近更新 更多