Reputation: 83
I have a NumPy array with data [3, 5]
, and I want to create a function which takes this array, and returns the following (2 x 3 x 2) NumPy array :
[[[3, 3],
[3, 3],
[3, 3]],
[[5, 5],
[5, 5],
[5, 5]]]
However, I have not been able to achieve this, using Numpy's repeat()
or tile()
functions.
For example:
x = np.array([3, 5])
y = np.repeat(x, [2, 3, 2])
Gives the following error:
ValueError: a.shape[axis] != len(repeats)
And:
x = np.array([3, 5])
y = np.tile(x, [2, 3, 2])
Creates a (2 x 3 x 4) array:
[[[3, 5, 3, 5],
[3, 5, 3, 5],
[3, 5, 3, 5]],
[[3, 5, 3, 5],
[3, 5, 3, 5],
[3, 5, 3, 5]]]
What should my function be?
Upvotes: 2
Views: 337
Reputation:
from functools import reduce
import numpy as np
import operator as op
DIM = (2, 3, 2) # size of each dimension
CAP = reduce(op.mul, DIM[1:]) # product of DIM
x = np.array([3, 5])
y = np.repeat(x, CAP).reshape(*DIM)
This will generate a 2x3 array of each element of x
, repeated.
DIM[0] should be len(x)
; otherwise, an exception will be raised. This is due to the parameter of np.reshape
being incompatible with the shape produced by np.repeat
(numpy doc).
This is also the reason why the ValueError
was raised in your case.
Upvotes: 1
Reputation: 231345
The signature for repeat
: np.repeat(a, repeats, axis=None)
It can be used as:
In [345]: np.repeat(x, [2,3])
Out[345]: array([3, 3, 5, 5, 5])
In other words, if the repeats is a list, it should match a
in size, saying how many times each element is repeated.
While we could expand the dimensions of x
, and try repeats (or tile), a simpler approach is to just expand x
, and reshape:
In [349]: np.repeat(x,6)
Out[349]: array([3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5])
In [350]: np.repeat(x,6).reshape(2,3,2)
Out[350]:
array([[[3, 3],
[3, 3],
[3, 3]],
[[5, 5],
[5, 5],
[5, 5]]])
Another approach is to expand x
to 3d, and apply 2 repeats. I had to try several things before I got it right:
In [357]: x[:,None,None]
Out[357]:
array([[[3]],
[[5]]])
In [358]: x[:,None,None].repeat(2,2)
Out[358]:
array([[[3, 3]],
[[5, 5]]])
In [359]: x[:,None,None].repeat(2,2).repeat(3,1)
Out[359]:
array([[[3, 3],
[3, 3],
[3, 3]],
[[5, 5],
[5, 5],
[5, 5]]])
np.tile
does something similar (multiple repeats):
In [361]: np.tile(x[:,None,None],(1,3,2))
Out[361]:
array([[[3, 3],
[3, 3],
[3, 3]],
[[5, 5],
[5, 5],
[5, 5]]])
To use repeat
or tile
I usually need to review the docs and/or try several things. Especially when expanding the dimensions.
The problem is even simpler with broadcasting
:
In [370]: res = np.zeros((2,3,2), int)
In [371]: res[:] = x[:,None,None]
In [372]: res
Out[372]:
array([[[3, 3],
[3, 3],
[3, 3]],
[[5, 5],
[5, 5],
[5, 5]]])
though getting the expansion of x
dimensions right took a few tries.
Upvotes: 1
Reputation: 1151
You could use np. tile
, you just miss dividing by the number of elements at repetition axis, in your case it's 1D
x = np.array([3, 5])
y = np.tile(x, [2, 3, 2 // x.shape[0]])
def get_nd(a, shape):
shape = np.array(shape)
a_shape = np.ones_like(shape)
a_shape[-a.ndim:] = a.shape
shape = (shape * 1/a_shape).astype('int')
return np.tile(a, shape)
get_nd(x, (2, 3, 2))
Transpose desired shape, if you are targeting (2, 3, 6)
, then ask for (6, 3, 2)
then transpose the resultant matrix
get_nd(x, (2, 3, 6)).T
Or use the following function instead
def get_nd_rep(a, shape):
shape = np.array(shape)
x_shape = np.ones_like(shape)
x_shape[-a.ndim:] = a.shape
shape = (shape * 1/x_shape).astype('int')
return np.tile(a, shape).T
get_nd_rep(x, (2, 3, 2))
Upvotes: 1