Broadcasting
Center Columns
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=0means "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 ofawithout copying memory — the trailing dimension matches, exactly as inadd-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.