Python Data Analysis Reference

Pandas Cheat Sheet

Quickly find practical Pandas syntax for DataFrames, data cleaning, filtering, grouping, merging, reshaping, importing, and exporting data.

  • Copy-ready Python
  • Practical DataFrame examples
  • Beginner to advanced
  • Printable reference
sales_analysis.py
import pandas as pd

df = pd.read_csv("sales.csv")

summary = (
    df.dropna(subset=["Sales"])
      .groupby("Region")["Sales"]
      .sum()
      .sort_values(ascending=False)
)

print(summary)
Region Total Sales
West 48,250
East 42,180
North 36,940

Start here

How to Use This Pandas Cheat Sheet

Use this reference while cleaning, transforming, combining, and analyzing tabular data with Python. Find the task you need, copy the relevant pattern, and adapt the column names and values to your dataset.

Find a task

Search for a Pandas operation such as filtering rows, filling missing values, grouping data, merging DataFrames, or exporting a file.

Adapt the example

Replace the sample DataFrame, column names, conditions, and output paths with values that match your own project.

Verify the result

Test transformations on a small copy of your data and inspect the result before overwriting files or using it in a production workflow.

Standard Pandas setup

The examples on this page use the conventional pd alias and assume that Pandas is installed in your active Python environment.

pip install pandas

import pandas as pd

This page focuses specifically on Pandas and DataFrame workflows. For variables, loops, functions, collections, and general language syntax, use the complete Python Cheat Sheet .

Current image: Pandas Cheat Sheet showing DataFrame tables, code, filtering, merging, and data analysis charts

Essential Pandas syntax

Pandas Quick Reference

Use these common Pandas patterns to import, inspect, select, clean, summarize, combine, and export tabular data.

Common Pandas commands with descriptions and copy buttons
Task Syntax Purpose Copy
Import Pandas import pandas as pd Import Pandas with its standard alias.
Read a CSV file df = pd.read_csv("file.csv") Load CSV data into a DataFrame.
Preview rows df.head() Display the first five rows.
Select columns df[["name", "sales"]] Return a DataFrame containing selected columns.
Filter rows df.loc[df["sales"] > 1000] Keep rows that satisfy a Boolean condition.
Sort values df.sort_values("sales", ascending=False) Sort rows by sales from highest to lowest.
Remove missing values df.dropna(subset=["sales"]) Remove rows where the sales value is missing.
Group and summarize df.groupby("region", as_index=False)["sales"].sum() Calculate total sales for each region.
Merge DataFrames pd.merge(orders, customers, on="customer_id", how="left") Match customer data to every order using a left join.
Create a pivot table df.pivot_table(index="region", values="sales", aggfunc="sum") Summarize sales by region in a pivot table.
Create a column df.assign(profit=df["sales"] - df["cost"]) Create a profit column without changing the original DataFrame.
Export to CSV df.to_csv("cleaned.csv", index=False) Save the DataFrame without writing its index.
Quick tip

Most Pandas operations return a new object. Assign the result to a variable when you need to keep the transformed DataFrame.

Set up Pandas

Installation & Imports

Install Pandas in an isolated Python environment, import it with the conventional pd alias, and confirm which version your project is using.

Installation commands

Commands for installing and updating Pandas
Method Command Use Copy
Install with pip python -m pip install pandas Install the latest compatible release from PyPI.
Upgrade with pip python -m pip install --upgrade pandas Upgrade Pandas in the active Python environment.
Install with conda conda install -c conda-forge pandas Install Pandas from the conda-forge channel.
Include Excel support python -m pip install "pandas[excel]" Install Pandas with optional dependencies for Excel files.
Python Standard import
import pandas as pd

The alias pd is the standard convention used throughout Pandas documentation and most Python data projects.

Python Check the installed version
import pandas as pd

print(pd.__version__)

Checking the version helps you compare behavior with the correct API documentation and release notes.

Python Inspect the complete environment
import pandas as pd

pd.show_versions()
Best practice

Install Pandas inside a virtual environment. This keeps project dependencies isolated and reduces conflicts between package versions.

Core data structures

Series & DataFrames

A Series stores one labeled sequence of values. A DataFrame combines multiple labeled columns into a two-dimensional table.

Series vs. DataFrame

Differences between a Pandas Series and DataFrame
Feature Series DataFrame
Dimensions One-dimensional Two-dimensional
Labels Index labels Row index and column labels
Data types One dtype for the stored values Each column can use a different dtype
Constructor pd.Series() pd.DataFrame()
Common use One variable, column, or time series Complete tables and structured datasets
Spreadsheet comparison Similar to one labeled column Similar to an entire worksheet table

Create the core structures

Python Create a Series
import pandas as pd

sales = pd.Series(
    [1250, 980, 1430],
    index=["East", "North", "West"],
    name="sales"
)

print(sales)

The values form a one-dimensional array, while the region names form its index.

Python Create a DataFrame
import pandas as pd

sales = pd.DataFrame({
    "region": ["East", "North", "West"],
    "revenue": [1250, 980, 1430],
    "orders": [18, 14, 21]
})

print(sales)

Each dictionary key becomes a column, and Pandas creates a default numbered index.

Useful structure attributes

Attributes for inspecting Pandas Series and DataFrames
Attribute Returns Example Copy
shape Number of rows and columns df.shape
index Row labels df.index
columns DataFrame column labels df.columns
dtypes Data type of each column df.dtypes
size Total number of values df.size
ndim Number of dimensions df.ndim

Automatic index alignment

Python Add Series by matching their labels
import pandas as pd

first = pd.Series([10, 20], index=["A", "B"])
second = pd.Series([1, 2], index=["B", "C"])

result = first + second

print(result)
Expected output
A     NaN
B    21.0
C     NaN
dtype: float64
Why this happens

Pandas aligns values by their index labels before calculating. Only label B exists in both Series, so the unmatched labels produce missing values.

Build tabular data

Creating DataFrames

Create DataFrames from dictionaries, records, tuples, Series, or existing Pandas objects. Choose the structure that most closely matches your source data.

Common DataFrame constructors

Common methods for creating Pandas DataFrames
Source Syntax Best for Copy
Dictionary of lists pd.DataFrame({"name": ["Ava", "Leo"], "sales": [1200, 980]}) Column-oriented data already stored in Python lists.
List of dictionaries pd.DataFrame([{"name": "Ava", "sales": 1200}, {"name": "Leo", "sales": 980}]) Record-oriented data such as API or JSON responses.
List of tuples pd.DataFrame([("Ava", 1200), ("Leo", 980)], columns=["name", "sales"]) Compact row data with separately supplied column names.
Dictionary of Series pd.DataFrame({"sales": pd.Series({"East": 1200, "West": 980})}) Columns that already have meaningful index labels.
Existing DataFrame new_df = source_df.copy() Creating an independent DataFrame for further work.

Control the index and column order

Python Add a custom index
import pandas as pd

data = {
    "region": ["East", "West"],
    "sales": [1250, 1430]
}

df = pd.DataFrame(
    data,
    index=["order_101", "order_102"]
)

df.index.name = "order_id"

print(df)

A custom index can identify rows with meaningful labels instead of default row numbers.

Python Choose columns and dtypes
import pandas as pd

data = {
    "sales": [1250, 1430],
    "region": ["East", "West"],
    "internal_id": [101, 102]
}

df = pd.DataFrame(
    data,
    columns=["region", "sales"]
)

df = df.astype({
    "region": "str",
    "sales": "int64"
})

print(df.dtypes)

The columns argument controls selection and order, while astype() applies explicit data types.

Create a DataFrame from uneven records

Python Missing keys become missing values
import pandas as pd

records = [
    {"product": "Laptop", "sales": 1800},
    {"product": "Monitor"},
    {"product": "Keyboard", "sales": 420}
]

df = pd.DataFrame(records)

print(df)
Expected output
    product   sales
0    Laptop  1800.0
1   Monitor     NaN
2  Keyboard   420.0
Missing keys

When records do not contain identical keys, Pandas creates the complete set of columns and fills unavailable values with a missing-value marker.

Common error

Lists within a dictionary must have matching lengths. Otherwise, pd.DataFrame() raises a ValueError.

Load external data

Importing Data

Load data from text files, spreadsheets, JSON documents, Parquet files, databases, and HTML tables. Specify columns and data types during import when practical.

Common Pandas readers

Pandas functions for importing common data formats
Format Syntax Result Copy
CSV df = pd.read_csv("sales.csv") Loads a comma-separated file into a DataFrame.
Excel df = pd.read_excel("sales.xlsx", sheet_name="Orders") Loads a selected worksheet into a DataFrame.
JSON df = pd.read_json("records.json") Converts compatible JSON data into a Pandas object.
Parquet df = pd.read_parquet("sales.parquet") Loads a column-oriented Parquet dataset.
SQL df = pd.read_sql_query("SELECT * FROM sales", connection) Loads the result of a database query into a DataFrame.
HTML tables tables = pd.read_html("https://example.com/report") Returns a list containing each detected HTML table.

Control the import

Python Import selected CSV data
import pandas as pd

df = pd.read_csv(
    "sales.csv",
    usecols=[
        "order_id",
        "region",
        "sales",
        "order_date"
    ],
    dtype={
        "order_id": "int64",
        "region": "str",
        "sales": "float64"
    },
    parse_dates=["order_date"],
    na_values=["", "N/A", "unknown"]
)

Import only required columns and define important data types while reading the file.

Python Import an Excel worksheet
import pandas as pd

df = pd.read_excel(
    "sales.xlsx",
    sheet_name="Orders",
    usecols="A:E",
    header=0,
    parse_dates=["order_date"]
)

print(df.head())

Select the worksheet, column range, header row, and date columns during import.

Read a large CSV in chunks

Python Process part of the file at a time
import pandas as pd

matching_rows = []

for chunk in pd.read_csv(
    "large_sales.csv",
    chunksize=100_000
):
    filtered = chunk.loc[
        chunk["sales"] > 1000
    ]

    matching_rows.append(filtered)

result = pd.concat(
    matching_rows,
    ignore_index=True
)

print(result.head())
Large files

Use usecols, explicit data types, filters supported by the storage format, or chunksize to reduce unnecessary memory usage.

Import from a SQL database

Python · SQLite Use query parameters
import pandas as pd
from sqlite3 import connect

query = """
SELECT order_id, region, sales
FROM sales
WHERE region = ?
"""

with connect("sales.db") as connection:
    df = pd.read_sql_query(
        query,
        connection,
        params=("East",)
    )

print(df.head())
SQL safety

Pandas does not sanitize SQL statements. Use the parameter system supported by your database driver instead of inserting untrusted values directly into a query string.

