Eliminación de secuencias duplicadas en archivos FASTA

Detección y eliminación de secuencias duplicadas ignorando identificadores

Este script analiza archivos FASTA para localizar secuencias biológicas idénticas, independientemente del nombre del encabezado. Cuando encuentra repeticionse, solicita al usuario elegir manualmente cuál conservar o mantener todas.

import os
from collections import defaultdict

def cargar_fasta(ruta_archivo):
    registros = []
    with open(ruta_archivo, "r", encoding="utf-8") as f:
        cabecera = None
        lineas_secuencia = []

        for linea in f:
            linea = linea.rstrip()
            if linea.startswith(">"):
                if cabecera is not None:
                    registros.append((cabecera, "".join(lineas_secuencia)))
                cabecera = linea
                lineas_secuencia = []
            else:
                lineas_secuencia.append(linea)

        if cabecera is not None:
            registros.append((cabecera, "".join(lineas_secuencia)))

    return registros

def depurar_manual(registros):
    mapa_secuencias = defaultdict(list)
    for cab, sec in registros:
        mapa_secuencias[sec].append(cab)

    registros_filtrados = []

    for sec, cabeceras in mapa_secuencias.items():
        if len(cabeceras) == 1:
            registros_filtrados.append((cabeceras[0], sec))
            continue

        print("\n\u26a0\ufe0f Duplicado detectado (secuencia idéntica):")
        for i, cab in enumerate(cabeceras):
            print(f"  [{i}] {cab}")

        print("Indica el número de la secuencia a CONSERVAR (ej. 0)")
        print("o presiona Enter para guardar todas:")

        eleccion = input("Tu elección: ").strip()

        if eleccion == "":
            for cab in cabeceras:
                registros_filtrados.append((cab, sec))
        else:
            try:
                indice = int(eleccion)
                registros_filtrados.append((cabeceras[indice], sec))
            except (ValueError, IndexError):
                print("Entrada no válida, se mantienen todas por defecto")
                for cab in cabeceras:
                    registros_filtrados.append((cab, sec))

    return registros_filtrados

def guardar_fasta(registros, ruta_salida):
    with open(ruta_salida, "w", encoding="utf-8") as out:
        for cab, sec in registros:
            out.write(cab + "\n")
            out.write(sec + "\n")

if __name__ == "__main__":
    entrada = r"D:\ejemplos\secuencias_original.fasta"
    salida = r"D:\ejemplos\secuencias_sin_duplicados.fasta"

    datos = cargar_fasta(entrada)
    datos_depurados = depurar_manual(datos)
    guardar_fasta(datos_depurados, salida)

    print(f"\nProceso completado. Se conservaron {len(datos_depurados)} secuencias")
    print(f"Archivo generado: {salida}")

Detección de secuencias idénticas y mismo identificador

En esta segunda versión, primero se eliminan secuencias duplicadas con intervención manual y luego se procesan aquellas entradas que comparten el mismo identificador pero tienen secuencias diferentes, permitiendo renombrarlas interactivamente.

from collections import defaultdict

def leer_fasta(archivo):
    entradas = []
    with open(archivo, "r", encoding="utf-8") as f:
        cabecera = None
        fragmentos = []
        for linea in f:
            linea = linea.rstrip()
            if linea.startswith(">"):
                if cabecera is not None:
                    entradas.append((cabecera, "".join(fragmentos)))
                cabecera = linea
                fragmentos = []
            else:
                fragmentos.append(linea)
        if cabecera is not None:
            entradas.append((cabecera, "".join(fragmentos)))
    return entradas

def extraer_id(encabezado):
    return encabezado[1:].split()[0]

def depurar_por_secuencia(entradas):
    agrupacion = defaultdict(list)
    for cab, seq in entradas:
        agrupacion[seq].append(cab)

    resultado = []
    for seq, cabeceras in agrupacion.items():
        if len(cabeceras) == 1:
            resultado.append((cabeceras[0], seq))
            continue

        print("\n\u26a0\ufe0f Secuencias duplicadas encontradas:")
        for i, cab in enumerate(cabeceras):
            print(f"  [{i}] {cab}")

        print("Elige el índice a CONSERVAR (ej. 0) o Enter para mantener todo:")
        opcion = input("Opción: ").strip()

        if opcion == "":
            for cab in cabeceras:
                resultado.append((cab, seq))
        else:
            try:
                idx = int(opcion)
                resultado.append((cabeceras[idx], seq))
            except (ValueError, IndexError):
                print("Opción inválida, conservando todos")
                for cab in cabeceras:
                    resultado.append((cab, seq))
    return resultado

def renombrar_por_id(entradas):
    grupos_id = defaultdict(list)
    for cab, seq in entradas:
        id_seq = extraer_id(cab)
        grupos_id[id_seq].append((cab, seq))

    resultado_final = []
    for id_seq, items in grupos_id.items():
        if len(items) == 1:
            resultado_final.extend(items)
            continue

        print(f"\n\u26a0\ufe0f ID '{id_seq}' repetido con secuencias diferentes:")
        for i, (cab, seq) in enumerate(items):
            sugerencia = f"{id_seq}_v{i+1}"
            print(f"\n[{i}]")
            print(f"Original: {cab}")
            print(f"Longitud: {len(seq)}")
            print(f"Sugerencia: {sugerencia}")

            nuevo_id = input("Nuevo ID (sin >, Enter mantiene original): ").strip()
            if nuevo_id:
                cab_mod = ">" + nuevo_id
            else:
                cab_mod = cab
            resultado_final.append((cab_mod, seq))
    return resultado_final

def escribir_fasta(entradas, salida):
    with open(salida, "w", encoding="utf-8") as f:
        for cab, seq in entradas:
            f.write(cab + "\n")
            f.write(seq + "\n")

if __name__ == "__main__":
    archivo_in = r"D:\datos\raw_sequences.fasta"
    archivo_out = r"D:\datos\sequences_clean.fasta"

    datos = leer_fasta(archivo_in)
    datos = depurar_por_secuencia(datos)
    datos = renombrar_por_id(datos)
    escribir_fasta(datos, archivo_out)

    print(f"\n\u2705 Limpieza finalizada. {len(datos)} secuencias guardadas")
    print(f"\U0001f4c4 Archivo: {archivo_out}")

Etiquetas: FASTA Python deduplicación secuencias interactivo

Publicado el 7-7 01:29