Validation Pipelines
Regex Validate
Test each string in a column against a regex pattern and return a boolean
validity list. By hand, call re.match(pattern, s) in a loop. With pandas,
Series.str.match(pattern) applies the same match to every element and
returns a boolean Series.
By hand
re.match anchors the match at the start of the string. Adding $ to the
pattern anchors it at the end too, requiring a full-string match. The loop
tests each code: two uppercase letters followed by exactly three digits pass;
lowercase inputs, wrong letter count, or wrong digit count fail.
naive.py
Replay: real traced execution (multi-file project)
import re
pattern = r'^[A-Z]{2}\d{3}$'
codes = ['AB123', 'cd456', 'XY789', 'A1234', 'MN001', 'ZZ99']
result = []
for s in codes:
result.append(re.match(pattern, s) is not None)
print('RESULT:', result)
import re
1import re2pattern = r'^[A-Z]{2}\d{3}$'pattern ← '^[A-Z]{2}\\d{3}$'
1import re2pattern = r'^[A-Z]{2}\d{3}$'3codes = ['AB123', 'cd456', 'XY789', 'A1234', 'MN001', 'ZZ99']values this step'^[A-Z]{2}\\d{3}$'patterncodes ← ['AB123', 'cd456', 'XY789', 'A1234', 'MN001', 'ZZ99']
2pattern = r'^[A-Z]{2}\d{3}$'3codes = ['AB123', 'cd456', 'XY789', 'A1234', 'MN001', 'ZZ99']4result = []values this step['AB123', 'cd456', 'XY789', 'A1234', 'MN001', 'ZZ99']codesresult ← []
3codes = ['AB123', 'cd456', 'XY789', 'A1234', 'MN001', 'ZZ99']4result = []5for s in codes:values this step[]results ← 'AB123'
4result = []5for s in codes:6 result.append(re.match(pattern, s) is not None)values this step'AB123'sresult ← [True]
5for s in codes:6 result.append(re.match(pattern, s) is not None)7print('RESULT:', result)values this step[] → [True]results ← 'cd456'
4result = []5for s in codes:6 result.append(re.match(pattern, s) is not None)values this step'AB123' → 'cd456'sresult ← [True, False]
5for s in codes:6 result.append(re.match(pattern, s) is not None)7print('RESULT:', result)values this step[True] → [True, False]results ← 'XY789'
4result = []5for s in codes:6 result.append(re.match(pattern, s) is not None)values this step'cd456' → 'XY789'sresult ← [True, False, True]
5for s in codes:6 result.append(re.match(pattern, s) is not None)7print('RESULT:', result)values this step[True, False] → [True, False, True]results ← 'A1234'
4result = []5for s in codes:6 result.append(re.match(pattern, s) is not None)values this step'XY789' → 'A1234'sresult ← [True, False, True, False]
5for s in codes:6 result.append(re.match(pattern, s) is not None)7print('RESULT:', result)values this step[True, False, True] → [True, False, True, False]results ← 'MN001'
4result = []5for s in codes:6 result.append(re.match(pattern, s) is not None)values this step'A1234' → 'MN001'sresult ← [True, False, True, False, True]
5for s in codes:6 result.append(re.match(pattern, s) is not None)7print('RESULT:', result)values this step[True, False, True, False] → [True, False, True, False, True]results ← 'ZZ99'
4result = []5for s in codes:6 result.append(re.match(pattern, s) is not None)values this step'MN001' → 'ZZ99'sresult ← [True, False, True, False, True, False]
5for s in codes:6 result.append(re.match(pattern, s) is not None)7print('RESULT:', result)values this step[True, False, True, False, True] → [True, False, True, False, True, False]resultfor s in codes:
4result = []5for s in codes:6 result.append(re.match(pattern, s) is not None)stdout ← RESULT: [True, False, True, False, True, False]
6 result.append(re.match(pattern, s) is not None)7print('RESULT:', result)values this stepRESULT: [True, False, True, False, True, False]stdout
With pandas
.str.match(pattern) uses re.match semantics — anchored at the start,
with $ anchoring the end. The same pattern r'^[A-Z]{2}\d{3}$' is used
in both halves, so the results are identical.
library.py
import pandas as pd
from dalib.display import set_display
set_display()
pattern = r'^[A-Z]{2}\d{3}$'
codes = ['AB123', 'cd456', 'XY789', 'A1234', 'MN001', 'ZZ99']
df = pd.DataFrame({'x': codes})
flags = df['x'].str.match(pattern)
result = flags.tolist()
print('index:', flags.index.tolist())
print('dtype:', flags.dtype)
print('values:', flags.tolist())
print('RESULT:', result)
index: [0, 1, 2, 3, 4, 5]
dtype: bool
values: [True, False, True, False, True, False]
RESULT: [True, False, True, False, True, False]
Implementation notes
.str.matchusesre.match(start-anchored). Without$, a string like'AB123extra'would pass because the pattern matches the start. Include$whenever full-string matching is required..str.fullmatch(pattern)(pandas 1.1+) anchors both ends automatically, sor'[A-Z]{2}\d{3}'without^or$gives the same result.re.matchreturns a match object orNone;is not Noneconverts it to bool in the naive half.