Parte 8·8.7·12 min de leitura

Reprodutibilidade e Boas Práticas

Por que as análises de bioinformática falham em se reproduzir, e as práticas que as tornam confiáveis — controle de versão, containerização, gerenciadores de fluxo de trabalho e os hábitos de gestão de dados que separam pipelines de nível de pesquisa dos de nível de produção.

reprodutibilidadesnakemakenextflowcontainersboas práticasfluxo de trabalho

A bioinformática tem um problema de reprodutibilidade. Uma pesquisa de 2015 descobriu que mais de 70% dos pesquisadores tentaram e falharam em reproduzir a análise computacional de outro laboratório. As causas são bem compreendidas: versões de software não documentadas, caminhos de arquivo codificados, análises executadas interativamente sem registrar etapas, sementes aleatórias não fixadas e modificações de dados sem rastreamento de proveniência.

Isso importa cientificamente (resultados que não podem ser reproduzidos não podem ser construídos sobre) e praticamente (você não será capaz de reproduzir sua própria análise seis meses depois sem essas práticas). Este capítulo cobre as práticas que separam a análise que funciona uma vez da análise que funciona de forma confiável.

A Pilha de Reprodutibilidade

A reprodutibilidade total requer abordar múltiplas camadas:

Código (versão exata)
↓
Ambiente de software (versões exatas de ferramentas, dependências)
↓
Dados (entrada exata, com checksums)
↓
Ambiente de computação (OS, arquitetura de CPU)
↓
Aleatoriedade (sementes fixadas)
↓
Documentação (parâmetros, decisões tomadas)

A falha em qualquer camada quebra a reprodutibilidade. Cada camada tem sua solução.

Controle de Versão: Git para Análise

Código sem controle de versão é não científico. Todo script de análise, configuração de pipeline e etapa de processamento de dados deve estar no git.

O que commitar:

  • Scripts de análise (Python, R, bash)
  • Arquivos de configuração de pipeline (regras Snakemake/Nextflow)
  • Arquivos de parâmetros (planilhas de amostras, configuração YAML)
  • Funções utilitárias personalizadas

O que NÃO commitar:

  • Dados brutos (muito grandes; use ferramentas de gerenciamento de dados)
  • Arquivos intermediários (muito grandes; regenere do pipeline)
  • Credenciais ou chaves de API
  • Arquivos binários grandes (use Git LFS se necessário)

Granularidade do commit: commit por etapa lógica ("adicionar etapa de trimming de leitura ao pipeline"), não por tecla e não por semana. O histórico de commits deve contar a história da análise.

Estratégia de branch para análises: use feature branches para mudanças experimentais; mescle ao main quando validado. Tags de releases que correspondem a submissões de artigos — você precisa ser capaz de verificar exatamente o código que gerou as figuras.

main: código de análise estável e revisado
├── feature/add-deseq2-analysis
├── feature/try-alternative-normalization
└── tags: submission-v1, revision-v1

Gerenciamento de Ambiente

O Problema de Dependências

As ferramentas de bioinformática são notoriamente conhecidas por cadeias de dependências complexas e sensibilidade à versão. Uma análise executada com DESeq2 versão 1.28 pode fornecer resultados diferentes da mesma análise com a versão 1.36 (devido a atualizações de algoritmo). As ferramentas podem conflitar com as dependências umas das outras.

Conda e Mamba

Conda cria ambientes isolados com versões de pacotes reproduzíveis:

bash
# Create environment from specification file
conda env create -f environment.yml

# environment.yml
name: rnaseq-analysis
channels:
  - bioconda
  - conda-forge
dependencies:
  - python=3.10
  - snakemake=7.32
  - fastqc=0.12
  - star=2.7.10
  - samtools=1.17
  - r-base=4.3
  - bioconductor-deseq2=1.40

Sempre fixe versões exatas no environment.yml. python>=3.8 não é reproduzível; python=3.10.6 é.

Mamba: substituto drop-in para conda com resolução de dependências mais rápida (solucionador C++ vs. Python). Use mamba onde você usaria conda.

Containers: Docker e Singularity

