Skip to content

Joining and Splitting Arrays: concatenate, stack, split

Data often arrives in pieces: one file per month, one array per branch. NumPy can join pieces into one array and split one array into pieces. The key question is always: along which axis?

  • concatenate joins along an axis that already exists: the result has the same number of dimensions.
  • stack joins along a new axis: the result has one more dimension.

How to run the examples

Create a new file named join_and_split.py in your NumpyTutorials folder (see the setup in lesson 01). Any name works; this one just matches the topic. Start the file with these lines:

import numpy as np

Then copy the examples of this page into the same file, one after another, in the order shown, and run it with python join_and_split.py (with your virtual environment active). Some examples use variables created in earlier ones, so keep everything in the one file. Each Output box shows what that example prints.

1. concatenate 1D arrays

jan = np.array([10, 20, 30])
feb = np.array([40, 50])
mar = np.array([60])

print("concatenate:", np.concatenate([jan, feb, mar]))
Output
concatenate: [10 20 30 40 50 60]

The arrays go in a list (note the square brackets), and they can have different lengths.

2. concatenate 2D arrays: axis=0 and axis=1

store_a = np.array([[1, 2, 3],
                    [4, 5, 6]])
store_b = np.array([[7, 8, 9]])

print("axis=0 (add rows):\n", np.concatenate([store_a, store_b], axis=0))

extra_column = np.array([[100],
                         [200]])
print("axis=1 (add columns):\n", np.concatenate([store_a, extra_column], axis=1))

try:
    np.concatenate([store_a, store_b], axis=1)       # 2 rows vs 1 row
except ValueError as error:
    print("Error:", error)
Output
axis=0 (add rows):
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
axis=1 (add columns):
 [[  1   2   3 100]
 [  4   5   6 200]]
Error: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 0, the array at index 0 has size 2 and the array at index 1 has size 1
  • axis=0 joins vertically: the rows of store_b are added under store_a. The number of columns must match (3 and 3).
  • axis=1 joins horizontally: columns are added on the right. The number of rows must match (2 and 2).
  • store_a has 2 rows and store_b has 1, so joining them side by side fails.

Rule: all sizes must be equal except along the axis you join on.

3. vstack, hstack, column_stack

These are shortcuts with easy names, and they also handle 1D arrays nicely:

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print("vstack([a, b]):\n", np.vstack([a, b]))         # 1D arrays become rows
print("hstack([a, b]):", np.hstack([a, b]))           # one long row
print("column_stack([a, b]):\n", np.column_stack([a, b]))   # 1D arrays become columns
Output
vstack([a, b]):
 [[1 2 3]
 [4 5 6]]
hstack([a, b]): [1 2 3 4 5 6]
column_stack([a, b]):
 [[1 4]
 [2 5]
 [3 6]]
FunctionDoesTwo 1D arrays of 3 give
np.vstackstacks vertically (as rows)shape (2, 3)
np.hstackstacks horizontallyshape (6,)
np.column_stackuses each 1D array as a columnshape (3, 2)

4. stack vs concatenate

print("concatenate([a, b]) shape:", np.concatenate([a, b]).shape)
print("stack([a, b]) shape      :", np.stack([a, b]).shape, "\n", np.stack([a, b]))
print("stack([a, b], axis=1) shape:", np.stack([a, b], axis=1).shape, "\n", np.stack([a, b], axis=1))

week1 = np.array([[1, 2], [3, 4]])
week2 = np.array([[5, 6], [7, 8]])
print("stack of two 2x2 tables shape:", np.stack([week1, week2]).shape)
Output
concatenate([a, b]) shape: (6,)
stack([a, b]) shape      : (2, 3) 
 [[1 2 3]
 [4 5 6]]
stack([a, b], axis=1) shape: (3, 2) 
 [[1 4]
 [2 5]
 [3 6]]
stack of two 2x2 tables shape: (2, 2, 2)
  • concatenate of two (3,) arrays gives (6,): still 1D.
  • stack of two (3,) arrays gives (2, 3): a new first axis, "which array".
  • stack(..., axis=1) puts the new axis second: (3, 2).
  • Two 2x2 tables stacked give a 3D array (2, 2, 2), for example "week x row x column".

stack needs all arrays to have exactly the same shape.

5. split and array_split

days = np.arange(1, 13)
print("split into 3 equal parts:", np.split(days, 3))
print("split at indexes [2, 5]  :", np.split(days, [2, 5]))     # [:2], [2:5], [5:]

