Skip to content

Quick-reference cheat sheet

Chapter 25 4 code blocks 2670 words

The code in this chapter uses these filesrun it on your own machine

Everything in the guide, compressed. This chapter is meant to be printed — use the PDF button and export chapter 25 alone. Nothing here is explained; every item links back to the chapter that explains it.

SQL — core syntax

The order you write vs the order it runsWRITE: SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT RUN: FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT ↑ aliases are born here
Task Syntax Ch.
Pick columns SELECT a, b AS label FROM t 2
Filter rows WHERE x > 5 AND (y = 'a' OR z IS NULL) 2
Sort ORDER BY a DESC, b ASC 2
Limit / paginate LIMIT 10 OFFSET 20 2
Unique rows SELECT DISTINCT a, b 2
List membership WHERE city IN ('A','B') 3
Range (inclusive) WHERE g BETWEEN 80 AND 90 3
Text pattern WHERE name LIKE 'Kar%' · %=many, _=one 3
Missing values WHERE g IS NULL / IS NOT NULL 1, 3
Replace NULL COALESCE(a, b, 'default') 3
Guard division x / NULLIF(y, 0) 3
Aggregate COUNT/SUM/AVG/MIN/MAX 4
Group GROUP BY a HAVING COUNT(*) > 1 4
Join JOIN t2 ON t1.id = t2.id 5
Keep unmatched LEFT JOIN t2 ON ... 5
Named step WITH s AS (SELECT ...) SELECT ... FROM s 6
Existence test WHERE EXISTS (SELECT 1 FROM ...) 6
Rank ROW_NUMBER() OVER (PARTITION BY g ORDER BY x DESC) 7
Previous row LAG(x) OVER (ORDER BY d) 7
Running total SUM(x) OVER (ORDER BY d) 7
If/else CASE WHEN c THEN a ELSE b END 8
Stack results UNION ALL 9
In both / only first INTERSECT / EXCEPT 9
Speed up a filter CREATE INDEX i ON t(col) 11
See the plan EXPLAIN QUERY PLAN <query> 11

SQL — the patterns worth memorising

python48 lines
-- Top N per group ................................................ Ch.7
WITH r AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY g ORDER BY x DESC) rn FROM t)
SELECT * FROM r WHERE rn <= 3;

-- Which X has no Y?  (anti-join) ................................. Ch.5
SELECT a.* FROM a LEFT JOIN b ON a.id = b.a_id WHERE b.a_id IS NULL;
-- NULL-safe alternative:
SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE b.a_id = a.id);

-- Find duplicates ................................................ Ch.4
SELECT k1, k2, COUNT(*) FROM t GROUP BY k1, k2 HAVING COUNT(*) > 1;

-- Pivot: rows to columns ......................................... Ch.8
SELECT g,
       SUM(CASE WHEN s = 'a' THEN 1 ELSE 0 END) AS a,
       SUM(CASE WHEN s = 'b' THEN 1 ELSE 0 END) AS b
FROM t GROUP BY g;

-- Percentage of a group matching a condition ..................... Ch.4
ROUND(100.0 * SUM(CASE WHEN cond THEN 1 ELSE 0 END) / COUNT(*), 1)

-- Each row's share of the total .................................. Ch.7
ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 1)

-- Month-over-month change ........................................ Ch.7
(n - LAG(n) OVER (ORDER BY m)) * 100.0 / LAG(n) OVER (ORDER BY m)

-- Second / Nth highest ........................................... Ch.13
WITH r AS (SELECT x, DENSE_RANK() OVER (ORDER BY x DESC) k FROM t)
SELECT DISTINCT x FROM r WHERE k = 2;

-- Median (no MEDIAN() in standard SQL) ........................... Ch.13
WITH o AS (SELECT x, ROW_NUMBER() OVER (ORDER BY x) rn, COUNT(*) OVER () n FROM t)
SELECT AVG(x) FROM o WHERE rn IN ((n+1)/2, (n+2)/2);

-- Safe date range (never BETWEEN on a timestamp) ................. Ch.3
WHERE d >= '2026-01-01' AND d < '2026-02-01';

-- Aggregate BEFORE joining, to avoid fan-out ..................... Ch.5
WITH per AS (SELECT fk, COUNT(*) n FROM child GROUP BY fk)
SELECT p.*, per.n FROM parent p LEFT JOIN per ON per.fk = p.id;

-- Safe update ritual ............................................. Ch.10
SELECT * FROM t WHERE cond;        -- 1. look
BEGIN TRANSACTION;                  -- 2. protect
UPDATE t SET c = v WHERE cond;      -- 3. same WHERE
SELECT * FROM t WHERE cond;         -- 4. check
COMMIT;                             -- or ROLLBACK

SQL — gotchas in one line each

