简而言之:我有两个矩阵(或数组):
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
广播或切片可能会这样做吗?
我觉得我可能错过了一些明显的东西。
答案 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.]]