获得矩阵的相邻元素的总和

时间:2018-01-27 09:13:50

标签: python matrix

我有一个矩阵 例如。

sqlite3 $DIRECTORY/real.db ".restore $DOWNLOADDIRECTORY/temp.db"

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

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

在python中执行此操作的最有效方法是什么?

到目前为止,我已经提出了

[[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]]

获取指数,但这些并未考虑边缘和角落情况。

5 个答案:

答案 0 :(得分:3)

解决此任务的正确工具似乎是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卷积视为将滤波器移过矩阵​​并在每个位置计算元素乘法的总和。严格来说,过滤器需要进行镜像,但在您的情况下,它们无论如何都是对称的。以下是网上发现的原则的随机说明:

enter image description here

与您的情况唯一不同的是,我们希望将滤镜放在任何地方,包括“边界”或“角落”位置。这可以通过对原始矩阵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个元素和水平/垂直相邻点上的4个二元素所包围。因此,位置(2,2)处的卷积输出中的条目为48。位置D[0,0]只有一个对角线邻居(1)和两个水平/垂直邻居(55)。卷积输出矩阵中的条目符合预期110

答案 1 :(得分:0)

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)])

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

答案 2 :(得分:0)

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]]

答案 3 :(得分:0)

暂时忽略转角情况,一种方法是将矩阵的移位版本叠加在一起。

例如,给定矩阵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]]

答案 4 :(得分:0)

事实证明,我发现有一种比我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]]