Conda captura pacotes Python/R, mas não o OS subjacente, bibliotecas do sistema ou binários compilados. Os containers capturam tudo:

Docker: empacota todo o ambiente de tempo de execução (OS, bibliotecas do sistema, ferramentas) em uma imagem portátil. A mesma imagem Docker é executada identicamente em qualquer máquina Linux.

dockerfile
FROM continuumio/miniconda3:23.3.1-0
COPY environment.yml .
RUN conda env create -f environment.yml

Singularity/Apptainer: alternativa ao Docker projetada para ambientes HPC onde o Docker requer acesso root. Converte imagens Docker; executa sem root. Padrão na maioria dos clusters de computação de pesquisa.

Melhores práticas: defina seu ambiente como um Dockerfile ou arquivo de ambiente Conda, construa o container e registre o hash da imagem na configuração do seu pipeline. O hash da imagem (SHA256) é uma impressão digital exata do ambiente de software.

Containers em clusters HPC

A maioria dos clusters HPC não permite Docker (requer root), mas suporta Singularity/Apptainer. Nextflow e Snakemake suportam Singularity nativamente: especifique uma imagem de container por regra/processo, e o gerenciador de fluxo de trabalho puxa e executa o container automaticamente. Esta é a abordagem recomendada para portabilidade do pipeline em sistemas HPC.

Gerenciadores de Fluxo de Trabalho

Scripts bash ad hoc para análises de várias etapas têm modos de falha previsíveis:

  • Nenhuma maneira de retomar da falha
  • Re-executa toda a análise quando você muda uma etapa
  • Sem gerenciamento de paralelização
  • Sem especificação de recursos

Os gerenciadores de fluxo de trabalho resolvem todos esses problemas.

Snakemake

Snakemake usa um sistema baseado em regras: define regras que especificam entradas, saídas e o comando shell. O Snakemake constrói um DAG de dependência a partir dos arquivos de saída alvo e executa apenas o que é necessário.

python
# Snakefile
rule trim_reads:
    input:
        r1 = "data/raw/{sample}_R1.fastq.gz",
        r2 = "data/raw/{sample}_R2.fastq.gz"
    output:
        r1 = "results/trimmed/{sample}_R1.fastq.gz",
        r2 = "results/trimmed/{sample}_R2.fastq.gz"
    threads: 4
    resources:
        mem_mb = 8000
    shell:
        "trimmomatic PE -threads {threads} {input.r1} {input.r2} "
        "{output.r1} /dev/null {output.r2} /dev/null ..."

rule star_align:
    input:
        r1 = "results/trimmed/{sample}_R1.fastq.gz",
        r2 = "results/trimmed/{sample}_R2.fastq.gz",
        index = "data/reference/star_index"
    output:
        bam = "results/aligned/{sample}.bam"
    ...

Recursos-chave do Snakemake:

  • Wildcards ({sample}): escala automaticamente para todas as amostras
  • Checkpointing: se uma etapa falha, a re-execução retoma daquela etapa, não do início
  • Integração com HPC: envia cada regra como um job de cluster separado automaticamente
  • Suporte a container: diretiva conda: ou singularity: por regra
  • Geração de relatório: relatório HTML com estatísticas de tempo de execução e proveniência de arquivos de saída

Nextflow

Nextflow usa um paradigma de dataflow: os processos comunicam via canais (filas de dados). Mais poderoso para fluxos de trabalho de ramificação complexos e computação em nuvem.

groovy
process TRIM_READS {
    input:
    tuple val(sample), path(reads)
    
    output:
    tuple val(sample), path("*.trimmed.fastq.gz")
    
    script:
    """
    trimmomatic PE ${reads[0]} ${reads[1]} ...
    """
}

workflow {
    reads = Channel.fromFilePairs("data/raw/*_{R1,R2}.fastq.gz")
    trimmed = TRIM_READS(reads)
    ALIGN(trimmed)
}

nf-core: uma coleção comunitária de pipelines Nextflow para tarefas comuns de bioinformática (RNA-seq, ChIP-seq, chamada de variantes, etc.) seguindo boas práticas. Para análises padrão, usar um pipeline nf-core é frequentemente melhor do que escrever o seu próprio — os pipelines são extensivamente testados, documentados e mantidos.

Quando Usar Qual

Caso de UsoFerramenta
Pipeline de análise personalizadoSnakemake (nativo Python, fácil de aprender)
Pipeline de produção, nuvemNextflow (melhor suporte à nuvem)
RNA-seq/ATAC-seq padrãoPipeline nf-core
Scripts únicos, JupyterAmbiente conda + git

Gerenciamento de Dados

Dados Brutos Imutáveis

Os dados brutos nunca devem ser modificados. Trate-os como somente leitura:

  • Defina permissões de diretório para somente leitura após o download inicial
  • Verifique checksums (MD5/SHA256) após o download e periodicamente
  • Mantenha dados brutos separados de dados processados

Nunca modifique no lugar: se uma etapa de processamento requer conversão de formato, escreva em um novo arquivo. A cadeia de bruto → processado deve ser totalmente rastreável.

Versionamento de Dados com DVC

DVC (Data Version Control): extensão do Git para versionar arquivos de dados grandes. Armazena dados em S3/GCS/armazenamento local; commita apenas ponteiros (checksums) para o git.

bash
dvc add data/raw/rnaseq_counts.tsv    # track file in DVC
git add data/raw/rnaseq_counts.tsv.dvc  # commit pointer
dvc push                              # push data to remote storage

Isso fornece o mesmo fluxo de trabalho que o git para dados: dvc pull para baixar a versão exata dos dados correspondente a um commit.

Planilhas de Amostras e Metadados

Toda análise deve ter uma planilha de amostras estruturada que mapeia IDs de amostras para:

  • Caminhos de arquivos (dados brutos)
  • Metadados biológicos (condição, genótipo, tecido, ponto de tempo)
  • Metadados técnicos (lote, data de sequenciamento, preparação de biblioteca)
  • Métricas de QC (se disponíveis)

Mantenha planilhas de amostras no git (são pequenas). Nunca codifique informações de amostra em nomes de arquivos como única fonte de verdade — os nomes de arquivos são alterados, copiados incorretamente ou truncados.

Notebooks Computacionais: Melhores Práticas do Jupyter

Os notebooks Jupyter são excelentes para análise exploratória e relatórios, mas problemáticos para reprodutibilidade quando usados ingenuamente:

  • A ordem de execução de células não é registrada — os notebooks podem estar em um estado que não pode ser reproduzido executando de cima para baixo
  • O estado oculto se acumula ao re-executar células em ordem arbitrária
  • Notebooks grandes são difíceis de revisar e testar

Melhores práticas:

  1. Reiniciar e executar tudo antes de commitar — garantir que o notebook é executado de forma limpa de cima para baixo
  2. Notebooks parametrizados: use Papermill para executar notebooks com diferentes parâmetros — cada execução produz um notebook de saída separado, criando um registro
  3. Extrair código reutilizável para módulos .py; notebooks devem chamar funções, não defini-las
  4. Limpar saídas antes de commitar (ou use o hook git nbstripout) — dados de saída em notebooks tornam os diffs ilegíveis e inflam o tamanho do repositório
  5. Fixar o ambiente do kernel nos metadados do notebook ou em um requirements.txt complementar

Aleatoriedade e Sementes

Qualquer análise usando aleatorização deve fixar a semente aleatória:

  • Agrupamento (inicialização k-means)
  • Redução de dimensionalidade (t-SNE, UMAP)
  • Divisões treinamento/teste em ML
  • Bootstrap e testes de permutação
  • Gradiente descendente estocástico (redes neurais)
python
import numpy as np
import random
import torch

SEED = 42
np.random.seed(SEED)
random.seed(SEED)
torch.manual_seed(SEED)
# For CUDA:
torch.cuda.manual_seed_all(SEED)

Relate a semente nos métodos. Se a análise for sensível à escolha da semente, execute com múltiplas sementes e relate a distribuição dos resultados.

Qualidade do Código na Análise

O código de análise na academia frequentemente tem padrões de qualidade mais baixos do que o software de produção — mas isso custa reprodutibilidade:

Parâmetros configuráveis: nunca codifique caminhos de arquivo, limiares ou parâmetros em scripts. Use um arquivo de configuração (YAML/TOML) ou argumentos de linha de comando. Isso torna trivial a re-execução com diferentes parâmetros e documenta quais parâmetros foram usados.

yaml
# config.yaml
input:
  counts: data/counts_matrix.tsv
  metadata: data/sample_metadata.csv
deseq2:
  fdr_threshold: 0.05
  lfc_threshold: 1.0
  reference_level: "control"
output:
  dir: results/deseq2/

Logging sobre instruções print: use o módulo logging do Python ou futile.logger do R. Os logs devem incluir timestamps, valores de parâmetros e métricas-chave (tamanho da entrada, tamanho da saída, tempo de execução). Um arquivo de log é um registro do que realmente aconteceu durante uma execução.

Saídas intermediárias: grave resultados intermediários em etapas-chave. Se uma etapa downstream falha, você pode retomar sem re-executar etapas upstream caras (mesmo sem um gerenciador de fluxo de trabalho).

Relatórios e Documentação

Clareza da seção de métodos: a seção de métodos de um artigo de bioinformática deve ser reproduzível. Inclua nomes exatos de ferramentas, versões, parâmetros de linha de comando e versões de genoma de referência/anotação usados.

Métodos ruins: "Realizamos análise de expressão diferencial usando DESeq2 com parâmetros padrão."

Métodos bons: "A análise de expressão diferencial foi realizada com DESeq2 v1.40.0 (Love et al. 2014), usando GLM binomial negativo com teste de Wald. A fórmula de design era ~batch + condition, onde batch corrigiu para as duas execuções de sequenciamento. Os genes foram filtrados para aqueles com contagens normalizadas médias > 10 em todas as amostras. Os p-valores foram ajustados com FDR Benjamini-Hochberg; genes com padj < 0,05 e |log2FC| > 1 foram chamados de diferencialmente expressos."

Tabelas suplementares: forneça todos os resultados processados (tabelas completas de expressão diferencial, não apenas os principais hits) como arquivos para download. Revisores e futuros pesquisadores precisam dos resultados completos para validar descobertas ou usar como informação prévia.

A Lista de Verificação de Reprodutibilidade

Antes de submeter um artigo ou compartilhar uma análise:

  • Todo o código em um repositório git com um README
  • Ambiente de software especificado (conda environment.yml ou Dockerfile)
  • Dados brutos arquivados com checksums
  • O pipeline de análise é executado de ponta a ponta de dados brutos a figuras
  • Todas as sementes aleatórias fixadas e documentadas
  • Parâmetros de análise em arquivos de configuração, não codificados
  • A seção de métodos inclui versões exatas de ferramentas e parâmetros
  • Tabelas de resultados completas em dados suplementares (não apenas destaques filtrados)
  • Repositório marcado na versão de submissão
O teste de seis meses

Um autoteste comum para reprodutibilidade: "Alguém sem conhecimento deste projeto pode reproduzir as figuras-chave em seis meses usando apenas o repositório e a seção de métodos do artigo?" Se a resposta for não, a análise não é verdadeiramente reproduzível. O "alguém" pode ser você — os membros do laboratório partem, a memória desvanece e você precisará revisitar esta análise.

Resumo de Ferramentas

CategoriaFerramentaFinalidade
Controle de versãoGit + GitHub/GitLabVersionamento e colaboração de código
AmbienteConda/MambaGerenciamento de pacotes
ContainersDocker + SingularityReprodutibilidade completa do ambiente
Fluxo de trabalhoSnakemakeGerenciamento de pipeline (nativo Python)
Fluxo de trabalhoNextflow + nf-coreGerenciamento de pipeline (nuvem/HPC)
Versionamento de dadosDVCVersionamento de arquivos grandes
NotebooksPapermillExecução de notebook parametrizado
AleatoriedadeSementes fixadasReprodutibilidade estocástica

A reprodutibilidade não é um requisito burocrático — é o padrão científico básico que permite que as descobertas sejam construídas sobre. Cada hora investida nessas práticas economiza muitas horas de confusão, re-análise e falha de replicação.