Missing Values
Fill with Column Mean
Replace missing values with the mean of the observed (non-missing) values in
the same column. By hand, compute the mean in a first pass (sum and count of
non-None values), then fill in a second pass. With pandas, Series.fillna (Series.mean()) combines both steps — mean() skips NaN by default.
By hand
First pass: accumulate total and count over the non-None entries, then
mean = round(total / count, 2). Second pass: walk values again and append
each element unchanged or substitute mean for None. The trace shows
total and count climbing in the first loop and filled growing in the
second.
values = [3.1, None, 7.2, None, 5.0, None, 8.4, 2.9]
total = 0.0
count = 0
for v in values:
if v is not None:
total = total + v
count = count + 1
mean = round(total / count, 2)
filled = []
for v in values:
filled.append(v if v is not None else mean)
print('RESULT:', [round(v, 2) for v in filled])
values ← [3.1, None, 7.2, None, 5.0, None, 8.4, 2.9]
1values = [3.1, None, 7.2, None, 5.0, None, 8.4, 2.9]2total = 0.0values this step[3.1, None, 7.2, None, 5.0, None, 8.4, 2.9]valuestotal ← 0.0
1values = [3.1, None, 7.2, None, 5.0, None, 8.4, 2.9]2total = 0.03count = 0values this step0.0totalcount ← 0
2total = 0.03count = 04for v in values:values this step0countv ← None, total ← 3.1, count ← 1
pass 1 of 43count = 04for v in values:5 if v is not None:6 total = total + v7 count = count + 18mean = round(total / count, 2)values this step3.1 → Nonev0.0 → 3.1total0 → 1countAll 4 passes — pass 1 is the card above pass vtotalcount1 3.1 → None 0.0 → 3.1 0 → 1 2 7.2 → None 3.1 → 10.3 1 → 2 3 5.0 → None 10.3 → 15.3 2 → 3 4 8.4 → 2.9 15.3 → 23.700000000000003 3 → 4 total ← 26.6
5if v is not None:6 total = total + v7 count = count + 1values this step23.700000000000003 → 26.6totalcount ← 5
6 total = total + v7 count = count + 18mean = round(total / count, 2)values this step4 → 5countfor v in values:
3count = 04for v in values:5 if v is not None:mean ← 5.32
7 count = count + 18mean = round(total / count, 2)9filled = []values this step5.32meanfilled ← []
8mean = round(total / count, 2)9filled = []10for v in values:values this step[]filledv ← 3.1, filled ← [3.1]
pass 1 of 89filled = []10for v in values:11 filled.append(v if v is not None else mean)12print('RESULT:', [round(v, 2) for v in filled])values this step2.9 → 3.1v[] → [3.1]filledAll 8 passes — pass 1 is the card above pass vfilled1 2.9 → 3.1 [] → [3.1] 2 3.1 → None [3.1] → [3.1, 5.32] 3 None → 7.2 [3.1, 5.32] → [3.1, 5.32, 7.2] 4 7.2 → None [3.1, 5.32, 7.2] → [3.1, 5.32, 7.2, 5.32] 5 None → 5.0 [3.1, 5.32, 7.2, 5.32] → [3.1, 5.32, 7.2, 5.32, 5.0] 6 5.0 → None [3.1, 5.32, 7.2, 5.32, 5.0] → [3.1, 5.32, 7.2, 5.32, 5.0, 5.32] 7 None → 8.4 [3.1, 5.32, 7.2, 5.32, 5.0, 5.32] → [3.1, 5.32, 7.2, 5.32, 5.0, 5.32, 8.4] 8 8.4 → 2.9 [3.1, 5.32, 7.2, 5.32, 5.0, 5.32, 8.4] → [3.1, 5.32, 7.2, 5.32, 5.0, 5.32, 8.4, 2.9] for v in values:
9filled = []10for v in values:11 filled.append(v if v is not None else mean)stdout ← RESULT: [3.1, 5.32, 7.2, 5.32, 5.0, 5.32, 8.4, 2.9]
11 filled.append(v if v is not None else mean)12print('RESULT:', [round(v, 2) for v in filled])values this stepRESULT: [3.1, 5.32, 7.2, 5.32, 5.0, 5.32, 8.4, 2.9]stdout
With pandas
s.mean() returns the mean of all non-NaN entries (skipna is True by
default). Passing it straight to fillna substitutes that value at every NaN
position. The snapshot shows the original series under before: and the
computed mean so both substitution points are visible.
import pandas as pd
from dalib.display import set_display
set_display()
values = [3.1, None, 7.2, None, 5.0, None, 8.4, 2.9]
s = pd.Series(values, dtype=float)
m = round(s.mean(), 2)
filled = s.fillna(m)
result = [round(v, 2) for v in filled.tolist()]
print('index:', s.index.tolist())
print('dtype:', s.dtype)
print('before:', s.tolist())
print('mean:', m)
print('RESULT:', result)
index: [0, 1, 2, 3, 4, 5, 6, 7]
dtype: float64
before: [3.1, nan, 7.2, nan, 5.0, nan, 8.4, 2.9]
mean: 5.32
RESULT: [3.1, 5.32, 7.2, 5.32, 5.0, 5.32, 8.4, 2.9]
Implementation notes
Series.mean()hasskipna=Trueby default — it ignoresNaNwhen computing the average, which is exactly the behaviour the naive loop replicates with theif v is not Noneguard.- Mean imputation preserves the column mean but reduces variance and can distort correlations. It is most appropriate when data is missing at random and the proportion of missing values is small.
- For a median or mode fill instead of mean, use
s.fillna(s.median())ors.fillna(s.mode()[0]). - Cross-reference:
fill-constant(this chapter) for the simpler case of filling with a fixed value rather than a computed statistic. - Cross-reference:
list-sum-mean(python-data-basics) for the underlying sum-and-divide mean computation this naive half applies.