All paths · #039 · August 31, 2026 · 1 min read
How much regex do you need for data work?
Anchors, classes, quantifiers, groups, four patterns to steal, and how to use them from pandas and SQL: the 20 regex pieces that cover 99% of data cleaning.
Get the free PDF
One page, print-ready, free to share. No signup needed.
Regex is a write-only language: nobody remembers it, everybody needs it. These 20 pieces cover 99% of data-cleaning work, and the same patterns run in Python, pandas and SQL. The print-ready A4 PDF is at the bottom.
Anchors and classes
^start/end$: the edges of the string.\d \w \s: digit, word character, whitespace..: any single character.[A-Za-z]: build your own class.
Quantifiers
+/*: one-or-more / zero-or-more.?: optional.{2,4}: between 2 and 4 repeats..*: greedy. Eats everything it can.
Groups
(abc): capture it.(?:abc): group without capturing.a|b: or.\1: reuse capture 1.
Patterns to steal
\d{4}-\d{2}-\d{2}: ISO date.[\w.]+@[\w.]+\.\w+: email-ish.\d+[.,]\d+: decimal number.\s{2,}: runs of extra spaces.
In pandas
s.str.contains(r'…'): filter rows.s.str.extract(r'(…)'): pull the group into a column.s.str.replace(r'…',''): clean.
In SQL
x ~ 'regex': Postgres match.REGEXP_LIKE(x, r): most other dialects.REGEXP_REPLACE(x, r, ''): clean in place.
Columns out of chaos
# "order #4521 shipped 2026-08-12 (US)"
s = df["raw"]
df["order_id"] = s.str.extract(r"#(\d+)")
df["date"] = s.str.extract(r"(\d{4}-\d{2}-\d{2})")
df["country"] = s.str.extract(r"\((\w{2})\)")
extract returns whatever the (group) captures. One pattern per concept beats one giant pattern.
The trap: greedy by default
| Pattern | On this input | It matches |
|---|---|---|
<b>(.*)</b> | <b>a</b> <b>b</b> | a</b> <b>b : too much |
<b>(.*?)</b> | <b>a</b> <b>b</b> | a : lazy stops early |
^\d+$ | abc123 | nothing: anchors save you |
Add ? after * or + to make it lazy, and anchor with ^ $ whenever you mean the whole string.
Frequently asked questions
What regex do I need to extract dates from text?
For ISO dates, \d{4}-\d{2}-\d{2}: four digits, dash, two digits, dash, two digits. In pandas: s.str.extract(r'(\d{4}-\d{2}-\d{2})'). Wrap the part you want in parentheses: extract returns whatever the group captures.
What is the difference between greedy and lazy matching?
Quantifiers are greedy by default: .* eats as much as it can while still allowing a match, so <b>(.*)</b> on a string with two bold tags captures everything between the FIRST opening and the LAST closing tag. Adding ? makes it lazy: (.*?) stops at the first closing tag.
Can I use regex in SQL?
Yes. Postgres matches with the ~ operator (x ~ 'regex'), most other dialects have REGEXP_LIKE(x, r), and REGEXP_REPLACE(x, r, '') cleans in place. Same patterns, same syntax core as Python.
How do I use regex in pandas?
Three string methods cover most needs: s.str.contains(r'…') to filter rows, s.str.extract(r'(…)') to pull a captured group into a new column, s.str.replace(r'…', '') to clean. All three take the same patterns as this sheet.
Get the free PDF
One page, print-ready, free to share. No signup needed.