NumPy 3D Arrays in Python: Shape, Indexing and Axes

A 3D array in Python is a NumPy array with three dimensions, created most easily by reshaping a flat one:

import numpy as np

a = np.arange(24).reshape(2, 3, 4)     # 2 blocks, 3 rows, 4 columns
a.shape                                # (2, 3, 4)

Read the shape from the outside in: 2 blocks, each holding 3 rows, each holding 4 columns. That ordering is what makes indexing and axis= make sense.

All output here comes from real runs on Python 3.12.5, NumPy 2.5.3.

Creating a 3D array in NumPy

Writing one out by hand shows the structure clearly, even if it’s not how you’d do it in practice:

import numpy as np

# written out by hand: a list of 2D blocks
a = np.array([[[1, 2, 3],
               [4, 5, 6]],

              [[7, 8, 9],
               [10, 11, 12]]])

print(a)
print("\nshape:", a.shape, " ndim:", a.ndim, " size:", a.size)
print("read the shape as (blocks, rows, columns)")

Output:

[[[ 1  2  3]
  [ 4  5  6]]

 [[ 7  8  9]
  [10 11 12]]]

shape: (2, 2, 3)  ndim: 3  size: 12
read the shape as (blocks, rows, columns)
Command Prompt showing a NumPy 3D array printed with its shape ndim and size
Two blocks separated by a blank line, each a 2×3 grid.

Notice how the printout groups things. NumPy prints a blank line between blocks, so you can count the outermost dimension by counting the groups.

Python lists cannot do this on their own. A list of lists of lists works, but it has no shape, no axis and none of the arithmetic.

Ways to build a 3D array

In practice you rarely type the values. These five cover nearly everything:

import numpy as np

print("zeros:", np.zeros((2, 3, 4)).shape)
print("ones :", np.ones((2, 3, 4)).shape)
print("full :", np.full((2, 2, 2), 7).shape)
print("random:", np.random.rand(2, 3, 4).shape)

print()
# the usual way: count up, then fold into shape
a = np.arange(24).reshape(2, 3, 4)
print(a)
print("\n2 x 3 x 4 = 24, which is why arange(24) fits exactly")

Output:

zeros: (2, 3, 4)
ones : (2, 3, 4)
full : (2, 2, 2)
random: (2, 3, 4)

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

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

2 x 3 x 4 = 24, which is why arange(24) fits exactly
CallGives you
np.zeros((2, 3, 4))All zeros
np.ones((2, 3, 4))All ones
np.full((2, 2, 2), 7)Every element set to 7
np.random.rand(2, 3, 4)Random floats from 0 to 1
np.arange(24).reshape(2, 3, 4)0 to 23, folded into shape

The dimensions must multiply to the number of elements you have. 2 × 3 × 4 is 24, which is why arange(24) fits. There is more on sizing in NumPy empty arrays.

Indexing and slicing a 3D array

Each index you supply removes one dimension from the result:

import numpy as np

a = np.arange(24).reshape(2, 3, 4)

print("a[0]       ->", a[0].shape, " one whole block")
print("a[0, 1]    ->", a[0, 1].shape, "   one row of that block")
print("a[0, 1, 2] ->", a[0, 1, 2], "      a single number")

print()
print("slices keep the dimensions you leave in:")
print("a[:, 0, :] ->", a[:, 0, :].shape, " row 0 from every block")
print("a[..., 0]  ->", a[..., 0].shape, " column 0 from every row")

print()
print("a[..., 0] means 'all earlier axes, then index 0 on the last one':")
print(a[..., 0])

Output:

a[0]       -> (3, 4)  one whole block
a[0, 1]    -> (4,)    one row of that block
a[0, 1, 2] -> 6       a single number

slices keep the dimensions you leave in:
a[:, 0, :] -> (2, 4)  row 0 from every block
a[..., 0]  -> (2, 3)  column 0 from every row

a[..., 0] means 'all earlier axes, then index 0 on the last one':
[[ 0  4  8]
 [12 16 20]]
