Módulo 9 — Bucles y condicionales
Duración estimada: 4 h
Objetivos
Al finalizar este módulo serás capaz de:
- Tomar decisiones en un programa con
if/elif/else. - Repetir una acción un número conocido de veces con
for, y un número desconocido de veces conwhile. - Parsear un archivo FASTA a mano, línea a línea, con un bucle.
- Filtrar secuencias por longitud y contar bases, y reconocer un error típico al hacerlo.
Condicionales: if / elif / else
count = 12
if count > 10:
categoria = "abundante"
elif count > 3:
categoria = "frecuente"
else:
categoria = "escaso"
print(categoria)abundante
Python evalúa las condiciones en orden: si count > 10 es verdadero, usa esa rama y se salta el resto; si no, prueba elif count > 3; si ninguna se cumple, usa else. Solo se ejecuta una de las ramas.
Bucles for: repetir un número conocido de veces
Ya usaste for linea in f: en el módulo 7 para recorrer un archivo, y for id_seq, seq in secuencias.items(): en el módulo 8 para recorrer un diccionario. for también recorre directamente los caracteres de una cadena:
secuencia = "ATGC"
for base in secuencia:
print(base)Bucles while: repetir hasta que se cumpla una condición
A diferencia de for, con while no sabes de antemano cuántas vueltas dará — sigue repitiendo mientras la condición sea verdadera. Es la herramienta correcta cuando buscas algo sin saber dónde está, por ejemplo la posición del codón de inicio ATG en una secuencia:
secuencia = "GGGATGCCTTAA"
i = 0
while i < len(secuencia) and secuencia[i:i+3] != "ATG":
i += 1
print(f"Codon de inicio encontrado en la posicion {i}")Codon de inicio encontrado en la posicion 3
En cada vuelta, i += 1 avanza una posición (es la forma corta de escribir i = i + 1), y secuencia[i:i+3] mira los 3 caracteres a partir de esa posición. El bucle para en cuanto encuentra "ATG", o si llega al final de la secuencia sin encontrarlo (por eso la condición comprueba primero i < len(secuencia): sin ese límite, si "ATG" no apareciera nunca, el bucle no terminaría jamás).
Parsear un FASTA a mano
Con for, if y un diccionario (módulo 8), ya tienes todo lo necesario para convertir un archivo FASTA en una estructura de datos que puedas consultar: un diccionario que asocie cada identificador con su secuencia completa.
secuencias = {}
id_actual = None
with open("sequences.fasta") as f:
for linea in f:
linea = linea.strip()
if linea.startswith(">"):
id_actual = linea[1:]
secuencias[id_actual] = ""
else:
secuencias[id_actual] += linea
for id_seq, seq in secuencias.items():
print(id_seq, len(seq))seq1|Arabidopsis_thaliana|rbcL 50
seq2|Quercus_robur|matK 49
seq3|Pinus_sylvestris|rbcL 45
seq4|Fragaria_vesca|trnL 34
linea.strip() elimina el salto de línea del final (si no lo hicieras, se colaría dentro de tus secuencias). Cuando la línea es una cabecera, linea[1:] se queda con todo menos el primer carácter (el >), y abre una entrada nueva en el diccionario con secuencia vacía. Cuando no es cabecera, es una línea de secuencia, y se añade (+=) a la entrada que se abrió más recientemente.
Filtrar por longitud
Con el diccionario ya construido, filtrar es solo un for con un if dentro:
for id_seq, seq in secuencias.items():
if len(seq) > 40:
print(f"{id_seq}: secuencia larga ({len(seq)} bases)")
else:
print(f"{id_seq}: secuencia corta ({len(seq)} bases)")Contar bases — y un error típico al hacerlo
Quieres contar cuántas veces aparece cada base en una secuencia:
secuencia_mixta = "ATGccTAGgtA"
conteo = {"A": 0, "C": 0, "G": 0, "T": 0}
for base in secuencia_mixta:
if base in conteo:
conteo[base] = conteo[base] + 1
print(conteo)
print(sum(conteo.values()), "de", len(secuencia_mixta), "caracteres"){'A': 3, 'C': 0, 'G': 2, 'T': 2}
7 de 11 caracteres
Solo se han contado 7 de las 11 bases, y sin ningún error ni aviso — el mismo tipo de fallo silencioso que viste en el módulo 8. La causa: muchos archivos reales de secuencias usan minúsculas para marcar regiones de baja confianza o repetitivas (soft-masking), y "c" no es igual que "C" para Python, así que if base in conteo los descarta sin más. La solución es normalizar cada base con .upper() antes de comprobarla:
conteo = {"A": 0, "C": 0, "G": 0, "T": 0}
for base in secuencia_mixta:
base = base.upper()
if base in conteo:
conteo[base] = conteo[base] + 1
print(conteo)
print(sum(conteo.values()), "de", len(secuencia_mixta), "caracteres"){'A': 3, 'C': 2, 'G': 3, 'T': 3}
11 de 11 caracteres
Ejercicios
- Dado
count = 7, usaif/elif/elsepara clasificarlo como"abundante"(>10),"frecuente"(>3) o"escaso", e imprime el resultado. - Usa un bucle
whilepara encontrar la posición del primer codón de parada"TAA"en la secuencia"ATGCGTATCTAAGGC". - Recorre con
forla secuencia"ATGCGT"e imprime cada base junto con su posición (pista: usaenumerate(secuencia), que en cada vuelta te da(indice, base)). - Parsea
sequences.fastaa mano con el patrón visto (for+if+ diccionario), guardando el resultado en una variablesecuencias. - A partir del diccionario
secuenciasdel ejercicio anterior, imprime solo las secuencias con más de 40 bases. - Cuenta las bases A/C/G/T de
secuencias["seq2|Quercus_robur|matK"]. - Repite el conteo de bases sobre la cadena
"atgCCGgtNa"sin normalizar mayúsculas/minúsculas. Comprueba que la suma de tu conteo no coincide con la longitud total de la cadena. - (Reto) Corrige el ejercicio anterior normalizando cada base con
.upper(). ¿Sigue sin coincidir la suma con la longitud? ¿Por qué (pista:"N"es una base ambigua, no A/C/G/T)?
Soluciones
count = 7 if count > 10: categoria = "abundante" elif count > 3: categoria = "frecuente" else: categoria = "escaso" print(categoria)secuencia = "ATGCGTATCTAAGGC" i = 0 while i < len(secuencia) and secuencia[i:i+3] != "TAA": i += 1 print(i)secuencia = "ATGCGT" for i, base in enumerate(secuencia): print(i, base)- Igual que el ejemplo de la sección “Parsear un FASTA a mano”.
for id_seq, seq in secuencias.items(): if len(seq) > 40: print(id_seq, len(seq))secuencia = secuencias["seq2|Quercus_robur|matK"] conteo = {"A": 0, "C": 0, "G": 0, "T": 0} for base in secuencia: if base in conteo: conteo[base] = conteo[base] + 1 print(conteo)- La suma es mucho menor que la longitud: casi todo se descarta por estar en minúsculas.
secuencia_mixta = "atgCCGgtNa" conteo = {"A": 0, "C": 0, "G": 0, "T": 0} for base in secuencia_mixta: if base in conteo: conteo[base] = conteo[base] + 1 print(sum(conteo.values()), "de", len(secuencia_mixta)) - Añadiendo
base = base.upper()dentro del bucle, antes delif, la suma sigue sin coincidir del todo, porque"N"(base ambigua, “podría ser cualquiera”) no es ninguna de las cuatro claves del diccionario — y eso es correcto:Nno es A, C, G ni T, así que no debe contarse como ninguna de ellas.