Adding NumPy matrices with different size / shape

In short: I have two matrices (or arrays):

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

I have a block_2 offset in the coordinate system of block_1 element.

 pos = (1,1) 

I want to be able to add them (quickly) to get:

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

In the long run, I would like to quickly add two different forms of the matrix, where one of the matrices can be shifted. The resulting matrix must be in the form of the first matrix, and the overlapping elements between the two matrices are added up. If there is no overlap, only the first matrix returns without mutation.

I have a function that works fine, but it looks ugly, and in different ways:

 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 

Can a broadcast or cut do this?

I feel that maybe I am missing something obvious.

+8
python numpy
source share
4 answers

You just need to find the overlapping range and then add the arrays using slicing.

 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] 

They are added in place, but you can also create a new array. I might have missed some corner cases, but it seems to work fine.

+3
source share

A simple solution, similar to MATLAB's solution:

 import numpy as np block_1 = np.zeros((5,4)) #sample data 1 block_2 = np.ones((4,3)) #sample data 2 block_1[1:5,1:4] = block_1[1:5,1:4] + block_2 print(block_1) 

So pack it as a reusable function:

 import numpy as np #Usage: # addAtPos(xycoor) # - mat1 : matrix to be added # - mat2 : add this matrix to mat1 # - xycoor: tuple (x,y) containing coordinates def addAtPos(mat1, mat2, xycoor): size_x, size_y = np.shape(mat2) coor_x, coor_y = xycoor end_x, end_y = (coor_x + size_x), (coor_y + size_y) mat1[coor_x:end_x, coor_y:end_y] = mat1[coor_x:end_x, coor_y:end_y] + mat2 return mat1 block_1 = np.zeros((5,4)) block_2 = np.ones((3,3)) pos = (1,1) #print result print(addAtPos(block_1, block_2, pos)) 
+12
source share

I'm sure there is a quick NumPy way to do this, but there is a more efficient way to do this even in normal 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 """ 
+1
source share

This is great, and here's how to extend the addition to the 3D matrix by adding a few lines to the jorgeca code:

 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] 

This can help someone who wants to do the same thing in 3d (like me).

+1
source share

All Articles