Guía de Aprendizaje Progresivo de Snakemake

¿Qué es Snakemake?

Snakemake es un sistema de gestión de flujos de trabajo (workflow management) que te permite crear pipelines reproducibles y escalables. Utiliza una sintaxis basada en Python y trabaja con reglas que definen cómo transformar archivos de entrada en archivos de salida.

Ventajas principales:

  • Ejecución automática solo de pasos necesarios
  • Paralelización automática
  • Portabilidad (local, cluster, cloud)
  • Reproducibilidad científica

Instalación

# Con conda (recomendado)
conda install -c bioconda snakemake

# Con pip
pip install snakemake

# Verificar instalación
snakemake --version

NIVEL 1: FUNDAMENTOS BÁSICOS

1.1 Tu Primera Regla

Crea un archivo Snakefile:

rule saludo:
    output:
        "saludo.txt"
    shell:
        "echo 'Hola desde Snakemake!' > {output}"

Ejecutar:

snakemake -c1

Conceptos clave:

  • rule: define una tarea
  • output: archivo(s) que se generan
  • shell: comando a ejecutar
  • {output}: wildcard que se reemplaza por el nombre del archivo

1.2 Añadiendo Entrada (Input)

rule copiar_archivo:
    input:
        "datos_originales.txt"
    output:
        "datos_copia.txt"
    shell:
        "cp {input} {output}"

Primero crea el archivo de entrada:

echo "Datos de prueba" > datos_originales.txt
snakemake -c1

1.3 Múltiples Reglas Encadenadas

rule crear_datos:
    output:
        "datos.txt"
    shell:
        "echo 'linea1\nlinea2\nlinea3' > {output}"

rule contar_lineas:
    input:
        "datos.txt"
    output:
        "conteo.txt"
    shell:
        "wc -l {input} > {output}"

rule all:
    input:
        "conteo.txt"

La regla all: Define el objetivo final. Snakemake trabaja hacia atrás para determinar qué ejecutar.


NIVEL 2: CONCEPTOS INTERMEDIOS

2.1 Wildcards (Comodines)

Procesa múltiples archivos con una sola regla:

MUESTRAS = ["muestra1", "muestra2", "muestra3"]

rule all:
    input:
        expand("resultados/{muestra}_procesado.txt", muestra=MUESTRAS)

rule procesar:
    input:
        "datos/{muestra}.txt"
    output:
        "resultados/{muestra}_procesado.txt"
    shell:
        "cat {input} | tr '[:lower:]' '[:upper:]' > {output}"

Crea los archivos de entrada:

mkdir -p datos
echo "texto minúscula" > datos/muestra1.txt
echo "otro texto" > datos/muestra2.txt
echo "más datos" > datos/muestra3.txt

Conceptos: - {muestra}: wildcard que toma diferentes valores - expand(): genera listas de archivos

2.2 Parámetros y Scripts Python

rule analizar_datos:
    input:
        "datos/{muestra}.txt"
    output:
        "analisis/{muestra}_stats.txt"
    params:
        min_length=5
    run:
        with open(input[0]) as f_in, open(output[0], 'w') as f_out:
            lines = f_in.readlines()
            f_out.write(f"Total líneas: {len(lines)}\n")
            f_out.write(f"Parámetro min_length: {params.min_length}\n")

Bloques run: permiten código Python directamente en la regla.

2.3 Scripts Externos

Snakefile:

rule procesar_con_script:
    input:
        "datos/{muestra}.csv"
    output:
        "resultados/{muestra}_limpio.csv"
    script:
        "scripts/limpiar_datos.py"

scripts/limpiar_datos.py:

import pandas as pd

# Snakemake proporciona objetos 'snakemake'
df = pd.read_csv(snakemake.input[0])
df_limpio = df.dropna()
df_limpio.to_csv(snakemake.output[0], index=False)

2.4 Archivos de Configuración

config.yaml:

muestras:
  - muestra1
  - muestra2
  - muestra3

parametros:
  umbral: 0.05
  iteraciones: 1000

Snakefile:

configfile: "config.yaml"

rule all:
    input:
        expand("resultados/{muestra}.txt", muestra=config["muestras"])

rule analizar:
    input:
        "datos/{muestra}.txt"
    output:
        "resultados/{muestra}.txt"
    params:
        umbral=config["parametros"]["umbral"]
    shell:
        "echo 'Umbral: {params.umbral}' > {output}"

NIVEL 3: TÉCNICAS AVANZADAS

3.1 Funciones de Input

Cuando el input depende de wildcards de forma compleja:

def obtener_archivos_input(wildcards):
    if wildcards.tipo == "completo":
        return ["datos/parte1.txt", "datos/parte2.txt"]
    else:
        return ["datos/parte1.txt"]

rule procesar_dinamico:
    input:
        obtener_archivos_input
    output:
        "resultados/{tipo}_resultado.txt"
    shell:
        "cat {input} > {output}"

3.2 Checkpoints (Puntos de Control)

Para cuando no sabes los outputs hasta ejecutar un paso:

checkpoint dividir_datos:
    input:
        "datos_grandes.txt"
    output:
        directory("chunks/")
    shell:
        """
        mkdir -p chunks
        split -l 100 {input} chunks/chunk_
        """

def agregar_chunks(wildcards):
    checkpoint_output = checkpoints.dividir_datos.get(**wildcards).output[0]
    chunks = glob.glob(f"{checkpoint_output}/chunk_*")
    return expand("procesados/{chunk}_proc.txt", 
                  chunk=[os.path.basename(c) for c in chunks])

rule procesar_chunk:
    input:
        "chunks/{chunk}"
    output:
        "procesados/{chunk}_proc.txt"
    shell:
        "wc -l {input} > {output}"

rule all:
    input:
        agregar_chunks

3.3 Recursos y Prioridades

rule tarea_pesada:
    input:
        "input.txt"
    output:
        "output.txt"
    threads: 4
    resources:
        mem_mb=4000,
        runtime=120  # minutos
    priority: 50
    shell:
        "mi_programa --threads {threads} {input} {output}"

Ejecutar con límites:

snakemake --cores 8 --resources mem_mb=16000

3.4 Conda/Mamba Integration

rule analisis_r:
    input:
        "datos.csv"
    output:
        "grafico.pdf"
    conda:
        "envs/r_ambiente.yaml"
    shell:
        "Rscript scripts/graficar.R {input} {output}"

envs/r_ambiente.yaml:

channels:
  - conda-forge
  - r
dependencies:
  - r-base=4.2
  - r-ggplot2
  - r-dplyr

Ejecutar:

snakemake --use-conda --cores 4

3.5 Módulos y Subworkflows

Organiza workflows grandes en módulos:

Snakefile:

module analisis:
    snakefile: "modules/analisis.smk"
    config: config

use rule * from analisis as analisis_*

rule all:
    input:
        rules.analisis_reporte_final.output

3.6 Reglas Temporales y Protegidas

rule paso_intermedio:
    input:
        "input.txt"
    output:
        temp("temporal.txt")  # Se borrará automáticamente
    shell:
        "procesar {input} > {output}"

rule resultado_final:
    input:
        "temporal.txt"
    output:
        protected("resultado_final.txt")  # No se puede sobrescribir
    shell:
        "finalizar {input} > {output}"

NIVEL 4: MEJORES PRÁCTICAS

4.1 Estructura de Proyecto Recomendada

proyecto/
├── Snakefile
├── config.yaml
├── envs/
│   ├── python_env.yaml
│   └── r_env.yaml
├── scripts/
│   ├── preprocesar.py
│   └── analizar.R
├── rules/
│   ├── control_calidad.smk
│   └── analisis.smk
├── datos/
│   └── muestras/
└── resultados/
    ├── qc/
    └── figuras/

4.2 Logging y Reportes

rule con_logs:
    input:
        "input.txt"
    output:
        "output.txt"
    log:
        "logs/{rule}.log"
    benchmark:
        "benchmarks/{rule}.txt"
    shell:
        "(mi_comando {input} > {output}) 2> {log}"

4.3 Validación de Configuración

from snakemake.utils import validate

configfile: "config.yaml"
validate(config, schema="schemas/config.schema.yaml")

# schemas/config.schema.yaml (formato JSON Schema)

4.4 Dry-run y Visualización

# Ver qué se ejecutará sin ejecutar
snakemake -n

# Ver el grafo de dependencias
snakemake --dag | dot -Tpdf > dag.pdf

# Reporte HTML interactivo
snakemake --report report.html

Ejercicios Prácticos por Nivel

Ejercicio Nivel 1

Crea un workflow que:

  1. Genere 3 archivos de texto con números aleatorios
  2. Cuente las líneas de cada archivo
  3. Combine todos los conteos en un resumen final

Ejercicio Nivel 2

Construye un pipeline que:

  1. Descargue datos de muestra
  2. Los procese con diferentes parámetros (desde config.yaml)
  3. Genere un reporte comparativo

Ejercicio Nivel 3

Desarrolla un workflow que:

  1. Use checkpoints para dividir archivos grandes
  2. Procese cada chunk en paralelo
  3. Agregue resultados con gestión de recursos
  4. Use ambientes conda separados para diferentes herramientas

Comandos Útiles de Referencia

# Ejecución básica
snakemake --cores 4

# Dry-run (ver qué se ejecutará)
snakemake -n

# Forzar re-ejecución de una regla
snakemake -R nombre_regla

# Limpiar outputs
snakemake --delete-all-output

# Desbloquear directorio (si hubo error)
snakemake --unlock

# Ejecutar regla específica
snakemake nombre_archivo_output

# Modo detallado
snakemake -p --verbose

# Con perfilado
snakemake --profile mi_perfil/

Recursos Adicionales

¡Buena suerte con tu aprendizaje de Snakemake!