A 2-row × 3-column grid centered column by column. The first nested loop computes each column's mean; the second subtracts the column mean from every element in that column. The replay shows means filling element by element, then result accumulating one centered row at a time.

By hand

With NumPy

a.mean(axis=0) reduces along axis 0 (rows), returning a (3,) array of column means. Subtracting that (3,) vector from the (2, 3) matrix broadcasts across both rows — the same rule as add-vector-to-rows but with a computed vector rather than a literal one.

naive.py
grid = [[1, 2, 3], [3, 6, 9]]
cols = len(grid[0])
means = []
for j in range(cols):
    col_sum = 0
    for row in grid:
        col_sum += row[j]
    means.append(col_sum / len(grid))
result = []
for row in grid:
    new_row = []
    for j in range(cols):
        new_row.append(row[j] - means[j])
    result.append(new_row)
print('RESULT:', result)
library.py
import numpy as np

grid = [[1, 2, 3], [3, 6, 9]]
a = np.array(grid, dtype=float)
means = a.mean(axis=0)
result = a - means
print('means: shape:', means.shape, 'dtype:', means.dtype, 'values:', means.tolist())
print('result: shape:', result.shape, 'dtype:', result.dtype)
print('result values:', result.tolist())
print('RESULT:', result.tolist())
means: shape: (3,) dtype: float64 values: [2.0, 4.0, 6.0]
result: shape: (2, 3) dtype: float64
result values: [[-1.0, -2.0, -3.0], [1.0, 2.0, 3.0]]
RESULT: [[-1.0, -2.0, -3.0], [1.0, 2.0, 3.0]]

Implementation notes

  • axis=0 means "reduce along rows" — collapse all rows into one value per column. a.mean(axis=0) on a (2, 3) array yields a (3,) means vector.
  • Broadcasting then stretches that (3,) vector across both rows of a without copying memory — the trailing dimension matches, exactly as in add-vector-to-rows.
  • After centering, every column's mean is zero (within floating-point precision). This is the first step in standardization (z-scores), where you also divide each column by its standard deviation — that lesson is in the roadmap statistics chapter.
  • Axis-based reductions (mean, sum, std, max) are covered in depth in the upcoming reductions chapter.
  • Shape, dtype, and values are shown explicitly here because ndarray.__repr__ output varies with NumPy version and print options.