ML Foundations
Handle zero-weight rows in a NumPy weighted mean
Separate rows with no weight from valid weighted averages without inventing a numeric result.
Evidence and testing
Review record: Tensor Almanac automated evidence gate (no independent human review). Example versions, recorded outputs and limitations appear below.
A zero total weight cannot define a weighted mean
A weighted mean divides the sum of weighted observations by the sum of their weights. NumPy average raises ZeroDivisionError when all weights along a reduced slice sum to zero. In a row-wise metric, a single row with no contributing observations can therefore prevent the whole reduction. [1]
A zero result would be ambiguous: it might be a real weighted mean or an absence of observations. Decide how downstream code represents missing rows. This example returns both a NaN placeholder and a separate boolean validity mask.
References: [1]
Reduce only rows with positive weight
Run python examples/numpy_zero_weighted_mean.py. The helper accepts equally shaped two-dimensional arrays and restricts inputs to finite values and finite nonnegative weights. It sums each row of weights, marks positive totals as valid, and calls numpy.average only on those rows. [1, 2]
The first row has a weighted mean of 6.0. The second row has three zero weights and receives no numeric result. The third uses weights zero, one and three and yields 4.5. The printed list uses None for the invalid row so that the result can be read without interpreting NaN as a measured value.
The boolean mask is returned alongside the array. Callers can exclude invalid rows or explicitly report them before computing a later aggregate. The example checks both the mask and the numerical results with assertions.
Executed with Python 3.12.14 and NumPy 2.3.5; stdout and checksum are in private evidence.
"""Compute row-wise weighted means only when total weight is positive."""
import numpy as np
def row_means(values, weights):
values = np.asarray(values, dtype=float)
weights = np.asarray(weights, dtype=float)
if values.ndim != 2 or values.shape != weights.shape:
raise ValueError('expected equally shaped matrices')
if not np.isfinite(values).all() or not np.isfinite(weights).all() or (weights < 0).any():
raise ValueError('expected finite values and nonnegative finite weights')
totals = weights.sum(axis=1)
valid = totals > 0
result = np.full(values.shape[0], np.nan)
if valid.any():
result[valid] = np.average(values[valid], weights=weights[valid], axis=1)
return result, valid
values = [[2, 6, 10], [2, 6, 10], [1, 3, 5]]
weights = [[1, 2, 1], [0, 0, 0], [0, 1, 3]]
result, valid = row_means(values, weights)
print('valid:', valid.tolist())
print('means:', [round(float(x), 2) if ok else None for x, ok in zip(result, valid)])
assert valid.tolist() == [True, False, True]
assert np.allclose(result[valid], [6.0, 4.5])
Recorded output
valid: [True, False, True] means: [6.0, None, 4.5]
Keep the contract explicit
This is a nonnegative-weight example. NumPy also accepts other weights, but negative weights can cancel to a zero total even when individual weights are nonzero. A signed-weight application needs its own denominator and interpretation policy rather than relying on this helper. [1]
The fixture is small and has ordinary magnitudes. It does not test overflow, missing values, precision at extreme scales or a production workload. If those cases matter, validate the totals and numerical stability for your data before using the calculation. The example makes no performance or model-quality claim.