try:
    np.split(days, 5)                       # 12 cannot be divided into 5 equal parts
except ValueError as error:
    print("Error:", error)

print("array_split into 5 parts :", np.array_split(days, 5))
Output
split into 3 equal parts: [array([1, 2, 3, 4]), array([5, 6, 7, 8]), array([ 9, 10, 11, 12])]
split at indexes [2, 5]  : [array([1, 2]), array([3, 4, 5]), array([ 6,  7,  8,  9, 10, 11, 12])]
Error: array split does not result in an equal division
array_split into 5 parts : [array([1, 2, 3]), array([4, 5, 6]), array([7, 8]), array([ 9, 10]), array([11, 12])]
  • np.split(days, 3) cuts 12 values into 3 equal parts of 4.
  • np.split(days, [2, 5]) cuts at positions 2 and 5: [:2], [2:5], [5:].
  • 12 values cannot be split into 5 equal parts, so split raises an error.
  • np.array_split allows unequal parts: the first parts get one extra value.

The result is a Python list of arrays, so you can loop over it or unpack it: a, b, c = np.split(days, 3).

6. vsplit and hsplit

table = np.arange(1, 17).reshape(4, 4)
print("table:\n", table)

top, bottom = np.vsplit(table, 2)            # split the rows
print("vsplit top:\n", top)
print("vsplit bottom:\n", bottom)

left, right = np.hsplit(table, [1])          # split the columns at column 1
print("hsplit left (first column):\n", left)
print("hsplit right shape:", right.shape)
Output
table:
 [[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]
 [13 14 15 16]]
vsplit top:
 [[1 2 3 4]
 [5 6 7 8]]
vsplit bottom:
 [[ 9 10 11 12]
 [13 14 15 16]]
hsplit left (first column):
 [[ 1]
 [ 5]
 [ 9]
 [13]]
hsplit right shape: (4, 3)
  • vsplit splits the rows (a horizontal cut): top half and bottom half.
  • hsplit splits the columns (a vertical cut). Here [1] cuts after the first column.

7. Real example: combine branch reports, split into quarters

pune = np.array([120, 130, 125, 140, 150, 160, 170, 165, 180, 190, 200, 210])
mumbai = np.array([220, 210, 230, 240, 250, 245, 260, 270, 280, 300, 310, 320])

report = np.vstack([pune, mumbai])                 # 2 branches x 12 months
print("report shape:", report.shape)

q1, q2, q3, q4 = np.hsplit(report, 4)              # 4 parts of 3 months
for name, quarter in zip(["Q1", "Q2", "Q3", "Q4"], [q1, q2, q3, q4]):
    print(f"{name} totals (Pune, Mumbai):", quarter.sum(axis=1))

with_total = np.column_stack([report, report.sum(axis=1)])   # add a total column
print("last 3 months + yearly total:\n", with_total[:, -4:])
Output
report shape: (2, 12)
Q1 totals (Pune, Mumbai): [375 660]
Q2 totals (Pune, Mumbai): [450 735]
Q3 totals (Pune, Mumbai): [515 810]
Q4 totals (Pune, Mumbai): [600 930]
last 3 months + yearly total:
 [[ 190  200  210 1940]
 [ 300  310  320 3135]]
  • vstack puts the two branches' 12-month rows into one 2x12 report.
  • hsplit(report, 4) cuts the 12 months into 4 quarters of 3 columns each, and we unpack them into q1 to q4.
  • column_stack adds a "yearly total" column on the right.

Exercises

Try these exercises on your own before moving on:

  1. Join np.zeros(3) and np.ones(2) into one array of 5 values.
  2. Put three 1D arrays of 4 values on top of each other (shape 3x4) with vstack, and next to each other (shape 4x3) with column_stack.
  3. Split np.arange(20) into parts of 5 and print the sum of each part.
  4. Split a 6x4 array into 3 row blocks with vsplit.

Summary

  • np.concatenate([a, b], axis=...) joins along an existing axis; other sizes must match.
  • vstack (rows), hstack (side by side) and column_stack (1D arrays as columns) are handy shortcuts.
  • np.stack adds a new axis; all arrays must have the same shape.
  • np.split needs equal parts or cut positions; np.array_split allows unequal parts.
  • vsplit cuts rows, hsplit cuts columns; all split functions return a list.

Next: Lesson 09: Vectorized Arithmetic and Ufuncs.