Skip to content

Expanding Dimensions

Adding new axes to arrays is essential for broadcasting and batch operations.

Mental Model

expand_dims inserts a size-1 axis at the position you specify, like sliding an empty shelf into a bookcase. The data doesn't change -- only the shape gains a new dimension. This is the standard way to promote an array so that broadcasting aligns axes correctly.

np.expand_dims

The np.expand_dims function inserts a new axis at the specified position.

1. Axis Parameter

```python import numpy as np

def main(): x = np.zeros((2, 3, 4)) print(f"{x.shape = }", end="\n\n")

y0 = np.expand_dims(x, axis=0)
y1 = np.expand_dims(x, axis=1)
y2 = np.expand_dims(x, axis=2)
y3 = np.expand_dims(x, axis=3)
print(f"{y0.shape = }")
print(f"{y1.shape = }")
print(f"{y2.shape = }")
print(f"{y3.shape = }")

if name == "main": main() ```

Output:

``` x.shape = (2, 3, 4)

y0.shape = (1, 2, 3, 4) y1.shape = (2, 1, 3, 4) y2.shape = (2, 3, 1, 4) y3.shape = (2, 3, 4, 1) ```

2. Axis Meaning

The axis specifies where the new size-1 dimension is inserted.

Using np.newaxis

The np.newaxis constant adds dimensions via indexing syntax.

1. Explicit Slicing

```python import numpy as np

def main(): x = np.zeros((2, 3, 4)) print(f"{x.shape = }", end="\n\n")

y1 = x[np.newaxis, :]
y2 = x[:, np.newaxis]
y3 = x[:, :, np.newaxis]
y4 = x[:, :, :, np.newaxis]
print(f"{y1.shape = }")
print(f"{y2.shape = }")
print(f"{y3.shape = }")
print(f"{y4.shape = }")

if name == "main": main() ```

Output:

``` x.shape = (2, 3, 4)

y1.shape = (1, 2, 3, 4) y2.shape = (2, 1, 3, 4) y3.shape = (2, 3, 1, 4) y4.shape = (2, 3, 4, 1) ```

2. With Ellipsis

```python import numpy as np

def main(): x = np.zeros((2, 3, 4)) print(f"{x.shape = }", end="\n\n")

z1 = x[np.newaxis, ...]
z2 = x[..., np.newaxis]
print(f"{z1.shape = }")
print(f"{z2.shape = }")

if name == "main": main() ```

Output:

``` x.shape = (2, 3, 4)

z1.shape = (1, 2, 3, 4) z2.shape = (2, 3, 4, 1) ```

Using None

None is an alias for np.newaxis in indexing.

1. Explicit Slicing

```python import numpy as np

def main(): x = np.zeros((2, 3, 4)) print(f"{x.shape = }", end="\n\n")

y1 = x[None, :]
y2 = x[:, None]
y3 = x[:, :, None]
y4 = x[:, :, :, None]
print(f"{y1.shape = }")
print(f"{y2.shape = }")
print(f"{y3.shape = }")
print(f"{y4.shape = }")

if name == "main": main() ```

2. With Ellipsis

```python import numpy as np

def main(): x = np.zeros((2, 3, 4)) print(f"{x.shape = }", end="\n\n")

z1 = x[None, ...]
z2 = x[..., None]
print(f"{z1.shape = }")
print(f"{z2.shape = }")

if name == "main": main() ```

Method Comparison

Three equivalent ways to add dimensions.

1. np.expand_dims

Most explicit and readable for complex insertions.

2. np.newaxis

Standard NumPy idiom, clear intent.

3. None Shorthand

Shortest syntax, common in concise code.

When Broadcasting Fails Without expand_dims

A common error: trying to operate on arrays whose shapes don't align.

```python import numpy as np

a = np.array([1, 2, 3]) # shape (3,) b = np.array([[10], [20], [30]]) # shape (3, 1)

This works — broadcasting aligns (3,) to (1, 3) automatically

print((a + b).shape) # (3, 3)

But this fails:

row_means = np.mean(b, axis=1) # shape (3,)

b - row_means # shape (3, 1) - (3,) → works by accident

For clarity, expand_dims makes intent explicit:

b - np.expand_dims(row_means, axis=1) # shape (3, 1) - (3, 1) → clear ```

When shapes are ambiguous, expand_dims makes the broadcasting intent explicit and prevents subtle bugs.


Exercises

Exercise 1. Given a = np.array([10, 20, 30]) (shape (3,)), use np.expand_dims to create a row vector of shape (1, 3) and a column vector of shape (3, 1). Verify the shapes by printing them.

Solution to Exercise 1
import numpy as np

a = np.array([10, 20, 30])

row = np.expand_dims(a, axis=0)
col = np.expand_dims(a, axis=1)

print(row.shape)  # (1, 3)
print(col.shape)  # (3, 1)

Exercise 2. Create a 2D array a = np.arange(12).reshape(3, 4). Use np.expand_dims to add a new axis at position 0, producing shape (1, 3, 4). Then add another axis at position 2 to the original array, producing shape (3, 1, 4). Print both shapes.

Solution to Exercise 2
import numpy as np

a = np.arange(12).reshape(3, 4)

b = np.expand_dims(a, axis=0)
print(b.shape)  # (1, 3, 4)

c = np.expand_dims(a, axis=1)  # axis=1 inserts between dim 0 and dim 1
# For the original (3, 4), inserting at position 2 means after dim 1
d = np.expand_dims(a, axis=2)
print(d.shape)  # (3, 4, 1)
# Inserting at axis=1 gives (3, 1, 4)
print(c.shape)  # (3, 1, 4)

Exercise 3. Given a = np.array([1, 2, 3]) and b = np.array([10, 20]), use np.expand_dims (or np.newaxis) on both arrays so that you can broadcast them into an outer-sum of shape (3, 2). Print the resulting array.

Solution to Exercise 3
import numpy as np

a = np.array([1, 2, 3])      # shape (3,)
b = np.array([10, 20])        # shape (2,)

# Make a into (3, 1) and b into (1, 2) for broadcasting
a_col = np.expand_dims(a, axis=1)  # (3, 1)
b_row = np.expand_dims(b, axis=0)  # (1, 2)

result = a_col + b_row
print(result)
# [[11 21]
#  [12 22]
#  [13 23]]
print(result.shape)  # (3, 2)