Parsing Sequence Data with Regular Expressions
After Completing This Topic
You'll be able to search for patterns in text with Python's re module, extract information from FASTA headers, and validate sequence data.
When You Need Regular Expressions
Let's look at a FASTA file header:
>sp|P04637|P53_HUMAN Cellular tumor antigen p53 OS=Homo sapiens OX=9606You want to extract just "P04637" (the UniProt ID) from this line. You could split the string with split("|"), but if the header format varies across files, you'd need different code each time.
Regular expressions (regex) search text by "pattern." Define a pattern like "alphanumeric characters between pipe symbols," and you can use the same code to extract from any header format.
Lab analogy โ just as an antibody binds to a specific epitope (pattern), regular expressions "bind" to specific patterns in text.
Python re Module Basics
import re
text = "EGFR expression: 12.5 ng/mL"
match = re.search(r"\d+\.\d+", text)if match: value = float(match.group()) print(f"Value: {value}")
assert value == 12.5re.search(pattern, text) โ finds the first part of the text that matches the pattern.
Core Pattern Characters
| Pattern | Meaning | Example | Match |
|---|---|---|---|
\d | One digit | \d\d | "42" |
\d+ | One or more digits | \d+ | "123", "4" |
\w | Alphanumeric + underscore | \w+ | "gene_1" |
. | Any single character | a.b | "a1b", "a-b" |
* | Previous pattern 0+ times | ab*c | "ac", "abc", "abbc" |
+ | Previous pattern 1+ times | ab+c | "abc", "abbc" (not "ac") |
? | Previous pattern 0 or 1 time | colou?r | "color", "colour" |
[ABC] | One of A, B, C | [ATGC]+ | "ATGCGTA" |
^ | Start of line | ^> | FASTA header line |
| | Or (OR) | cat|dog | "cat" or "dog" |
Prefixing patterns with r makes them raw strings (r"\d+"). This prevents backslashes (\) from conflicting with Python string escape sequences. Always use r"..." for regular expressions.
Practical: Parsing FASTA Headers
import re
header = ">sp|P04637|P53_HUMAN Cellular tumor antigen p53 OS=Homo sapiens OX=9606"
uniprot_id = re.search(r"\|(\w+)\|", header)if uniprot_id: print(f"UniProt ID: {uniprot_id.group(1)}")
organism = re.search(r"OS=(.+?) OX=", header)if organism: print(f"Organism: {organism.group(1)}")Output:
UniProt ID: P04637
Organism: Homo sapiens() โ the part inside parentheses is a capture group. Use group(1) to extract only the content within the parentheses.
.+? โ adding ? makes it a lazy match (minimum matching). It matches as little as possible. Without ?, .+ matches as much as possible (greedy), which may produce unexpected results.
Practical: Finding Motifs in DNA Sequences
When you want to find all restriction enzyme recognition sites in a sequence:
import re
sequence = "ATCGAATTCGCGAATTCTTGAATTCAA"
# EcoRI recognition site: GAATTCsites = [m.start() for m in re.finditer(r"GAATTC", sequence)]print(f"EcoRI cut positions: {sites}")print(f"Number of cut sites: {len(sites)}")
assert len(sites) == 3assert sites == [4, 13, 20]re.finditer() finds all matches iteratively. re.search() finds only the first, but finditer() finds them all.
Practical: Sequence Validation
Validate whether an input sequence is a valid DNA sequence:
import re
def is_valid_dna(seq: str) -> bool: return bool(re.fullmatch(r"[ATGCatgc]+", seq))
print(is_valid_dna("ATGCGATCGA"))print(is_valid_dna("ATGXYZ"))print(is_valid_dna(""))
assert is_valid_dna("ATGCGATCGA") == Trueassert is_valid_dna("ATGXYZ") == Falseassert is_valid_dna("") == Falsere.fullmatch() โ the entire string must match the pattern. Unlike re.search(), partial matches are not allowed.
re Module Key Functions
| Function | Purpose | Returns |
|---|---|---|
re.search(pattern, text) | Find first match | Match object or None |
re.findall(pattern, text) | All matches as a list | List of strings |
re.finditer(pattern, text) | Iterate all matches | Match object iterator |
re.sub(pattern, replacement, text) | Replace pattern | New string |
re.fullmatch(pattern, text) | Check full string match | Match object or None |
Try It Yourself (Faded Example)
Fill in the blanks to complete a code that extracts the gene name from a FASTA header.
importheader = ">gene_BRCA1 | Homo sapiens | chromosome 17"match = re.(r"gene_(\w+)", header)if match:gene_name = match.group()print(f"Gene: {gene_name}")
Common Errors & Solutions
Q: The pattern seems right but None is returned
Check the case. re.search(r"gaattc", "GAATTC") won't match. To ignore case, add the re.IGNORECASE flag: re.search(r"gaattc", "GAATTC", re.IGNORECASE)
Q: \d doesn't work
Check if you prefixed the string with r. "\d" causes Python to try interpreting \d as an escape sequence. Using r"\d" treats it as a raw string, passing it directly to the regex engine.
Q: Regular expressions are too complex. Do I have to use them?
For simple cases, string methods like in, startswith(), split() are more readable. Regular expressions shine when patterns are complex or you need to handle multiple formats at once. If you need a "search that Ctrl+F can't do," that's when you use regex.