3

Consider, for reference:

>>> x, y = np.ones((2, 2, 2)), np.zeros((2, 2, 2))
>>> np.concatenate((x, y, x, y), axis=2)
array([[[ 1.,  1.,  0.,  0.,  1.,  1.,  0.,  0.],
        [ 1.,  1.,  0.,  0.,  1.,  1.,  0.,  0.]],

       [[ 1.,  1.,  0.,  0.,  1.,  1.,  0.,  0.],
        [ 1.,  1.,  0.,  0.,  1.,  1.,  0.,  0.]]])

We have stacked the arrays along the innermost dimension, merging it - the resulting shape is (2, 2, 8). But suppose I wanted those innermost elements to lie side-by-side instead (this would only work because every dimension of the source arrays is the same, including the one I want to 'stack' in), producing a result with shape (2, 2, 4, 2) as follows?

array([[[[ 1.,  1.],
         [ 0.,  0.],
         [ 1.,  1.],
         [ 0.,  0.]],

        [[ 1.,  1.],
         [ 0.,  0.],
         [ 1.,  1.],
         [ 0.,  0.]]],


       [[[ 1.,  1.],
         [ 0.,  0.],
         [ 1.,  1.],
         [ 0.,  0.]],

        [[ 1.,  1.],
         [ 0.,  0.],
         [ 1.,  1.],
         [ 0.,  0.]]]])

The best approach I have is to reshape each source array first, to add a 1-length dimension right before the last:

def pad(npa):
    return npa.reshape(npa.shape[:-1] + (1, npa.shape[-1]))

np.concatenate((pad(x), pad(y), pad(x), pad(y)), axis=2) # does what I want
# np.hstack might be better? I always want the second-last dimension, now

But I feel like I am reinventing a wheel. Have I overlooked something that will do this more directly?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153

1 Answers1

4

You can do it as follows:

>>> xx = x[..., None, :]
>>> yy = y[..., None, :]
>>> np.concatenate((xx, yy, xx, yy), axis=2).shape
(2, 2, 4, 2)
>>> np.concatenate((xx, yy, xx, yy), axis=2)
array([[[[ 1.,  1.],
         [ 0.,  0.],
         [ 1.,  1.],
         [ 0.,  0.]],

        [[ 1.,  1.],
         [ 0.,  0.],
         [ 1.,  1.],
         [ 0.,  0.]]],


       [[[ 1.,  1.],
         [ 0.,  0.],
         [ 1.,  1.],
         [ 0.,  0.]],

        [[ 1.,  1.],
         [ 0.,  0.],
         [ 1.,  1.],
         [ 0.,  0.]]]])
>>> 

What this example does is change the shape (no data is copied) of the arrays. Slicing with None or equivalently np.newaxis adds an axis:

>>> xx.shape
(2, 2, 1, 2)
>>> xx
array([[[[ 1.,  1.]],

        [[ 1.,  1.]]],


       [[[ 1.,  1.]],

        [[ 1.,  1.]]]])
>>> 
YXD
  • 31,741
  • 15
  • 75
  • 115
  • 1
    There is no broadcasting here, you're just adding a dimension using newaxis. – Bi Rico Apr 17 '14 at 22:25
  • 1
    I'd recommend removing any reference to adding axes using `None`. It is bad practice. `np.newaxis` is the way to go. The maxime is *be explicit*: naming is everything, or at least a lot. Take a look at [this magic line](https://github.com/numpy/numpy/blob/master/numpy/core/numeric.py#L361) – eickenberg Apr 18 '14 at 09:51
  • @eickenberg it's in the docs too: http://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#numpy.newaxis Bi Rico you are right I was on autopilot, editing – YXD Apr 18 '14 at 09:53
  • @BiRico it's clearly a simpler way to add the dimension than what I clumsily put together, though. :) – Karl Knechtel Apr 19 '14 at 08:40