Snakemake 基础概念:Snakefile、规则、输入输出、通配符
说实话,刚接触 Snakemake 的时候,我也觉得它就是把命令行包装一下。但用久了才发现,真正撑起整个流程的是四个核心概念:Snakefile、规则 (rule)、输入/输出、通配符 (wildcards)。把这四个东西吃透,后面跟 Seurat 集成就顺了。
我记得最早自己想写一个批量跑单细胞数据的流程,不知道从哪下手。后来就是把这四个概念一个一个拆开看,才明白 Snakemake 到底在替我们做什么。
Snakefile:整个流程的“总剧本”
Snakefile 本质上就是一个 Python 文件(只不过后缀名不一样)。你写的所有规则、参数、函数都放在这里面。Snakemake 启动时会先读这个文件,然后根据里面的规则去干活。
我个人习惯把 Snakefile 放在项目根目录下,跟数据目录 data/ 和结果目录 results/ 平级。结构清晰,不容易乱。
# 一个最简的 Snakefile
rule all:
input: "results/final_output.txt"
rule prepare_data:
input: "data/raw.csv"
output: "data/processed.csv"
shell: "python preprocess.py {input} {output}"
规则 (rule):一次转换的“说明书”
规则是 Snakemake 的最小执行单元。每个规则定义三个东西:输入是什么、输出是什么、用什么命令转换。
规则的名字随便起,但我会用 动词+名词 的格式,比如 run_qc、normalize_counts。看名字就知道这步要干嘛。
一个规则的核心结构其实只有五个字段——但我通常只用三个:input、output、shell 或 run。另外两个 params 和 log 属于进阶内容,后面章节会讲到。
| 字段 | 作用 |
|---|---|
input |
规则的输入文件,可以是列表或字符串 |
output |
规则要生成的文件,支持通配符 |
shell |
在 Shell 里执行的命令 |
run |
用 Python 代码执行逻辑(代替 shell) |
params |
额外的参数,可以在 shell/run 里引用 |
input / output:搭依赖的“骨架”
输入和输出就是规则之间的桥梁。Snakemake 看到一个规则的输出是另一个规则的输入,就会自动排好先后关系。
举个例子:我有个规则 run_qc 输出 qc_results.tsv,另一个规则 run_filter 的输入就是 qc_results.tsv。Snakemake 会先跑 run_qc,再跑 run_filter。完全不用你写任何顺序声明。
写输入输出的时候,有两点要注意——
- 路径尽量用相对路径(比如
data/),不要写死绝对路径,不然换个环境就崩了。 - 文件名字要有语义,比如
{sample}_aligned.bam,方便后面引用。
我曾经在项目里写过一个规则,输入写成了 "results/{sample}/bam",结果 Snakemake 一直找不到文件。后来发现少写了文件后缀。嗯,这种坑特别容易踩。
通配符 (wildcards):“批量处理”的灵魂
通配符是 Snakemake 最厉害的地方。没有它,我们得写几十个重复的规则。有了它,一个规则就能处理所有样本。
通配符用花括号表示,比如 {sample}。Snakemake 在运行时会自动将 {sample} 替换成实际样本名。
看个典型例子:
rule align_star:
input:
"data/raw/{sample}_R1.fastq",
"data/raw/{sample}_R2.fastq"
output:
"results/alignment/{sample}.bam"
shell:
"STAR --readFilesIn {input} --outFileNamePrefix results/alignment/{sample}."
如果你有一个样本叫 sample1,Snakemake 就会把 {sample} 替换成 sample1,然后执行命令。多个样本的话,只需要在 rule all 里列出每个样本要生成的文件,或者通过管道传递。
{sample},输出也得用 {sample}。如果写成 {sample_name} 和 {sample},Snakemake 会报错,因为它无法推断对应关系。
一张图看懂规则执行流程
下面这张图把 Snakefile、规则、输入输出、通配符串联起来,展示 Snakemake 是如何一步步推导和执行的。
一个完整的实战片段
把上面的概念串起来,写一个真实场景:我们要对三个样本的 FASTQ 做质控、比对、定量。用 Snakemake 写出来就是:
# Snakefile
SAMPLES = ["PBMC_1", "PBMC_2", "PBMC_3"]
rule all:
input:
expand("results/counts/{sample}_counts.tsv", sample=SAMPLES)
rule fastqc:
input: "data/raw/{sample}_R1.fastq"
output: "results/qc/{sample}_fastqc.html"
shell: "fastqc {input} -o results/qc/"
rule star_align:
input: "data/raw/{sample}_R1.fastq"
output: "results/alignment/{sample}.bam"
shell: "STAR --genomeDir ref/ --readFilesIn {input} --outFileNamePrefix results/alignment/{sample}."
rule featurecounts:
input: "results/alignment/{sample}.bam"
output: "results/counts/{sample}_counts.tsv"
shell: "featureCounts -a ref/annotation.gtf -o {output} {input}"
注意 rule all 里用 expand() 函数批量展开通配符,生成三个样本的输出文件列表。Snakemake 看到这些目标,就开始逆向查找依赖,自动决定先跑哪些规则。
避坑指南
- 通配符名称拼写不一致:输入和输出里通配符名字必须一模一样,否则 Snakemake 会报
WildcardError。 - 忘记写
rule all:如果不写rule all,Snakemake 不知道最终要生成什么文件,它会把 Snakefile 里最后一个规则当作默认目标。 - 路径写死绝对路径:换成 Linux/Mac/Windows 都可能出问题。哪怕用
os.path.join生成相对路径也好。 - 输出文件写成了目录:Snakemake 检查输出是否存在时,如果是目录会出错。尽量指定具体文件名,比如
results/{sample}/qc.txt而不是results/{sample}/。