Optional dependencies

Excel, Parquet, HTML, and database integrations may require additional packages. Install only the dependencies required by your workflow.

Understand the dataset

Inspecting Data

Examine rows, columns, data types, missing values, descriptive statistics, category frequencies, and memory usage before changing the dataset.

Essential inspection methods

Common methods for inspecting a Pandas DataFrame
Task Syntax What it shows Copy
First rows df.head(5) The first five rows by default.
Last rows df.tail(5) The final five rows by default.
Random sample df.sample(5, random_state=42) A reproducible random selection of rows.
Structure summary df.info(memory_usage="deep") Columns, non-null counts, dtypes, and memory usage.
Descriptive statistics df.describe(include="all") Summary statistics for numeric and non-numeric columns.
Missing values df.isna().sum() The number of missing values in each column.
Distinct values df.nunique(dropna=True) The number of distinct non-missing values per column.
Value frequencies df["region"].value_counts(dropna=False) How often each value, including missing values, occurs.
Memory by column df.memory_usage(deep=True) Estimated memory consumption for each column.

Run an initial inspection

Python Preview and summarize
print(df.head())
print(df.tail())
print(df.sample(5, random_state=42))

df.info(memory_usage="deep")

summary = df.describe(
    include="all"
)

print(summary)

Use several views because the beginning or end of a file may not represent the entire dataset.

Python Inspect category frequencies
region_counts = (
    df["region"]
    .value_counts(
        dropna=False,
        normalize=False
    )
)

region_percent = (
    df["region"]
    .value_counts(
        dropna=False,
        normalize=True
    )
    .mul(100)
    .round(1)
)

print(region_counts)
print(region_percent)

Frequency checks reveal dominant categories, unexpected labels, and missing values.

Build a column-quality report

Python Summarize every column
import pandas as pd

quality_report = pd.DataFrame({
    "dtype": df.dtypes.astype(str),
    "missing": df.isna().sum(),
    "missing_percent": (
        df.isna()
        .mean()
        .mul(100)
        .round(1)
    ),
    "unique_values": df.nunique(
        dropna=True
    )
})

quality_report = quality_report.sort_values(
    "missing",
    ascending=False
)

print(quality_report)
Useful workflow

Inspect the DataFrame immediately after import and again after major cleaning or transformation steps. This makes unexpected changes easier to detect.

Missing-value detail

Empty strings are not automatically considered missing values by isna(). Normalize blank text separately or define it with na_values during import.

Select subsets precisely

Selecting Rows & Columns

Select columns directly, use loc for label-based selection, use iloc for position-based selection, and use at or iat for individual values.

Selection quick reference

Common methods for selecting Pandas rows and columns
Selection Syntax Result Copy
One column df["sales"] Returns the selected column as a Series.
Multiple columns df[["region", "sales"]] Returns the selected columns as a DataFrame.
Row by label df.loc["order_102"] Returns the row matching the index label.
Labels and columns df.loc[["order_101", "order_103"], ["region", "sales"]] Selects specified row labels and column labels.
Row by position df.iloc[0] Returns the first row by integer position.
Position slice df.iloc[0:3, 1:3] Selects the first three rows and columns 1–2.
Scalar by labels df.at["order_102", "sales"] Returns one value using row and column labels.
Scalar by position df.iat[1, 2] Returns one value using row and column positions.
Return type

df["sales"] returns a Series. Use double brackets, df[["sales"]], when you need a one-column DataFrame.

Example DataFrame

Python Create labeled sales data
import pandas as pd

df = pd.DataFrame(
    {
        "product": [
            "Laptop",
            "Monitor",
            "Keyboard",
            "Mouse"
        ],
        "region": [
            "East",
            "West",
            "North",
            "East"
        ],
        "sales": [
            1800,
            1260,
            420,
            180
        ],
        "orders": [
            12,
            18,
            25,
            30
        ]
    },
    index=[
        "order_101",
        "order_102",
        "order_103",
        "order_104"
    ]
)

print(df)

Label selection with loc

Python · loc Select labeled rows and columns
selection = df.loc[
    ["order_101", "order_103"],
    ["product", "region", "sales"]
]

print(selection)

Both lists contain labels: index labels for the rows and names for the columns.

Python · loc Slice between labels
selection = df.loc[
    "order_101":"order_103",
    "region":"sales"
]

print(selection)

Label-based slices include both the starting and ending labels when those labels exist.

Position selection with iloc

Python · iloc Select by position
selection = df.iloc[
    [0, 2],
    [0, 2]
]

print(selection)

This selects rows 0 and 2 together with columns 0 and 2.

Python · iloc Slice rows and columns
selection = df.iloc[
    0:3,
    1:3
]

print(selection)

Position 3 is excluded, following standard Python slice behavior.

Common mistake

An integer passed to loc is treated as an index label, not as a row position. Use iloc when the number represents a position.

Keep matching records

Filtering Data

Build Boolean conditions to keep rows that match numeric ranges, categories, text patterns, missing-value rules, or several criteria at once.

Filtering quick reference

Common Pandas filtering patterns
Filter Syntax Keeps Copy
Comparison df.loc[df["sales"] > 1000] Rows where sales exceed 1,000.
AND conditions df.loc[(df["sales"] > 500) & (df["region"] == "East")] Rows matching both conditions.
OR conditions df.loc[(df["region"] == "East") | (df["region"] == "West")] Rows matching either condition.
Exclude matches df.loc[~df["region"].isin(["North", "South"])] Rows outside the supplied category list.
List membership df.loc[df["region"].isin(["East", "West"])] Rows whose region appears in the list.
Numeric range df.loc[df["sales"].between(500, 1500)] Rows between 500 and 1,500, including both boundaries.
Text contains df.loc[df["product"].str.contains("pro", case=False, na=False, regex=False)] Rows containing the literal text, regardless of case.
Non-missing values df.loc[df["sales"].notna()] Rows where sales has a usable value.
Query expression df.query("sales > 1000 and region == 'East'") Rows matching a readable query expression.
Boolean operators

Use &, |, and ~ for Pandas conditions. Do not use Python’s and, or, or not with Boolean Series.

Combine several conditions

Python Build a reusable Boolean mask
minimum_sales = 500
allowed_regions = ["East", "West"]

mask = (
    df["sales"].ge(minimum_sales)
    & df["region"].isin(allowed_regions)
    & df["active"].eq(True)
)

result = df.loc[
    mask,
    [
        "product",
        "region",
        "sales"
    ]
]

print(result)

Naming the mask makes complex filtering logic easier to read, test, and reuse.

Python Filter text safely
search_term = "pro"

mask = df["product"].str.contains(
    search_term,
    case=False,
    na=False,
    regex=False
)

result = df.loc[
    mask,
    ["product", "sales"]
]

print(result)

Set regex=False when the search term should be treated as literal text rather than a regular expression.

Filter with query()

Python · query Reference local variables with @
minimum_sales = 500
allowed_regions = ["East", "West"]

result = df.query(
    "sales >= @minimum_sales "
    "and region in @allowed_regions"
)

print(result)
query() variables

Prefix a Python variable with @ inside a query() expression. Use only trusted expressions rather than constructing query strings from untrusted input.

filter() means something else

DataFrame.filter() selects labels such as column names. It does not filter rows according to their cell values.

Organize labels and rows

Sorting & Renaming

Sort records by one or more columns, control the position of missing values, organize index labels, and rename inconsistent rows or columns.

Sorting and renaming quick reference

Common Pandas sorting and label-renaming patterns
Task Syntax Result Copy
Ascending values df.sort_values("sales") Sorts sales from lowest to highest.
Descending values df.sort_values("sales", ascending=False) Sorts sales from highest to lowest.
Multiple columns df.sort_values(["region", "sales"], ascending=[True, False]) Sorts regions A–Z, then sales high to low.
Missing values first df.sort_values("sales", na_position="first") Moves missing sales values to the beginning.
Sort index df.sort_index(ascending=True) Sorts rows according to their index labels.
Top values df.nlargest(5, "sales") Returns the five rows with the largest sales values.
Rename columns df.rename(columns={"Sales Total": "sales", "Sales Region": "region"}) Renames only the supplied column labels.
Rename index labels df.rename(index={"old_id": "new_id"}) Replaces selected index labels.
Reset index df.reset_index(drop=True) Creates a new default numbered index.

Sort with several rules

Python Multi-column sorting
sorted_df = df.sort_values(
    by=[
        "region",
        "sales",
        "order_date"
    ],
    ascending=[
        True,
        False,
        True
    ],
    na_position="last",
    kind="stable"
)

print(sorted_df)

Each value in ascending corresponds to the column in the same position inside by.

Python Keep a clean numbered index
sorted_df = (
    df.sort_values(
        "sales",
        ascending=False
    )
    .reset_index(drop=True)
)

print(sorted_df)

Resetting the index after sorting creates consecutive row labels without retaining the previous index as a column.

Clean inconsistent column names

Python Normalize every column label
df = df.rename(
    columns=lambda name: (
        str(name)
        .strip()
        .lower()
        .replace(" ", "_")
        .replace("-", "_")
    )
)

print(df.columns)
Consistent labels

Clean column names immediately after import. Consistent lowercase labels without surrounding spaces make later selections, joins, and formulas easier to maintain.

Set and restore an index

Python Use a column as the index
indexed_df = (
    df.set_index(
        "order_id",
        verify_integrity=True
    )
    .sort_index()
)

print(indexed_df)

verify_integrity=True raises an error when the proposed index contains duplicate values.

Python Restore the index as a column
restored_df = indexed_df.reset_index()

print(restored_df)

Without drop=True, the existing index becomes a regular DataFrame column.

Preserve the original

Sorting and renaming methods return a new object by default. Assign the result to a variable when you want to keep the change.

Detect and handle gaps

Missing Values

Identify missing observations, remove incomplete records when necessary, or fill values using rules that preserve the meaning of the data.

Missing-data quick reference

Common Pandas methods for detecting and handling missing data
Task Syntax Result Copy
Detect missing values df.isna() Returns a Boolean mask with the same shape as the DataFrame.
Count by column df.isna().sum() Counts missing values in every column.
Keep complete values df.loc[df["sales"].notna()] Keeps rows where sales is not missing.
Drop incomplete rows df.dropna(subset=["order_id", "sales"]) Drops rows missing either critical value.
Drop empty columns df.dropna(axis="columns", how="all") Drops columns containing no usable values.
Require enough values df.dropna(thresh=3) Keeps rows containing at least three non-missing values.
Fill one value df["discount"].fillna(0) Replaces missing discounts with zero.
Fill by column df.fillna({"region": "Unknown", "discount": 0}) Uses a different replacement for each supplied column.
Forward fill df["price"].ffill(limit=1) Propagates the previous valid price across one missing row.
Backward fill df["price"].bfill(limit=1) Uses the next valid price for one preceding gap.
Interpolate df["temperature"].interpolate(method="linear") Estimates numeric gaps between known observations.

