Handling NaN (Not a Number) is a daily task for Python developers working with data. Because NaN is designed to fail standard equality checks (NaN != NaN), using if x == NaN will never work.
This guide explains how to correctly identify NaN values in standard Python, NumPy, and Pandas.
Why NaN Breaks Equality Checks
Before diving into solutions, understanding why standard comparisons fail is essential.
val = float('nan')
print(val == val) # False
print(val == float('nan')) # FalseUsing Standard Python (math.isnan)
For single float values, the built-in math library provides the correct approach.
import math
val = float('nan')
if val == float('nan'):
print("This never prints!")
if math.isnan(val):
print("Value is NaN")Output:
Value is NaNValidating User Input
A common use case is sanitizing data before calculations:
import math
def safe_divide(a, b):
result = a / b if b != 0 else float('nan')
if math.isnan(result):
return "Cannot compute"
return result
print(safe_divide(10, 0)) # Cannot compute
print(safe_divide(10, 2)) # 5.0Output:
Cannot compute
5.0Using Pandas (DataFrames & Series)
In data science workflows, Pandas provides isna() (or its alias isnull()), which handles both None and NaN and works on entire DataFrames.
import pandas as pd
import numpy as np
s = pd.Series([1, np.nan, None, 4])
print(s.isna())Output:
0 False
1 True
2 True
3 False
dtype: boolFiltering and Counting NaN Values
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['Alice', 'Bob', None],
'score': [85, np.nan, 92]
})
print(df.isna().sum())
rows_with_nan = df[df.isna().any(axis=1)]
print(rows_with_nan)
clean_rows = df.dropna()
print(clean_rows)Output:
name 1
score 1
dtype: int64
name score
1 Bob NaN
2 None 92.0
name score
0 Alice 85.0Using NumPy (Arrays)
NumPy provides vectorized functions optimized for performance on large arrays.
import numpy as np
arr = np.array([1.5, np.nan, 2.5, np.nan])
mask = np.isnan(arr)
print(mask) # [False True False True]
if np.isnan(arr).any():
print("Array contains NaN values!")
if np.isnan(arr).all():
print("Array is completely empty!")
nan_count = np.isnan(arr).sum()
print(f"Found {nan_count} NaN values") # Found 2 NaN valuesOutput:
[False True False True]
Array contains NaN values!
Found 2 NaN valuesReplacing NaN Values
import numpy as np
arr = np.array([1.0, np.nan, 3.0, np.nan, 5.0])
clean_arr = np.nan_to_num(arr, nan=0.0)
print(clean_arr) # [1. 0. 3. 0. 5.]
mean_val = np.nanmean(arr) # Ignores NaN when computing mean
arr[np.isnan(arr)] = mean_val
print(arr) # [1. 3. 3. 3. 5.]Output:
[1. 0. 3. 0. 5.]
[1. 3. 3. 3. 5.]The Equality Trick (No Imports)
When imports are not available, you can exploit the IEEE 754 property that NaN is not equal to itself.
def is_nan(x):
return x != x
print(is_nan(float('nan'))) # True
print(is_nan(42)) # False
print(is_nan(None)) # False (be careful!)Output:
True
False
FalseHandling NaN in Comparisons
NaN values can cause unexpected behavior in sorting and comparisons:
import numpy as np
data = [3, np.nan, 1, np.nan, 2]
print(sorted(data)) # [1, 2, 3, nan, nan]
print(np.nan > 5) # False
print(np.nan < 5) # False
print(np.nan == 5) # FalseOutput:
[3, nan, 1, nan, 2]
False
False
FalseSummary
| Context | Function | Notes |
|---|---|---|
| Single value | math.isnan(x) |
Standard library, no dependencies |
| NumPy array | np.isnan(arr) |
Vectorized, high performance |
| Pandas | df.isna() |
Handles None and NaN together |
| No imports | x != x |
Works but less readable |