NaN-Aware Functions (nanmean, nansum)

Reviewed & published by Brayan K

NaN-aware functions like np.nanmean and np.nansum compute the usual statistics while quietly skipping missing values, so a single NaN no longer poisons your result.

Part of the free NumPy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll see how an ordinary reduction collapses to NaN, swap in the nan* family, detect missing values with np.isnan, and run NaN-aware reductions along an axis.

What You'll Learn in This Lesson

1 Poisoned Reductions and the Fix

A NaN ("not a number") infects any arithmetic it touches, so a plain mean() or sum() over data with even one NaN returns nan. The nan* family — np.nanmean, np.nansum, and friends — ignores the NaNs and computes the statistic over the valid values only.

import numpy as np

data = np.array([1.0, 2.0, np.nan, 4.0, 5.0])

# A normal reduction is poisoned by the NaN
print(data.mean())       # nan
print(data.sum())        # nan

# NaN-aware versions skip the missing value
print(np.nanmean(data))  # 3.0
print(np.nansum(data))   # 12.0

# ✅ Expected output:
# nan
# nan
# 3.0
# 12.0

The whole reduction toolbox has a NaN-aware twin: nanmax, nanmin, nanstd, nanvar, nanmedian, and nanpercentile.

import numpy as np

data = np.array([1.0, 2.0, np.nan, 4.0, 5.0])

print(np.nanmax(data))            # 5.0
print(np.nanmin(data))            # 1.0
print(round(np.nanstd(data), 4))  # 1.5811

# ✅ Expected output:
# 5.0
# 1.0
# 1.5811

2 Detecting NaN with np.isnan

You cannot test for NaN with == because NaN is not equal to anything — not even itself. Use np.isnan(arr), which returns a boolean mask. Sum the mask to count missing values, or invert it with ~ to keep only the valid ones.

import numpy as np

a = np.array([1.0, np.nan, 3.0, np.nan])

print(np.isnan(a))         # [False  True False  True]
print(np.isnan(a).sum())   # 2  (two missing)

# Keep only the valid values
clean = a[~np.isnan(a)]
print(clean)         # [1. 3.]
print(clean.mean())  # 2.0

# ✅ Expected output:
# [False  True False  True]
# 2
# [1. 3.]
# 2.0

3 NaN-Aware Reductions Along an Axis

Just like their normal counterparts, the nan* functions accept an axis argument, so you can summarize each row or column while ignoring the NaNs scattered through a 2D array. nanpercentile reads cut points the same way.

import numpy as np

m = np.array([[1.0, 2.0, np.nan],
              [4.0, np.nan, 6.0]])

print(np.nanmean(m, axis=0))   # [2.5 2.  6. ]  (down columns)
print(np.nansum(m, axis=1))    # [ 3. 10.]      (across rows)

# A NaN-aware median / percentile
data = np.array([1.0, 2.0, np.nan, 4.0, 5.0])
print(np.nanpercentile(data, 50))   # 3.0

# ✅ Expected output:
# [2.5 2.  6. ]
# [ 3. 10.]
# 3.0

One caution: if an entire row or column is NaN, nanmean has nothing to average and returns nan for that slice (with a warning) — a useful signal that the data is fully missing there.

🎯 YOUR TURN: Fill in the Blanks

Replace each ___ so the program shows the poisoned mean, then the NaN-aware mean.

import numpy as np

temps = np.array([20.0, np.nan, 22.0, 24.0, np.nan])

# Normal mean is poisoned
print(temps.___())       # fill in: mean    -> nan

# NaN-aware mean skips the gaps
print(np.___(temps))     # fill in: nanmean -> 22.0

# Expected output:
# nan
# 22.0

Expected output: nan then 22.0. (Answers: mean, nanmean.)

⚠️ Common Errors & Quick Reference

❌ Testing for NaN with ==

x == np.nan is always False because NaN is not equal to itself.

✅ Fix: use np.isnan(x) to detect missing values.

❌ Wondering why mean() is nan

A single NaN poisons any normal reduction over the array.

✅ Fix: switch to the NaN-aware twin — np.nanmean, np.nansum, np.nanstd, and so on.

TaskCode
Mean ignoring NaNnp.nanmean(a)
Sum ignoring NaNnp.nansum(a)
Detect missingnp.isnan(a)
Count missingnp.isnan(a).sum()
Drop missinga[~np.isnan(a)]

🎯 Mini Challenge: Clean a Sensor Stream

A sensor stream has gaps recorded as NaN. Count the missing readings, then compute the NaN-aware mean, max, min, and standard deviation.

