NumPy Shape Errors: Diagnose and Fix Array Operations

Fix NumPy shape errors by identifying what each dimension means, comparing the input shapes and checking the intended result. These examples show common failures, a suitable repair and a value check so a silent broadcasting mistake is easier to catch.

Intentional errors are caught in the runnable examples. Exact exception wording can differ by NumPy version.

Start with shape, ndim and size

Write down what each dimension represents, then inspect both operands before choosing a fix. Axis zero does not inherently mean samples: that is a convention you assign to the data. Use small rectangular arrays to distinguish rows from columns.

import numpy as np

def expect_error(label, operation, error_type=ValueError):
    try:
        operation()
    except error_type as error:
        print(label + ':', type(error).__name__)
    else:
        raise AssertionError('Expected ' + error_type.__name__)

a = np.arange(12).reshape(3, 4)
print("shape:", a.shape, "ndim:", a.ndim, "size:", a.size)
print(a)
assert a.shape == (3, 4)

Expected output

shape: (3, 4) ndim: 2 size: 12
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

Cannot Reshape: Preserve the Element Count

A reshape changes dimensions without inventing values. The target dimension product must equal size. One -1 can infer a dimension when the other dimensions divide the size. See reshape().

a = np.arange(10)
expect_error('10 elements into 3 by 4', lambda: a.reshape(3, 4))
print("Explicit:", a.reshape(2, 5).shape)
print("Inferred:", a.reshape(-1, 5).shape)
expect_error('Two inferred dimensions', lambda: a.reshape(-1, -1))
assert a.reshape(-1, 5).shape == (2, 5)

Expected output

10 elements into 3 by 4: ValueError
Explicit: (2, 5)
Inferred: (2, 5)
Two inferred dimensions: ValueError

Operands Could Not Be Broadcast Together

Compare dimensions from the right: each pair must be equal or one must be 1. For (3, 4) plus (3,), the trailing 4 and 3 conflict. To add one value per row, reshape the vector to (3, 1). Review broadcasting.

a = np.zeros((3, 4))
b = np.array([10, 20, 30])
expect_error('Trailing dimensions conflict', lambda: a + b)
b_col = b[:, None]
result = a + b_col
print("Column-vector shape:", b_col.shape)
print(result)
np.testing.assert_array_equal(result[:, 0], b)
assert result.shape == a.shape

Expected output

Trailing dimensions conflict: ValueError
Column-vector shape: (3, 1)
[[10. 10. 10. 10.]
 [20. 20. 20. 20.]
 [30. 30. 30. 30.]]

A Shape-Compatible Operation Can Still Be Wrong

On a square array, a row-offset vector can accidentally align with columns without an exception. Check actual values and intended meaning, not only whether code runs. Adding a singleton axis makes the row intent explicit.

a = np.zeros((3, 3))
offsets = np.array([10, 20, 30])
wrong = a + offsets
correct = a + offsets[:, None]
print("Column offsets:")
print(wrong)
print("Intended row offsets:")
print(correct)
assert not np.array_equal(wrong, correct)
np.testing.assert_array_equal(correct[:, 0], offsets)

Expected output

Column offsets:
[[10. 20. 30.]
 [10. 20. 30.]
 [10. 20. 30.]]
Intended row offsets:
[[10. 10. 10.]
 [20. 20. 20.]
 [30. 30. 30.]]

Reduction Axes and keepdims

axis=0 combines rows and leaves one value per column. axis=1 combines columns and leaves one value per row. keepdims preserves reduced axes with length one when broadcasting the result back. See axis reduction.

x = np.arange(12, dtype=float).reshape(3, 4)
column_means = x.mean(axis=0)
row_means = x.mean(axis=1, keepdims=True)
columns_centered = x - column_means
rows_centered = x - row_means
print("Column mean shape:", column_means.shape)
print("Row mean shape:", row_means.shape)
print("Row-centered values:")
print(rows_centered)
np.testing.assert_allclose(columns_centered.mean(axis=0), 0)
np.testing.assert_allclose(rows_centered.mean(axis=1), 0)

Expected output

Column mean shape: (4,)
Row mean shape: (3, 1)
Row-centered values:
[[-1.5 -0.5  0.5  1.5]
 [-1.5 -0.5  0.5  1.5]
 [-1.5 -0.5  0.5  1.5]]

