Добавление смещенных матриц NumPy различного размера / формы - PullRequest
9 голосов
/ 27 марта 2012

Вкратце: у меня есть две матрицы (или массивы):

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 ]

14 голосов
/ 27 марта 2012

Простое решение, похожее на решение 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.]]
4 голосов
/ 27 марта 2012

Вам просто нужно найти перекрывающийся диапазон, а затем добавить массивы с помощью нарезки.

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 голосов
/ 03 сентября 2014

Это здорово, и вот как расширить дополнение к 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 (например,меня).

1 голос
/ 31 мая 2018

Вот отличный код @ jorgeca как функция, с некоторыми тестами - я расширил фрагменты, чтобы сделать его немного более читабельным:

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.]]
1 голос
/ 27 марта 2012

Я уверен, что есть быстрый способ 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
"""
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...