John Rowlay
John Rowlay

Reputation: 83

Tiling in Numpy

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

Answers (3)

user1858670
user1858670

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

hpaulj
hpaulj

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]]])

multiple repeats

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]]])

tile

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.

broadcasting

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

4.Pi.n
4.Pi.n

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))

Update

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

Related Questions