48

I have some trouble understanding what numpy's dstack function is actually doing. The documentation is rather sparse and just says:

Stack arrays in sequence depth wise (along third axis).

Takes a sequence of arrays and stack them along the third axis to make a single array. Rebuilds arrays divided by dsplit. This is a simple way to stack 2D arrays (images) into a single 3D array for processing.

So either I am really stupid and the meaning of this is obvious or I seem to have some misconception about the terms 'stacking', 'in sequence', 'depth wise' or 'along an axis'. However, I was of the impression that I understood these terms in the context of vstack and hstack just fine.

Let's take this example:

In [193]: a
Out[193]: 
array([[0, 3],
       [1, 4],
       [2, 5]])
In [194]: b
Out[194]: 
array([[ 6,  9],
       [ 7, 10],
       [ 8, 11]])
In [195]: dstack([a,b])
Out[195]: 
array([[[ 0,  6],
        [ 3,  9]],

       [[ 1,  7],
        [ 4, 10]],

       [[ 2,  8],
        [ 5, 11]]])

First of all, a and b don't have a third axis so how would I stack them along 'the third axis' to begin with? Second of all, assuming a and b are representations of 2D-images, why do I end up with three 2D arrays in the result as opposed to two 2D-arrays 'in sequence'?

3
  • 9
    Just like vstack with 1D arrays stacks them along a new axis perpendicular to that 1D axis, dstack with 2D arrays stacks them along a new axis perpendicular to both 2D axes. Like stacking a bunch of sheets of paper on top of each other.
    – abarnert
    Commented Aug 4, 2014 at 10:30
  • @abarnert But why do I end up with three sheets of paper when trying to stack two on top of each other?
    – timgeb
    Commented Aug 4, 2014 at 10:31
  • 2
    You see 3 2D arrays in the result because you're looking along the first axis, rather than the third. Commented Aug 4, 2014 at 10:55

4 Answers 4

91

It's easier to understand what np.vstack, np.hstack and np.dstack* do by looking at the .shape attribute of the output array.

Using your two example arrays:

print(a.shape, b.shape)
# (3, 2) (3, 2)
  • np.vstack concatenates along the first dimension...

    print(np.vstack((a, b)).shape)
    # (6, 2)
    
  • np.hstack concatenates along the second dimension...

    print(np.hstack((a, b)).shape)
    # (3, 4)
    
  • and np.dstack concatenates along the third dimension.

    print(np.dstack((a, b)).shape)
    # (3, 2, 2)
    

Since a and b are both two dimensional, np.dstack expands them by inserting a third dimension of size 1. This is equivalent to indexing them in the third dimension with np.newaxis (or alternatively, None) like this:

print(a[:, :, np.newaxis].shape)
# (3, 2, 1)

If c = np.dstack((a, b)), then c[:, :, 0] == a and c[:, :, 1] == b.

You could do the same operation more explicitly using np.concatenate like this:

print(np.concatenate((a[..., None], b[..., None]), axis=2).shape)
# (3, 2, 2)

* Importing the entire contents of a module into your global namespace using import * is considered bad practice for several reasons. The idiomatic way is to import numpy as np.

1
  • 1
    More clearly, the third array index now gives me the level of the plane my original 2D index is located. I still index the same way as the 2D array, but now add the level 0 or 1 in your example. So: dstack([a,b]) ; index[0][0][0] = 0; index[0][0][1] = 6. or in plane english "get index 0,0 from level 1"
    – user249806
    Commented Jun 29, 2018 at 20:51
6

Let x == dstack([a, b]). Then x[:, :, 0] is identical to a, and x[:, :, 1] is identical to b. In general, when dstacking 2D arrays, dstack produces an output such that output[:, :, n] is identical to the nth input array.

If we stack 3D arrays rather than 2D:

x = numpy.zeros([2, 2, 3])
y = numpy.ones([2, 2, 4])
z = numpy.dstack([x, y])

then z[:, :, :3] would be identical to x, and z[:, :, 3:7] would be identical to y.

As you can see, we have to take slices along the third axis to recover the inputs to dstack. That's why dstack behaves the way it does.

5

I'd like to take a stab at visually explaining this (even though the accepted answer makes enough sense, it took me a few seconds to rationalise this to my mind). If we imagine the 2d-arrays as a list of lists, where the 1st axis gives one of the inner lists and the 2nd axis gives the value in that list, then the visual representation of the OP's arrays will be this:

a = [
      [0, 3],
      [1, 4],
      [2, 5]
    ]
b = [
      [6,  9],
      [7, 10],
      [8, 11]
    ]
# Shape of each array is [3,2]

Now, according to the current documentation, the dstack function adds a 3rd axis, which means each of the arrays end up looking like this:

a = [
      [[0], [3]],
      [[1], [4]],
      [[2], [5]]
    ]
b = [
      [[6],  [9]],
      [[7], [10]],
      [[8], [11]]
    ]
# Shape of each array is [3,2,1]

Now, stacking both these arrays in the 3rd dimension simply means that the result should look, as expected, like this:

dstack([a,b]) = [
                  [[0, 6], [3, 9]],
                  [[1, 7], [4, 10]],
                  [[2, 8], [5, 11]]
                ]
# Shape of the combined array is [3,2,2]

Hope this helps.

1
  • when i am checking for print(dstack([a,a]).shape) it gives output as (3,2,2). But when i am trying to perform dstack on a itself : dstack(a).shape it is giviing output as (1,2,3) Why ? a is a list, [a,b] is also a list
    – jasmin
    Commented Nov 9, 2023 at 11:17
1

Because you mention "images", I think this example would be useful. If you're using Keras to train a 2D convolution network with the input X, then it is best to keep X with the dimension (#images, dim1ofImage, dim2ofImage).

image1 = np.array([[4,2],[5,5]])
image2 = np.array([[3,1],[6,7]])

image1 = image1.reshape(1,2,2)
image2 = image2.reshape(1,2,2)

X = np.stack((image1,image2),axis=1) 
X
array([[[[4, 2],
         [5, 5]],
        [[3, 1],
        [6, 7]]]])

np.shape(X)         
X = X.reshape((2,2,2))   
X 
array([[[4, 2],
        [5, 5]],
       [[3, 1],
        [6, 7]]])

X[0] # image 1
array([[4, 2],
       [5, 5]])
X[1] # image 2
array([[3, 1],
       [6, 7]])             

Not the answer you're looking for? Browse other questions tagged or ask your own question.