添加不同尺寸/形状的NumPy矩阵

时间:2012-03-27 09:00:38

标签: python numpy

简而言之:我有两个矩阵(或数组):

import numpy

block_1 = numpy.matrix([[ 0, 0, 0, 0, 0],
                        [ 0, 0, 0, 0, 0],
                        [ 0, 0, 0, 0, 0],
                        [ 0, 0, 0, 0, 0]])

block_2 = numpy.matrix([[ 1, 1, 1],
                        [ 1, 1, 1],
                        [ 1, 1, 1],
                        [ 1, 1, 1]])

我在block_2元素坐标系中有block_1的位移。

pos = (1,1)

我希望能够(快速)添加它们,以获得:

[[0 0 0 0 0]
 [0 1 1 1 0]
 [0 1 1 1 0]
 [0 1 1 1 0]]

总之:我希望能够快速地将两个不同的形状矩阵添加到一起,其中一个矩阵可以移位。得到的矩阵必须具有第一矩阵的形状,并且两个矩阵之间的重叠元素相加。如果没有重叠,则只返回未突变的第一个矩阵。

我有一个功能很好的功能,但它有点难看,而且元素化:

def add_blocks(block_1, block_2, pos):
    for i in xrange(0, block_2.shape[0]):
        for j in xrange(0, block_2.shape[1]):
            if (i + pos[1] >= 0) and (i + pos[1] < block_1.shape[0])
               and (j + pos[0] >= 0) and (j + pos[0] < block_1.shape[1]):
                block_1[pos[1] + i, pos[0] + j] += block_2[i,j]
    return block_1

广播或切片可能会这样做吗?

我觉得我可能错过了一些明显的东西。

5 个答案:

答案 0 :(得分:14)

一个看起来像MATLAB解决方案的简单解决方案是:

import numpy as np

block1 = np.zeros((5,4))
block2 = np.ones((3,2))

block1[1:4,2:4] += block2  # use array slicing

print(block1)

[[0. 0. 0. 0.]
 [0. 0. 1. 1.]
 [0. 0. 1. 1.]
 [0. 0. 1. 1.]
 [0. 0. 0. 0.]]

因此将其打包为可重复使用的函数:

import numpy as np

def addAtPos(mat1, mat2, xypos):
    """
    Add two matrices of different sizes in place, offset by xy coordinates
    Usage:
      - mat1: base matrix
      - mat2: add this matrix to mat1
      - xypos: tuple (x,y) containing coordinates
    """
    x, y = xypos
    ysize, xsize = mat2.shape
    xmax, ymax = (x + xsize), (y + ysize)
    mat1[y:ymax, x:xmax] += mat2
    return mat1

block1 = np.zeros((5,4))
block2 = np.ones((3,2))
pos = (2,1)
print(addAtPos(block1, block2, pos))

[[0. 0. 0. 0.]
 [0. 0. 1. 1.]
 [0. 0. 1. 1.]
 [0. 0. 1. 1.]
 [0. 0. 0. 0.]]

答案 1 :(得分:4)

您只需找到重叠范围,然后使用切片添加数组。

b1 = np.zeros((4,5))
b2 = np.ones((4,3))
pos_v, pos_h = 2, 3  # offset
v_range1 = slice(max(0, pos_v), max(min(pos_v + b2.shape[0], b1.shape[0]), 0))
h_range1 = slice(max(0, pos_h), max(min(pos_h + b2.shape[1], b1.shape[1]), 0))

v_range2 = slice(max(0, -pos_v), min(-pos_v + b1.shape[0], b2.shape[0]))
h_range2 = slice(max(0, -pos_h), min(-pos_h + b1.shape[1], b2.shape[1]))

b1[v_range1, h_range1] += b2[v_range2, h_range2]

它们已就地添加,但您也可以创建一个新数组。不过,我可能已经错过了一些极端情况,但似乎工作正常。

答案 2 :(得分:2)

这很棒,以及如何通过在jorgeca的代码中添加几行来扩展3D矩阵:

import numpy as np

#two 3d arrays, of different size.
b1 = np.zeros((5,5,5), dtype=np.int) # a 5x5x5 matrix of zeroes
b2 = np.ones((3,3,3), dtype=np.int)  # a 3x3x3 matrix of ones

pos_v, pos_h, pos_z = 2, 2, 2  # a 3d offset -> to plonk b2 in the corner of b1

