2

According to What is the equivalent of MATLAB's repmat in NumPy, I tried to build 3x3x5 array from 3x3 array using python.

In Matlab, this work as I expected.

a = [1,1,1;1,2,1;1,1,1];
a_= repmat(a,[1,1,5]);

size(a_) = 3 3 5

But for numpy.tile

b = numpy.array([[1,1,1],[1,2,1],[1,1,1]])
b_ = numpy.tile(b, [1,1,5])

b_.shape = (1, 3, 15)

If I want to generate the same array as in Matlab, what is the equivalent?

Edit 1

The output I would expect to get is

b_(:,:,1) =

1 1 1  
1 2 1  
1 1 1  

b_(:,:,2) =

1 1 1  
1 2 1  
1 1 1  

b_(:,:,3) =

1 1 1  
1 2 1  
1 1 1  

b_(:,:,4) =  

1 1 1  
1 2 1  
1 1 1  
b_(:,:,5) =

1 1 1  
1 2 1  
1 1 1  

but what @farenorth and the numpy.dstack give is

[[[1 1 1 1 1]  
[1 1 1 1 1]  
[1 1 1 1 1]]  

[[1 1 1 1 1]  
[2 2 2 2 2]  
[1 1 1 1 1]]  

[[1 1 1 1 1]  
[1 1 1 1 1]  
[1 1 1 1 1]]]  
Community
  • 1
  • 1
TinyEpic
  • 541
  • 1
  • 11
  • 18
  • `numpy.dstack((b,b,b,b,b))` gives weird output with the correct shape. – TinyEpic Aug 26 '15 at 23:40
  • 1
    `@farenorth` gave you the right thing. Did you look at `b_[:,:,i]` for i in 0:4? Don't confuse display styles with actual values. – hpaulj Aug 27 '15 at 01:14
  • 1
    MATLAB changes the fist dimension fastest, the last slowest. So it displays 'planes' as (:,:,1) etc. `numpy` iterates the last dimension fastest, the 1st last. So it displays 'planes' as 2d blocks, in this case 3x5. – hpaulj Aug 27 '15 at 01:18

2 Answers2

12

NumPy functions are not, in general, 'drop-in' replacements for matlab functions. Often times there are subtle difference to how the 'equivalent' functions are used. It does take time to adapt, but I've found the transition to be very worthwhile.

In this case, the np.tile documentation indicates what happens when you are trying to tile an array to higher dimensions than it is defined,

numpy.tile(A, reps)

Construct an array by repeating A the number of times given by reps.

If reps has length d, the result will have dimension of max(d, A.ndim).

If A.ndim < d, A is promoted to be d-dimensional by prepending new axes. So a shape (3,) array is promoted to (1, 3) for 2-D replication, or shape (1, 1, 3) for 3-D replication. If this is not the desired behavior, promote A to d-dimensions manually before calling this function.

In this case, your array is being cast to a shape of [1, 3, 3], then being tiled. So, to get your desired behavior just be sure to append a new singleton-dimension to the array where you want it,

>>> b_ = numpy.tile(b[..., None], [1, 1, 5])
>>> print(b_.shape)
(3, 3, 5)

Note here that I've used None (i.e. np.newaxis) and ellipses notation to specify a new dimension at the end of the array. You can find out more about these capabilities here.

Another option, which is inspired by the OP's comment would be:

b_ = np.dstack((b, ) * 5)

In this case, I've used tuple multiplication to 'repmat' the array, which is then constructed by np.dstack.

As @hpaulj indicated, Matlab and NumPy display matrices differently. To replicate the Matlab output you can do something like:

>>> for idx in xrange(b_.shape[2]):
...    print 'b_[:, :, {}] = \n{}\n'.format(idx, str(b_[:, :, idx]))
...
b_[:, :, 0] = 
[[1 1 1]
 [1 2 1]
 [1 1 1]]

b_[:, :, 1] = 
[[1 1 1]
 [1 2 1]
 [1 1 1]]

b_[:, :, 2] = 
[[1 1 1]
 [1 2 1]
 [1 1 1]]

b_[:, :, 3] = 
[[1 1 1]
 [1 2 1]
 [1 1 1]]

b_[:, :, 4] = 
[[1 1 1]
 [1 2 1]
 [1 1 1]]

Good luck!

farenorth
  • 10,165
  • 2
  • 39
  • 45
  • Hi @farenorth, This generate different output, similar to the numpy.dstack(), from the Matlab version. – TinyEpic Aug 26 '15 at 23:37
  • Thank you very much. You answer is very clear and complete. Sorry for my lack of understanding Python-numpy array. – TinyEpic Aug 27 '15 at 14:30
  • No problem! I remember being boggled by *both* of the issues that you've raised here when I was switching from Matlab. – farenorth Aug 27 '15 at 14:33
3

Let's try the comparison, taking care to diversify the shapes and values.

octave:7> a=reshape(0:11,3,4)
a =
    0    3    6    9
    1    4    7   10
    2    5    8   11

octave:8> repmat(a,[1,1,2])
ans =
ans(:,:,1) =
    0    3    6    9
    1    4    7   10
    2    5    8   11
ans(:,:,2) =    
    0    3    6    9
    1    4    7   10
    2    5    8   11

numpy equivalent - more or less:

In [61]: a=np.arange(12).reshape(3,4)    
In [62]: np.tile(a,[2,1,1])
Out[62]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]]])

numpy again, but with order F to better match the MATLAB Fortran-derived layout

In [63]: a=np.arange(12).reshape(3,4,order='F')    
In [64]: np.tile(a,[2,1,1])
Out[64]: 
array([[[ 0,  3,  6,  9],
        [ 1,  4,  7, 10],
        [ 2,  5,  8, 11]],

       [[ 0,  3,  6,  9],
        [ 1,  4,  7, 10],
        [ 2,  5,  8, 11]]])

I'm adding the new numpy dimension at the start, because in many ways it better replicates the MATLAB practice of adding it at the end.

Try adding the new dimension at the end. The shape is (3,4,5), but you might not like the display.

 np.tile(a[:,:,None],[1,1,2])

Another consideration - what happens when you flatten the tile?

octave:10> repmat(a,[1,1,2])(:).'
ans =    
    0    1    2    3    4    5    6    7    8    9   10   11 
0    1    2    3     4    5    6    7    8    9   10   11

with the order F a

In [78]: np.tile(a[:,:,None],[1,1,2]).flatten()
Out[78]: 
array([ 0,  0,  3,  3,  6,  6,  9,  9,  1,  1,  4,  4,  7,  7, 10, 10,  2,
        2,  5,  5,  8,  8, 11, 11])

In [79]: np.tile(a,[2,1,1]).flatten()
Out[79]: 
array([ 0,  3,  6,  9,  1,  4,  7, 10,  2,  5,  8, 11,  0,  3,  6,  9,  1,
        4,  7, 10,  2,  5,  8, 11])

with a C order array:

In [80]: a=np.arange(12).reshape(3,4)

In [81]: np.tile(a,[2,1,1]).flatten()
Out[81]: 
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,
        5,  6,  7,  8,  9, 10, 11])

This last one matches the Octave layout.

So does:

In [83]: a=np.arange(12).reshape(3,4,order='F')

In [84]: np.tile(a[:,:,None],[1,1,2]).flatten(order='F')
Out[84]: 
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,
        5,  6,  7,  8,  9, 10, 11])

Confused yet?

hpaulj
  • 221,503
  • 14
  • 230
  • 353