Adición de matrices NumPy desplazadas de diferentes tamaños / formas

En resumen: tengo dos matrices (o matrices):

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

Tengo el desplazamiento de block_2 en el sistema de coordenadas del elemento block_1 .

 pos = (1,1) 

Quiero poder agregarlos (rápidamente), para obtener:

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

En el largo: me gustaría una forma rápida de agregar dos matrices de formas diferentes, donde una de las matrices se puede desplazar. La matriz resultante debe tener la forma de la primera matriz, y se sumn los elementos superpuestos entre las dos matrices. Si no hay superposición, solo la primera matriz se devuelve sin cambios.

Tengo una función que funciona bien, pero es algo feo y elemental:

 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] = 0) and (j + pos[0] < block_1.shape[1]): block_1[pos[1] + i, pos[0] + j] += block_2[i,j] return block_1 

¿Puede la radiodifusión o corte tal vez hacer esto?

Siento que tal vez me esté perdiendo algo obvio.

Solo tiene que encontrar el rango de superposición y, a continuación, agregar los arreglos mediante el uso de segmentación.

 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] 

Se agregan en el lugar, pero también puede crear una nueva matriz. Puede que me haya perdido algunos casos de esquina, pero parece funcionar bien.

Una solución fácil que se parece a la solución MATLAB es:

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

Así que empácalo como una función reutilizable:

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

Esto es genial, y aquí es cómo extender la adición a una matriz 3D agregando algunas líneas al código de jorgeca:

 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] 

Esto podría ayudar a alguien que quiera hacer lo mismo en 3d (como yo).

Estoy seguro de que hay una forma rápida de hacer NumPy para hacer esto, pero hay una forma más eficiente de hacerlo incluso en Python normal:

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

Aquí está el gran código de @ jorgeca como una función, con algunas pruebas: expandí los cortes para tratar de hacerlo un poco más legible:

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

Salida:

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