Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Pandas›Handling Missing Data — isnull, dropna, fillna

Data Cleaning

Handling Missing Data — isnull, dropna, fillna

Handling missing data is the process of identifying and remediating gaps in your datasets to ensure accurate downstream analysis. Real-world data is rarely complete, and failing to address null values will cause your calculations to produce incorrect results or unexpected errors. You should reach for these tools immediately after loading your data to understand the quality and reliability of your features before proceeding to modeling or visualization.

Detecting Missing Values with isnull and isna

The foundational step in cleaning data is identifying where the gaps exist. In Pandas, missing data is typically represented by 'NaN' (Not a Number) or 'None' for object types. The methods isnull() and isna() are functionally identical, returning a boolean mask of the same shape as your input structure. By understanding that these methods evaluate each element individually, you can reason about how they behave: true represents a missing value, while false represents a populated cell. Beyond simple identification, wrapping these in sum() functions allows you to quantify missingness across columns efficiently. Because these tools return a boolean series or dataframe, they serve as the predicate for advanced filtering. Identifying nulls is not merely about finding errors; it is about diagnosing the structural health of your dataframe and deciding whether specific rows or columns are salvageable or must be discarded entirely from your analysis pipelines.

import pandas as pd
import numpy as np

# Create a sample DataFrame with missing values
df = pd.DataFrame({'A': [1, 2, np.nan], 'B': [5, np.nan, np.nan]})

# Check for missing values (returns boolean mask)
mask = df.isnull()
print(f"Boolean mask:\n{mask}")

# Calculate total missing values per column
missing_counts = df.isnull().sum()
print(f"\nMissing counts per column:\n{missing_counts}")

Removing Data with dropna

When data points are insufficient or inherently untrustworthy due to missing values, the most robust strategy is often removal. The dropna method provides a flexible interface for dropping either rows or columns. By adjusting the 'axis' parameter, you toggle between dropping records (axis=0) or variables (axis=1). The 'how' parameter adds logical nuance: setting it to 'any' drops an entry if even one value is missing, while 'all' only drops it if every value is empty. This is crucial because it allows you to define your threshold for data quality. If a row must be complete to be useful, 'how=any' is your default choice. Conversely, if you are performing a column-wise analysis and cannot tolerate missing entries, 'how=any' on axis 1 ensures consistent vector operations later. This method effectively transforms your dataset's shape, so always keep track of how many rows remain after filtering to avoid accidentally discarding too much of your sample size.

df = pd.DataFrame({'Sales': [100, np.nan, 300], 'Cost': [50, 60, np.nan]})

# Drop rows where any column is missing
cleaned_df = df.dropna(axis=0, how='any')
print(f"Dropped rows:\n{cleaned_df}")

# Drop columns where all values are missing
df_all_missing = pd.DataFrame({'A': [np.nan, np.nan], 'B': [1, 2]})
print(f"Dropped columns:\n{df_all_missing.dropna(axis=1, how='all')}")

Imputing Values with fillna

Sometimes removing data is not viable because you need to maintain the sample size for statistical power or specific time-series alignment. In these scenarios, imputation via fillna becomes the standard approach. You replace missing values with a placeholder, such as a zero, a mean value, or a forward-filled observation. Why does this matter? By filling values, you preserve the continuity of your data, but you must be careful; imputing with the mean, for example, can artificially reduce the variance of your dataset. It is essential to reason about the domain—if a missing value in a 'Sales' column represents a day where no transactions occurred, filling with zero is logical. If it represents a missing sensor reading, maybe interpolation or forward filling is safer. Understanding the impact of the chosen fill value ensures that you do not inadvertently skew your results or create false signals where there was only silence in the original collection process.

df = pd.DataFrame({'Score': [85, np.nan, 90, np.nan]})

# Fill missing scores with the mean of existing scores
mean_score = df['Score'].mean()
df['Score'] = df['Score'].fillna(mean_score)
print(f"Imputed data:\n{df}")

# Forward fill missing values in time-series data
time_series = pd.Series([10, np.nan, np.nan, 20])
print(f"Forward fill:\n{time_series.ffill()}")

Advanced Filling with Methods

Beyond constant value imputation, Pandas provides sophisticated filling mechanisms specifically for sequential or ordered data. The 'method' parameter—specifically 'ffill' (forward fill) and 'bfill' (backward fill)—leverages the implicit order of the index. A forward fill propagates the last valid observation forward to the next valid one, which is invaluable in time-series analysis where the most recent recorded price or state is often the best estimate for the current moment. Conversely, a backward fill propagates the next known value into the past. Choosing between these methods depends entirely on the nature of your data; if a measurement is persistent over time, forward filling is almost always the correct intuition. By using these methods, you avoid the manual calculation of averages and allow the temporal structure of your dataframe to dictate the most logical replacement values for the missing gaps you encountered during your initial inspection.

