Missing data: null and NaN

Polars · Essentials · 10 min

Real data has gaps, and mixing up null and NaN gives silently wrong counts and averages.

Polars has one missing-value marker for every type: null. It works the same in integer, string, date and boolean columns, so an integer column with a gap stays an integer column, unlike classic pandas where it turns into float.

NaN is different: it is a real floating-point value meaning "not a number", produced by things like 0.0 / 0.0. Polars does not treat NaN as missing. is_null() is false for NaN, null_count() doesn't count it, and mean() of a column containing NaN is NaN. Use is_nan() and fill_nan() for those, or convert with fill_nan(None).

Tools for nulls:

  • pl.col("x").is_null(), .is_not_null(), df.null_count()
  • pl.col("x").fill_null(0), .fill_null(strategy="forward") or "mean"
  • df.drop_nulls() or df.drop_nulls(subset=["x"])
  • pl.coalesce("a", "b", pl.lit(0)) takes the first non-null value

Aggregations skip nulls: sum, mean, min and friends ignore them, and count() counts only non-null values while pl.len() counts rows. Comparisons with null return null, which filter treats as false.

When reading pandas data, pl.from_pandas(pdf, nan_to_null=True) (the default) converts NaN markers into proper nulls.

Example

import polars as pl

df = pl.DataFrame({"x": [1.0, None, float("nan"), 4.0]})

print(df.select(
    nulls=pl.col("x").null_count(),
    nans=pl.col("x").is_nan().sum(),
    count=pl.col("x").count(),        # non-null values, NaN included
    rows=pl.len(),
).row(0))                             # (1, 1, 3, 4)

print(df["x"].mean())                 # nan  (NaN poisons the mean)
clean = df.with_columns(pl.col("x").fill_nan(None))
print(clean["x"].mean())              # 2.5  (nulls are skipped)
print(clean["x"].fill_null(strategy="forward").to_list())
# [1.0, 1.0, 1.0, 4.0]

Remember

  • null is the missing marker for every dtype; NaN is a float value
  • Aggregations skip nulls but not NaN
  • count() counts non-null values; pl.len() counts rows

Watch out for

  • Using is_null() to find NaN values in float columns: it returns false for NaN.

Check yourself

  1. A Float64 column holds [2.0, NaN, null]. What is its null_count()?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pola.rs/user-guide/expressions/missing-data/