3. Snakemake工作流结构:Snakefile、规则、输入输出、通配符
3.1 Snakefile:工作流的蓝图
每个 Snakemake 项目,起点都是一个叫 Snakefile 的文件。说白了,它就是整个工作流的「施工图纸」。Snakemake 启动时,默认在当前目录找这个文件,然后逐行解析里面的规则。
我个人习惯把 Snakefile 放在项目的根目录下,和数据、脚本、配置文件分开。这样结构清爽,找东西也方便。文件内容就是纯 Python 加规则声明——对,它本质上是 Python,所以你可以写循环、条件判断,甚至导入第三方库。
一个最简单的 Snakefile 长这样:
rule all:
input:
"results/final.txt"
嗯,别小看这三行,它定义了一个终极目标。Snakemake 看到 rule all,就知道最终要产出 results/final.txt,然后反向去推导需要跑哪些前置规则。
snakefile 或 Snakefile.txt,Snakemake 会直接忽略它。
3.2 规则:核心执行单元
规则是 Snakemake 的「灵魂」。每个规则描述了三件事:输入什么、输出什么、怎么干。我刚开始用的时候,总觉得规则就是一个个「小脚本」,后来才意识到——它们更像是一张张「承诺书」:你给我正确的输入,我保证生成对应的输出。
规则的基本骨架:
rule <规则名称>:
input:
"路径/文件名"
output:
"路径/文件名"
shell:
"要执行的命令"
除了 shell,你还可以用 run 直接写 Python 代码,或者用 script 调用外部脚本。三者的选择逻辑很简单:命令短用 shell,逻辑复杂用 run,脚本太长就扔进单独文件用 script 调用。
| 执行方式 | 适用场景 | 示例 |
|---|---|---|
| shell | 单行命令或管道 | shell: "gzip {input} > {output}" |
| run | Python 逻辑处理 | run: with open(output[0], "w") as f: ... |
| script | 外部 R/Python 脚本 | script: "scripts/analyze.py" |
3.3 输入输出:规则间的纽带
规则不是孤岛。一个规则的输出,往往是另一个规则的输入。Snakemake 正是通过这种「文件依赖」自动推导出执行顺序的。你不需要告诉它先跑哪个后跑哪个——它自己看文件。
我见过不少新手把输入输出写成绝对路径,结果换个机器就崩了。我的建议是:始终用相对路径,以项目根目录为基准。这样工作流才能移植。
多个输入输出怎么定义?用列表或字典:
rule 合并样本:
input:
fastq1="data/sample1_R1.fastq",
fastq2="data/sample1_R2.fastq"
output:
bam="results/sample1.bam",
bai="results/sample1.bam.bai"
shell:
"bwa mem {input.fastq1} {input.fastq2} | samtools sort > {output.bam}"
" && samtools index {output.bam}"
用命名的方式,代码可读性直接拉满。你想想看,哪天回头维护这个工作流,看到 {input.fastq1} 比 {input[0]} 舒服多了。
shell: "mkdir -p $(dirname {output})",或者在 Python 里用 os.makedirs()。
3.4 通配符:动态匹配的利器
这才是 Snakemake 最「骚」的地方。没有通配符,你只能一条规则处理一个文件;有了通配符,一条规则就能处理成千上万个文件。
通配符用花括号 { } 包裹,写在输入输出文件名里。比如 data/{sample}.fastq 里的 {sample} 就是一个通配符。运行时,Snakemake 会根据实际需要输出的文件,反向解析出通配符的值。
举个例子:
rule 比对:
input:
"data/{sample}.fastq"
output:
"results/{sample}.bam"
shell:
"bwa mem ref.fa {input} | samtools sort > {output}"
当你执行 snakemake results/sample1.bam 时,Snakemake 自动把 {sample} 匹配成 sample1。然后它发现 data/sample1.fastq 存在,就执行比对命令。
通配符可以同时有多个:
output: "results/{sample}_{lane}.bam"
匹配时,Snakemake 会同时解析 {sample} 和 {lane} 两个变量。
避坑指南:通配符的匹配范围
我曾经在项目中遇到过一个问题:通配符匹配得太「贪心」了。比如文件名是 sample_01_clean.fastq,你用 {sample}_clean.fastq 去匹配,{sample} 会吞掉 sample_01 还是 sample_01_?
答案是:Snakemake 的匹配默认是非贪婪的,它会在满足整体模式的前提下,让每个通配符匹配最短的可能值。但为了保险,我建议在命名时加上分隔符,比如 sample_{id}.clean.fastq,这样逻辑一目了然。
3.5 整体结构一览
把上面这些概念串起来,一个典型的 Snakemake 工作流结构大致是这样的:
从上图可以清楚看到:Snakefile 统领全局,它包含多个规则;规则之间通过「输入→输出」形成数据流水线;通配符则是连接文件名与变量的桥梁,让一条规则能处理无数个样本。而你只需要告诉 Snakemake 最终想要什么文件,剩下的推导、调度、执行——它全包了。