Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elementwise multiply 1D-numpy arrays (shapes (k,1) or (k,)) and have result have the shape of the first

I would like to implement a diagonal matrix apply function that is created by providing the diagonal d first, and then doing a bunch of matrix-vector multiplications with x. Of course I wouldn't want to create an actual matrix because all that's needed is a elementwise vector multiplication.

Now, some users are going to provide a diagonal d of shape (k,), some of shape (k,1). Also, x can have shapes (k,) and (k,1). I would like the apply() method to behave just like the * for numpy matrices in that the result has the same shape as the input x.

Hence the question: In Python/Numpy, is there a non-iffy way to elementwise-multiply two np.arrays x and y of shapes (k,) or (k,1) (in any combination) such that the resulting array has the shape of x?

I experimented a little with [:,None],

x = np.empty((4,1))
y = np.empty(4)
(x * y).shape         # (4,4)  -- nope
(y * y).shape         # (4,)   -- yes
(x * y[:,None]).shape # (4, 1) -- yes
(y * y[:,None]).shape # (4,4)  -- nope

and I could certainly wrap my code in if len(x.shape)==...:, but that doesn't feel very pythonic.

Suggestions?

like image 377
Nico Schlömer Avatar asked Apr 22 '12 12:04

Nico Schlömer


People also ask

What happens when you multiply NumPy arrays?

multiply() function is used when we want to compute the multiplication of two array. It returns the product of arr1 and arr2, element-wise.

What is 1d array in NumPy?

One dimensional array contains elements only in one dimension. In other words, the shape of the NumPy array should contain only one value in the tuple.


1 Answers

Now that I understand your question, my suggestion would be simply to reshape. Calling reshape returns a view, so it doesn't incur any big copying costs or anything like that. Simply reshape the arrays, multiply, and reshape again:

>>> def shape_preserving_mult(x, y):
...     return (x.reshape((-1,)) * y.reshape((-1))).reshape(x.shape)
... 

Or more concisely, as you and rroowwllaanndd pointed out:

>>> def shape_preserving_mult(x, y):
...     return x * y.reshape(x.shape)
... 
>>> shape_preserving_mult(x, y)
array([[ 0],
       [ 1],
       [ 4],
       [ 9],
       [16]])
>>> shape_preserving_mult(x, y.T)
array([[ 0],
       [ 1],
       [ 4],
       [ 9],
       [16]])
>>> shape_preserving_mult(x.T, y)
array([[ 0,  1,  4,  9, 16]])
>>> shape_preserving_mult(x.T, y.T)
array([[ 0,  1,  4,  9, 16]])

The substance of my previous suggestion remains below.

It's worth noting that if you multiply a numpy array of shape (1, 4) with an array of shape (4,) you get something close to what you want.

>>> x = numpy.arange(5).reshape((5, 1))
>>> y = numpy.arange(5)
>>> x.shape
(5, 1)
>>> x.T.shape
(1, 5)
>>> y * x.T
array([[ 0,  1,  4,  9, 16]])

This doesn't have the shape of a, but it does have the shape of a.T. You could always call T on the result again. This will work on arrays of shape (5,) too, because the transpose operation on a 1-d array causes no change. So perhaps you could do this:

>>> def transposed_mult(x, y):
...     return (x.T * y).T
... 
>>> transposed_mult(x, y)
array([[ 0],
       [ 1],
       [ 4],
       [ 9],
       [16]])

But of course this causes the opposite problem if you pass an array of shape (1, 5):

>>> transposed_mult(x.T, y)
array([[ 0,  0,  0,  0,  0],
       [ 0,  1,  2,  3,  4],
       [ 0,  2,  4,  6,  8],
       [ 0,  3,  6,  9, 12],
       [ 0,  4,  8, 12, 16]])

So transposed_mult does the exact thing you asked for in your original post, but if you need any further flexibility, it won't work as expected. And indeed, it seems you need additional flexibility.

like image 77
senderle Avatar answered Nov 15 '22 05:11

senderle