I don't know if the title of the question makes sense, but I couldn't think of a better title.
Please consider the following scenario: I have two numpy arrays a
and b
, b
has shape (2, 2, 5)
and a
has shape (5,)
. I would like to multiply each element of a
by each 2x2 ARRAY in b
, I DON'T want to multiply each element in a
by each ELEMENT in each 2x2 array in b
, which is what happens if I simply do a * b
The following code demonstrates my problem and my desired result:
import numpy as np
np.random.seed(1234)
class MyClass:
def __mul__(self, other):
print(f'{type(self).__name__} * {other}')
return other
def __rmul__(self, other):
print(f'{other} * {type(self).__name__}')
return other
a = np.full((5,), MyClass())
b = np.random.uniform(-1, 1, (2, 2, 5))
a * b
# MyClass * -0.6169610992422154
# MyClass * 0.24421754207966373
# ...
# MyClass * 0.5456532432247481
# MyClass * 0.7652823812722331
# Desired result:
[ai * bi for ai, bi in zip(a, np.moveaxis(b, -1, 0))]
# MyClass * [[-0.6169611 -0.45481479]
# [-0.28436546 0.12239237]]
# ...
# MyClass * [[ 0.55995162 0.75186527]
# [-0.25949849 0.76528238]]
# EDIT: Solution suggested by "Guimoute", does the same as a * b but also introduces addition (not a solution).
b @ a
# -0.6169610992422154 * MyClass
# 0.24421754207966373 * MyClass
# ...
# 0.5456532432247481 * MyClass
# 0.7652823812722331 * MyClass
c = np.split(np.moveaxis(b, -1, 0).reshape(-1, 2), 5)
# c is now a list of numpy arrays, with length = 5
a * c
# Raises an exception
# ValueError: operands could not be broadcast together with shapes (5,) (5,2,2)
EDIT: I should probably clarify that what I'm asking for is a numpy solution, rather than one involving a python loop ie: [ai * bi for ai, bi in zip(a, np.moveaxis(b, -1, 0))]
.
EDIT: I added the suggestion by "Guimoute", involving matmul @ operator, which as can be seen is clearly not a solution.
EDIT: To address complaints that it was hard to verify potential solutions without having to check the order of some printed outputs I have added the following example which includes a function to check whether what a function does is a solution:
import numpy as np
np.random.seed(1234)
class MyClass:
def __mul__(self, other):
return self
def __rmul__(self, other):
return self
def solution_involving_python_loop(a, b):
return np.array([ai * bi for ai, bi in zip(a, np.moveaxis(b, -1, 0))])
def is_valid_solution(func):
return func(a, b).ndim == 1
a = np.full((5,), MyClass())
b = np.random.uniform(-1, 1, (2, 2, 5))
print(is_valid_solution(solution_involving_python_loop))
# True
CodePudding user response:
import numpy as np
np.random.seed(1234)
class MyClass:
def __mul__(self, other):
print(f'{type(self).__name__} * {other}')
return other
def __rmul__(self, other):
print(f'{other} * {type(self).__name__}')
return other
a = np.full((5,), MyClass())
b = np.random.uniform(-1, 1, (2, 2, 5))
your_sol = np.array([ai * bi for ai, bi in zip(a, np.moveaxis(b, -1, 0))])
your_required_array = np.moveaxis(a*b, -1, 0).astype(float)
CodePudding user response:
Solution: Split b
into an array (with dtype=object) containing all the 2x2 arrays
c = np.empty((5,), dtype=object)
c[:] = np.split(np.moveaxis(b, -1, 0).reshape(-1, 2), 5)
# c is now an object array of numpy arrays, with length = 5
a * c
# MyClass * [[-0.6169611 -0.45481479]
# [-0.28436546 0.12239237]]
# ...
# MyClass * [[ 0.55995162 0.75186527]
# [-0.25949849 0.76528238]]
Not a solution that I like, but nobody seems to have a better one.
CodePudding user response:
You want to use the matrix multiplication operator @
:
import numpy as np
np.random.seed(1234)
a = np.random.randn(5)
b = np.random.uniform(-1, 1, (2, 2, 5))
print(b@a)
# [[0.35278726 0.16635596]
# [1.89830443 0.25534444]]