data cleaning
pandas
NaN replacement
data preprocessing
dataframe manipulation

How to replace NaN values in a dataframe column

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Replacing missing values in a single pandas column is one of the most common data-cleaning tasks. The right replacement depends on what the column means, because filling a missing category with "unknown" is very different from filling a missing measurement with a median or a forward-filled value.

Basic Replacement with fillna

If you already know the value you want, fillna is the direct tool:

python
1import pandas as pd
2import numpy as np
3
4df = pd.DataFrame({
5    "city": ["Toronto", np.nan, "Montreal", np.nan]
6})
7
8df["city"] = df["city"].fillna("Unknown")
9print(df)

Output:

python
1        city
20    Toronto
31    Unknown
42   Montreal
53    Unknown

This is the clearest approach when the replacement is a fixed scalar value.

Replacing Numeric NaN Values with a Statistic

For numeric columns, a constant is not always the best choice. A common pattern is to fill missing values with the mean or median of the non-missing data.

python
1import pandas as pd
2import numpy as np
3
4df = pd.DataFrame({
5    "score": [10.0, np.nan, 18.0, np.nan, 14.0]
6})
7
8median_score = df["score"].median()
9df["score"] = df["score"].fillna(median_score)
10
11print(df)

Median is often safer than mean when outliers exist. The important part is that you compute the statistic from the real data rather than hard-coding a guess.

Forward Fill and Backward Fill

Some columns are sequential, such as timestamps, sensor values, or labels that persist until changed. In those cases, carrying nearby values forward or backward may be more sensible than using a global average.

python
1import pandas as pd
2import numpy as np
3
4df = pd.DataFrame({
5    "status": ["open", np.nan, np.nan, "closed", np.nan]
6})
7
8df["status"] = df["status"].ffill()
9print(df)

Backward fill works the other way:

python
df["status"] = df["status"].bfill()

Use these only when row order has meaning. On an unsorted table, forward-filling can silently create bad data.

Conditional Replacement

Sometimes you do not want one universal fill rule. You may want to fill values differently based on other columns or specific row conditions.

python
1import pandas as pd
2import numpy as np
3
4df = pd.DataFrame({
5    "type": ["retail", "retail", "wholesale", "wholesale"],
6    "discount": [np.nan, 5.0, np.nan, 12.0]
7})
8
9df.loc[df["type"] == "retail", "discount"] = (
10    df.loc[df["type"] == "retail", "discount"].fillna(0.0)
11)
12
13df.loc[df["type"] == "wholesale", "discount"] = (
14    df.loc[df["type"] == "wholesale", "discount"].fillna(10.0)
15)
16
17print(df)

This pattern is useful when missing values mean different things in different subgroups.

Preserving Data Types

Type handling is where many quick fixes go wrong. A numeric column with missing values may already have been upcast to floating point because classic NumPy integer arrays do not support NaN.

If you want nullable integer behavior, use pandas extension dtypes:

python
1import pandas as pd
2
3s = pd.Series([1, None, 3], dtype="Int64")
4s = s.fillna(0)
5print(s)
6print(s.dtype)

Output:

python
10    1
21    0
32    3
4dtype: Int64

That is better than accidentally converting an integer column into generic object data or losing the intended dtype during cleanup.

Fill One Column Without Touching the Whole DataFrame

It is common to see code like:

python
df = df.fillna(0)

That fills every column, which may be too broad. If only one column should change, keep the operation scoped:

python
df["score"] = df["score"].fillna(0)

This is safer and easier to review.

When You Should Not Fill Missing Values

Not every NaN should be replaced. Sometimes the missingness itself carries information. For example:

  • a missing shipped date may mean the order is still pending
  • a missing lab result may mean the test was not performed
  • a missing category may signal an upstream data issue

In those cases, blindly filling values can hide a real modeling or business problem. Good imputation starts with understanding why the value is missing.

Common Pitfalls

The most common mistake is filling an entire DataFrame with one value when only one column should be changed. That often corrupts columns that need different handling.

Another issue is using forward fill on unsorted or unrelated rows. ffill() depends on row order, so it only makes sense when the order reflects real sequence meaning.

Developers also often ignore dtype changes. Filling missing numeric values in a nullable integer column can produce a different dtype if you are not careful.

Finally, do not pick an imputation rule just because it is easy to code. Mean, median, constant values, and carry-forward all imply different assumptions about the data.

Summary

  • Use fillna for direct replacement of missing values in a pandas column.
  • For numeric columns, median or mean fills are often better than arbitrary constants.
  • Use ffill or bfill only when row order is meaningful.
  • Scope the operation to the target column instead of filling the entire DataFrame by accident.
  • Check the resulting dtype and make sure the replacement strategy matches the meaning of the missing data.

Course illustration
Course illustration

All Rights Reserved.