Command Prompt showing NumPy 3D array indexing with one two and three indices and the resulting shapes
One index gives a block, two give a row, three give a number.
  • a[0] — the first block, shape (3, 4).
  • a[0, 1] — one row of that block, shape (4,).
  • a[0, 1, 2] — a single value.
  • a[:, 0, :] — row 0 from every block, shape (2, 4).
  • a[..., 0] — the ellipsis fills in whatever axes you did not mention.

... is worth learning. a[..., 0] means “the first item along the last axis”, and it keeps working if the array gains dimensions later.

What does axis mean in a NumPy 3D array?

This is the part that actually trips people up, and the rule is simpler than it looks: the axis you name is the one that disappears.

import numpy as np

a = np.arange(24).reshape(2, 3, 4)

# summing along an axis REMOVES that axis
for axis in (0, 1, 2):
    result = a.sum(axis=axis)
    print(f"sum(axis={axis}) -> shape {result.shape}")

print()
print("axis=0 collapses the 2 blocks, leaving 3x4:")
print(a.sum(axis=0))

print()
print("axis=2 collapses the 4 columns, leaving 2x3:")
print(a.sum(axis=2))

Output:

sum(axis=0) -> shape (3, 4)
sum(axis=1) -> shape (2, 4)
sum(axis=2) -> shape (2, 3)

axis=0 collapses the 2 blocks, leaving 3x4:
[[12 14 16 18]
 [20 22 24 26]
 [28 30 32 34]]

axis=2 collapses the 4 columns, leaving 2x3:
[[ 6 22 38]
 [54 70 86]]
Command Prompt showing how summing a NumPy 3D array along axis 0 1 and 2 produces different shapes
(2, 3, 4) loses whichever axis you sum along.
OperationResult shapeMeaning
a.sum(axis=0)(3, 4)Add the blocks together
a.sum(axis=1)(2, 4)Add the rows within each block
a.sum(axis=2)(2, 3)Add the columns within each row

Check it against the numbers. a.sum(axis=0) starts with 12, which is 0 + 12: the first element of block one plus the first of block two.

The same rule governs mean, max, min and the rest. Name the axis you want gone.

An RGB image is a 3D array

If the block-row-column model still feels abstract, here is a 3D array you already understand:

import numpy as np
import matplotlib.pyplot as plt

# a colour image IS a 3D array: (height, width, 3)
height, width = 120, 200
picture = np.zeros((height, width, 3), dtype=np.uint8)

picture[:, :, 0] = np.linspace(0, 255, width)          # red rises left to right
picture[:, :, 1] = np.linspace(0, 255, height)[:, None]  # green rises top to bottom
picture[:, :, 2] = 120                                   # blue is constant

print("shape:", picture.shape, "-> (height, width, colour channels)")
print("one pixel:", picture[60, 100], "= [R G B]")

fig, axes = plt.subplots(1, 4, figsize=(12, 3))
axes[0].imshow(picture); axes[0].set_title("all 3 channels")
for i, name in enumerate(("Red", "Green", "Blue")):
    axes[i + 1].imshow(picture[:, :, i], cmap="gray", vmin=0, vmax=255)
    axes[i + 1].set_title(f"{name}: picture[:, :, {i}]")
for ax in axes: ax.axis("off")
plt.tight_layout(); plt.show()

Output:

shape: (120, 200, 3) -> (height, width, colour channels)
one pixel: [128 128 120] = [R G B]
A colour gradient image beside its separated red green and blue channels showing a NumPy 3D array
Shape (120, 200, 3): height, width, and one layer per colour. Blue is flat grey because it is constant.

Every photo you have ever opened is this shape. picture[:, :, 0] is the red layer, and the three greyscale panels are exactly that slice.

A single pixel is picture[y, x], which returns three numbers rather than one. That is the third dimension in the most literal form there is.

It also explains a common Matplotlib error: imshow accepts (h, w) or (h, w, 3), and complains about anything else. See plotting NumPy arrays for the 2D case.