Axis Is Out of Bounds

For a 2D array the valid axis integers are 0, 1, -1 and -2. Negative axes count from the end. Check ndim after slicing, because selecting a single row often removes an axis.

a = np.arange(12).reshape(3, 4)
expect_error('Invalid reduction axis', lambda: a.sum(axis=2), np.exceptions.AxisError)
row = a[0]
print("Single row shape:", row.shape)
expect_error('Row is now one dimensional', lambda: row.sum(axis=1), np.exceptions.AxisError)
kept = a[0:1]
print("Row with axis retained:", kept.shape)
print("Row total:", kept.sum(axis=1))
assert kept.shape == (1, 4)

Expected output

Invalid reduction axis: AxisError
Single row shape: (4,)
Row is now one dimensional: AxisError
Row with axis retained: (1, 4)
Row total: [6]

concatenate Joins an Existing Axis

Inputs must have matching ranks and matching dimensions except along the concatenation axis. Changing the axis can fix a valid join, but should match the intended row or feature meaning.

a = np.ones((2, 3))
b = np.zeros((2, 3))
print("More rows:", np.concatenate([a, b], axis=0).shape)
print("More columns:", np.concatenate([a, b], axis=1).shape)
c = np.zeros((2, 4))
expect_error('Different column counts for vertical join', lambda: np.concatenate([a, c], axis=0))
print("Valid horizontal join:", np.concatenate([a, c], axis=1).shape)

Expected output

More rows: (4, 3)
More columns: (2, 6)
Different column counts for vertical join: ValueError
Valid horizontal join: (2, 7)

Fix a Rank Mismatch without Losing Row Meaning

A row shaped (3,) cannot be concatenated vertically with a (2, 3) table. Add a leading dimension to create (1, 3). Flattening everything would remove the table structure.

a = np.arange(6).reshape(2, 3)
row = np.array([10, 20, 30])
expect_error('Different ranks', lambda: np.concatenate([a, row], axis=0))
joined = np.concatenate([a, row[None, :]], axis=0)
print(joined)
assert joined.shape == (3, 3)
np.testing.assert_array_equal(joined[-1], row)

Expected output

Different ranks: ValueError
[[ 0  1  2]
 [ 3  4  5]
 [10 20 30]]

stack Adds an Axis: Same Shape Can Hide Different Layouts

stack requires equal input shapes. With two (2, 3) arrays, axis=0 and axis=1 both yield (2, 2, 3), but the positions have different meanings. Verify where an original array appears.

a = np.ones((2, 3))
b = np.zeros((2, 3))
by_block = np.stack([a, b], axis=0)
by_row = np.stack([a, b], axis=1)
print("Block-first:")
print(by_block)
print("Row-first:")
print(by_row)
np.testing.assert_array_equal(by_block[0], a)
np.testing.assert_array_equal(by_row[:, 0, :], a)
assert not np.array_equal(by_block, by_row)
expect_error('Unequal stack shapes', lambda: np.stack([a, np.zeros((1, 3))]))

Expected output

Block-first:
[[[1. 1. 1.]
  [1. 1. 1.]]

 [[0. 0. 0.]
  [0. 0. 0.]]]
Row-first:
[[[1. 1. 1.]
  [0. 0. 0.]]

 [[1. 1. 1.]
  [0. 0. 0.]]]
Unequal stack shapes: ValueError

Matrix Multiplication: Match the Inner Dimensions

For 2D arrays, (m, k) @ (k, n) produces (m, n). A vector of length k on the right produces shape (m,). Transposing or padding arbitrary data just to avoid the error can change the calculation. Use @ for matrix multiplication; * is elementwise. Higher-dimensional dot and matmul do not have identical rules.

a = np.arange(6).reshape(2, 3)
b = np.arange(9).reshape(3, 3)
expect_error('Vector has the wrong length', lambda: a @ np.array([1, 2]))
v = np.array([1, 2, 3])
print("Matrix result shape:", (a @ b).shape)
print("Vector result:", a @ v)
np.testing.assert_array_equal(a @ v, [8, 26])

Expected output

Vector has the wrong length: ValueError
Matrix result shape: (2, 3)
Vector result: [ 8 26]