df_ts = pd.DataFrame({'Price': [100, np.nan, np.nan, 105]})

# Forward fill propagates the previous value
df_ts['ffill'] = df_ts['Price'].fillna(method='ffill')

# Backward fill propagates the subsequent value
df_ts['bfill'] = df_ts['Price'].fillna(method='bfill')
print(f"Temporal filling results:\n{df_ts}")

Combining Filters for Complex Cleaning

In sophisticated pipelines, you rarely rely on one method alone. Often, you will combine these techniques: first, you use isnull() to audit the dataset, then drop rows that are too sparsely populated to be useful, and finally use fillna() to handle the remaining minor gaps. This layered approach is the hallmark of a resilient data preparation pipeline. You might, for example, calculate a threshold of 70% non-null values before deciding to drop a column entirely. By chaining these operations, you create a declarative script that documents your data cleaning logic. Remember that any change to the structure of your dataframe, especially dropping rows, has downstream effects on your indexes. When in doubt, always visualize or inspect the null distribution before and after your transformations to ensure that the cleanup process has improved the data quality without introducing accidental biases or stripping away critical information required for your final model or analytical conclusion.

df = pd.DataFrame({'A': [1, np.nan, 3], 'B': [np.nan, 5, 6], 'C': [7, 8, 9]})

# Threshold-based cleaning: keep columns with at least 2 non-null values
cleaned = df.dropna(thresh=2, axis=1)

# Then fill remaining missing values with column median
final = cleaned.fillna(cleaned.median())
print(f"Final cleaned dataset:\n{final}")

Key points

  • Null values are identified using isnull() or isna(), which return a boolean mask of the dataset.
  • The sum() method is the most efficient way to quantify the extent of missing data per column.
  • The dropna() method allows for flexible row or column removal based on the existence of missing values.
  • Using the 'how' parameter in dropna() lets you specify whether to remove data based on any or all missing values.
  • Imputation with fillna() is essential when preserving data volume is prioritized over using only observed values.
  • Constant value imputation, such as using the mean or zero, requires careful consideration of domain-specific context.
  • Sequential filling methods like ffill and bfill are specifically designed for time-series or ordered data.
  • Chaining cleaning operations provides a clear, documented, and reproducible pipeline for handling real-world datasets.

Common mistakes

  • Mistake: Expecting dropna() to modify the DataFrame in place. Why it's wrong: By default, Pandas returns a new copy and does not change the original object. Fix: Use the inplace=True parameter or reassign the result to the variable.
  • Mistake: Using fillna() with a scalar value on a non-numeric column. Why it's wrong: Trying to fill missing strings with a float or vice versa can lead to unintended type casting (e.g., converting a series of strings to objects/floats). Fix: Ensure the fill value matches the dtype of the target column.
  • Mistake: Forgetting that isnull() returns a boolean mask. Why it's wrong: Users often try to perform arithmetic on the result of isnull() directly. Fix: Use .sum() or .value_counts() on the boolean mask to get counts, or use boolean indexing to filter data.
  • Mistake: Misinterpreting 'any' vs 'all' in dropna(). Why it's wrong: Using 'any' (default) drops rows with even one missing value, which is often too aggressive for large datasets. Fix: Use how='all' if you only want to drop rows where every single column is NaN.
  • Mistake: Assuming fillna() handles 'None' and 'NaN' identically. Why it's wrong: While they both represent missing data, specific methods might interact differently with custom object types. Fix: Use info() or isna().sum() to confirm the nature of missing values before applying mass imputation.

Interview questions

How can you identify missing data within a Pandas DataFrame?

To identify missing data, you use the isnull() method, which returns a Boolean mask of the same shape as your DataFrame, where True indicates a missing value like NaN or None. For a quick summary, chaining isnull().sum() is the standard approach; it calculates the total count of null values per column. This is essential for understanding data quality, as it helps determine if a column has enough data to be useful or if it requires significant cleaning.

What is the primary function of dropna() and how do you customize its behavior?

The dropna() function is used to remove missing values from a DataFrame or Series. By default, it drops any row that contains at least one NaN value. You can customize this by using the 'axis' parameter to drop columns instead of rows, or the 'how' parameter, where setting it to 'all' only drops records where every single value is missing. This is a destructive operation, so it is often used when a row lacks critical information that cannot be safely imputed.

