5

I'd like to add two numpy arrays of different shapes, but without broadcasting, rather the "missing" values are treated as zeros. Probably easiest with an example like

[1, 2, 3] + [2] -> [3, 2, 3]

or

[1, 2, 3] + [[2], [1]] -> [[3, 2, 3], [1, 0, 0]]

I do not know the shapes in advance.

I'm messing around with the output of np.shape for each, trying to find the smallest shape which holds both of them, embedding each in a zero-ed array of that shape and then adding them. But it seems rather a lot of work, is there an easier way?

Thanks in advance!

edit: by "a lot of work" I meant "a lot of work for me" rather than for the machine, I seek elegance rather than efficiency: my effort getting the smallest shape holding them both is

def pad(a, b) :
    sa, sb = map(np.shape, [a, b])
    N = np.max([len(sa),len(sb)])
    sap, sbp = map(lambda x : x + (1,)*(N-len(x)), [sa, sb])
    sp = np.amax( np.array([ tuple(sap), tuple(sbp) ]), 1)

not pretty :-/

3 Answers 3

4

I'm messing around with the output of np.shape for each, trying to find the smallest shape which holds both of them, embedding each in a zero-ed array of that shape and then adding them. But it seems rather a lot of work, is there an easier way?

Getting the np.shape is trivial, finding the smallest shape that holds both is very easy, and of course adding is trivial, so the only "a lot of work" part is the "embedding each in a zero-ed array of that shape".

And yes, you can eliminate that, by just calling the resize method (or the resize function, if you want to make copies instead of changing them in-place). As the docs explain:

Enlarging an array: … missing entries are filled with zeros

For example, if you know the dimensionality statically:

>>> a1 = np.array([[1, 2, 3], [4, 5, 6]])
>>> a2 = np.array([[2], [2]])
>>> shape = [max(a.shape[axis] for a in (a1, a2)) for axis in range(2)]
>>> a1.resize(shape)
>>> a2.resize(shape)
>>> print(a1 + a2)
array([[3, 4, 3],
       [4, 5, 6]])
Sign up to request clarification or add additional context in comments.

1 Comment

Resize doesn't do the right thing here, try a2.resize(4, 4). This will also fail if the inputs have different dims.
1

This is the best I could come up with:

import numpy as np

def magic_add(*args):
    n = max(a.ndim for a in args)
    args = [a.reshape((n - a.ndim)*(1,) + a.shape) for a in args]
    shape = np.max([a.shape for a in args], 0)
    result = np.zeros(shape)

    for a in args:
        idx = tuple(slice(i) for i in a.shape)
        result[idx] += a
    return result

You can clean up the for loop a little if you know how many dimensions you expect on result, something like:

for a in args:
    i, j = a.shape
    result[:i, :j] += a

1 Comment

Thanks Bi, that's pretty slick
0

You may try my solution - for dimension 1 arrays you have to expand your arrays to dimension 2 (as shown in the example below), before passing it to the function.

import numpy as np
import timeit


matrix1 = np.array([[0,10],
                    [1,20],
                    [2,30]])
matrix2 = np.array([[0,10],
                    [1,20],
                    [2,30],
                    [3,40]])
matrix3 = np.arange(0,0,dtype=int) # empty numpy-array
matrix3.shape = (0,2) # reshape to 0 rows
matrix4 = np.array([[0,10,100,1000],
                    [1,20,200,2000]])
matrix5 = np.arange(0,4000,1)
matrix5 = np.reshape(matrix5,(4,1000))
matrix6 = np.arange(0.0,4000,0.5)
matrix6 = np.reshape(matrix6,(20,400))
matrix1 = np.array([1,2,3])
matrix1 = np.expand_dims(matrix1, axis=0)
matrix2 = np.array([2,1])
matrix2 = np.expand_dims(matrix2, axis=0)


def add_2d_matrices(m1, m2, pos=(0,0), filler=None):
    """
    Add two 2d matrices of different sizes or shapes,
    offset by xy coordinates, whereat x is "from left to right" (=axis:1)
    and y is "from top to bottom" (=axis:0)
    Parameterse:
        - m1: first matrix
        - m2: second matrix
        - pos: tuple (x,y) containing coordinates for m2 offset,
        - filler: gaps are filled with the value of filler (or zeros)
    Returns:
        - 2d array (float):
            containing filler-values, m1-values, m2-values
            or the sum of m1,m2 (at overlapping areas)
    Author:
        Reinhard Daemon, Austria
    """
    # determine shape of final array:
    _m1 = np.copy(m1)
    _m2 = np.copy(m2)
    x,y = pos
    y1,x1 = _m1.shape
    y2,x2 = _m2.shape
    xmax = max(x1, x2+x)
    ymax = max(y1, y2+y)

    # fill-up _m1 array with zeros:
    y1,x1 = _m1.shape
    diff = xmax - x1
    _z = np.zeros((y1,diff))
    _m1 = np.hstack((_m1,_z))
    y1,x1 = _m1.shape
    diff = ymax - y1
    _z = np.zeros((diff,x1))
    _m1 = np.vstack((_m1,_z))

    # shift _m2 array by 'pos' and fill-up with zeros:
    y2,x2 = _m2.shape
    _z = np.zeros((y2,x))
    _m2 = np.hstack((_z,_m2))
    y2,x2 = _m2.shape
    diff = xmax - x2
    _z = np.zeros((y2,diff))
    _m2 = np.hstack((_m2,_z))
    y2,x2 = _m2.shape
    _z = np.zeros((y,x2))
    _m2 = np.vstack((_z,_m2))
    y2,x2 = _m2.shape
    diff = ymax - y2
    _z = np.zeros((diff,x2))
    _m2 = np.vstack((_m2,_z))

    # add the 2 arrays:
    _m3 = _m1 + _m2

    # find and fill the "unused" positions within the summed array:
    if filler not in (None,0,0.0):
        y1,x1 = m1.shape
        y2,x2 = m2.shape
        x1min = 0
        x1max = x1-1
        y1min = 0
        y1max = y1-1
        x2min = x
        x2max = x + x2-1
        y2min = y
        y2max = y + y2-1
        for xx in range(xmax):
            for yy in range(ymax):
                if x1min <= xx <= x1max and y1min <= yy <= y1max:
                    continue
                if x2min <= xx <= x2max and y2min <= yy <= y2max:
                    continue
                _m3[yy,xx] = filler

    return(_m3)






t1 = timeit.Timer("add_2d_matrices(matrix5, matrix6, pos=(1,1), filler=111.111)", \
"from __main__ import add_2d_matrices,matrix5,matrix6")
print("ran:",t1.timeit(number=10), "milliseconds")

print("\n\n")
my_res = add_2d_matrices(matrix1, matrix2, pos=(1,1), filler=99.99)
print(my_res)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.