NaN, inf and missing values
Why one gap poisons a whole sum, and why `== np.nan` never finds anything.
np.nannp.isnannp.nanmeannp.nansumnp.isfinitenp.nan_to_numWatch it happen
Play it through, or step back and forth yourself.
xnp.nan is how a float array says "I don't know". A sensor dropped out, a form wasn't filled in, a division didn't work. It's a float, so an integer array can't hold one — reading data with gaps always gives you floats.
The idea
np.nan — "not a number" — is how a float array says I don't know. A sensor dropped a reading, a field was blank, a division didn't work.
It's a float, always. An integer array cannot hold one, which is why loading a CSV with gaps hands you float columns whether you wanted them or not.
It spreads, and that's correct
x = np.array([3.0, np.nan, 7.5])
x.sum() # nan
x.mean() # nan
x.max() # nanNot a bug. If one value is genuinely unknown then the true total is genuinely unknown, and NumPy refuses to guess. The alternative — silently pretending the gap is a zero — would give you a confidently wrong answer, which is worse.
The equality trap
Two unknowns aren't equal to each other, so:
np.nan == np.nan # False
x == np.nan # all False — finds nothing, raises nothingThat's the dangerous one: you write a filter, it silently matches zero rows, and nothing tells you. Use the function that asks the right question:
np.isnan(x) # True where the gaps are
x[~np.isnan(x)] # keep only the real values
np.isnan(x).sum() # how many are missing
np.isnan(m).any(axis=1) # which rows have any gap at allThe nan-functions
Rather than masking every time, most reductions have a gap-skipping twin:
np.nansum(x) np.nanmean(x) np.nanstd(x)
np.nanmax(x) np.nanmin(x) np.nanmedian(x)np.nanmean divides by the number of real values, so it matches what you'd get by dropping the gaps first. Worth being deliberate about that: skipping a missing value and treating it as zero are different claims about your data, and only one of them is usually true.
Filling gaps
x[np.isnan(x)] = 0 # explicit
np.nan_to_num(x) # nan->0, inf->a huge finite number
np.nan_to_num(x, nan=np.nanmean(x)) # fill with the mean insteadinf is a different thing
np.inf isn't unknown — it's unbounded. Dividing by zero in an array produces it with a RuntimeWarning rather than raising:
np.array([1.0]) / 0 # [inf] — with a warning
np.array([0.0]) / 0 # [nan] — 0/0 really is undefinedBefore feeding data to anything that will choke on it, the check you usually want catches both problems at once:
np.isfinite(x).all() # True only if no nan and no inf
x[np.isfinite(x)] # keep only usable valuesPractice
Write it yourself. The answer is there when you want it.
Putting the kettle on…
Starting up…
Write it yourself
not gradedPrint x, then x.mean() and np.nanmean(x) next to each other. Show that x == np.nan finds nothing while np.isnan(x) finds both gaps. Print x with the gaps dropped, and finish with np.nan_to_num(x).
Your turn
4 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.
How many values in x are missing? Return the count.
Return x with the missing values removed.
Return the mean of x, ignoring the gaps.
Return x with every missing value replaced by 0.0, keeping the shape.