Explain how fillna() works and why it is often preferred over simply dropping rows.

The fillna() method allows you to replace NaN values with specific substitutes, such as a constant value, the mean, the median, or even the forward-fill/backward-fill methods. We often prefer fillna() over dropna() because dropping rows can lead to a significant loss of information or introduce bias into the dataset. By imputing data, we preserve the sample size, which is generally better for statistical models unless the amount of missing data is prohibitively large.

Compare the 'forward fill' (ffill) and 'backward fill' (bfill) methods in fillna(). When would you use one over the other?

Forward fill (method='ffill') propagates the last valid observation forward to next valid, while backward fill (method='bfill') uses the next valid observation to fill gaps. You would use ffill for time-series data where the current state is likely to persist until a new event occurs. Conversely, bfill is useful when the future state is expected to represent the current state, such as in reverse-chronological datasets or specific technical signals where upcoming changes are known.

How does setting a threshold in the dropna() function help in cleaning a dataset?

The 'thresh' parameter in dropna() allows you to define the minimum number of non-NA values required to keep a row or column. For example, if you set thresh=5, Pandas will drop any row that has fewer than 5 non-null entries. This is an advanced technique for cleaning because it allows you to retain data that is 'mostly complete' while systematically removing entries that are effectively empty, offering a much finer level of control than the standard dropna() behavior.

Discuss the trade-offs between using dropna() and fillna() when preparing data for machine learning models.

The trade-off involves balancing sample size against data accuracy. Using dropna() reduces your dataset size, which can be detrimental if you have limited data points, but it ensures that every value you provide to a model is grounded in observation. Using fillna() maintains sample size, but by injecting imputed values, you risk introducing noise or artificial correlations into your model. You must evaluate if the missing data is missing at random before deciding whether to drop or impute.

All Pandas interview questions →

Check yourself

1. You have a DataFrame where you want to remove any row that contains at least one missing value. Which command is most appropriate?

  • A.df.dropna(how='all')
  • B.df.dropna(how='any')
  • C.df.dropna(inplace=False)
  • D.df.drop_duplicates()
Show answer

B. df.dropna(how='any')
how='any' is the default behavior that drops a row if any column is null. 'all' only drops rows if every column is null. 'inplace=False' is default and doesn't perform the drop on the current variable. 'drop_duplicates' removes duplicate rows, not missing data.

2. What is the result of applying df.isnull().sum() to a DataFrame?

  • A.A count of missing values per column
  • B.A boolean DataFrame indicating missing values
  • C.A total count of missing values in the entire DataFrame
  • D.The indices of all rows with missing values
Show answer

A. A count of missing values per column
isnull() creates a mask of True/False values. .sum() defaults to axis 0 (columns), resulting in the count of missing values per column. The other options describe different operations (boolean mask, aggregation total, or boolean indexing).

3. If you perform 'df.fillna(0)', what happens to the DataFrame?

  • A.All missing values in the entire DataFrame are replaced with 0
  • B.Only the first column's missing values are replaced with 0
  • C.Nothing happens because you did not specify axis=0
  • D.The original DataFrame is updated in place
Show answer

A. All missing values in the entire DataFrame are replaced with 0
Calling fillna(0) on a DataFrame applies the replacement to all missing values across all columns. It does not update the original DataFrame by default (it returns a copy), and it does not require an axis argument to operate globally.

4. Why might you prefer 'df.fillna(method='ffill')' over 'df.fillna(0)'?

  • A.To replace missing data with the most frequent value
  • B.To replace missing data with the previous non-null value
  • C.To fill missing values only in the first row
  • D.To drop all rows that contain missing values
Show answer

B. To replace missing data with the previous non-null value
ffill (forward fill) propagates the last valid observation forward to fill gaps. The other options describe different approaches like statistical imputation, simple constant filling, or row dropping.

5. Which of the following expressions correctly filters a DataFrame 'df' to keep only rows where the column 'price' is not missing?

  • A.df[df['price'].isnull()]
  • B.df.dropna(subset=['price'])
  • C.df.fillna(subset=['price'])
  • D.df[~df['price'].notnull()]
Show answer

B. df.dropna(subset=['price'])
dropna(subset=['price']) specifically targets the 'price' column to remove rows where it is NaN. The isnull() option filters for missing values, not non-missing. fillna doesn't filter data, and the last option uses a double negative that incorrectly selects missing values.

Take the full Pandas quiz →

← PreviousMultiIndex and Hierarchical IndexingNext →Removing Duplicates

Pandas

34 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app