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)
  1. import re

    1import re2pattern = r'^[A-Z]{2}\d{3}$'
  2. 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}$'pattern
  3. codes ← ['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']codes
  4. result ← []

    3codes = ['AB123', 'cd456', 'XY789', 'A1234', 'MN001', 'ZZ99']4result = []5for s in codes:
    values this step[]result
  5. s ← 'AB123'

    4result = []5for s in codes:6    result.append(re.match(pattern, s) is not None)
    values this step'AB123's
  6. result ← [True]

    5for s in codes:6    result.append(re.match(pattern, s) is not None)7print('RESULT:', result)
    values this step[] [True]result
  7. s ← 'cd456'

    4result = []5for s in codes:6    result.append(re.match(pattern, s) is not None)
    values this step'AB123' 'cd456's
  8. result ← [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]result
  9. s ← 'XY789'

    4result = []5for s in codes:6    result.append(re.match(pattern, s) is not None)
    values this step'cd456' 'XY789's
  10. result ← [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]result
  11. s ← 'A1234'

    4result = []5for s in codes:6    result.append(re.match(pattern, s) is not None)
    values this step'XY789' 'A1234's
  12. result ← [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]result
  13. s ← 'MN001'

    4result = []5for s in codes:6    result.append(re.match(pattern, s) is not None)
    values this step'A1234' 'MN001's
  14. result ← [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]result
  15. s ← 'ZZ99'

    4result = []5for s in codes:6    result.append(re.match(pattern, s) is not None)
    values this step'MN001' 'ZZ99's
  16. result ← [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]result
  17. for s in codes:

    4result = []5for s in codes:6    result.append(re.match(pattern, s) is not None)
  18. 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.match uses re.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, so r'[A-Z]{2}\d{3}' without ^ or $ gives the same result.
  • re.match returns a match object or None; is not None converts it to bool in the naive half.