Elegant method for element-wise multiplication along shared dimensions of two different-sized arrays?

Welcome to Programming Tutorial official website. Today - we are going to cover how to solve / find the solution of this error Elegant method for element-wise multiplication along shared dimensions of two different-sized arrays? on this date .

Assume that I have two arrays, say array A of shape (X,Y) and array B of shape (X,Y,Z). It is my goal to obtain an array C composed of an element-wise multiplication of A and B along Z. I could do this with a for loop, e.g.:

import numpy as np
C = np.zeros(B.shape)
for z in range(Z):
    C[:,:,z] = A*B[:,:,z]

Ideally, however, I would like to avoid the use of a for loop. Do you know of a more elegant way to achieve this?

Answer

You can element-wise multiply with arrays of shape (x, y) and (z, x, y).

For example:

a = np.arange(12).reshape((4, 3))
b = np.ones((4, 3, 2))

b[:, :, 0] = 10

x = a * b # :( error

However, if we do

c = np.ones((2, 4, 3))
c[0, :, :] = 10
y = a * c

This works and we get

array([[[  0.,  10.,  20.],
        [ 30.,  40.,  50.],
        [ 60.,  70.,  80.],
        [ 90., 100., 110.]],

       [[  0.,   1.,   2.],
        [  3.,   4.,   5.],
        [  6.,   7.,   8.],
        [  9.,  10.,  11.]]])

Now, to move an axis of an array, you can use the np.moveaxis() function.

b_moved = np.moveaxis(b, -1, 0) # move last axis of b to first position

x = a * b_moved # In the shape of b_moved

y = np.moveaxis(x, 0, -1) # Move first axis of x to the last position

And this gives

x: shape 2x4x3
array([[[  0.,  10.,  20.],
        [ 30.,  40.,  50.],
        [ 60.,  70.,  80.],
        [ 90., 100., 110.]],

       [[  0.,   1.,   2.],
        [  3.,   4.,   5.],
        [  6.,   7.,   8.],
        [  9.,  10.,  11.]]])

y: shape 4x3x2
array([[[  0.,   0.],
        [ 10.,   1.],
        [ 20.,   2.]],

       [[ 30.,   3.],
        [ 40.,   4.],
        [ 50.,   5.]],

       [[ 60.,   6.],
        [ 70.,   7.],
        [ 80.,   8.]],

       [[ 90.,   9.],
        [100.,  10.],
        [110.,  11.]]])

In summary, you can write a function that does this for you:

def emult3(arr2d, arr3d):
    # make sure shapes are compatible
    assert (arr2d.ndim, arr3d.ndim) == (2, 3)
    assert arr2d.shape == arr3d.shape[0:2] 

    # do the calculations
    x = arr2d * np.moveaxis(arr3d, -1, 0)
    return np.moveaxis(x, 0, -1)