Cheat sheet
SQL cheat sheet
SQL cheat sheet grouped by task: filter, group and count, join, NULLs, dates, CTEs and window functions. Every query runs on a real store database.
Cheat sheet · 12 tasks · 49 snippets, each run on pandas 2.3
The pandas you reach for on the job, grouped by task. Every snippet runs as written on the Harbor & Pine tables, and each group links to the levels that teach it.
pandas 2.3. Each table is already a DataFrame with the same name as the SQL table (customers, orders, order_items …); date columns are parsed as datetime64; pd and np are imported. The last line of each snippet is the result.
orders.head(10)
orders.tail(3) orders.shape # (rows, columns)
orders.dtypes
orders.info() products.describe() # numeric columns
products.describe(include="object") # text columns orders["status"].value_counts()
orders["status"].value_counts(normalize=True).round(3) # shares orders["customer_id"].nunique() products["unit_price"] # Series
products[["name", "category", "unit_price"]] # DataFrame products.loc[products["category"] == "Lighting", ["name", "unit_price"]] products.iloc[:5, :3] orders[orders["channel"] == "marketplace"] products[(products["category"] == "Decor") & (products["unit_price"] < 50)] Use & | ~ (not and/or/not) and wrap every condition in brackets.
products[products["category"].isin(["Decor", "Kitchen"])]
products[products["unit_price"].between(50, 100)] # inclusive both ends customers[customers["email"].str.contains("@gmail.com", regex=False, na=False)] orders.query("channel == 'app' and shipping_fee > 10") products.sort_values(["category", "unit_price"], ascending=[True, False]) products.nlargest(10, "unit_price")[["name", "unit_price"]] products.sort_values("unit_price", ascending=False).reset_index(drop=True) items = order_items.copy()
items["revenue"] = items["quantity"] * items["unit_price"] * (1 - items["discount_pct"] / 100)
items.head() products["tier"] = np.where(products["unit_price"] >= 200, "premium", "standard")
products["tier"].value_counts() products["band"] = pd.cut(products["unit_price"], bins=[0, 50, 200, np.inf],
labels=["under 50", "50-199", "200+"], right=False)
products["band"].value_counts() right=False makes each band include its left edge: 50 lands in "50-199".
orders.groupby("channel").size() orders.groupby("channel")["shipping_fee"].mean().round(2) products.groupby("category", as_index=False).agg(
products=("product_id", "count"),
avg_price=("unit_price", "mean"),
max_price=("unit_price", "max"),
) orders.groupby(["channel", "status"]).size().unstack(fill_value=0) per_customer = orders.groupby("customer_id").size()
per_customer[per_customer >= 20] orders.pivot_table(index="channel", columns="status",
values="order_id", aggfunc="count", fill_value=0) lines = order_items.merge(products[["product_id", "name", "category"]],
on="product_id", how="inner", validate="many_to_one")
len(lines) == len(order_items) validate= raises an error if the key is not unique where you expect it to be.
m = customers.merge(orders[["customer_id"]].drop_duplicates(),
on="customer_id", how="left", indicator=True)
m[m["_merge"] == "left_only"] customers[~customers["customer_id"].isin(orders["customer_id"])] lines = order_items.merge(products, on="product_id", suffixes=("_paid", "_list"))
lines["revenue"] = lines["quantity"] * lines["unit_price_paid"] * (1 - lines["discount_pct"] / 100)
lines.groupby("category")["revenue"].sum().sort_values(ascending=False).round(2) Both tables have unit_price: suffixes keep both and say which is which.
customers.isna().sum() products[products["unit_cost"].isna()] customers["referral_source"].fillna("unknown").value_counts()
products.dropna(subset=["unit_cost"]) products["unit_cost"].mean() # average of the known costs
products["unit_cost"].fillna(0).mean() # treats missing as 0: lower customers["referral_source"].count() # non-missing only
len(customers) # every row
customers["referral_source"].value_counts(dropna=False) customers["state_clean"] = customers["state"].str.strip().str.upper()
customers["state_clean"].value_counts().head() customers["email"].str.split("@").str[1].value_counts().head() key = customers["email"].str.strip().str.lower()
customers[key.duplicated(keep=False)].sort_values("email") customers.assign(key=customers["email"].str.strip().str.lower()) \
.drop_duplicates(subset="key", keep="first") pd.to_datetime(pd.Series(["2025-03-01", "not a date"]), errors="coerce") orders["order_date"].dt.year.value_counts().sort_index()
orders["order_date"].dt.day_name().value_counts() orders.groupby(orders["order_date"].dt.to_period("M")).size().tail(12) orders[(orders["order_date"] >= "2024-03-01") & (orders["order_date"] < "2024-04-01")] r = (returns.merge(order_items[["order_item_id", "order_id"]], on="order_item_id")
.merge(orders[["order_id", "order_date"]], on="order_id"))
(r["returned_on"] - r["order_date"].dt.normalize()).dt.days.describe() products["price_rank"] = products.groupby("category")["unit_price"].rank(method="dense", ascending=False)
products[products["price_rank"] <= 3].sort_values(["category", "price_rank"]) o = orders.sort_values("order_date")
o["order_n"] = o.groupby("customer_id").cumcount() + 1
o[["customer_id", "order_date", "order_n"]].head() monthly = orders.groupby(orders["order_date"].dt.to_period("M")).size()
pd.DataFrame({
"orders": monthly,
"running": monthly.cumsum(),
"mom_pct": (monthly.pct_change() * 100).round(1),
"avg_3m": monthly.rolling(3).mean().round(1),
}) done = orders.loc[orders["status"] == "completed", ["order_id"]]
lines = order_items.merge(done, on="order_id")
revenue = (lines["quantity"] * lines["unit_price"] * (1 - lines["discount_pct"] / 100)).sum()
round(revenue / lines["order_id"].nunique(), 2) (web_sessions.groupby("source")["converted"].mean() * 100).round(2).sort_values(ascending=False) lines = order_items.merge(products[["product_id", "category"]], on="product_id")
lines["returned"] = lines["order_item_id"].isin(returns["order_item_id"])
(lines.groupby("category")["returned"].mean() * 100).round(1) https://analystquest.com/resources/pandas-cheat-sheet/ · Free levels to practise every snippet: analystquest.com
Cheat sheet
SQL cheat sheet grouped by task: filter, group and count, join, NULLs, dates, CTEs and window functions. Every query runs on a real store database.
Cheat sheet
Excel formulas for analysts, grouped by task: SUMIFS, COUNTIFS, XLOOKUP, IF, text cleaning, dates and duplicates, with the shortcuts that save the most time.
Interview prep
Questions from real analyst interviews, each with a model answer, the answer that loses points and a level to practise it. Terms you are unsure of are in the glossary.