855. 生信可重复性指南
一句话概述:可重复性 = 别人拿到你的代码和数据,能得到完全一样的结果——这是科学的基石,也是生信工程师的专业素养体现。
核心知识点速查表
| 层面 | 工具/方法 | 目的 |
|---|
| 代码版本 | Git | 追踪代码变更历史 |
| 数据版本 | DVC | 追踪大文件版本 |
| 软件环境 | Conda/Docker | 锁定软件版本 |
| 流程管理 | Snakemake/Nextflow | 自动化分析流程 |
| 文档记录 | README + Jupyter | 记录分析步骤 |
| 数据共享 | Zenodo/Figshare | 长期存储数据 |
一、Conda环境锁定
# Conda环境管理 —— 可重复性的第一步
# 1. 创建项目专用环境
conda create -n my_project \ # 创建环境
python=3.11 \ # 指定Python版本
numpy=1.26.0 \ # 指定numpy版本
pandas=2.1.0 \ # 指定pandas版本
-y # 自动确认
# 2. 导出精确环境(包含所有依赖版本)
conda activate my_project # 激活环境
conda env export > environment.yml # 导出环境文件
# environment.yml 内容示例:
# name: my_project
# channels:
# - conda-forge
# - bioconda
# - defaults
# dependencies:
# - python=3.11.5
# - numpy=1.26.0
# - pandas=2.1.0
# - samtools=1.18
# - bwa=0.7.17
# 3. 别人用这个文件重建完全相同的环境
conda env create -f environment.yml # 从文件创建环境
# → 所有软件版本完全一致!
# 4. 更严格的锁定(conda-lock)
conda install -c conda-forge conda-lock # 安装conda-lock
conda-lock -f environment.yml \ # 生成锁文件
-p linux-64 # 指定平台
# → 生成conda-lock.yml,包含精确的包hash值
二、Docker容器化
# Dockerfile —— 把整个分析环境打包成容器
# 选择基础镜像(Bioconductor官方镜像包含R+常用包)
FROM bioconductor/bioconductor_docker:3.19
# 安装系统依赖
RUN apt-get update && apt-get install -y \
samtools=1.18-1 \ # 指定samtools版本
bwa \ # BWA比对工具
fastp \ # 质控工具
&& rm -rf /var/lib/apt/lists/* # 清理缓存
# 安装Python依赖
RUN pip install \
numpy==1.26.0 \ # 锁定numpy版本
pandas==2.1.0 \ # 锁定pandas版本
scanpy==1.10.0 # 锁定scanpy版本
# 安装R包
RUN R -e "BiocManager::install(c( \
'DESeq2', \
'clusterProfiler', \
'limma' \
), version = '3.19')" # 指定Bioconductor版本
# 复制分析脚本
COPY scripts/ /app/scripts/ # 复制脚本到容器
COPY Snakefile /app/ # 复制Snakemake流程
# 设置工作目录
WORKDIR /app # 设置工作目录
# 入口命令
CMD ["snakemake", "--cores", "all"] # 默认运行Snakemake
# Docker使用命令
# 构建镜像(给镜像打上日期标签)
docker build -t my_analysis:2026.01 . # 构建镜像
# 运行分析
docker run \
-v $(pwd)/data:/app/data \ # 挂载数据目录
-v $(pwd)/results:/app/results \ # 挂载结果目录
my_analysis:2026.01 # 使用固定版本镜像
# HPC环境用Apptainer(原Singularity)
apptainer build analysis.sif \ # 构建SIF镜像
docker://my_analysis:2026.01 # 从Docker镜像转换
apptainer exec \ # 在容器中运行
--bind data:/app/data \ # 绑定数据目录
analysis.sif \ # SIF镜像
snakemake --cores 16 # 运行流程
三、Snakemake可重复性流程
# Snakefile —— 可重复性分析流程示例
# 配置文件(所有参数集中管理)
configfile: "config.yaml" # 读取配置文件
# 定义所有样本
SAMPLES = config["samples"] # 从配置文件获取样本列表
# 最终目标
rule all:
input:
expand("results/counts/{sample}.counts.txt", # 每个样本的计数
sample=SAMPLES),
"results/deseq2/de_results.csv" # 差异表达结果
# 规则1:质控
rule fastp:
input:
r1 = "data/raw/{sample}_R1.fq.gz", # 原始R1
r2 = "data/raw/{sample}_R2.fq.gz" # 原始R2
output:
r1 = "data/clean/{sample}_R1.fq.gz", # 质控后R1
r2 = "data/clean/{sample}_R2.fq.gz" # 质控后R2
log: "logs/fastp/{sample}.log" # 日志文件
conda: "envs/qc.yaml" # 指定conda环境
threads: 4 # 使用4线程
shell:
"""
fastp -i {input.r1} -I {input.r2} \
-o {output.r1} -O {output.r2} \
-q 20 -l 50 \
--thread {threads} \
2> {log}
"""
# 规则2:比对
rule bwa_align:
input:
r1 = "data/clean/{sample}_R1.fq.gz", # 质控后R1
r2 = "data/clean/{sample}_R2.fq.gz" # 质控后R2
output:
bam = "results/aligned/{sample}.sorted.bam" # 排序BAM
params:
ref = config["reference"] # 参考基因组路径
log: "logs/bwa/{sample}.log"
conda: "envs/align.yaml" # 比对环境
threads: 8
shell:
"""
bwa mem -t {threads} {params.ref} \
{input.r1} {input.r2} \
2> {log} \
| samtools sort -@ 4 \
-o {output.bam}
samtools index {output.bam}
"""
# 配置文件 config.yaml 示例:
# samples:
# - sample1
# - sample2
# - sample3
# reference: /data/ref/hg38.fa
# annotation: /data/ref/gencode.v44.gtf
四、项目目录规范
# 可重复性项目标准目录结构
my_project/
├── README.md # 项目说明(必须!)
├── LICENSE # 许可证
├── .gitignore # Git忽略规则
├── environment.yml # Conda环境定义
├── Dockerfile # Docker容器定义
├── Snakefile # 主流程文件
├── config.yaml # 配置参数
├── data/ # 数据目录
│ ├── raw/ # 原始数据(只读!)
│ └── processed/ # 处理后数据
├── scripts/ # 分析脚本
│ ├── 01_qc.sh # 质控脚本
│ ├── 02_align.sh # 比对脚本
│ └── 03_analysis.R # 分析脚本
├── envs/ # Conda子环境定义
│ ├── qc.yaml # 质控工具环境
│ └── align.yaml # 比对工具环境
├── results/ # 分析结果
│ ├── figures/ # 图片
│ └── tables/ # 表格
├── notebooks/ # Jupyter/R笔记本
└── docs/ # 文档
# 核心原则:
# 1. 原始数据永远不修改(只读)
# 2. 所有中间结果可以重新生成
# 3. 配置参数与代码分离
# 4. 每个分析步骤有日志
五、种子数固定与随机性控制
# 随机性控制 —— 可重复性的隐形杀手
import numpy as np # 数值计算
import random # Python随机数
import os # 系统操作
def set_all_seeds(seed=42):
"""设置所有随机种子,确保结果可重复"""
random.seed(seed) # Python内置随机数
np.random.seed(seed) # NumPy随机数
os.environ['PYTHONHASHSEED'] = str(seed) # Python哈希种子
# 如果用PyTorch
try:
import torch # PyTorch
torch.manual_seed(seed) # CPU种子
torch.cuda.manual_seed_all(seed) # GPU种子
torch.backends.cudnn.deterministic = True # 确定性模式
except ImportError:
pass
# 如果用TensorFlow
try:
import tensorflow as tf # TensorFlow
tf.random.set_seed(seed) # TF种子
except ImportError:
pass
set_all_seeds(42) # 在脚本最开始调用
# R语言中设置种子
# set.seed(42) # R中设置种子
常见报错与解决
| 问题 | 原因 | 解决方法 |
|---|
| 同样代码不同结果 | 随机种子未固定 | 所有随机操作设置seed |
| 包版本冲突 | 依赖版本不兼容 | 用conda-lock精确锁定 |
| Docker镜像太大 | 安装了不需要的包 | 用多阶段构建,精简依赖 |
Snakemake结果不一致 | 配置文件有差异 | 把config.yaml纳入Git |
| 数据文件缺失 | 大文件未用DVC追踪 | 用DVC管理大文件 |
| 线程数不同导致结果差异 | 某些工具多线程不确定 | 固定线程数或用单线程验证 |
速查表
# 可重复性检查清单
□ 代码在Git中(含.gitignore)
□ 环境有environment.yml或Dockerfile
□ 参数在config文件中(不硬编码)
□ 随机种子已固定
□ 原始数据不可修改
□ 有README说明如何运行
□ 流程用Snakemake/Nextflow管理
□ 结果可一键重新生成
# 工具选择
代码版本: Git + GitHub/GitLab
数据版本: DVC (Data Version Control)
环境锁定: Conda + Docker/Apptainer
流程管理: Snakemake / Nextflow
文档: README + Jupyter Notebook
共享: Zenodo (DOI) + GitHub
# 核心原则
1. 版本锁定: 所有软件指定版本号
2. 环境隔离: 每个项目独立环境
3. 参数外置: 配置与代码分离
4. 自动化: 一键运行全流程
5. 文档化: 记录为什么这样做