user
user

Reputation: 2103

Multiplying NumPy arrays by scalars

I have a NumPy array of shape (2,76020,2). Basically it is made of two columns containing 76020 rows each, and each row has two entries.

I want to multiply each column by a different weight, say column 1 by 3 and column 2 by 5. For example:

m = 
[3,4][5,8]
[1,2][2,2]

a = [3,5]

I want:

[9,12][25,40]
[3,6][10,10]

I thought I could just multiply m*a, but that gives me instead:

[9,20][15,40]
[3,10][6,10]

How can I write this multiplication?

Upvotes: 1

Views: 2598

Answers (3)

B. M.
B. M.

Reputation: 18628

It's a problem of broadcasting: you must align the dimensions to multiply, here the second:

m = array(
[[[3,4],[5,8]],
[[1,2],[2,2]]])
a = array([3,5])
print(a[None,:,None].shape, m*a[None,:,None])
"""
(1, 2, 1)

[[[ 9 12]
  [25 40]]

 [[ 3  6]
  [10 10]]]
"""  

Upvotes: 3

hpaulj
hpaulj

Reputation: 231365

Your description is ambiguous

Basically it is made of two columns containing 76020 rows each, and each row has two entries.

In (2,76020,2), which 2 is columns, and which is entries?

I believe your m is (that display is also ambiguous)

In [8]: m
Out[8]: 
array([[[3, 4],
        [5, 8]],

       [[1, 2],
        [2, 2]]])


In [9]: m*a
Out[9]: 
array([[[ 9, 20],
        [15, 40]],

       [[ 3, 10],
        [ 6, 10]]])

That's the same as m*a[None,None,:]. When broadcasting, numpy automatically adds dimensions at the beginning as needed. Or iteratively:

In [6]: m[:,:,0]*3
Out[6]: 
array([[ 9, 15],
       [ 3,  6]])

In [7]: m[:,:,1]*5
Out[7]: 
array([[20, 40],
       [10, 10]])

Since m is (2,2,2) shape, we can't off hand tell which axis a is supposed to multiply.

According to the accepted answer, you want to multiply along the middle axis

In [16]: m*a[None,:,None]
Out[16]: 
array([[[ 9, 12],
        [25, 40]],

       [[ 3,  6],
        [10, 10]]])

But what if m was (2,3,2) in shape? a would then have to have 3 values

In [17]: m=np.array([[[3,4],[5,8],[0,0]],[[1,2],[2,2],[1,1]]])

In [18]: m*a[None,:,None]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-f631c33646b7> in <module>()
----> 1 m*a[None,:,None]

ValueError: operands could not be broadcast together with shapes (2,3,2) (1,2,1)

The alternative broadcastings work

In [19]: m*a[:,None,None]
Out[19]: 
array([[[ 9, 12],
        [15, 24],
        [ 0,  0]],

       [[ 5, 10],
        [10, 10],
        [ 5,  5]]])

In [20]: m*a[None,None,:]
Out[20]: 
array([[[ 9, 20],
        [15, 40],
        [ 0,  0]],

       [[ 3, 10],
        [ 6, 10],
        [ 3,  5]]])

Now if m had distinct dimensions, e.g. (3,1000,2), we could tell at a glance with axis a 2 element weight array would work with.

Upvotes: 1

DilithiumMatrix
DilithiumMatrix

Reputation: 18627

As @B.M. says, this is a 'array broadcasting' issue. (The idea behind his answer is correct, but I think his and the OP's dimensions aren't matching up correctly.)

>>> m = np.array([[[3,4],[5,8]],[[1,2],[2,2]]])
>>> print(m)
[[[3 4]
  [5 8]]

 [[1 2]
  [2 2]]]
>>> print(m.shape)
(2, 2, 2)
>>> a = np.array([3,5])
>>> print(a.shape)
(2,)

We need the shapes of m and a to match, so we have to 'broadcast' a to the correct shape:

>>> print(a[:, np.newaxis, np.newaxis].shape)
(2, 1, 1)
>>> b = a[:, np.newaxis, np.newaxis] * m
>>> print(b)
[[[ 9 12]
  [15 24]]

 [[ 5 10]
  [10 10]]]

In this way the first dimension of a is preserved, and maps to each element of the first dimension of m. But there are also two new dimensions ('axes') created to 'broadcast' into the other two dimensions of m.

Note: np.newaxis is (literally) None, they have the same effect. The former is more readable to understand what's happening. Additionally, just in terms of standard terminology, the first dimension (axis) is generally referred to as the 'rows', and the second axis the 'columns'.

Upvotes: 2

Related Questions