Normalize missing-value markers

Python Convert inconsistent markers to pd.NA
import pandas as pd

text_columns = df.select_dtypes(
    include=["string", "object"]
).columns

df[text_columns] = df[text_columns].replace(
    r"^\s*$",
    pd.NA,
    regex=True
)

df = df.replace(
    ["N/A", "NA", "unknown", "-"],
    pd.NA
)

print(df.isna().sum())
Blank text

Empty strings and whitespace are not always recognized as missing automatically. Normalize them before calculating missing-value totals.

Fill according to column meaning

Python Use explicit business defaults
clean_df = df.fillna({
    "region": "Unknown",
    "discount": 0,
    "active": False
})

median_sales = clean_df[
    "sales"
].median()

clean_df["sales"] = clean_df[
    "sales"
].fillna(median_sales)

Use a replacement that matches the meaning and data type of each column.

Python Drop rows missing critical fields
required_columns = [
    "order_id",
    "product",
    "sales"
]

clean_df = df.dropna(
    subset=required_columns
).reset_index(drop=True)

print(clean_df)

Drop a row only when the missing fields are essential to the intended analysis.

Fill ordered observations

Python Forward fill within each product
df = df.sort_values([
    "product",
    "order_date"
])

df["price"] = (
    df.groupby("product")["price"]
    .ffill(limit=1)
)

Sort the data first and fill within the correct group to avoid carrying a value into an unrelated product.

Python Interpolate internal numeric gaps
df["temperature"] = (
    df["temperature"]
    .interpolate(
        method="linear",
        limit=2,
        limit_area="inside"
    )
)

This fills up to two internal gaps without extrapolating beyond the known observations.

Avoid blind filling

Do not replace every missing value with zero. Zero, unknown, not applicable, and not yet recorded can represent different states and may change totals, averages, or model results.

Standardize and validate

Cleaning Data

Convert columns to appropriate data types, identify values that fail conversion, standardize inconsistent categories, validate acceptable ranges, and remove fields that are not needed.

Data-cleaning quick reference

Common Pandas methods for cleaning and converting data
Task Syntax Result Copy
Infer nullable dtypes df = df.convert_dtypes() Converts columns to suitable nullable Pandas data types.
Set explicit dtypes df = df.astype({"region": "string", "active": "boolean"}) Applies specified data types to selected columns.
Convert numbers df["sales"] = pd.to_numeric(df["sales"], errors="coerce") Converts invalid numeric text to a missing value.
Convert dates df["order_date"] = pd.to_datetime(df["order_date"], errors="coerce") Converts invalid dates to NaT.
Replace categories df["region"] = df["region"].replace({"E": "East", "W": "West"}) Standardizes known category variants.
Limit numeric values df["discount"] = df["discount"].clip(lower=0, upper=1) Restricts values to the inclusive range 0–1.
Remove columns df = df.drop(columns=["temporary_note"], errors="ignore") Removes the column if it exists.
Select numeric columns numeric_df = df.select_dtypes(include="number") Returns only numeric columns for validation or analysis.

Convert values and preserve errors

Python Validate numeric conversion
raw_sales = df["sales"].copy()

df["sales"] = pd.to_numeric(
    df["sales"],
    errors="coerce"
)

invalid_sales = (
    raw_sales.notna()
    & df["sales"].isna()
)

invalid_rows = df.loc[
    invalid_sales
]

print(invalid_rows)

Preserve the original Series so you can identify values that became missing during conversion.

Python Validate date conversion
raw_dates = df[
    "order_date"
].copy()

df["order_date"] = pd.to_datetime(
    df["order_date"],
    format="%Y-%m-%d",
    errors="coerce"
)

invalid_dates = (
    raw_dates.notna()
    & df["order_date"].isna()
)

print(df.loc[invalid_dates])

Supplying a known format makes the expected date structure explicit.

Conversion behavior

Use errors="raise" when invalid input should stop the workflow. Use errors="coerce" when invalid values should become missing and be reviewed separately.

Standardize category values

Python Map known variants to canonical labels
region_mapping = {
    "E": "East",
    "east": "East",
    "EAST": "East",
    "W": "West",
    "west": "West",
    "WEST": "West",
    "N": "North",
    "north": "North",
    "NORTH": "North"
}

df["region"] = (
    df["region"]
    .replace(region_mapping)
    .astype("string")
)

valid_regions = [
    "East",
    "West",
    "North",
    "South"
]

invalid_regions = df.loc[
    df["region"].notna()
    & ~df["region"].isin(valid_regions)
]

print(invalid_regions)

Validate numeric ranges

Python Find out-of-range values
invalid_discount = (
    df["discount"].notna()
    & ~df["discount"].between(
        0,
        1,
        inclusive="both"
    )
)

invalid_rows = df.loc[
    invalid_discount,
    ["order_id", "discount"]
]

print(invalid_rows)

Review invalid values before deciding whether to correct, remove, or cap them.

Python Cap confirmed boundary errors
df["discount"] = (
    df["discount"]
    .clip(
        lower=0,
        upper=1
    )
)

Use clipping only when the valid boundaries are known and values outside them are confirmed errors.

Recheck the cleaned dataset

Python Run post-cleaning checks
df = df.convert_dtypes()

print(df.head())
print(df.dtypes)
print(df.isna().sum())
print(df.describe(include="all"))
Keep an audit trail

Avoid silently changing invalid source values. Record which rows failed validation and which cleaning rule was applied, especially in recurring or production workflows.

Clean and transform text

String Operations

Normalize capitalization and whitespace, search text, replace substrings, split values into columns, extract structured patterns, and validate text formats.

String-method quick reference

Common Pandas string methods and examples
Task Syntax Result Copy
String dtype df["name"] = df["name"].astype("string") Converts the column to Pandas string dtype.
Trim whitespace df["name"] = df["name"].str.strip() Removes whitespace from both ends.
Lowercase df["email"] = df["email"].str.lower() Converts each email address to lowercase.
Title case df["name"] = df["name"].str.title() Capitalizes words for display-oriented text.
Text length df["name_length"] = df["name"].str.len() Returns the number of characters in each value.
Find literal text df["product"].str.contains("pro", case=False, na=False, regex=False) Returns a Boolean Series for literal matches.
Replace literal text df["code"] = df["code"].str.replace("-", "", regex=False) Removes literal hyphens from every code.
Split into columns df[["category", "region"]] = df["label"].str.split("-", n=1, expand=True) Splits each label once and expands it into two columns.
Extract a pattern df["code"].str.extract(r"([A-Z]{2})-(\d{4})") Extracts regex capture groups into separate columns.
Pad identifiers df["customer_id"] = df["customer_id"].str.zfill(6) Adds leading zeros until each identifier has six characters.

Normalize messy text

Python Clean customer names
df["customer_name"] = (
    df["customer_name"]
    .astype("string")
    .str.strip()
    .str.replace(
        r"\s+",
        " ",
        regex=True
    )
    .str.title()
)

This removes outer whitespace, collapses repeated internal whitespace, and applies consistent capitalization.

Python Normalize email addresses
df["email"] = (
    df["email"]
    .astype("string")
    .str.strip()
    .str.lower()
)

valid_email_shape = (
    df["email"]
    .str.fullmatch(
        r"[^@\s]+@[^@\s]+\.[^@\s]+",
        na=False
    )
)

invalid_emails = df.loc[
    ~valid_email_shape
    & df["email"].notna()
]

This performs a practical format check, not complete verification that an email account exists.

Split structured text

Python Split one column into several
df[[
    "department",
    "region",
    "team"
]] = df["assignment"].str.split(
    "-",
    n=2,
    expand=True,
    regex=False
)

With expand=True, the resulting pieces become separate DataFrame columns.

Python · Regex Extract named code groups
parts = df["product_code"].str.extract(
    r"^(?P<category>[A-Z]{2})-"
    r"(?P<number>\d{4})$"
)

df = df.join(parts)

print(df[[
    "product_code",
    "category",
    "number"
]])

Named capture groups become clearly labeled result columns.

Combine text columns

Python Create a full-name column
df["full_name"] = (
    df["first_name"]
    .astype("string")
    .str.strip()
    .str.cat(
        df["last_name"]
        .astype("string")
        .str.strip(),
        sep=" ",
        na_rep=""
    )
    .str.strip()
)
Literal or regex?

Use regex=False when searching for or replacing ordinary text. Enable regex only when the pattern intentionally contains regular expression syntax.

Related reference

For anchors, groups, quantifiers, lookarounds, and practical patterns, use the Regex Cheat Sheet .

Work with temporal data

Date & Time Operations

Parse date strings, extract calendar components, filter date ranges, calculate durations, create date sequences, and convert between time zones.

Date and time quick reference

Common Pandas date and time operations
Task Syntax Result Copy
Parse dates df["date"] = pd.to_datetime(df["date"], errors="coerce") Converts valid values and changes invalid dates to NaT.
Parse as UTC df["created_at"] = pd.to_datetime(df["created_at"], utc=True) Creates timezone-aware UTC timestamps.
Extract year df["year"] = df["date"].dt.year Returns the calendar year as an integer.
Month name df["month"] = df["date"].dt.month_name() Returns names such as January and February.
Weekday name df["weekday"] = df["date"].dt.day_name() Returns the weekday name for each timestamp.
Monthly period df["month_period"] = df["date"].dt.to_period("M") Groups each timestamp into a calendar month period.
Remove time df["day"] = df["date"].dt.normalize() Sets the time component to midnight while preserving datetime dtype.
Round down to hour df["hour"] = df["date"].dt.floor("h") Rounds timestamps down to the beginning of the hour.
Format for display df["date_label"] = df["date"].dt.strftime("%Y-%m-%d") Creates formatted strings from datetime values.
Create date range pd.date_range("2026-01-01", periods=7, freq="D") Creates seven consecutive daily timestamps.
Keep datetime dtype

Use dt.strftime() only when you need display text. Formatting converts timestamps into strings, so keep the original datetime column for sorting, filtering, and calculations.

Extract calendar components

Python Create reusable calendar columns
df["order_date"] = pd.to_datetime(
    df["order_date"],
    format="%Y-%m-%d",
    errors="coerce"
)