Add and Remove Singleton Dimensions Deliberately

None, newaxis and expand_dims insert a length-one axis. squeeze removes length-one axes; specifying axis prevents unrelated singleton axes from disappearing. Removing a non-singleton axis is invalid.

x = np.arange(6)
column = x[:, None]
row = x[None, :]
print("Column / row:", column.shape, row.shape)
np.testing.assert_array_equal(column, np.expand_dims(x, axis=1))
print("Flat again:", np.squeeze(column, axis=1).shape)
expect_error('Cannot squeeze six elements', lambda: np.squeeze(column, axis=0))
one = np.array([[7]])
print("All axes removed:", np.squeeze(one).shape)
print("One axis retained:", np.squeeze(one, axis=1).shape)

Expected output

Column / row: (6, 1) (1, 6)
Flat again: (6,)
Cannot squeeze six elements: ValueError
All axes removed: ()
One axis retained: (1,)

Transpose, Views and Contiguous Storage

Transpose swaps axes of a 2D array but does not turn a 1D vector into a column. A transposed array can be non-contiguous. Request contiguous storage only when needed; it is a storage adjustment, not a repair for the wrong axis meaning. See views and strides.

m = np.arange(12).reshape(3, 4)
mt = m.T
contiguous = np.ascontiguousarray(mt)
print("Original / transpose:", m.shape, mt.shape)
print("Transpose C contiguous:", mt.flags.c_contiguous)
print("Adjusted C contiguous:", contiguous.flags.c_contiguous)
vector = np.arange(3)
print("1D transpose:", vector.T.shape)
np.testing.assert_array_equal(mt, contiguous)
assert vector.T.shape == (3,)

Expected output

Original / transpose: (3, 4) (4, 3)
Transpose C contiguous: False
Adjusted C contiguous: True
1D transpose: (3,)

3D Broadcasting: Name Every Dimension

Here axes mean depth, row and column. A (4,) vector offsets columns across all depths and rows. A depth offset must be shaped (2, 1, 1). Missing leading dimensions are treated as length one during broadcasting.

t = np.arange(24).reshape(2, 3, 4)
bias = np.array([1, 2, 3, 4])
depth_bias = np.array([10, 20])[:, None, None]
print("Column offset shape:", (t + bias).shape)
print("Depth offset shape:", (t + depth_bias).shape)
np.testing.assert_array_equal((t + depth_bias)[0] - t[0], np.full((3, 4), 10))
np.testing.assert_array_equal((t + depth_bias)[1] - t[1], np.full((3, 4), 20))

Expected output

Column offset shape: (2, 3, 4)
Depth offset shape: (2, 3, 4)

Boolean Indexing Needs a Matching Mask

A mask used to select rows must match the row count. Boolean indexing does not use arithmetic broadcasting to stretch a short mask. Choose the intended dimension before making a condition. See indexing and slicing.

a = np.arange(12).reshape(3, 4)
bad = np.array([True, False])
expect_error('Wrong row mask length', lambda: a[bad], IndexError)
row_mask = a[:, 0] >= 4
selected = a[row_mask]
print("Selected rows:")
print(selected)
assert selected.shape == (2, 4)

Expected output

Wrong row mask length: IndexError
Selected rows:
[[ 4  5  6  7]
 [ 8  9 10 11]]

Practical Sales Pipeline: Check Shape and Business Totals

This synthetic table has days down rows and Books, Pens and Gifts across columns. Multiply each product by its price and sum across products for daily revenue. Check prices, row counts and known totals; compatible shapes alone cannot verify that product order matches.

sales = np.array([[2, 3, 1], [0, 4, 2], [1, 0, 3], [3, 2, 0]])
products = ['Books', 'Pens', 'Gifts']
prices = np.array([10, 2, 5])
assert sales.ndim == 2
assert prices.shape == (sales.shape[1],)
assert len(products) == sales.shape[1]
revenue = sales * prices
daily = revenue.sum(axis=1)
print("Sales / prices / revenue:", sales.shape, prices.shape, revenue.shape)
print("Daily revenue:", daily)
np.testing.assert_array_equal(daily, [31, 18, 25, 34])
assert daily.sum() == 108

Expected output

Sales / prices / revenue: (4, 3) (3,) (4, 3)
Daily revenue: [31 18 25 34]

Retain Row Totals for Broadcasting and Guard Zero Rows

Normalizing a sales row needs totals shaped (days, 1). A day with no units has no defined product share. Initialize the output with NaN and divide only where the denominator is positive. See sum() for totals.

units = np.array([[2, 3, 1], [0, 0, 0], [1, 0, 3]], dtype=float)
totals = units.sum(axis=1, keepdims=True)
shares = np.full(units.shape, np.nan)
np.divide(units, totals, out=shares, where=totals > 0)
print("Total shape:", totals.shape)
print("Product shares:")
print(np.round(shares, 3))
np.testing.assert_allclose(shares[[0, 2]].sum(axis=1), 1)
assert np.isnan(shares[1]).all()

Expected output

Total shape: (3, 1)
Product shares:
[[0.333 0.5   0.167]
 [  nan   nan   nan]
 [0.25  0.    0.75 ]]

Common Errors: Choose the Fix by Intent

Cannot reshape: compare size with the target dimension product. Cannot broadcast: compare trailing dimensions and add the singleton axis matching the intended meaning. Axis out of bounds: inspect ndim after slicing. Join dimension mismatch: check rank and every non-join dimension. stack shape mismatch: all input shapes must match. matmul dimension mismatch: match inner dimensions. Boolean index mismatch: check mask length or full mask shape. Print intermediate shapes and assert meaningful expected values. Large broadcast outputs can require substantial memory even when the inputs are small.

Four Practice Exercises

1. Add [1, 2, 3] to every row of a (5, 3) array. 2. Join three (10, 4) blocks vertically and calculate column means. 3. Add a column vector [10, 20, 30] to a (3, 5) array. 4. Reshape 24 values into (2, 3, 4), then subtract [1, 2, 3, 4] along the last axis. Predict each result shape before running the solution.

Solution 1: Feature Offsets for Every Row

The (3,) vector already aligns with the three columns; no additional dimension is necessary.

a = np.arange(15).reshape(5, 3)
row = np.array([1, 2, 3])
result = a + row
print(result)
assert result.shape == (5, 3)
np.testing.assert_array_equal(result[0], [1, 3, 5])

Expected output

[[ 1  3  5]
 [ 4  6  8]
 [ 7  9 11]
 [10 12 14]
 [13 15 17]]

Solution 2: Join Blocks and Compute Column Means

Join along axis zero to add rows. Mean along axis zero leaves one mean per column.

blocks = [np.full((10, 4), i) for i in range(3)]
big = np.concatenate(blocks, axis=0)
means = big.mean(axis=0)
print("Joined shape:", big.shape)
print("Column means:", means)
assert big.shape == (30, 4)
np.testing.assert_array_equal(means, [1, 1, 1, 1])

Expected output

Joined shape: (30, 4)
Column means: [1. 1. 1. 1.]

Solution 3: One Offset per Row

Insert the column axis so a (3, 1) vector expands across five columns.

column = np.array([10, 20, 30])[:, None]
b = np.arange(15).reshape(3, 5)
result = b + column
print(result)
assert result.shape == (3, 5)
np.testing.assert_array_equal(result[:, 0], [10, 25, 40])

Expected output

[[10 11 12 13 14]
 [25 26 27 28 29]
 [40 41 42 43 44]]

Solution 4: Subtract a Last-Axis Bias

The (4,) bias applies to columns in every row and depth.

t = np.arange(24).reshape(2, 3, 4)
bias = np.array([1, 2, 3, 4])
result = t - bias
print(result)
assert result.shape == (2, 3, 4)
np.testing.assert_array_equal(result[0, 0], [-1, -1, -1, -1])
np.testing.assert_array_equal(result[1, 2], [19, 19, 19, 19])

Expected output

[[[-1 -1 -1 -1]
  [ 3  3  3  3]
  [ 7  7  7  7]]

 [[11 11 11 11]
  [15 15 15 15]
  [19 19 19 19]]]

Practice in Google Colab

Open in Google Colab View on GitHub
All sample data is embedded. Run the examples and practise diagnosing and repairing shape errors. Save a copy in Drive to keep your changes.

Continue with NumPy tutorials, shape, broadcasting and reshape(). References: NumPy broadcasting guide, reshape documentation and matmul documentation.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer