Rob Falck
Rob Falck

Reputation: 2704

numpy: multiply arbitrary shape array along first axis

I want to multiply an array along it's first axis by some vector.

For instance, if a is 2D, b is 1D, and a.shape[0] == b.shape[0], we can do:

a *= b[:, np.newaxis]

What if a has an arbitrary shape? In numpy, the ellipsis "..." can be interpreted as "fill the remaining indices with ':'". Is there an equivalent for filling the remaining axes with None/np.newaxis?

The code below generates the desired result, but I would prefer a general vectorized way to accomplish this without falling back to a for loop.

from __future__ import print_function

import numpy as np


def foo(a, b):
    """
    Multiply a along its first axis by b
    """
    if len(a.shape) == 1:
        a *= b
    elif len(a.shape) == 2:
        a *= b[:, np.newaxis]
    elif len(a.shape) == 3:
        a *= b[:, np.newaxis, np.newaxis]
    else:
        n = a.shape[0]
        for i in range(n):
           a[i, ...] *= b[i]          

n = 10
b = np.arange(n)

a = np.ones((n, 3))
foo(a, b)
print(a)

a = np.ones((n, 3, 3))
foo(a, b)
print(a)

Upvotes: 3

Views: 1663

Answers (2)

Carlos Horn
Carlos Horn

Reputation: 1293

Following the idea of the accepted answer, you could skip the variable assignment to the transpose as follows:

arr = np.tile(np.arange(10, dtype=float), 3).reshape(3, 10)
print(arr)
factors = np.array([0.1, 1, 10])
arr.T[:, :] *= factors
print(arr)

Which would print

[[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]]
[[ 0.   0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9]
 [ 0.   1.   2.   3.   4.   5.   6.   7.   8.   9. ]
 [ 0.  10.  20.  30.  40.  50.  60.  70.  80.  90. ]]

Upvotes: 2

user2357112
user2357112

Reputation: 281476

Just reverse the order of the axes:

transpose = a.T
transpose *= b

a.T is a transposed view of a, where "transposed" means reversing the order of the dimensions for arbitrary-dimensional a. We assign a.T to a separate variable so the *= doesn't try to set the a.T attribute; the results still apply to a, since the transpose is a view.

Demo:

In [55]: a = numpy.ones((2, 2, 3))

In [56]: a
Out[56]: 
array([[[1., 1., 1.],
        [1., 1., 1.]],

       [[1., 1., 1.],
        [1., 1., 1.]]])

In [57]: transpose = a.T

In [58]: transpose *= [2, 3]

In [59]: a
Out[59]: 
array([[[2., 2., 2.],
        [2., 2., 2.]],

       [[3., 3., 3.],
        [3., 3., 3.]]])

Upvotes: 5

Related Questions