2. Snakefile 基础:规则定义、输入输出、通配符、参数
好,咱们正式开始动手写 Snakefile。说白了一个 Snakefile 就是一个 规则文件。你告诉 Snakemake:“我要从 A 文件跑到 B 文件,中间用什么脚本跑,跑完后产出什么东西。” 它本质就是一个依赖关系图,也就是 DAG。我最初上手时,就觉得这玩意儿跟 Makefile 很像,但 Snakemake 在 Python 的基础上加了一层威力巨大的 通配符 系统。这其实是整个 DAG 审计的起点。
2.1 规则的骨架
一个规则长啥样?核心就四个关键字:rule、input、output、shell 或 run。比如下面这个最简单的例子:
rule say_hello:
output: "hello.txt"
shell:
"echo 'Hello, Snakemake!' > hello.txt"
我刚开始写的时候,习惯把 input 和 output 写成绝对路径。后来被同事喷了:“你写绝对路径,换个机器怎么跑?” 所以记住了——用 相对路径,并且尽量用 config 来管理。
规则还可以有个名字,比如上面那个叫 say_hello。Snakemake 默认会按顺序推断执行,但真正决定顺序的是 输出依赖。你有一个规则产出 hello.txt,另一个规则输入 hello.txt,那它俩就形成一条边(edge)。DAG 就是这么构建出来的。
2.2 输入与输出
input 和 output 支持多种形式。最常见的是字符串或列表。看个例子:
rule process_data:
input:
"data/raw.csv",
"config/params.yaml"
output:
"results/processed.csv"
shell:
"python scripts/process.py {input} {output}"
这里 {input} 会自动展开成两个文件的路径。我个人习惯在 shell 里显式写出每个输入,比如 {input[0]},这样管道脚本的参数顺序就不会错。踩过坑嘛——有一次我把两个输入顺序搞反,结果下游脚本把 CSV 文件当 YAML 读,直接崩溃。
画重点:input 里的文件可以不存在于磁盘上,Snakemake 会自动寻找能产出它的规则。如果找不到,抛错。
2.3 通配符
通配符是 Snakemake 的精髓,没有它,你就得手写成百上千条规则。比如你要处理一百个样本,用通配符一条规则就搞定:
rule align:
input:
"data/{sample}.fastq"
output:
"results/{sample}.bam"
shell:
"bwa mem ref.fa {input} > {output}"
这里 {sample} 就是通配符。你运行 snakemake results/sampleA.bam,它自动把 {sample} 替换成 sampleA。我在项目中遇到过最头疼的事是通配符冲突。比如两个规则用了同一个通配符名但指向不同文件,Snakemake 会报错:”AmbiguousRuleException“。怎么解决?把通配符名字写得更具体。比如 {sample_id} 和 {batch_id}。
{sample}.fastq,输出就叫 {sample}.bam,保持变量名在全局唯一。
2.4 参数
参数 (params) 是用来传非文件信息的。比如线程数、阈值、标志位。本质上 params 是一个字典,你在 shell 里通过 {params.key} 引用。
rule variant_call:
input:
bam="results/{sample}.bam",
ref="genome.fa"
output:
vcf="results/{sample}.vcf"
params:
qual=30,
threads=4
shell:
"bcftools mpileup -f {input.ref} -q {params.qual} "
"--threads {params.threads} {input.bam} -o {output.vcf}"
你可能问:为什么不用 input 里放个参数文件?因为参数文件不是真正的数据依赖,改了内容但文件名不变,Snakemake 不会重新执行。而 params 如果变了,Snakemake 会判定规则“状态不同”,自动重跑。
input 的配置文件里,结果调整了线程数,Snakemake 死活不重新跑。最后发现它只看文件名,不看文件内容。所以我建议:一切会影响运行结果但又不是文件的参数,扔进 params。
2.5 规则之间的依赖关系
咱们用一张图串起这节的核心逻辑。下面的 SVG 展示了从单个规则到通配符展开、再到多规则拼接的完整流程:
这个流程很简单:定义规则、填充输入输出、用通配符批量展开、通过 params 注入非文件参数,然后把命令扔进 shell 或 run 里。当你写好两条以上规则,它们之间的输入输出会自动连接成一张图。审计 DAG,本质上就是检查这张图有没有断边、有没有循环、有没有多余节点。
- 规则定义:一个 rule 块,包含名字和 body。
- 输入输出:决定了文件依赖关系,是 DAG 的边。
- 通配符:用
{wildcard}达到“一规则多文件”的效果。 - 参数:
params用来传线程数、阈值等非文件参数,变了会自动重跑。
嗯,第2章就到这儿。Snakefile 的玩法远不止这些,后面咱们会接触到 checkpoint、log、benchmark 等实用功能。你只要把规则、输入输出、通配符和参数这四个家伙搞明白,后面的路就好走多了。