Looping over a 3D array

Iterating gives you blocks, not numbers, which isn’t what most people expect the first time:

import numpy as np

a = np.arange(24).reshape(2, 3, 4)

# iterating gives you 2D blocks, not numbers
for i, block in enumerate(a):
    print(f"block {i} has shape {block.shape} and sums to {block.sum()}")

print()
# to reach every individual value
print("flat values:", list(a.ravel())[:8], "...")

print()
# and with their coordinates
for index, value in list(np.ndenumerate(a))[:5]:
    print(f"  {index} -> {value}")

Output:

block 0 has shape (3, 4) and sums to 66
block 1 has shape (3, 4) and sums to 210

flat values: [np.int64(0), np.int64(1), np.int64(2), np.int64(3), np.int64(4), np.int64(5), np.int64(6), np.int64(7)] ...

  (0, 0, 0) -> 0
  (0, 0, 1) -> 1
  (0, 0, 2) -> 2
  (0, 0, 3) -> 3
  (0, 1, 0) -> 4

ravel() flattens to one dimension when you genuinely want every value in order.

np.ndenumerate gives the coordinates alongside each value, which is the equivalent of enumerate for arrays of any shape.

That said, an explicit loop over a NumPy array is usually a sign you want a vectorised operation instead. Loops are slower by a wide margin.

Reshaping and transposing a 3D array

reshape changes the shape and keeps the order. transpose reorders the axes themselves:

import numpy as np

flat = np.arange(24)
print("flat      :", flat.shape)

cube = flat.reshape(2, 3, 4)
print("reshaped  :", cube.shape)

print("back flat :", cube.reshape(-1).shape, "  -1 means 'work it out'")
print("to 2D     :", cube.reshape(6, 4).shape)

print()
# transpose reorders the axes rather than the data
print("transpose(2, 0, 1):", cube.transpose(2, 0, 1).shape)

print()
try:
    flat.reshape(2, 3, 5)
except ValueError as err:
    print("reshape(2, 3, 5) ->", err)

Output:

flat      : (24,)
reshaped  : (2, 3, 4)
back flat : (24,)   -1 means 'work it out'
to 2D     : (6, 4)

transpose(2, 0, 1): (4, 2, 3)

reshape(2, 3, 5) -> cannot reshape array of size 24 into shape (2,3,5)
Command Prompt showing NumPy reshape and transpose on a 3D array plus the ValueError from an impossible reshape
-1 lets NumPy work out the missing dimension.

reshape(-1) is the idiomatic flatten. You can use -1 for exactly one dimension and NumPy computes it.

The error message from an impossible reshape is blunt but useful: it tells you the size and the shape you asked for, so the mismatch is easy to spot.

transpose(2, 0, 1) moves the last axis to the front, which is how you convert between image formats that put channels first or last. For the 2D version see transposing an array.

Related NumPy array guides:

Frequently asked questions

How do I create a 3D array in Python?

Use NumPy: np.arange(24).reshape(2, 3, 4), or np.zeros((2, 3, 4)) for an empty one. The constructors are listed in the NumPy array creation reference.

How do I read a 3D array shape?

From the outside in. (2, 3, 4) means 2 blocks, each with 3 rows, each with 4 columns.

What does axis mean in a 3D array?

The axis you name is the one that gets collapsed. sum(axis=0) on a (2, 3, 4) array returns a (3, 4) result.

How do I access a single element in a 3D array?

Give all three indices: a[0, 1, 2]. Fewer indices return a slice rather than a number.

What does a[…, 0] mean?

The ellipsis stands for every axis you did not mention, so it takes index 0 along the last axis only.

Is an image a 3D array?

Yes. A colour image has shape (height, width, 3), with one layer each for red, green and blue.

Why does reshape raise a ValueError?

The new dimensions must multiply to the existing number of elements. 24 values cannot become a (2, 3, 5) array, which needs 30.