pandas (Python) Cheat Sheet
40 essentials with syntax and working examples. Search or filter by group.
read_csv
Loads a CSV file into a DataFrame. The other examples assume: import pandas as pd; df = pd.read_csv("sales_flat.csv").
pd.read_csv(path, sep=',', usecols=None, dtype=None)import pandas as pd
df = pd.read_csv("sales_flat.csv")
print(df.shape)read_csv(parse_dates=)
Converts date columns to datetime while reading, so .dt and resample work right away.
pd.read_csv(path, parse_dates=["col"])orders = pd.read_csv("orders.csv", parse_dates=["order_date"])
print(orders["order_date"].dtype)read_sql
Runs a SQL query and returns the result as a DataFrame.
pd.read_sql(query, connection)import sqlite3
con = sqlite3.connect("sunshine_grocers.db")
stores = pd.read_sql("SELECT * FROM stores WHERE region = 'South'", con)
con.close()to_csv
Saves a DataFrame to CSV. index=False avoids writing the row index as an extra column.
df.to_csv(path, index=False)df[df["region"] == "South"].to_csv("south_sales.csv", index=False)to_excel
Writes to an .xlsx file (needs openpyxl). Use pd.ExcelWriter to write several sheets into one file.
df.to_excel(path, sheet_name='Sheet1', index=False)by_region = df.groupby("region", as_index=False)["net_sales"].sum()
by_cat = df.groupby("category", as_index=False)["net_sales"].sum()
with pd.ExcelWriter("summary.xlsx") as writer:
by_region.to_excel(writer, sheet_name="Region", index=False)
by_cat.to_excel(writer, sheet_name="Category", index=False)head / tail / shape
First or last n rows (5 by default) and the (rows, columns) size.
df.head(n) | df.tail(n) | df.shapeprint(df.head(3))
print(df.shape)info / dtypes
Column names, data types and non-null counts. The first check after loading data.
df.info() | df.dtypesdf.info()
print(df.dtypes)describe
Summary statistics (count, mean, std, min, quartiles, max) for numeric columns.
df.describe(include=None)print(df[["quantity", "unit_price", "net_sales"]].describe().round(2))value_counts
Counts each distinct value of a column; normalize=True returns proportions.
s.value_counts(normalize=False)print(df["channel"].value_counts())
print(df["segment"].value_counts(normalize=True).round(3))Select columns
One column returns a Series; a list of columns returns a DataFrame.
df["col"] | df[["col1", "col2"]]sales = df["net_sales"]
subset = df[["order_date", "store_name", "net_sales"]]loc
Selects by labels and boolean conditions. Also the safe way to assign values to a filtered subset.
df.loc[row_condition, columns]south = df.loc[df["region"] == "South", ["store_name", "net_sales"]]
df.loc[df["discount"] > 0, "promo"] = "Yes"iloc
Selects by integer position (0-based, end excluded).
df.iloc[rows, cols]first_rows = df.iloc[:5, :4]
last_row = df.iloc[-1]Boolean filter
Filters rows with conditions. Wrap each condition in parentheses and use &, |, ~ (not and/or).
df[(cond1) & (cond2)] | | for OR | ~ for NOTbig_online = df[(df["channel"] == "Online") & (df["net_sales"] > 30)]
print(len(big_online))isin
Keeps rows whose value is in a list (like SQL IN).
df[df["col"].isin([v1, v2])]fresh = df[df["category"].isin(["Produce", "Dairy", "Bakery"])]query
Filters with a readable text expression. Use @var to reference Python variables.
df.query("expression")min_sales = 25
result = df.query("region == 'West' and net_sales > @min_sales")between
TRUE when the value is within a range (inclusive by default). Works with ISO date strings too.
s.between(left, right, inclusive='both')jan_2025 = df[df["order_date"].between("2025-01-01", "2025-01-31")]New column / assign
Creates calculated columns with vectorized math. assign returns a new DataFrame, handy for chaining.
df["new"] = expr | df.assign(new=expr)df["profit"] = df["net_sales"] - df["cost"]
df = df.assign(margin_pct=lambda d: d["profit"] / d["net_sales"] * 100)rename
Renames columns (or index labels) and returns a new DataFrame.
df.rename(columns={"old": "new"})out = df.rename(columns={"net_sales": "Net Sales", "store_name": "Store"})sort_values
Sorts rows by one or more columns.
df.sort_values(by, ascending=True)top = df.sort_values(["net_sales", "order_date"], ascending=[False, True]).head(10)drop / drop_duplicates
Removes columns, or removes repeated rows (all columns or a subset).
df.drop(columns=[...]) | df.drop_duplicates(subset=None, keep='first')dirty = pd.read_csv("sales_dirty.csv")
clean = dirty.drop_duplicates()
print(len(dirty) - len(clean), "duplicates removed")
slim = clean.drop(columns=["brand", "cost"])str methods
Vectorized text cleaning on a column.
s.str.strip() | .str.title() | .str.upper() | .str.replace() | .str.contains()dirty = pd.read_csv("sales_dirty.csv")
dirty["store_name"] = dirty["store_name"].str.strip().str.title()
dirty["category"] = dirty["category"].str.title()to_numeric / astype
Converts text to numbers; errors='coerce' turns bad values into NaN. astype changes a column's type.
pd.to_numeric(s, errors='coerce') | s.astype(type)dirty = pd.read_csv("sales_dirty.csv")
dirty["net_sales"] = pd.to_numeric(dirty["net_sales"].str.replace("$", "", regex=False))
dirty["quantity"] = dirty["quantity"].str.extract(r"(\d+)", expand=False).astype(int)apply
Runs a function on each value (Series) or each row (axis=1). Flexible but slower than vectorized code.
s.apply(func) | df.apply(func, axis=1)def size_band(x):
return "Large" if x >= 30 else "Small"
df["basket"] = df["net_sales"].apply(size_band)
print(df["basket"].value_counts())where (numpy)
Fast vectorized IF for creating a column from a condition.
np.where(condition, value_if_true, value_if_false)import numpy as np
df["is_promo"] = np.where(df["discount"] > 0, "Promo", "Regular")isna / notna
Detects missing values; .sum() gives the count per column.
df.isna().sum() | s.notna()dirty = pd.read_csv("sales_dirty.csv")
print(dirty.isna().sum())fillna
Replaces missing values. Assign the result back (inplace on a single column doesn't work with copy-on-write in pandas 3).
s.fillna(value) | df.fillna({"col": value})dirty = pd.read_csv("sales_dirty.csv")
dirty["region"] = dirty["region"].fillna("Unknown")dropna
Removes rows with missing values (optionally only in some columns).
df.dropna(subset=None, how='any')dirty = pd.read_csv("sales_dirty.csv")
with_region = dirty.dropna(subset=["region"])groupby + sum
Splits rows into groups and aggregates each one. as_index=False returns a regular DataFrame.
df.groupby(keys)[col].sum()by_region = (df.groupby("region", as_index=False)["net_sales"].sum()
.sort_values("net_sales", ascending=False))
print(by_region)groupby + agg (named)
Several aggregations at once with clear output names.
df.groupby(key).agg(new=("col", "func"), ...)summary = df.groupby("store_name").agg(
sales=("net_sales", "sum"),
orders=("order_id", "nunique"),
avg_line=("net_sales", "mean"),
).round(2)
print(summary.head())groupby + transform
Returns a group result aligned to every original row, e.g. each line's share of its region.
df.groupby(key)[col].transform(func)df["region_total"] = df.groupby("region")["net_sales"].transform("sum")
df["pct_of_region"] = df["net_sales"] / df["region_total"] * 100nlargest / nsmallest
Top or bottom n values without sorting the whole dataset.
s.nlargest(n) | df.nlargest(n, col)top5 = df.groupby("product_name")["net_sales"].sum().nlargest(5)
print(top5)merge
Joins two DataFrames on key columns (like SQL JOIN). Use validate= to catch unexpected duplicates.
pd.merge(left, right, on=key, how='inner'|'left'|'right'|'outer')orders = pd.read_csv("orders.csv")
stores = pd.read_csv("stores.csv")
m = orders.merge(stores[["store_id", "store_name", "region"]],
on="store_id", how="left", validate="many_to_one")concat
Stacks DataFrames vertically (or side by side with axis=1).
pd.concat([df1, df2], ignore_index=True)y2024 = df[df["order_date"] < "2025-01-01"]
y2025 = df[df["order_date"] >= "2025-01-01"]
all_years = pd.concat([y2024, y2025], ignore_index=True)pivot_table
Builds an Excel-style pivot table.
df.pivot_table(index, columns, values, aggfunc='sum', fill_value=0, margins=False)pt = df.pivot_table(index="region", columns="category",
values="net_sales", aggfunc="sum",
fill_value=0, margins=True).round(0)
print(pt)melt
Unpivots wide columns into long rows (the opposite of pivot).
df.melt(id_vars, value_vars, var_name, value_name)budget = pd.read_csv("budget.csv")
long = budget.melt(id_vars=["month", "store_id"],
value_vars=["revenue_budget", "expense_budget"],
var_name="type", value_name="amount")crosstab
Frequency table of two columns. normalize='index' gives row percentages.
pd.crosstab(index, columns, normalize=False)print(pd.crosstab(df["segment"], df["channel"], normalize="index").round(3))to_datetime
Converts text to datetime. format='mixed' handles columns with several date formats.
pd.to_datetime(s, format=None, errors='raise')df["order_date"] = pd.to_datetime(df["order_date"])
dirty = pd.read_csv("sales_dirty.csv")
dirty["order_date"] = pd.to_datetime(dirty["order_date"], format="mixed").dt accessor
Extracts parts of datetime values.
s.dt.year | .dt.month | .dt.day_name() | .dt.to_period('M')df["order_date"] = pd.to_datetime(df["order_date"])
df["year"] = df["order_date"].dt.year
df["weekday"] = df["order_date"].dt.day_name()
df["month"] = df["order_date"].dt.to_period("M")resample
Groups a time series by period: 'D' day, 'W' week, 'MS' month start, 'QS' quarter start, 'YS' year start.
df.resample(rule, on=date_col).agg(...)df["order_date"] = pd.to_datetime(df["order_date"])
monthly = df.resample("MS", on="order_date")["net_sales"].sum()
print(monthly.head())rolling / pct_change
Moving windows (e.g. 3-month average) and period-over-period growth.
s.rolling(window).mean() | s.pct_change()df["order_date"] = pd.to_datetime(df["order_date"])
monthly = df.resample("MS", on="order_date")["net_sales"].sum()
trend = pd.DataFrame({
"sales": monthly,
"ma_3m": monthly.rolling(3).mean(),
"mom_pct": monthly.pct_change() * 100,
}).round(1)