Análisis de datos de secuencia mediante expresiones regulares
Al finalizar este tema
Podrá utilizar el módulo re de Python para buscar patrones en el texto, extraer información de las cabeceras FASTA y validar los datos de secuencia.
Cuándo utilizar expresiones regulares
Observemos la cabecera de un archivo FASTA:
>sp|P04637|P53_HUMAN Cellular tumor antigen p53 OS=Homo sapiens OX=9606En esta línea, quiero extraer solo "P04637" (ID de UniProt). Podría dividir la cadena con split("|"), pero si el formato del encabezado varía entre archivos, tendría que escribir un código diferente cada vez.
Las expresiones regulares (regex) buscan texto mediante "patrones". Si defino un patrón como "caracteres alfanuméricos entre símbolos de barra vertical", puedo extraerlo con el mismo código, independientemente del formato del encabezado.
Como analogía en un experimento, al igual que un anticuerpo específico se une a un epítopo específico (patrón), una expresión regular se "une" a un patrón específico en el texto.
Fundamentos del módulo re de Python
import re
text = "EGFR expression: 12.5 ng/mL"
match = re.search(r"\d+\.\d+", text)if match: value = float(match.group()) print(f"Valor numérico: {value}")
assert value == 12.5re.search(patrón, texto) — Busca la primera parte del texto que coincida con el patrón.
Patrones clave
| Patrón | Significado | Ejemplo | Resultado de la coincidencia |
|---|---|---|---|
\d | Un dígito | \d\d | "42" |
\d+ | Uno o más dígitos | \d+ | "123", "4" |
\w | Carácter alfanumérico + guion bajo | \w+ | "gene_1" |
. | Cualquier carácter | a.b | "a1b", "a-b" |
* | El patrón anterior, cero o más veces | ab*c | "ac", "abc", "abbc" |
+ | El patrón anterior, una o más veces | ab+c | "abc", "abbc" (no "ac") |
? | El patrón anterior, cero o una vez | colou?r | "color", "colour" |
[ABC] | A, B o C | [ATGC]+ | "ATGCGTA" |
^ | Inicio de la línea | ^> | Línea de encabezado FASTA |
| | O (OR) | cat|dog | "cat" o "dog" |
Si se antepone r al patrón, se convierte en una cadena literal (r"\d+"). Esto evita que la barra invertida (\) entre en conflicto con los escapes de cadenas de Python. En las expresiones regulares, siempre use el formato r"...".
En la práctica: Análisis de encabezados FASTA
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)}")Salida:
Salida:
UniProt ID: P04637
Organism: Homo sapiens() — La parte entre paréntesis es un grupo de captura. Se extrae solo el contenido entre paréntesis con group(1).
.+? — Si se añade ?, se realiza una coincidencia mínima (lazy match). Coincide con la menor cantidad posible. Si se usa solo .+ sin ?, coincidirá con la mayor cantidad posible (greedy), lo que puede dar lugar a resultados diferentes de los deseados.
En la práctica: búsqueda de motivos en una secuencia de ADN
Cuando se desea encontrar todas las secuencias de reconocimiento de una enzima de restricción específica en una secuencia:
import re
sequence = "ATCGAATTCGCGAATTCTTGAATTCAA"
# Sitio de reconocimiento de EcoRI: GAATTCsites = [m.start() for m in re.finditer(r"GAATTC", sequence)]print(f"Posiciones de corte de EcoRI: {sites}")print(f"Número de sitios de corte: {len(sites)}")
assert len(sites) == 3assert sites == [4, 13, 20]re.finditer() busca repetidamente todas las coincidencias. re.search() solo encuentra la primera, pero finditer() encuentra todas.
Caso práctico: validación de secuencias
Valida si la secuencia ingresada es una secuencia de ADN válida:
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() — La cadena completa debe coincidir con el patrón. A diferencia de re.search(), no se permiten coincidencias parciales.
Resumen de las funciones principales del módulo re
| Función | Uso | Valor de retorno |
|---|---|---|
re.search(patrón, texto) | Buscar la primera coincidencia | Objeto Match o None |
re.findall(patrón, texto) | Devolver todas las coincidencias en una lista | Lista de cadenas |
re.finditer(patrón, texto) | Iterar sobre todas las coincidencias | Iterador de objetos Match |
re.sub(patrón, reemplazo, texto) | Reemplazar el patrón con otra cadena | Nueva cadena |
re.fullmatch(patrón, texto) | Comprobar si la cadena completa coincide con el patrón | Objeto Match o None |
Pongámoslo en práctica (Ejemplo simplificado)
Complete los espacios en blanco para completar el código que extrae el nombre del gen del encabezado FASTA.
importheader = ">gene_BRCA1 | Homo sapiens | chromosome 17"match = re.(r"gene_(\w+)", header)if match:gene_name = match.group()print(f"Gen: {gene_name}")
Errores comunes y soluciones
P: Parece que el patrón coincide, pero se devuelve None
Compruebe si distingue entre mayúsculas y minúsculas. re.search(r"gaattc", "GAATTC") no coincide. Para ignorar las mayúsculas y minúsculas, agregue la bandera re.IGNORECASE: re.search(r"gaattc", "GAATTC", re.IGNORECASE)
P: \d no funciona
Compruebe si ha agregado r al principio de la cadena. "\d" es porque Python intenta interpretar \d como una secuencia de escape. Si lo escribe como r"\d", se tratará como una cadena literal y se pasará directamente al motor de expresiones regulares.
P: Las expresiones regulares son demasiado complejas. ¿Es necesario usarlas?
En casos simples, los métodos de cadena como in, startswith() y split() son más fáciles de leer. Las expresiones regulares son útiles cuando el patrón es complejo o cuando es necesario procesar varios formatos a la vez. Si necesita una búsqueda que no se pueda realizar con "CTRL+F", utilice expresiones regulares.