df = df.assign(
    order_year=df["order_date"].dt.year,
    order_quarter=df["order_date"].dt.quarter,
    order_month=df["order_date"].dt.month,
    month_name=df["order_date"].dt.month_name(),
    weekday=df["order_date"].dt.day_name(),
    is_month_end=df["order_date"].dt.is_month_end
)

print(df.head())

Filter a date range

Python Use explicit boundaries
start_date = pd.Timestamp(
    "2026-01-01"
)

end_date = pd.Timestamp(
    "2026-04-01"
)

mask = df["order_date"].between(
    start_date,
    end_date,
    inclusive="left"
)

quarter_one = df.loc[mask]

This includes January 1 but excludes April 1, producing a clear half-open reporting interval.

Python Filter relative to today
today = pd.Timestamp.now().normalize()

cutoff = today - pd.Timedelta(
    days=30
)

recent_orders = df.loc[
    df["order_date"].between(
        cutoff,
        today,
        inclusive="both"
    )
]

Normalize the current timestamp when the comparison should operate at calendar-day level.

Calculate durations

Python · Timedelta Calculate processing time
df["created_at"] = pd.to_datetime(
    df["created_at"],
    errors="coerce",
    utc=True
)

df["completed_at"] = pd.to_datetime(
    df["completed_at"],
    errors="coerce",
    utc=True
)

df["processing_time"] = (
    df["completed_at"]
    - df["created_at"]
)

df["processing_hours"] = (
    df["processing_time"]
    .dt.total_seconds()
    .div(3600)
    .round(2)
)

Work with time zones

Python · UTC Convert aware timestamps
df["created_at"] = pd.to_datetime(
    df["created_at"],
    utc=True
)

df["created_copenhagen"] = (
    df["created_at"]
    .dt.tz_convert(
        "Europe/Copenhagen"
    )
)

Convert timezone-aware UTC timestamps to the timezone required for reporting or display.

Python · Local time Localize naive timestamps
df["local_time"] = pd.to_datetime(
    df["local_time"],
    errors="coerce"
)

df["local_time"] = (
    df["local_time"]
    .dt.tz_localize(
        "Europe/Copenhagen",
        ambiguous="NaT",
        nonexistent="shift_forward"
    )
)

Localizing assigns a timezone to timestamps that do not already contain timezone information.

Timezone distinction

Use tz_localize() to assign a timezone to naive timestamps. Use tz_convert() only after timestamps are timezone-aware.

Summarize data by category

GroupBy & Aggregation

Use groupby() to split rows into groups, calculate summaries for each group, and combine the results into a new Series or DataFrame.

GroupBy quick reference

Common Pandas GroupBy and aggregation operations
Task Syntax Result Copy
Sum by group df.groupby("region")["sales"].sum() Returns one sales total for each region.
Multiple groups df.groupby(["region", "product"])["sales"].sum() Returns one total for each region and product combination.
Multiple statistics df.groupby("region")["sales"].agg(["sum", "mean", "count"]) Calculates several summaries for every group.
Named aggregation df.groupby("region").agg(total_sales=("sales", "sum")) Creates an aggregation with a clearly named output column.
SQL-style result df.groupby("region", as_index=False)["sales"].sum() Keeps the grouping key as a regular DataFrame column.
Group result per row df.groupby("region")["sales"].transform("sum") Returns a group total aligned with every original row.
size() versus count()

size() counts every row in a group. The count() method excludes missing values in the selected column.

Basic GroupBy operations

Python · Aggregation Sum and average by group
regional_sales = (
    df.groupby("region")["sales"]
    .sum()
    .sort_values(ascending=False)
)

average_sales = (
    df.groupby("region")["sales"]
    .mean()
)

Select a value column after groupby(), then apply the required aggregation.

Python · Counts Count rows and unique values
rows_per_region = (
    df.groupby("region")
    .size()
)

orders_per_region = (
    df.groupby("region")["order_id"]
    .count()
)

customers_per_region = (
    df.groupby("region")["customer_id"]
    .nunique()
)

Use nunique() when you need the number of distinct values inside each group.

Group by multiple columns

Python · Multiple keys Summarize sales by region and product
product_summary = (
    df.groupby(
        ["region", "product"],
        as_index=False,
        sort=False
    )
    .agg(
        total_sales=("sales", "sum"),
        average_sale=("sales", "mean"),
        order_count=("order_id", "count")
    )
    .sort_values(
        "total_sales",
        ascending=False
    )
)

Named aggregation

Python · Named output Create a clean regional summary
regional_summary = (
    df.groupby(
        "region",
        as_index=False
    )
    .agg(
        total_sales=("sales", "sum"),
        average_sale=("sales", "mean"),
        largest_sale=("sales", "max"),
        order_count=("order_id", "count"),
        unique_customers=(
            "customer_id",
            "nunique"
        )
    )
)

Transform grouped data

Python · Percentage Calculate each row’s share of its group
region_total = (
    df.groupby("region")["sales"]
    .transform("sum")
)

df = df.assign(
    region_total=region_total,
    region_share=(
        df["sales"]
        .div(region_total)
    )
)

The transformed totals retain the same index as the original DataFrame.

Python · Comparison Compare rows with the group average
region_average = (
    df.groupby("region")["sales"]
    .transform("mean")
)

df["above_region_average"] = (
    df["sales"] > region_average
)

Use transform() when the calculated group value must remain aligned with every source row.

Filter entire groups

Python · Group filter Keep regions that meet a sales threshold
high_value_regions = (
    df.groupby("region")
    .filter(
        lambda group:
        group["sales"].sum() >= 100_000
    )
)

Missing and categorical group keys

Python · Missing keys Include missing group values
summary = (
    df.groupby(
        "region",
        dropna=False,
        as_index=False
    )
    .agg(
        total_sales=("sales", "sum")
    )
)

Set dropna=False when rows with missing grouping keys must be included in the result.

Python · Categories Control categorical groups
observed_summary = (
    df.groupby(
        "category",
        observed=True
    )["sales"]
    .sum()
)

all_categories = (
    df.groupby(
        "category",
        observed=False
    )["sales"]
    .sum()
)

Use observed=False when unobserved categories must also appear in the grouped result.

Prefer specific GroupBy methods

Use built-in aggregations, agg(), or transform() when they can express the operation. Although GroupBy.apply() is flexible, it is often slower.

Pandas 3 categorical behavior

observed=True is the default for categorical groupers. Set observed=False explicitly when the result must include categories that do not appear in the data.

Combine multiple DataFrames

Merge, Join & Concat

Combine related tables by matching columns or indexes, append rows from multiple DataFrames, and verify that joins preserve the expected relationships.

Combining DataFrames quick reference

Common Pandas operations for combining DataFrames
Task Syntax Result Copy
Inner merge left.merge(right, on="customer_id", how="inner") Keeps keys found in both DataFrames.
Left merge left.merge(right, on="customer_id", how="left") Keeps every row from the left DataFrame.
Outer merge left.merge(right, on="customer_id", how="outer") Keeps the union of keys from both DataFrames.
Different key names left.merge(right, left_on="customer_id", right_on="id") Matches columns that use different names.
Index join left.join(right, how="left") Combines columns by matching index values.
Append rows pd.concat([df_a, df_b], ignore_index=True) Stacks DataFrames vertically and creates a new index.
Combine columns pd.concat([df_a, df_b], axis="columns") Places DataFrames side by side using their indexes.
Left anti join left.merge(right, on="customer_id", how="left_anti") Keeps left-side keys that do not appear on the right.
Choose the right combining method

Use merge() for database-style joins on columns, join() primarily for index-based combinations, and concat() to stack complete objects by rows or columns.

Merge on a shared column

Python · Left merge Add customer details to orders
orders = pd.DataFrame({
    "order_id": [101, 102, 103],
    "customer_id": [1, 2, 4],
    "sales": [125.00, 89.50, 210.00]
})

customers = pd.DataFrame({
    "customer_id": [1, 2, 3],
    "customer_name": [
        "Avery",
        "Morgan",
        "Jordan"
    ]
})

order_details = orders.merge(
    customers,
    on="customer_id",
    how="left",
    validate="many_to_one"
)
Validate expected relationships

Use validate="one_to_one", validate="one_to_many", or validate="many_to_one" to detect duplicate merge keys that would unexpectedly multiply rows.

Understand merge types

Python · Inner join Keep matching records only
matched = orders.merge(
    customers,
    on="customer_id",
    how="inner",
    validate="many_to_one"
)

An inner merge excludes keys that appear in only one of the two DataFrames.

Python · Outer join Keep records from both tables
all_records = orders.merge(
    customers,
    on="customer_id",
    how="outer",
    indicator=True
)

print(
    all_records["_merge"]
    .value_counts()
)

The indicator column identifies rows originating from the left, right, or both DataFrames.

Merge different key names

Python · Different columns Match differently named keys
combined = orders.merge(
    customers,
    left_on="customer_id",
    right_on="id",
    how="left",
    suffixes=("_order", "_customer"),
    validate="many_to_one"
)
Control overlapping column names

Use suffixes=("_left", "_right") when both DataFrames contain non-key columns with the same name. This prevents ambiguous default names such as name_x and name_y.

Audit a merge

Python · Indicator Find unmatched records
audit = orders.merge(
    customers,
    on="customer_id",
    how="outer",
    indicator="merge_source"
)

unmatched = audit.loc[
    audit["merge_source"] != "both"
]

The indicator contains left_only, right_only, or both.

Python · Pandas 3 Use an anti join
unknown_customers = (
    orders.merge(
        customers,
        on="customer_id",
        how="left_anti"
    )
)

A left anti join returns rows whose keys occur only in the left DataFrame.

Join by index

Python · Index alignment Join columns using index values
customer_index = (
    customers
    .set_index("customer_id")
)

order_index = (
    orders
    .set_index("customer_id")
)

joined = order_index.join(
    customer_index,
    how="left",
    validate="many_to_one"
)

Concatenate rows and columns

Python · Rows Append monthly datasets
monthly_frames = [
    january_orders,
    february_orders,
    march_orders
]

quarter_orders = pd.concat(
    monthly_frames,
    ignore_index=True,
    sort=False
)

Use ignore_index=True when the original row indexes should not be preserved.

Python · Columns Combine aligned features
customer_features = pd.concat(
    [
        customer_totals,
        customer_counts,
        customer_averages
    ],
    axis="columns",
    join="inner"
)

Column-wise concatenation aligns rows by index rather than by their physical position.

Check duplicate and missing merge keys

Duplicate keys on both sides can produce a many-to-many Cartesian result and greatly increase the number of rows. Pandas also matches null merge keys with other null keys, which differs from typical SQL behavior.

Concatenate once

Collect DataFrames in a list and call pd.concat() once. Repeatedly concatenating inside a loop creates unnecessary copies and is less efficient.

Reshape data for analysis

Pivot & Reshape

Convert data between long and wide formats, create spreadsheet-style summaries, reshape hierarchical indexes, expand list-like values, and build cross-tabulations.

Reshaping quick reference

Common Pandas pivoting and reshaping operations
Task Syntax Result Copy
Long to wide df.pivot(index="date", columns="region", values="sales") Creates one column for each unique region.
Pivot with aggregation df.pivot_table(index="region", values="sales", aggfunc="sum") Aggregates duplicate index and column combinations.
Wide to long df.melt(id_vars="product", var_name="month", value_name="sales") Converts column labels into row values.
Index level to columns df.unstack("region") Moves an index level into the columns.
Column level to index df.stack() Moves a column level into the row index.
Expand lists into rows df.explode("tags", ignore_index=True) Creates one row for every list item.
Frequency table pd.crosstab(df["region"], df["status"]) Counts combinations of two categorical variables.
Indicator columns pd.get_dummies(df, columns=["region"], dtype="int8") Converts categories into separate indicator columns.
pivot() versus pivot_table()

Use pivot() when every index and column combination has exactly one value. Use pivot_table() when duplicate combinations must be aggregated.

Convert long data to wide format

Python · Pivot Create one column for each region
sales = pd.DataFrame({
    "date": [
        "2026-01-01",
        "2026-01-01",
        "2026-01-02",
        "2026-01-02"
    ],
    "region": [
        "East",
        "West",
        "East",
        "West"
    ],
    "sales": [
        1250,
        1480,
        1320,
        1510
    ]
})

wide_sales = sales.pivot(
    index="date",
    columns="region",
    values="sales"
)
Duplicate combinations cause an error

pivot() raises a ValueError when more than one value exists for the same index and column combination. Use pivot_table() with an aggregation function when duplicates are expected.

Create an aggregated pivot table

Python · Summary Summarize sales by region and product
sales_summary = df.pivot_table(
    values="sales",
    index="region",
    columns="product",
    aggfunc="sum",
    fill_value=0
)

Missing region and product combinations are replaced with zero.

Python · Totals Add grand totals and multiple statistics
detailed_summary = df.pivot_table(
    values="sales",
    index="region",
    columns="product",
    aggfunc=["sum", "mean"],
    fill_value=0,
    margins=True,
    margins_name="Total"
)

Multiple aggregation functions create hierarchical column labels.

Convert wide data to long format

Python · Melt Unpivot monthly columns into rows
monthly_sales = pd.DataFrame({
    "product": [
        "Keyboard",
        "Mouse"
    ],
    "jan": [
        1250,
        890
    ],
    "feb": [
        1380,
        940
    ],
    "mar": [
        1420,
        1010
    ]
})

long_sales = monthly_sales.melt(
    id_vars="product",
    value_vars=[
        "jan",
        "feb",
        "mar"
    ],
    var_name="month",
    value_name="sales"
)

Stack and unstack index levels

Python · Unstack Move an index level into columns
grouped_sales = (
    df.groupby(
        ["region", "product"]
    )["sales"]
    .sum()
)

wide_summary = grouped_sales.unstack(
    level="product",
    fill_value=0
)

unstack() moves a row-index level to the column axis.

Python · Stack Move columns back into the index
long_summary = (
    wide_summary
    .stack()
    .rename("sales")
    .reset_index()
)

stack() moves a column level into the row index.

Expand list-like values

Python · Explode Create one row for every tag
products = pd.DataFrame({
    "product_id": [
        101,
        102
    ],
    "tags": [
        ["office", "wireless"],
        ["gaming", "accessory"]
    ]
})

product_tags = products.explode(
    "tags",
    ignore_index=True
)

Create cross-tabulations

Python · Counts Count status values by region
status_counts = pd.crosstab(
    index=df["region"],
    columns=df["status"],
    margins=True,
    margins_name="Total"
)

Without values and aggfunc, crosstab() returns frequency counts.

Python · Percentages Calculate row percentages
status_percentages = pd.crosstab(
    index=df["region"],
    columns=df["status"],
    normalize="index"
).mul(100).round(1)

normalize="index" makes every row total equal to one before conversion to percentages.

Encode categorical variables

Python · Indicator columns Create dummy variables from categories
encoded = pd.get_dummies(
    df,
    columns=[
        "region",
        "status"
    ],
    prefix={
        "region": "region",
        "status": "status"
    },
    dummy_na=True,
    dtype="int8"
)
Keep identifiers during reshaping

When using melt(), place identifiers such as product IDs, dates, or customer IDs in id_vars. Only measurement columns should normally be converted into variable and value rows.

Pandas 3 categorical behavior

pivot_table() uses observed=True by default for categorical groupers. Set observed=False explicitly when unobserved categories must also appear in the result.

Apply reusable transformations

Apply, Map & Transform

Map individual values, apply functions across rows or columns, and create transformed results that preserve the original DataFrame structure.

Function application quick reference

Common Pandas methods for applying functions
Task Syntax Result Copy
Map with dictionary df["region_name"] = df["region"].map(region_map) Replaces Series values using a lookup mapping.
Map with function df["sales_label"] = df["sales"].map(lambda value: f"${value:,.2f}") Applies a scalar function to every Series value.
Map every DataFrame cell numeric.map(lambda value: round(value, 2), na_action="ignore") Applies a scalar function element by element.
Apply to columns df[["sales", "cost"]].apply("mean", axis="index") Calculates one mean for each selected column.
Apply to rows df.apply(classify_order, axis="columns") Passes each complete row to a function.
Apply to a Series df["sales"].apply(calculate_tax) Invokes a function on the Series values.
Normalize a Series df["sales"].transform(lambda values: values / values.max()) Returns transformed values with the original index.
Group transformation df.groupby("region")["sales"].transform("mean") Returns each group’s mean beside every original row.
Choose the method by input shape

Use map() for individual values, apply() for complete rows or columns, and transform() when the result must remain aligned with the original index.

Map values with a dictionary

Python · Series.map Convert region codes into readable labels
region_map = {
    "N": "North",
    "S": "South",
    "E": "East",
    "W": "West"
}

df["region_name"] = (
    df["region_code"]
    .map(region_map)
    .fillna("Unknown")
)
Unmapped dictionary values become missing

When a value is not present in the mapping dictionary, Series.map() normally returns a missing value. Add fillna() or validate the source categories when every value requires a label.

Map values with functions

Python · Series Format values for display
df["sales_label"] = (
    df["sales"]
    .map(
        lambda value:
        f"${value:,.2f}",
        na_action="ignore"
    )
)

The na_action="ignore" argument prevents the formatting function from receiving missing values.

Python · DataFrame Round every numeric cell
numeric = df[
    [
        "sales",
        "cost",
        "discount"
    ]
]

rounded = numeric.map(
    lambda value: round(value, 2),
    na_action="ignore"
)

DataFrame.map() applies a scalar function to every selected cell.

Pandas 3 change

DataFrame.applymap() was removed in Pandas 3. Use DataFrame.map() for element-wise DataFrame operations.

Apply functions to columns

Python · Column-wise Calculate the range of numeric columns
def value_range(column):
    return column.max() - column.min()

column_ranges = df[
    [
        "sales",
        "cost",
        "discount"
    ]
].apply(
    value_range,
    axis="index"
)

Apply functions to rows

Python · Row-wise Classify orders using multiple columns
def classify_order(row):
    if row["sales"] >= 1000:
        return "High value"

    if row["sales"] >= 500:
        return "Medium value"

    return "Standard"

df["order_class"] = df.apply(
    classify_order,
    axis="columns"
)
Prefer vectorized conditions when possible

Row-wise apply() is useful for complex logic involving several columns, but vectorized arithmetic, comparisons, and built-in Pandas methods are usually faster.

Pass arguments to a custom function

Python · Function arguments Calculate tax from a Series
def calculate_tax(
    amount,
    tax_rate
):
    return amount * tax_rate

df["tax"] = df["sales"].apply(
    calculate_tax,
    tax_rate=0.20
)

Additional keyword arguments are passed directly to the custom function.

Python · Vectorized Calculate the same result directly
tax_rate = 0.20

df["tax"] = (
    df["sales"]
    .mul(tax_rate)
)

Direct vectorized arithmetic is simpler and usually faster for this type of calculation.

Transform values without changing alignment

Python · Normalize Scale sales between zero and one
df["sales_scaled"] = (
    df["sales"]
    .transform(
        lambda values:
        (
            values - values.min()
        )
        / (
            values.max()
            - values.min()
        )
    )
)

The transformed Series retains the original row index.

Python · Group comparison Compare sales with the regional mean
regional_mean = (
    df.groupby("region")["sales"]
    .transform("mean")
)

df["difference_from_mean"] = (
    df["sales"]
    - regional_mean
)

Group transformation broadcasts each regional mean back to its original rows.

Use built-in methods first

Before writing a custom function, check whether a vectorized Pandas method already performs the operation. Built-in string, datetime, arithmetic, aggregation, and GroupBy methods are generally clearer and more efficient.

Detect and remove repeated records

Duplicate Data

Identify repeated rows, define the columns that make a record unique, keep the correct occurrence, audit duplicate groups, and detect duplicate index labels.

Duplicate data quick reference

Common Pandas operations for finding and removing duplicate data
Task Syntax Result Copy
Mark repeated rows df.duplicated() Marks duplicates after their first occurrence.
Mark every duplicate df.duplicated(keep=False) Marks every row belonging to a duplicate group.
Check selected columns df.duplicated(subset=["email"], keep=False) Finds rows sharing the same email address.
Remove duplicate rows df.drop_duplicates(ignore_index=True) Keeps the first occurrence and creates a new index.
Keep last occurrence df.drop_duplicates(subset="customer_id", keep="last") Keeps the final row for each customer ID.
Remove every repeated value df.drop_duplicates(subset="order_id", keep=False) Keeps only order IDs that occur exactly once.
Count duplicate rows df.duplicated().sum() Returns the number of duplicates after first occurrences.
Check index uniqueness df.index.is_unique Returns whether every index label is unique.
Define what makes a record unique

A completely repeated row and a repeated business key are not always the same problem. Use subset to specify identifiers such as an order ID, email address, or combination of customer and date.

Find completely duplicated rows

Python · Detection Inspect every repeated row
duplicate_mask = df.duplicated(
    keep=False
)

duplicate_rows = (
    df.loc[duplicate_mask]
    .sort_values(
        by=list(df.columns),
        kind="stable"
    )
)

duplicate_count = duplicate_mask.sum()

print(duplicate_rows)
print(f"Duplicate rows: {duplicate_count}")

Find duplicates using selected columns

Python · One key Find repeated order IDs
duplicate_orders = df.loc[
    df.duplicated(
        subset="order_id",
        keep=False
    )
].sort_values(
    "order_id",
    kind="stable"
)

This returns every row connected to a repeated order ID.

Python · Composite key Check multiple identifying columns
key_columns = [
    "customer_id",
    "order_date",
    "product_id"
]

duplicate_purchases = df.loc[
    df.duplicated(
        subset=key_columns,
        keep=False
    )
].sort_values(
    key_columns,
    kind="stable"
)

A composite key identifies duplicates using a combination of fields.

Remove duplicate rows

Python · Keep first Keep the first occurrence
clean_orders = df.drop_duplicates(
    subset="order_id",
    keep="first",
    ignore_index=True
)

The first row for each order ID is retained.

Python · Keep last Keep the last occurrence
latest_orders = df.drop_duplicates(
    subset="order_id",
    keep="last",
    ignore_index=True
)

The final row in the current DataFrame order is retained.

First and last depend on row order

Sort the DataFrame by the relevant date or version column before using keep="first" or keep="last". Otherwise, the retained row may not represent the oldest or newest record.

Keep the newest record

Python · Latest record Sort before removing duplicates
df["updated_at"] = pd.to_datetime(
    df["updated_at"],
    errors="coerce",
    utc=True
)

latest_customers = (
    df.sort_values(
        "updated_at",
        kind="stable"
    )
    .drop_duplicates(
        subset="customer_id",
        keep="last",
        ignore_index=True
    )
)

Audit duplicate groups before removal

Python · Audit Count occurrences of each business key
duplicate_audit = (
    df.groupby(
        "customer_id",
        dropna=False
    )
    .size()
    .rename("row_count")
    .loc[lambda counts: counts > 1]
    .sort_values(ascending=False)
    .reset_index()
)

print(duplicate_audit)
Preserve an audit trail

Inspect or export duplicate groups before removing them from important datasets. This makes it possible to explain which records were removed and why.

Detect duplicate index labels

Python · Index Inspect repeated index labels
has_unique_index = df.index.is_unique

duplicate_index_rows = df.loc[
    df.index.duplicated(
        keep=False
    )
]

print(has_unique_index)
print(duplicate_index_rows)

Duplicate index labels are separate from duplicate row values.

Python · Index cleanup Keep one row per index label
unique_index_df = df.loc[
    ~df.index.duplicated(
        keep="first"
    )
].copy()

Negating the duplicate mask keeps the first row for each index label.

Prevent duplicate labels

Python · Validation Disallow duplicate row and column labels
validated_df = (
    unique_index_df
    .set_flags(
        allows_duplicate_labels=False
    )
)

print(
    validated_df.flags
    .allows_duplicate_labels
)
Normalize text before duplicate checks

Values such as "Alice@example.com", "alice@example.com", and "alice@example.com " are different strings. Normalize case and whitespace before checking for duplicate text identifiers.

Save processed data

Export Data

Export DataFrames to CSV, Excel, JSON, Parquet, and SQL while controlling indexes, missing values, number formats, compression, and output structure.

Data export quick reference

Common Pandas methods for exporting DataFrames
Format Syntax Use case Copy
CSV df.to_csv("output.csv", index=False) Portable tabular data for spreadsheets and other tools.
Compressed CSV df.to_csv("output.csv.gz", index=False, compression="gzip") Smaller text files for storage or transfer.
Excel df.to_excel("report.xlsx", sheet_name="Sales", index=False) Reports intended for Excel users.
JSON records df.to_json("output.json", orient="records", indent=2) Structured records for APIs and web applications.
JSON Lines df.to_json("output.jsonl", orient="records", lines=True) Streaming and large-data processing pipelines.
Parquet df.to_parquet("output.parquet", index=False) Efficient analytical storage with preserved data types.
SQL table df.to_sql("sales", connection, if_exists="append", index=False) Writes DataFrame records into a database table.
Clipboard df.to_clipboard(index=False, excel=True) Quickly pastes tabular data into a spreadsheet.
Decide whether the index is data

Pandas exports the index by default in several formats. Use index=False when the index is only an internal row counter. Preserve or name it when it contains meaningful identifiers.

Export a clean CSV file

Python · CSV Control columns, dates, numbers, and missing values
export_columns = [
    "order_id",
    "order_date",
    "region",
    "sales"
]

df.to_csv(
    "sales-report.csv",
    columns=export_columns,
    index=False,
    encoding="utf-8",
    na_rep="",
    float_format="%.2f",
    date_format="%Y-%m-%d"
)

Export compressed CSV data

Python · Gzip Compress using the filename extension
df.to_csv(
    "sales-report.csv.gz",
    index=False,
    compression="infer"
)

Pandas infers gzip compression from the .gz filename extension.

Python · ZIP Create a named CSV inside a ZIP file
compression_options = {
    "method": "zip",
    "archive_name": "sales-report.csv"
}

df.to_csv(
    "sales-report.zip",
    index=False,
    compression=compression_options
)

The archive contains a CSV file named sales-report.csv.

Export to Excel

Python · Excel Write one worksheet
df.to_excel(
    "sales-report.xlsx",
    sheet_name="Sales",
    index=False,
    freeze_panes=(1, 0)
)

The first worksheet row remains visible while the user scrolls.

Python · ExcelWriter Write multiple worksheets
with pd.ExcelWriter(
    "sales-workbook.xlsx"
) as writer:
    df.to_excel(
        writer,
        sheet_name="Transactions",
        index=False
    )

    regional_summary.to_excel(
        writer,
        sheet_name="Regional Summary",
        index=False
    )

    product_summary.to_excel(
        writer,
        sheet_name="Product Summary",
        index=False
    )

The context manager closes and saves the workbook automatically.

Excel requires a writer engine

Writing modern .xlsx files normally requires an optional engine such as openpyxl or XlsxWriter.

Export JSON records

Python · JSON array Create readable API-style records
df.to_json(
    "sales-report.json",
    orient="records",
    date_format="iso",
    indent=2,
    force_ascii=False
)

The result is a JSON array containing one object for each row.

Python · JSON Lines Write one record per line
df.to_json(
    "sales-report.jsonl",
    orient="records",
    lines=True,
    date_format="iso"
)

JSON Lines is useful when records are processed individually or in streaming workflows.

Export to Parquet

Python · Parquet Preserve types in a compressed analytical format
parquet_df = df.copy()

categorical_columns = parquet_df.select_dtypes(
    include="category"
).columns

for column in categorical_columns:
    parquet_df[column] = (
        parquet_df[column]
        .cat.remove_unused_categories()
    )

parquet_df.to_parquet(
    "sales-report.parquet",
    index=False,
    compression="snappy"
)
Parquet requires an optional dependency

Pandas requires either pyarrow or fastparquet to write Parquet files. Parquet is generally preferable to CSV when preserving data types and efficient analytical storage are important.

Export to a SQL database

Python · SQLite Write DataFrame rows into a database table
import sqlite3

with sqlite3.connect(
    "sales.db"
) as connection:
    rows_written = df.to_sql(
        name="sales",
        con=connection,
        if_exists="append",
        index=False,
        chunksize=1000,
        method="multi"
    )

print(
    f"Rows written: {rows_written}"
)
Use if_exists="replace" carefully

The replace option drops the existing table before writing the new data. Use append when new rows should be added, and verify the target database and table before replacing production data.

Validate exported data

Python · CSV check Read the exported file back
export_path = "sales-report.csv"

df.to_csv(
    export_path,
    index=False
)

check = pd.read_csv(
    export_path
)

assert len(check) == len(df)
assert list(check.columns) == list(df.columns)

A round-trip check can detect missing rows or unexpected columns.

Python · Parquet check Verify shape and data types
export_path = "sales-report.parquet"

df.to_parquet(
    export_path,
    index=False
)

check = pd.read_parquet(
    export_path
)

assert check.shape == df.shape

print(check.dtypes)

Parquet normally preserves data types more reliably than CSV.

Export only the required data

Select and order the output columns before exporting. Smaller, purpose-built files are easier to validate, transfer, document, and use in downstream systems.

Combine Pandas methods

Practical Pandas Examples

Use these complete workflows to import, clean, combine, summarize, reshape, validate, and export real-world datasets.

Practical workflow quick reference

Common steps in practical Pandas data workflows
Workflow step Syntax Purpose Copy
Import selected columns pd.read_csv("sales.csv", usecols=["date", "region", "sales"]) Loads only the columns required for analysis.
Convert numeric data df["sales"] = pd.to_numeric(df["sales"], errors="coerce") Converts valid values and marks invalid values as missing.
Remove unusable rows df = df.dropna(subset=["order_id", "sales"]) Removes records missing required values.
Summarize data df.groupby("region", as_index=False).agg(total_sales=("sales", "sum")) Creates one aggregated row for each region.
Add lookup data orders.merge(customers, on="customer_id", how="left", validate="many_to_one") Adds customer details while validating the relationship.
Create a pivot report df.pivot_table(index="month", columns="region", values="sales", aggfunc="sum") Creates a monthly report with one column per region.
Combine multiple files pd.concat(frames, ignore_index=True) Stacks imported files into one DataFrame.
Export the result summary.to_csv("regional-summary.csv", index=False) Saves the final report without an extra index column.

Example 1: Clean and summarize sales data

Python · Complete workflow Create a regional sales summary from CSV
import pandas as pd

sales = pd.read_csv(
    "sales.csv",
    usecols=[
        "order_id",
        "order_date",
        "region",
        "customer_id",
        "sales"
    ]
)

sales["order_date"] = pd.to_datetime(
    sales["order_date"],
    errors="coerce"
)

sales["sales"] = pd.to_numeric(
    sales["sales"],
    errors="coerce"
)

sales["region"] = (
    sales["region"]
    .astype("string")
    .str.strip()
    .str.title()
)

sales = (
    sales.dropna(
        subset=[
            "order_id",
            "order_date",
            "region",
            "sales"
        ]
    )
    .drop_duplicates(
        subset="order_id",
        keep="last"
    )
    .loc[lambda frame: frame["sales"] >= 0]
)

regional_summary = (
    sales.groupby(
        "region",
        as_index=False
    )
    .agg(
        total_sales=("sales", "sum"),
        average_sale=("sales", "mean"),
        order_count=("order_id", "nunique"),
        customer_count=(
            "customer_id",
            "nunique"
        )
    )
    .sort_values(
        "total_sales",
        ascending=False
    )
)

regional_summary["average_sale"] = (
    regional_summary["average_sale"]
    .round(2)
)

regional_summary.to_csv(
    "regional-sales-summary.csv",
    index=False
)

print(regional_summary)
Convert data types before filtering

Parse dates and numeric values before applying business rules. This prevents string comparisons and invalid values from producing misleading results.

Example 2: Enrich orders with customer data

Python · Merge workflow Validate and audit a customer lookup
import pandas as pd

orders = pd.read_csv(
    "orders.csv"
)

customers = pd.read_csv(
    "customers.csv"
)

customers["email"] = (
    customers["email"]
    .astype("string")
    .str.strip()
    .str.lower()
)

duplicate_customers = customers.loc[
    customers.duplicated(
        subset="customer_id",
        keep=False
    )
]

if not duplicate_customers.empty:
    raise ValueError(
        "Customer IDs must be unique."
    )

enriched_orders = orders.merge(
    customers[
        [
            "customer_id",
            "customer_name",
            "email",
            "segment"
        ]
    ],
    on="customer_id",
    how="left",
    validate="many_to_one",
    indicator="customer_match"
)

unmatched_orders = enriched_orders.loc[
    enriched_orders["customer_match"]
    == "left_only"
]

enriched_orders = enriched_orders.drop(
    columns="customer_match"
)

print(
    f"Unmatched orders: "
    f"{len(unmatched_orders)}"
)

enriched_orders.to_parquet(
    "enriched-orders.parquet",
    index=False
)
Audit unmatched keys

A successful merge does not guarantee that every lookup key matched. Use indicator to find orders without a corresponding customer record.

Example 3: Build a monthly trend report

Python · Time series Create monthly totals and regional columns
import pandas as pd

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

monthly_sales = (
    sales.dropna(
        subset=[
            "order_date",
            "region",
            "sales"
        ]
    )
    .groupby(
        [
            pd.Grouper(
                key="order_date",
                freq="ME"
            ),
            "region"
        ],
        as_index=False
    )
    .agg(
        total_sales=("sales", "sum")
    )
    .rename(
        columns={
            "order_date": "month"
        }
    )
)

monthly_report = monthly_sales.pivot_table(
    index="month",
    columns="region",
    values="total_sales",
    aggfunc="sum",
    fill_value=0
)

monthly_report["Total"] = (
    monthly_report.sum(
        axis="columns"
    )
)

monthly_report = (
    monthly_report
    .reset_index()
)

monthly_report.to_excel(
    "monthly-sales-report.xlsx",
    sheet_name="Monthly Sales",
    index=False
)
Month-end frequency

The ME frequency groups timestamps by calendar month and labels each group with its month-end date.

Example 4: Combine monthly CSV files

Python · Multiple files Import and combine a directory of CSV files
from pathlib import Path

import pandas as pd

data_directory = Path(
    "monthly-sales"
)

csv_files = sorted(
    data_directory.glob("*.csv")
)

if not csv_files:
    raise FileNotFoundError(
        "No CSV files were found."
    )

frames = []

for file_path in csv_files:
    monthly_data = pd.read_csv(
        file_path,
        parse_dates=["order_date"]
    )

    monthly_data["source_file"] = (
        file_path.name
    )

    frames.append(monthly_data)

all_sales = pd.concat(
    frames,
    ignore_index=True,
    sort=False
)

all_sales = all_sales.drop_duplicates(
    subset="order_id",
    keep="last",
    ignore_index=True
)

all_sales.to_parquet(
    "combined-sales.parquet",
    index=False
)

print(
    f"Files combined: {len(csv_files)}"
)

print(
    f"Rows exported: {len(all_sales)}"
)
Confirm that every file has the expected schema

concat() creates additional columns when input files use different column names. Validate required columns before combining files in production workflows.

Example 5: Generate a data quality report

Python · Quality audit Profile missing, unique, and duplicate values
import pandas as pd

quality_report = pd.DataFrame({
    "dtype": df.dtypes.astype("string"),
    "row_count": len(df),
    "missing_count": df.isna().sum(),
    "missing_percent": (
        df.isna()
        .mean()
        .mul(100)
        .round(2)
    ),
    "unique_count": df.nunique(
        dropna=True
    )
})

quality_report["duplicate_rows"] = (
    df.duplicated().sum()
)

quality_report = (
    quality_report
    .sort_values(
        by=[
            "missing_count",
            "unique_count"
        ],
        ascending=[
            False,
            True
        ]
    )
    .reset_index(
        names="column"
    )
)

quality_report.to_csv(
    "data-quality-report.csv",
    index=False
)

print(quality_report)
Separate transformation from reporting

Keep raw input, cleaned data, aggregated reports, and exported files as distinct stages. This makes the workflow easier to test, debug, repeat, and audit.

Write faster and safer Pandas code

Performance & Best Practices

Improve speed and memory efficiency by loading less data, choosing appropriate data types, using vectorized operations, processing large files in chunks, and avoiding unnecessary intermediate objects.

Performance quick reference

Common methods for improving Pandas performance
Practice Syntax Benefit Copy
Use vectorized arithmetic df["margin"] = df["sales"] - df["cost"] Avoids slow Python calls for every row.
Load selected columns pd.read_csv("sales.csv", usecols=["region", "sales"]) Reduces input time and memory usage.
Specify data types pd.read_csv("sales.csv", dtype={"region": "category"}) Avoids unnecessary type inference and may reduce memory.
Measure memory df.memory_usage(deep=True).sort_values(ascending=False) Identifies columns using the most memory.
Read in chunks pd.read_csv("sales.csv", chunksize=100_000) Processes files without loading every row at once.
Concatenate once result = pd.concat(frames, ignore_index=True) Avoids repeatedly copying a growing DataFrame.
Assign with loc df.loc[df["sales"] < 0, "sales"] = 0 Performs selection and assignment in one operation.
Iterate efficiently for row in df.itertuples(index=False): Is generally preferable when row iteration is unavoidable.
Optimize in the right order

First load less data, select efficient data types, and replace Python loops with built-in Pandas or NumPy operations. Measure the workflow before adding more advanced optimization techniques.

Prefer vectorized operations

Slower · Row-wise apply Avoid unnecessary Python function calls
def calculate_margin(row):
    return (
        row["sales"]
        - row["cost"]
    )

df["margin"] = df.apply(
    calculate_margin,
    axis="columns"
)

This calls a Python function separately for every DataFrame row.

Faster · Vectorized Operate on complete columns
df["margin"] = (
    df["sales"]
    - df["cost"]
)

df["margin_percent"] = (
    df["margin"]
    .div(df["sales"])
    .mul(100)
    .round(2)
)

Vectorized operations process complete arrays using optimized implementations.

Load only the required data

Python · Efficient import Select columns and data types while reading
sales = pd.read_csv(
    "sales.csv",
    usecols=[
        "order_id",
        "order_date",
        "region",
        "sales"
    ],
    dtype={
        "order_id": "int64",
        "region": "category",
        "sales": "float64"
    },
    parse_dates=[
        "order_date"
    ]
)
Explicit types improve predictability

Specifying data types can reduce inference work and prevent mixed-type columns. Confirm that the chosen integer and floating-point types can represent every expected value.

Measure memory usage

Python · Memory audit Find expensive columns
memory_by_column = (
    df.memory_usage(
        index=True,
        deep=True
    )
    .sort_values(
        ascending=False
    )
)

total_megabytes = (
    memory_by_column.sum()
    / 1024**2
)

print(memory_by_column)

print(
    f"Total memory: "
    f"{total_megabytes:.2f} MB"
)

df.info(
    memory_usage="deep",
    show_counts=True
)

Use categorical data selectively

Python · Repeated labels Convert low-cardinality text
before = (
    df["region"]
    .memory_usage(deep=True)
)

df["region"] = (
    df["region"]
    .astype("category")
)

after = (
    df["region"]
    .memory_usage(deep=True)
)

print({
    "before_bytes": before,
    "after_bytes": after
})

Categories can reduce memory when a column contains many repeated values and relatively few unique labels.

Python · Cardinality check Measure uniqueness before conversion
unique_values = (
    df["region"]
    .nunique(dropna=True)
)

row_count = len(df)

unique_ratio = (
    unique_values
    / row_count
)

print({
    "rows": row_count,
    "unique_values": unique_values,
    "unique_ratio": unique_ratio
})

Categories may provide little benefit when almost every value is unique.

Process large CSV files in chunks

Python · Chunking Aggregate without loading the complete file
partial_totals = []

for chunk in pd.read_csv(
    "large-sales.csv",
    usecols=[
        "region",
        "sales"
    ],
    dtype={
        "region": "string",
        "sales": "float64"
    },
    chunksize=100_000
):
    chunk_totals = (
        chunk.groupby(
            "region"
        )["sales"]
        .sum()
    )

    partial_totals.append(
        chunk_totals
    )

regional_totals = (
    pd.concat(partial_totals)
    .groupby(level=0)
    .sum()
    .sort_values(
        ascending=False
    )
)

print(regional_totals)
Chunking works best for reducible operations

Counts, sums, minimums, and maximums can be calculated for each chunk and combined later. Operations requiring the complete dataset may need a different strategy.

Concatenate once

Slower · Repeated concat Avoid growing a DataFrame in a loop
result = pd.DataFrame()

for file_path in csv_files:
    frame = pd.read_csv(
        file_path
    )

    result = pd.concat(
        [
            result,
            frame
        ],
        ignore_index=True
    )

Each iteration may copy an increasingly large amount of data.

Faster · Single concat Collect DataFrames before combining
frames = [
    pd.read_csv(file_path)
    for file_path in csv_files
]

result = pd.concat(
    frames,
    ignore_index=True,
    sort=False
)

A single concatenation avoids repeatedly rebuilding the result.

Assign values safely

Avoid · Chained assignment Do not assign through multiple selections
# Avoid this pattern
df[df["sales"] < 0]["sales"] = 0

This attempts to assign through an intermediate selected object.

Use · loc assignment Select rows and assign in one operation
negative_sales = (
    df["sales"] < 0
)

df.loc[
    negative_sales,
    "sales"
] = 0

This clearly modifies the original DataFrame using one assignment.

Copy-on-Write is standard in Pandas 3

Objects returned by indexing and DataFrame methods behave as independent objects, while Pandas delays physical copies internally until they are required. Chained assignment should still be replaced with one explicit loc operation.

Iterate only when necessary

Python · itertuples Process rows when vectorization is impossible
for row in df.itertuples(
    index=False
):
    process_order(
        order_id=row.order_id,
        sales=row.sales
    )
