Removing Missing Values in Pandas – When and How to Use dropna() 2026

Data Manipulation Mar 18, 2026

Removing Missing Values in Pandas – When and How to Use dropna() 2026

Removing missing values using dropna() is one of the simplest and fastest ways to clean your dataset. While not always the best strategy, it is often appropriate when missing values are few or when complete cases are required for analysis.

TL;DR — dropna() Parameters

  • axis=0 → Drop rows (default)
  • axis=1 → Drop columns
  • how="any" → Drop if any value is missing (default)
  • how="all" → Drop only if all values are missing
  • thresh=n → Keep rows with at least n non-missing values

1. Basic Usage of dropna()

import pandas as pd

# Create a small demo file so this example runs as-is
# (in your project, sales_data.csv is your real export)
import numpy as np
rng = np.random.default_rng(42)
n = 60
pd.DataFrame({
    "order_date": pd.date_range("2026-01-01", periods=n, freq="3D"),
    "customer_id": rng.integers(101, 121, n),
    "customer_name": rng.choice([" ana", "ben ", "chen", "dana"], n),
    "product_id": rng.integers(1, 9, n),
    "region": rng.choice(["North", "South", "East", "West"], n),
    "category": rng.choice(["Electronics", "Computers", "Books", "Home"], n),
    "payment_method": rng.choice(["card", "cash", "wallet"], n),
    "segment": rng.choice(["consumer", "business"], n),
    "quantity": rng.integers(1, 6, n),
    "amount": rng.uniform(50, 2500, n).round(2),
    "discount": rng.choice([0.0, 0.05, 0.1, 0.2], n),
}).assign(
    price=lambda d: (d["amount"] / d["quantity"]).round(2),
    profit=lambda d: (d["amount"] * rng.uniform(0.05, 0.3, n)).round(2),
).assign(
    ship_date=lambda d: d["order_date"] + pd.Timedelta(days=2),
    status=lambda d: np.where(d["amount"] > 2000, "priority", "standard"),
).to_csv("sales_data.csv", index=False)

df = pd.read_csv("sales_data.csv", parse_dates=["order_date"])

print(f"Original shape: {df.shape}")

# Drop rows with any missing values
df_clean = df.dropna()

print(f"After dropna(): {df_clean.shape}")
print(f"Rows removed: {len(df) - len(df_clean)}")

2. Common dropna() Strategies

# 1. Drop rows only if ALL values are missing
df_clean = df.dropna(how="all")

# 2. Drop columns that have too many missing values
df_clean = df.dropna(thresh=len(df)*0.7, axis=1)   # Keep columns with at least 70% non-missing

# 3. Drop rows based on specific columns only
df_clean = df.dropna(subset=["amount", "region", "customer_id"])

# 4. Drop rows with missing values in critical columns
critical_cols = ["order_date", "amount", "customer_id"]
df_clean = df.dropna(subset=critical_cols)

3. Real-World Example

# Before cleaning
print("Missing values before:")
print(df.isna().sum()[df.isna().sum() > 0])

# Clean strategy: Keep only complete records for key business columns
df_clean = df.dropna(subset=["order_date", "amount", "region", "customer_id"])

print(f"\nRows before: {len(df)}")
print(f"Rows after: {len(df_clean)}")
print(f"Percentage kept: {(len(df_clean)/len(df)*100):.1f}%")

4. Best Practices in 2026

  • Always check how many rows/columns will be removed before dropping
  • Use subset to drop based only on important business columns
  • Use thresh to keep columns that are mostly complete
  • Consider imputation instead of dropping when missingness is high (>10-20%)
  • Document your dropping strategy and the percentage of data lost
  • Never drop rows blindly without understanding the business impact

When dropna Is the Right Choice

dropna() removes rows, or columns with axis=1, that contain missing values. It is a good choice when gaps are few, random, and in rows you can afford to lose, and a poor one when it would remove a large or biased part of the data.

Its options give precise control: subset= limits the check to important columns, how="all" drops only fully empty rows, and thresh= keeps rows with at least a given number of real values.

Worked Example: Precise Dropping

Each option is applied to the same table so the row counts can be compared.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "id": [1, 2, 3, 4, 5],
    "price": [10.0, np.nan, 12.0, np.nan, 9.0],
    "note": [None, "ok", None, None, "late"],
})

print(len(df.dropna()))
print(len(df.dropna(subset=["price"])))
print(len(df.dropna(thresh=2)))
print(df.dropna(axis=1).columns.tolist())
print(f"kept {len(df.dropna(subset=['price'])) / len(df):.0%} of rows")

A plain dropna() keeps only one row because the note column is often empty. Limiting the check to price keeps three rows, and printing the kept share makes the cost of dropping visible.

Common Mistakes

  • Dropping on all columns when only a few matter.
  • Losing a large share of rows without noticing.
  • Dropping columns with axis=1 by accident.
  • Dropping data that is missing for a systematic reason.

Quick Summary

  • dropna() removes incomplete rows or columns.
  • subset= focuses on important columns.
  • how= and thresh= control strictness.
  • Always report how many rows were dropped.

Conclusion

Removing missing values with dropna() is fast and simple, but it should be used thoughtfully. In 2026, the best practice is to first understand the pattern of missingness, then decide whether to drop rows, drop columns, or use imputation. Use subset and thresh parameters to make your dropping strategy more intelligent and business-aligned.

Next steps:

  • Analyze the missing values in your current dataset and decide which strategy (drop rows, drop columns, or impute) is most appropriate

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices