Reputation: 5
I am trying to construct a matrix with numpy using an array of random numbers called noise with 4 values which should be multiplied with 8 different numbers from an array called "factor" resulting in 8 rows in the final matrix.
So the matrix should have 4 columns for each value in "noise" and 8 rows for each factor. I don't know how to achieve that.
This is the code I got so for:
import numpy as np
from numpy import random
n = 4
noise = np.random.normal(size=n)
matrix = np.zeros((8,n)) # Pre-allocate matrix
for i in range(1,8):
matrix[:,i] = [[noise]*i]
print(matrix)
I get the error message:
ValueError: setting an array element with a sequence.
Upvotes: 0
Views: 525
Reputation: 53029
What you are trying to construct is the outer product
n = 4
noise = np.random.normal(size=n)
noise
# array([ 2.39723122, -0.99797246, -0.14242618, -0.55921136])
m = 8
factor = np.random.randint(0,10,m)
factor
# array([0, 5, 7, 6, 7, 3, 8, 6])
np.outer(factor,noise)
# array([[ 0. , -0. , -0. , -0. ],
# [11.98615612, -4.98986228, -0.71213089, -2.79605682],
# [16.78061856, -6.98580719, -0.99698324, -3.91447955],
# [14.38338734, -5.98783473, -0.85455707, -3.35526818],
# [16.78061856, -6.98580719, -0.99698324, -3.91447955],
# [ 7.19169367, -2.99391737, -0.42727853, -1.67763409],
# [19.17784979, -7.98377964, -1.13940942, -4.47369091],
# [14.38338734, -5.98783473, -0.85455707, -3.35526818]])
The "outer" operation exists for many binary ufuncs. Because outer multiplication
np.multiply.outer(factor,noise)
is so common it has its own function outer
.
The outer operation (for multiplication or other ufuncs) is (for 1D operands) roughly equivalent to
np.multiply(*np.ix_(factor,noise))
If a reducing form (prod for multiply, sum for add, min for minimum, etc.) exists we can also write
np.prod(np.ix_(factor,noise))
Finally, (somewhat unrelated) in case of the product we can also use einsum
:
np.einsum('i,j',factor,noise)
Though einsum
is maybe overkill for this problem.
Upvotes: 2
Reputation: 231335
In [12]: n = 4
...: noise = np.random.normal(size=n)
In [13]: noise
Out[13]: array([-1.93374989, -1.16536624, 0.42338714, -2.39942219])
Let's look at what you are trying to assign - for specific i
:
In [14]: np.array([[noise]*1])
Out[14]: array([[[-1.93374989, -1.16536624, 0.42338714, -2.39942219]]])
In [15]: np.array([[noise]*2])
Out[15]:
array([[[-1.93374989, -1.16536624, 0.42338714, -2.39942219],
[-1.93374989, -1.16536624, 0.42338714, -2.39942219]]])
In [16]: np.array([[noise]*3])
Out[16]:
array([[[-1.93374989, -1.16536624, 0.42338714, -2.39942219],
[-1.93374989, -1.16536624, 0.42338714, -2.39942219],
[-1.93374989, -1.16536624, 0.42338714, -2.39942219]]])
You are applying the *3
to a list - which means 'replicate':
In [17]: [[noise]*3]
Out[17]:
[[array([-1.93374989, -1.16536624, 0.42338714, -2.39942219]),
array([-1.93374989, -1.16536624, 0.42338714, -2.39942219]),
array([-1.93374989, -1.16536624, 0.42338714, -2.39942219])]]
The shapes are (1,1,4), (1,2,4), (1,3,4) etc. Brackets in Python create a list; don't use them casually. But
In [21]: np.zeros((8,4))[:,1].shape
Out[21]: (8,)
you are trying to put each in a size (8,) slot.
Did you instead want to multiply the values of noise
by i
?
matrix = np.zeros((8,n)) # Pre-allocate matrix
for i in range(8):
matrix[i,:] = noise*i
should work, properly matching the matrix[i,:]
slot with the source, noise
.
Upvotes: 0
Reputation: 4263
You can just do this instead of using a loop:
matrix = np.random.normal(size=(1, n)) * np.arange(8).reshape(-1, 1)
Here, the left hand-side of the multiplication has the shape (1, n)
while the right-hand side has the shape (8, 1)
. So both of them will be broadcasted to the shape (8, n)
and then an element-wise multiplication is performed.
Upvotes: 1