v_range1 = slice(max(0, pos_v), max(min(pos_v + b2.shape[0], b1.shape[0]), 0))
h_range1 = slice(max(0, pos_h), max(min(pos_h + b2.shape[1], b1.shape[1]), 0))
z_range1 = slice(max(0, pos_z), max(min(pos_z + b2.shape[2], b1.shape[2]), 0))

v_range2 = slice(max(0, -pos_v), min(-pos_v + b1.shape[0], b2.shape[0]))
h_range2 = slice(max(0, -pos_h), min(-pos_h + b1.shape[1], b2.shape[1]))
z_range2 = slice(max(0, -pos_z), min(-pos_z + b1.shape[2], b2.shape[2]))

b1[v_range1, h_range1, z_range1] += b2[v_range2, h_range2, z_range2]

这可能会帮助那些想要在3d中做同样事情的人(比如我)。

答案 3 :(得分:1)

我确信有一种快速的NumPy方法可以做到这一点,但即使在普通的Python中也有一种更有效的方法:

block_1 = [ [ 0, 0, 0, 0, 0],
            [ 0, 0, 0, 0, 0],
            [ 0, 0, 0, 0, 0],
            [ 0, 0, 0, 0, 0]]

block_2 = [ [ 1, 1, 1],
            [ 1, 1, 1],
            [ 1, 1, 1],
            [ 1, 1, 1]]

pos = (1, 1)

x, y = pos

# width of the rows in block_2
length = len(block_2[0])

# skip the first y rows
for row_1, row_2 in zip(block_1[y:], block_2):
    # set length elements offset by x to the sum.
    row_1[x:length + x] = map(sum, zip(row_2, row_1[x:length + x]))

print '\n'.join(' '.join(map(str, row)) for row in block_1)

"""
0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
"""

答案 4 :(得分:1)

这里有@jorgeca's great code作为函数,有一些测试 - 我扩展了切片以试图让它更具可读性:

import numpy as np


def addAtPos(matrix1, matrix2, xypos, inPlace=False):
    """
    Add matrix2 into matrix1 at position xypos (x,y), in-place or in new matrix.
    Handles matrix2 going off edges of matrix1.
    """

    x, y = xypos
    h1, w1 = matrix1.shape
    h2, w2 = matrix2.shape

    # get slice ranges for matrix1
    x1min = max(0, x)
    y1min = max(0, y)
    x1max = max(min(x + w2, w1), 0)
    y1max = max(min(y + h2, h1), 0)

    # get slice ranges for matrix2
    x2min = max(0, -x)
    y2min = max(0, -y)
    x2max = min(-x + w1, w2)
    y2max = min(-y + h1, h2)

    if inPlace:
        # add matrix2 into matrix1, in place
        matrix1[y1min:y1max, x1min:x1max] += matrix2[y2min:y2max, x2min:x2max]
    else:
        # create and return a new matrix
        matrix1copy = matrix1.copy()        
        matrix1copy[y1min:y1max, x1min:x1max] += matrix2[y2min:y2max, x2min:x2max]
        return matrix1copy


def test_addAtPos():

    matrix1 = np.zeros((2,2))
    matrix2 = np.ones((2,2))

    test(addAtPos(matrix1, matrix2, ( 0, 0)), [[1,1],[1,1]])
    test(addAtPos(matrix1, matrix2, ( 2, 2)), [[0,0],[0,0]])
    test(addAtPos(matrix1, matrix2, (-1,-1)), [[1,0],[0,0]])
    test(addAtPos(matrix1, matrix2, ( 1,-1)), [[0,1],[0,0]])
    test(addAtPos(matrix1, matrix2, ( 1, 1)), [[0,0],[0,1]])
    test(addAtPos(matrix1, matrix2, (-1, 1)), [[0,0],[1,0]])


def test(actual, expected, message=''):
    "Compare actual and expected values and print OK or FAIL"
    passed = (actual == expected)
    if type(passed) == np.ndarray:
        passed = passed.all()
    actual = str(actual).replace('\n', '')
    expected = str(expected).replace('\n', '')
    if passed:
        print('[OK]  ', message, actual)
    else:
        print('[FAIL]', message, actual, ' != expected value of', expected)


test_addAtPos()

输出:

[OK]    [[1. 1.] [1. 1.]]
[OK]    [[0. 0.] [0. 0.]]
[OK]    [[1. 0.] [0. 0.]]
[OK]    [[0. 1.] [0. 0.]]
[OK]    [[0. 0.] [0. 1.]]
[OK]    [[0. 0.] [1. 0.]]