# Python NumPy For Your Grandma - 4.5 Stacking

Contents

In this section, we’ll see how the functions `hstack()`, `vstack()`, and `stack()` are a lot like the `concatenate()` function with some subtle differences that can come in handy.

Let’s start by building some helper arrays. We’ll make a (2,) array called `foo`, another (2,) array called `bar`, a (1,2) array called `baz`, a (1,3) array called `bingo`, and a (2,2) array called `bongo`.

``````import numpy as np

foo = np.array(['a', 'b'])
bar = np.array(['c', 'd'])
baz = np.array([['e', 'f']])
bingo = np.array([['g', 'h', 'i']])
bongo = np.array(
[['j', 'k'],
['l', 'm']]
)
``````

Now let’s take a look at `vstack()`. `vstack()` takes one argument - a sequence of arrays, and you could describe its algorithm in pseudocode as the following.

``````for each array in the sequence:
if the array is 1d
then promote the array to 2d by giving it a new front axis
if every array in the sequence has the same shape:
then concatenate the arrays along axis 0
otherwise throw an error
``````

Visually, you could imagine `vstack()` as vertically stacking 1-d or 2-d arrays. So if we `vstack()` `foo` and `bar`, we’ll get back a 2x2 array like this.

``````np.vstack((foo, bar))
## array([['a', 'b'],
##        ['c', 'd']], dtype='<U1')
``````

And if we `vstack()` `foo`, `bar`, and `baz`, we’ll get back a 3x2 array like this.

``````np.vstack((foo, bar, baz))
## array([['a', 'b'],
##        ['c', 'd'],
##        ['e', 'f']], dtype='<U1')
``````

And if `vstack()` `baz` and `bingo`, we’ll get an error because their shapes don’t align for `vstack()`.

``````np.vstack((baz, bingo))  # error
``````

Now let’s take a look at `hstack()`. `hstack()` also takes one argument - a sequence of arrays, and you could describe its algorithm in pseudocode as the following.

``````if every array in the sequence is 1d:
then concatenate the arrays along axis 0
else:
if every array has the same shape excluding axis 1:
then concatenate arrays along axis 1
otherwise throw an error
``````

Visually, you could imagine `hstack()` as horizontally stacking 1-d or 2-d arrays. So if we `hstack()` `foo` and `bar`, we’ll get back a 4x array like this.

``````np.hstack((foo, bar))
## array(['a', 'b', 'c', 'd'], dtype='<U1')
``````

If we `hstack()` `baz` and `bingo`, we’ll get back a 5x array like this.

``````np.hstack((baz, bingo))
## array([['e', 'f', 'g', 'h', 'i']], dtype='<U1')
``````

If we `hstack()` `foo` and `bingo`, we’ll get back an error because their shapes don’t align for `hstack()`.

``````np.hstack((foo, bingo))  # error
``````

And if we `hstack()` `bingo` and `bongo`, we’ll get another error because their shapes also don’t align for `hstack()`.

``````np.hstack((bingo, bongo))  # error
``````

Lastly, `stack()` takes two arguments, a sequence of arrays to combine, and `axis`, which tells stack to create a new axis along which to combine the arrays. In pseudocode, you could write stack’s algorithm as the following.

``````if every array is the same shape and axis is less than or equal to the dimensionality of the arrays:
then for each array:
insert a new axis where specified
then concatenate the arrays along the new axis
otherwise throw an error
``````

So, let’s see what happens if we `stack()` `foo` and `bar` with `axis=0`. Remember the algorithm. `foo` and `bar` each get a new axis 0, promoting them from 2x arrays to 1x2 arrays, and then they’re stacked along that new axis; so, row-wise, resulting in a new 2x2 array.

``````np.stack((foo, bar), axis = 0)
## array([['a', 'b'],
##        ['c', 'd']], dtype='<U1')
``````

If we `stack()` them along axis 1, they go from 2x arrays to 2x1 arrays and then get stacked column-wise into a new 2x2 array.

``````np.stack((foo, bar), axis = 1)
## array([['a', 'c'],
##        ['b', 'd']], dtype='<U1')
``````

If we try to `stack()` `foo` and `bar` with `axis=2` we’ll get an out of bounds error. Remember, the new axis must be in range of the arrays’ existing dimensionality.

``````np.stack((foo, bar), axis = 2)  # error
``````

You can also use the `axis = -1` shortcut to make the new axis the last axis. For `foo` and `bar`, it’d be like using `axis = 1`.

``````np.stack((foo, bar), axis = -1)
## array([['a', 'c'],
##        ['b', 'd']], dtype='<U1')
``````