Gotcha Fix Ch.
NOT IN + a NULL → zero rows, no error NOT EXISTS 3
COUNT(*) after LEFT JOIN → 0 becomes 1 COUNT(right.pk) 5
Join to the many side inflates SUM Aggregate first, then join 5
Right-table filter in WHERE kills the LEFT JOIN Put it in ON 5
col <> 'x' drops NULL rows silently OR col IS NULL 2
12/20*100 = 0 * 100.0 2
CASE without a NULL branch → ungraded reads as F WHEN x IS NULL first 8
GROUP BY hides empty groups LEFT JOIN from the dimension 4, 5
SUM over zero rows = NULL COALESCE(SUM(x),0) 4
ELSE 0 inside AVG(CASE...) Omit the ELSE 8
LIMIT with no ORDER BY Always sort first 2
Alias used in WHERE Repeat it, or use a CTE 2
Window function in WHERE CTE, then filter 7
Function wrapped round a filtered column Rewrite as a range 11
UPDATE/DELETE with no WHERE SELECT first, in a transaction 10
SQLite ignores foreign keys by default PRAGMA foreign_keys = ON 10
f-string SQL in Python Parameters ? 12
Missing conn.commit() with conn: 12

Which plot do I need?

Decide in three questions
  1. How many variables? 1 → distribution 2 → relationship 3+ → facet or grid
  2. What types are they? numeric / categorical / time
  3. How many rows? small → show points large → show density
Question Data Plot Ch.
How is it spread? 1 numeric histplot, kdeplot, ecdfplot 16
How many of each? 1 categorical countplot 17
Do they move together? 2 numeric scatterplot, jointplot 15, 20
Is there a trend? 2 numeric regplot, lmplot 19
Over time? time + numeric lineplot 15
Different between groups? cat + numeric boxplot, violinplot 17
Average per group? cat + numeric barplot (a mean!) 17
Two categories interacting? 2 categorical heatmap of crosstab 18
What relates to what? many numeric corr heatmap, pairplot 18, 20
Holds in every subgroup? any + category col= faceting 20
Where are the extremes? 1 numeric boxplot 17
Too many points to see 2 numeric, large n hexbin, 2D histplot, kdeplot 16, 20

Seaborn — function reference

Axes-level → figure-levelscatterplot, lineplot → relplot histplot, kdeplot, ecdfplot, rugplot → displot barplot, countplot, boxplot, violinplot, boxenplot, stripplot, swarmplot, pointplot → catplot regplot, residplot → lmplot axes-level : takes ax=, returns Axes, size via plt.figure(figsize=) figure-level: no ax=, returns Grid, size via height= and aspect=, supports col= / row=, title via g.figure.suptitle()
python43 lines
# the shape every seaborn call takes
sns.PLOT(data=df, x="col", y="col", hue="col", ...)

# relational .................................................. Ch.15
sns.scatterplot(data=d, x=, y=, hue=, size=, style=, alpha=)
sns.lineplot(data=d, x=, y=, hue=, errorbar=("ci", 95))
sns.relplot(data=d, x=, y=, col=, row=, kind="scatter"|"line", height=, aspect=)

# distribution ................................................ Ch.16
sns.histplot(data=d, x=, bins=|binwidth=, stat="count"|"density", kde=True,
             hue=, multiple="layer"|"dodge"|"stack"|"fill", element="bars"|"step")
sns.kdeplot(data=d, x=, y=, bw_adjust=1, fill=True, cut=0, common_norm=False)
sns.ecdfplot(data=d, x=, hue=)
sns.displot(data=d, x=, col=, kind="hist"|"kde"|"ecdf")

# categorical ................................................. Ch.17
sns.countplot(data=d, x=, hue=)                     # counts ROWS
sns.barplot(data=d, x=, y=, estimator="mean", errorbar=("ci",95))  # a MEAN
sns.boxplot(data=d, x=, y=, hue=, order=[...])
sns.violinplot(data=d, x=, y=, hue=, split=True, cut=0, inner="box")
sns.stripplot(data=d, x=, y=, jitter=.25, alpha=.6)  # small n
sns.swarmplot(data=d, x=, y=, size=3)                # small n only
sns.pointplot(data=d, x=, y=, hue=)
sns.catplot(data=d, x=, y=, col=, kind="box"|"bar"|"violin"|"strip"|"count")

# matrix (WIDE data) .......................................... Ch.18
sns.heatmap(matrix, annot=True, fmt=".2f", cmap="coolwarm",
            center=0, vmin=-1, vmax=1, mask=mask, linewidths=.5)
sns.clustermap(matrix, cmap="vlag", center=0)
# get a matrix first:
df.pivot(index=, columns=, values=) | df.pivot_table(...) | pd.crosstab(a, b)
df.corr(numeric_only=True)                           # pandas 2+ needs this

# regression .................................................. Ch.19
sns.regplot(data=d, x=, y=, order=2, lowess=True, robust=True, logistic=True, ci=95)
sns.residplot(data=d, x=, y=)
sns.lmplot(data=d, x=, y=, hue=, col=, height=, aspect=)

# grids ....................................................... Ch.20
sns.pairplot(d, hue=, vars=[...], diag_kind="kde", corner=True, kind="scatter")
sns.jointplot(data=d, x=, y=, kind="scatter"|"hex"|"kde"|"reg", height=)
g = sns.FacetGrid(d, col=, row=, col_wrap=3, height=, hue=)
g.map_dataframe(sns.scatterplot, x=, y=); g.add_legend()

Seaborn — styling and output

python24 lines
# theme ....................................................... Ch.21
sns.set_theme(style="whitegrid", context="talk", palette="deep", font_scale=1.1)
# style:   darkgrid | whitegrid | dark | white | ticks
# context: paper | notebook | talk | poster   (scales ink, NOT figure size)
sns.despine()

# palettes — match the palette TYPE to the data type
qualitative : "deep" "muted" "colorblind" "Set2"    # unordered categories
sequential  : "viridis" "rocket" "mako" "Blues"     # low → high
diverging   : "coolwarm" "vlag" "RdBu_r"            # midpoint — use center=0

# finishing (matplotlib)
plt.figure(figsize=(9, 5))                # axes-level only
plt.title("...") ; plt.xlabel("...") ; plt.ylabel("...")
plt.xticks(rotation=45, ha="right")
plt.legend(title="", bbox_to_anchor=(1.02, 1), loc="upper left")
plt.axhline(0, ls="--", color="grey")
ax.bar_label(ax.containers[0], fmt="%.0f")
plt.tight_layout()

# save — ALWAYS before plt.show()
plt.savefig("f.png", dpi=300, bbox_inches="tight")   # 300 print, 100 screen
g.savefig("f.png", dpi=300, bbox_inches="tight")     # figure-level
plt.show()

SQL ↔ pandas ↔ seaborn

python8 lines
import sqlite3, pandas as pd, numpy as np, seaborn as sns, matplotlib.pyplot as plt

conn = sqlite3.connect("school.db")
df = pd.read_sql("SELECT ... FROM ... WHERE ...", conn,
                 params=(value,),              # NEVER an f-string
                 parse_dates=["enrolled_at"])
conn.close()
sns.boxplot(data=df, x="course", y="grade")

Seaborn — gotchas in one line each

Gotcha Fix Ch.
barplot shows a mean, not a count countplot counts rows 17
plt.title() titles only the last facet g.figure.suptitle() 20
figsize ignored by figure-level height=, aspect= 20
savefig after show → blank file Save first 21
KDE invents density past the data cut=0, or use histplot 16
Bin count changes the story Try several; prefer binwidth 16
Boxplot hides bimodality violinplot 17
swarmplot drops points on large n stripplot or violinplot 17
Alphabetical category order is meaningless order=[...] 17
df.corr() errors on text columns numeric_only=True 18
Diverging colormap without a midpoint center=0, vmin/vmax 18
CI band read as a prediction interval It is uncertainty of the line 19
lmplot rejects ax= Use regplot 19
pairplot drops NaN rows silently Check isna() first 20
Sequential palette on unordered categories Use a qualitative palette 21
Colour as the only encoding Add marker/linestyle/labels 21
load_dataset() needs internet Save CSVs beforehand 14
Wide data into a long-format function df.melt() 14
Long data into heatmap df.pivot() 18
distplot / ci= histplot / errorbar= 14

The one-page workflow

From question to chart1. QUESTION Say it in one sentence. Name the output: how many rows, which columns. 2. SQL FROM → JOIN → WHERE → GROUP BY → SELECT Build it in layers, running each layer. Check COUNT(*) before and after every JOIN. ← fan-out Reduce here: filter, aggregate, select only what you need. 3. PANDAS pd.read_sql(q, conn, params=(...), parse_dates=[...]) df.info() ← the single most valuable call Fix dtypes. Count NaN. Decide, do not reflex-drop. 4. EDA shape → missing → 1 var → 2 vars → many vars → outliers Always colour by the obvious group. ← Simpson's paradox 5. CHART sns.set_theme(style="whitegrid", context="talk") Pick from the plot-picker table. Two or three variables, max. Label axes in human words, not column names. 6. SAVE plt.savefig(..., dpi=300, bbox_inches="tight") BEFORE plt.show() 7. WRITE The deliverable is 3-5 sentences of findings, not 20 charts. Report n. Report what you excluded and why.
💡 The three sentences to memorise
  1. SQL does not run in the order you write it — FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY.
  2. NULL means unknown, so every comparison with it is UNKNOWN and WHERE keeps only TRUE.
  3. Reduce in SQL, analyse in Python, and plot only what you can explain in a sentence.

Questions about this chapter?

The course, homework review and the other five books are in Uzbek. Questions in English are welcome on Telegram.

Write on Telegram Contents