Do not optimize from assumptions alone

Measure the real workflow with representative data. Techniques such as eval(), query(), or JIT compilation can add overhead and may not improve small or simple operations.

Diagnose common problems

Common Errors & Troubleshooting

Understand frequent Pandas errors involving column names, boolean conditions, chained assignment, incompatible data types, datetime accessors, string methods, pivots, and merges.

Error troubleshooting quick reference

Common Pandas errors with recommended fixes
Problem Typical fix Explanation Copy
KeyError print(df.columns.tolist()) Inspect the exact available column labels.
Ambiguous truth value if (df["sales"] > 0).any(): Reduces a boolean Series to one boolean value.
Chained assignment df.loc[df["sales"] < 0, "sales"] = 0 Selects and updates the original DataFrame in one operation.
.dt accessor error df["date"] = pd.to_datetime(df["date"], errors="coerce") Converts the column to a datetime-compatible data type.
.str accessor error df["name"] = df["name"].astype("string").str.strip() Converts the column to Pandas string dtype before cleaning.
Pivot duplicate error df.pivot_table(index="date", columns="region", values="sales", aggfunc="sum") Aggregates duplicate index and column combinations.
Merge type mismatch df["customer_id"] = df["customer_id"].astype("string") Creates a consistent merge-key data type.
Overlapping columns left.merge(right, on="id", suffixes=("_left", "_right")) Adds clear suffixes to repeated non-key column names.

Fix a missing column KeyError

Problem · KeyError The requested label does not exist
# Raises KeyError if the real
# column is named "Sales "
total = df["sales"].sum()

Column names are case-sensitive and may contain hidden whitespace.

Fix · Normalize labels Clean and inspect column names
df.columns = (
    df.columns
    .str.strip()
    .str.lower()
    .str.replace(
        " ",
        "_",
        regex=False
    )
)

print(df.columns.tolist())

required_columns = {
    "order_id",
    "sales"
}

missing_columns = (
    required_columns
    - set(df.columns)
)

if missing_columns:
    raise KeyError(
        f"Missing columns: "
        f"{sorted(missing_columns)}"
    )

Validate required columns before starting the transformation.

Fix an ambiguous boolean condition

Problem · ValueError A Series cannot become one implicit boolean
# Raises:
# ValueError: The truth value of a
# Series is ambiguous
if df["sales"] > 0:
    print("Positive sales found")

The comparison returns one boolean value for every row.

Fix · Reduce or filter State the intended boolean result
positive_sales = (
    df["sales"] > 0
)

if positive_sales.any():
    print("At least one positive sale")

if positive_sales.all():
    print("Every sale is positive")

positive_rows = df.loc[
    positive_sales
]

Use any(), all(), or boolean filtering according to the required result.

Fix chained assignment

Problem · Pandas 3 Assignment through an intermediate object
# Does not update df correctly
df["sales"][
    df["sales"] < 0
] = 0

With Copy-on-Write, chained assignment cannot update the original DataFrame.

Fix · Single assignment Use loc on the original DataFrame
negative_sales = (
    df["sales"] < 0
)

df.loc[
    negative_sales,
    "sales"
] = 0

Selection and assignment now happen in one operation.

Pandas 3 terminology

Older tutorials may discuss SettingWithCopyWarning. Pandas 3 uses Copy-on-Write, and chained assignment produces a ChainedAssignmentError warning because the pattern cannot update the original object.

Fix datetime and string accessor errors

Fix · Datetime Convert before using the dt accessor
df["order_date"] = pd.to_datetime(
    df["order_date"],
    errors="coerce"
)

invalid_dates = df.loc[
    df["order_date"].isna()
]

df["order_year"] = (
    df["order_date"]
    .dt.year
)

Invalid date values become NaT and can be inspected separately.

Fix · String Convert before using the str accessor
df["customer_name"] = (
    df["customer_name"]
    .astype("string")
    .str.strip()
    .str.replace(
        r"\s+",
        " ",
        regex=True
    )
    .str.title()
)

Pandas string dtype preserves missing values while enabling vectorized string methods.

Fix incompatible merge keys

Python · Merge keys Normalize key types before merging
orders["customer_id"] = (
    orders["customer_id"]
    .astype("string")
    .str.strip()
)

customers["customer_id"] = (
    customers["customer_id"]
    .astype("string")
    .str.strip()
)

combined = orders.merge(
    customers,
    on="customer_id",
    how="left",
    validate="many_to_one",
    indicator=True
)

unmatched = combined.loc[
    combined["_merge"]
    == "left_only"
]
Missing merge keys can match each other

Pandas may match null keys from one DataFrame with null keys in the other DataFrame. Inspect or remove missing keys before merging when this is not the intended behavior.

Fix duplicate pivot combinations

Problem · ValueError Pivot requires unique combinations
# Raises an error when more than
# one sales value exists for the
# same date and region
wide_sales = df.pivot(
    index="order_date",
    columns="region",
    values="sales"
)

pivot() cannot decide how duplicate values should be combined.

Fix · Aggregate duplicates Use pivot_table with an explicit rule
wide_sales = df.pivot_table(
    index="order_date",
    columns="region",
    values="sales",
    aggfunc="sum",
    fill_value=0
)

The aggregation function defines how repeated combinations are combined.

Diagnose unexpected merge results

Python · Merge audit Check uniqueness and row counts
print({
    "orders_rows": len(orders),
    "customers_rows": len(customers),
    "customer_keys_unique": (
        customers["customer_id"]
        .is_unique
    )
})

combined = orders.merge(
    customers,
    on="customer_id",
    how="left",
    suffixes=(
        "_order",
        "_customer"
    ),
    validate="many_to_one",
    indicator="merge_source"
)

print(
    combined["merge_source"]
    .value_counts()
)

print({
    "combined_rows": len(combined)
})
Inspect the smallest failing example

Print column names, data types, DataFrame shapes, missing-value counts, unique-key counts, and a few affected rows. Small targeted checks are usually more useful than printing an entire DataFrame.

Frequently asked questions

Pandas FAQ

Find concise answers to common questions about DataFrames, indexing, missing data, combining datasets, performance, exporting files, and changes introduced in Pandas 3.

What is Pandas used for?

Pandas is a Python library for working with structured data. It is commonly used to import, clean, filter, combine, summarize, reshape, analyze, and export tabular datasets.

Common data sources include CSV files, Excel workbooks, JSON, Parquet files, and SQL databases.

What is the difference between a Series and a DataFrame?

A Series is a one-dimensional labeled collection of values. A DataFrame is a two-dimensional table made from rows and columns.

Selecting one column with df["sales"] normally returns a Series, while selecting multiple columns with df[["sales", "cost"]] returns a DataFrame.

What is the difference between loc and iloc?

loc selects rows and columns by their labels. iloc selects them by integer position.

# Label-based selection
df.loc[10, "sales"]

# Position-based selection
df.iloc[0, 2]

Label slices with loc normally include the ending label, while positional slices with iloc exclude the ending position.

How do I handle missing values in Pandas?

Use isna() to detect missing values, dropna() to remove affected rows, and fillna(), ffill(), bfill(), or interpolate() to replace them.

The appropriate method depends on why the data is missing and how the completed values will be used.

What is the difference between merge, join, and concat?

Use merge() for database-style joins using one or more columns. Use join() primarily to combine DataFrames by their indexes. Use concat() to stack complete Pandas objects by rows or columns.

When merging, use validate and indicator to check key relationships and identify unmatched records.

What is the difference between map, apply, and transform?

Use map() for element-wise operations or Series value mappings. Use apply() when a function needs a complete row or column. Use transform() when the output must remain aligned with the original index.

Prefer vectorized Pandas operations whenever an existing method can express the calculation.

What happened to DataFrame.applymap in Pandas 3?

DataFrame.applymap() was deprecated in earlier versions and removed in Pandas 3. Use DataFrame.map() for element-wise DataFrame transformations.

rounded = numeric_df.map(
    lambda value: round(value, 2),
    na_action="ignore"
)
Why does chained assignment fail in Pandas 3?

Copy-on-Write is always active in Pandas 3. An intermediate object created through chained indexing behaves independently, so assigning through that object cannot update the original DataFrame.

# Avoid
df["sales"][df["sales"] < 0] = 0

# Use
df.loc[
    df["sales"] < 0,
    "sales"
] = 0
Why does a Pandas boolean condition say the truth value is ambiguous?

A comparison such as df["sales"] > 0 returns one boolean value for every row. Pandas cannot automatically decide whether you mean any row, every row, or a filtered result.

condition = df["sales"] > 0

condition.any()
condition.all()

positive_rows = df.loc[condition]
Can Pandas process datasets larger than available memory?

Pandas is primarily designed for in-memory analysis. Large CSV and JSON Lines files can sometimes be processed in chunks using the chunksize parameter.

Loading only required columns, selecting efficient data types, and aggregating each chunk can substantially reduce memory requirements. Workflows requiring the entire dataset at once may need a larger-scale processing system.

When should I use categorical data?

The category dtype is useful when a column contains many repeated values but relatively few unique labels, such as regions, departments, statuses, or product categories.

It may save little memory when nearly every row contains a unique value, so compare memory usage before and after conversion.

Why does pivot fail when pivot_table works?

pivot() requires exactly one value for every index and column combination. Duplicate combinations cause a ValueError.

pivot_table() can handle duplicates because an aggregation function such as sum, mean, or count defines how the values should be combined.

Which export format should I use?

Use CSV for broad compatibility, Excel for human-facing workbook reports, JSON for web and API records, Parquet for efficient analytical storage, and SQL for database workflows.

CSV is simple but does not preserve Pandas data types as reliably as Parquet. Excel and Parquet may require optional writer dependencies.

Use the search field for specific syntax

Search for a method, task, or error message to find the corresponding examples elsewhere in this Pandas cheat sheet.

Printable reference

Download the Pandas Cheat Sheet PDF

Download the free printable Pandas cheat sheet for quick offline access to DataFrame operations, data cleaning, filtering, grouping, merging, reshaping, importing, exporting, and practical examples.

Free printable reference

Pandas Cheat Sheet

Get the complete 16-page Pandas 3.x reference with syntax tables, copy-ready examples, best practices, troubleshooting guidance, and a practical data analysis workflow.

  • 16 printable A4 pages
  • Pandas 3.x syntax and examples
  • DataFrames, cleaning, GroupBy, merging, and reshaping
  • Performance tips and troubleshooting
  • No registration required
Download Free PDF

PDF format · 16 pages · Updated September 2026

Using the PDF

Save it for offline reference or print only the pages you use most. The interactive web version includes searchable sections and working copy buttons.