import numpy as np

readings = np.array([10.0, np.nan, 12.0, 15.0, np.nan, 18.0, 20.0])

print(np.isnan(readings).sum())        # 2  missing
print(np.nanmean(readings))            # 15.0
print(np.nanmax(readings))             # 20.0
print(np.nanmin(readings))             # 10.0
print(round(np.nanstd(readings), 4))   # 3.6878

# ✅ Expected output:
# 2
# 15.0
# 20.0
# 10.0
# 3.6878

❓ Frequently Asked Questions

Lesson complete — NaN-aware functions unlocked!

You can now spot a poisoned reduction, reach for np.nanmean and the rest of the nan* family, detect missing values with np.isnan, and run NaN-aware reductions along any axis.

Practice quiz

Why does a normal arr.mean() return nan when arr has one NaN?

  • A single NaN poisons the whole reduction
  • mean is broken
  • It rounds to nan
  • NaN counts as zero

Answer: A single NaN poisons the whole reduction. Arithmetic touching NaN yields NaN, so one NaN poisons the result.

For data = [1.0, 2.0, np.nan, 4.0, 5.0], what does np.nanmean(data) return?

  • nan
  • 3.0
  • 12.0
  • 2.4

Answer: 3.0. nanmean skips the NaN and averages 1, 2, 4, 5 to get 3.0.

What does np.nansum([1.0, 2.0, np.nan, 4.0, 5.0]) return?

  • nan
  • 10.0
  • 12.0
  • 15.0

Answer: 12.0. nansum ignores the NaN and adds 1+2+4+5 = 12.0.

Which function correctly detects NaN values?

  • x == np.nan
  • x is np.nan
  • np.isnan(x)
  • x != x only

Answer: np.isnan(x). np.isnan(x) is the reliable element-wise NaN check; == always fails.

Why can't you test for NaN with the == operator?

  • == is too slow
  • NaN is not equal to anything, not even itself
  • == only works on ints
  • It raises an error

Answer: NaN is not equal to anything, not even itself. By IEEE rules NaN never equals anything, so x == np.nan is always False.

How do you count NaN values in array a?

  • len(a)
  • a.count()
  • np.isnan(a).sum()
  • a.sum()

Answer: np.isnan(a).sum(). np.isnan(a) gives a boolean mask; .sum() counts the True entries.

Which of these is NOT part of the nan-aware family?

  • np.nanstd
  • np.nanmax
  • np.nanmedian
  • np.nanconcat

Answer: np.nanconcat. There is no np.nanconcat; nanstd, nanmax, and nanmedian all exist.

What does np.nanmean([[1.0,2.0,np.nan],[4.0,np.nan,6.0]], axis=0) return?

  • [2.5 2. 6.]
  • nan
  • [1.5 6.]
  • [3. 10.]

Answer: [2.5 2. 6.]. Down each column skipping NaN: [2.5, 2.0, 6.0].

What is np.nanmean essentially equivalent to?

  • a.sum()
  • a.mean() * 2
  • Filling NaN with 0 first
  • arr[~np.isnan(arr)].mean()

Answer: arr[~np.isnan(arr)].mean(). It drops the NaNs, then averages the remaining valid values.

If an entire row is NaN, what does nanmean return for that slice?

  • nan (with a warning)
  • 0.0
  • the row length
  • infinity

Answer: nan (with a warning). With nothing to average, nanmean returns nan and warns for that slice.

Continue this course

Frequently asked questions

Why does mean() return nan when my data has a NaN?

NaN means 'not a number', and by IEEE rules any arithmetic touching it produces NaN. A regular reduction like arr.mean() or arr.sum() adds every element, so a single NaN poisons the whole result and you get nan back. The NaN-aware functions such as np.nanmean and np.nansum exist precisely to skip those missing values and return a real number.

What is the difference between np.mean and np.nanmean?

np.mean includes every element, so one NaN makes the answer NaN. np.nanmean ignores NaN entries and averages only the valid values — equivalent to dropping the NaNs first. The same pattern holds across the family: nansum, nanstd, nanvar, nanmax, nanmin, nanmedian, and nanpercentile all compute their statistic while treating NaN as missing rather than as a number.

How do I detect or count NaN values in an array?

Use np.isnan(arr), which returns a boolean mask that is True wherever the value is NaN. Call .sum() on that mask to count the missing values, and use the inverted mask arr[~np.isnan(arr)] to keep only the valid entries. Note that you cannot test for NaN with == because NaN is not equal to anything, including itself, so np.isnan is the correct check.