Skip to content

Shape After Indexing

Indexing and slicing affect array dimensions differently.

Mental Model

A single integer index collapses that axis (the dimension disappears), while a slice preserves it -- even if the slice selects only one element. This is the most common source of "unexpected shape" bugs: a[0] drops a dimension, but a[0:1] keeps it.

Index vs Slice

Single index reduces dimensions; slice preserves them.

1. Single Index

```python import numpy as np

def main(): a = np.zeros((8, 8, 8, 8)) print(f"{a[1].shape = }")

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

Output:

a[1].shape = (8, 8, 8)

2. Slice of One

```python import numpy as np

def main(): a = np.zeros((8, 8, 8, 8)) print(f"{a[1:2].shape = }")

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

Output:

a[1:2].shape = (1, 8, 8, 8)

3. Key Difference

a[1] removes a dimension; a[1:2] keeps it with size 1.

Mixed Operations

Combining indices and slices on different axes.

1. Index Two Axes

```python import numpy as np

def main(): a = np.zeros((8, 8, 8, 8)) print(f"{a[1, :, 3, :].shape = }")

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

Output:

a[1, :, 3, :].shape = (8, 8)

2. Index and Slice

```python import numpy as np

def main(): a = np.zeros((8, 8, 8, 8)) print(f"{a[1:2, :, 3, :].shape = }")

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

Output:

a[1:2, :, 3, :].shape = (1, 8, 8)

3. All Slices

```python import numpy as np

def main(): a = np.zeros((8, 8, 8, 8)) print(f"{a[1:2, :, 3:4, :].shape = }")

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

Output:

a[1:2, :, 3:4, :].shape = (1, 8, 1, 8)

Complete Comparison

Side-by-side comparison of all cases.

1. Summary Table

```python import numpy as np

def main(): a = np.zeros((8, 8, 8, 8)) print(f"{a[1].shape = }") print(f"{a[1:2].shape = }") print(f"{a[1, :, 3, :].shape = }") print(f"{a[1:2, :, 3, :].shape = }") print(f"{a[1:2, :, 3:4, :].shape = }")

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

Output:

a[1].shape = (8, 8, 8) a[1:2].shape = (1, 8, 8, 8) a[1, :, 3, :].shape = (8, 8) a[1:2, :, 3, :].shape = (1, 8, 8) a[1:2, :, 3:4, :].shape = (1, 8, 1, 8)

2. Dimension Rule

Each integer index removes one dimension; each slice keeps it.

Silent Bug: Shape Collapse

Shape collapse from accidental integer indexing is one of the most common sources of downstream errors. A function expecting a 2D array receives 1D because somewhere upstream a[0] was used instead of a[0:1]. The error message appears far from the actual cause. When in doubt, use slices to preserve dimensions.

Practical Impact

Understanding shape changes is crucial for array operations.

1. Broadcasting

Shape mismatches cause broadcasting errors; use slices to preserve dimensions.

2. Neural Networks

Batch dimensions must be preserved; use [0:1] instead of [0].

3. Matrix Operations

Some operations require 2D arrays; slicing maintains dimensionality.


Exercises

Exercise 1. Given a = np.zeros((6, 5, 4, 3)), predict the shape of each expression without running the code, then verify:

  • a[0]
  • a[0:1]
  • a[2, :, 1]
  • a[2:3, :, 1:2]
Solution to Exercise 1
import numpy as np

a = np.zeros((6, 5, 4, 3))
print(a[0].shape)          # (5, 4, 3)
print(a[0:1].shape)        # (1, 5, 4, 3)
print(a[2, :, 1].shape)    # (5, 3)
print(a[2:3, :, 1:2].shape)# (1, 5, 1, 3)

Exercise 2. Create a 3D array a = np.zeros((4, 3, 2)). Extract a single row from the middle axis using an integer index (a[:, 1, :]) and using a slice (a[:, 1:2, :]). Print both shapes and explain why one has 2 dimensions and the other has 3.

Solution to Exercise 2
import numpy as np

a = np.zeros((4, 3, 2))
with_index = a[:, 1, :]     # integer index removes axis 1
with_slice = a[:, 1:2, :]   # slice preserves axis 1

print(f"Integer index shape: {with_index.shape}")  # (4, 2)
print(f"Slice shape: {with_slice.shape}")          # (4, 1, 2)
# The integer index removes the dimension entirely,
# while the slice keeps it with size 1.

Exercise 3. Given images = np.random.randn(32, 3, 64, 64) (a batch of 32 RGB images), extract the first image preserving all 4 dimensions (shape (1, 3, 64, 64)) using a slice. Then extract it with an integer index and verify the shape is (3, 64, 64). Show how to restore the batch dimension using np.expand_dims.

Solution to Exercise 3
import numpy as np

images = np.random.randn(32, 3, 64, 64)

first_slice = images[0:1]
print(f"With slice: {first_slice.shape}")  # (1, 3, 64, 64)

first_index = images[0]
print(f"With index: {first_index.shape}")  # (3, 64, 64)

restored = np.expand_dims(first_index, axis=0)
print(f"Restored: {restored.shape}")  # (1, 3, 64, 64)