第3节:Snakefile基本语法与规则定义归纳总结
讲到Snakefile,其实它就是个文本文件,你给它取名Snakefile就行。我刚开始写的时候总觉得要加什么头文件、引用库,结果发现——直接写规则就完了。Snakemake的语法继承自Python,所以里边的变量、函数、循环,你按Python写就行。但真正干活的是规则(rule),每个rule定义一个计算步骤。
这一节我们把最核心的语法掰开揉碎。嗯,很多教程直接甩一个复杂例子,新手就懵了。我习惯从最小骨架讲起,你理解了那几行,后面几百行就都能看懂。
规则的骨架
最简单的rule长这样:
rule my_step:
input:
"data/raw.txt"
output:
"results/clean.txt"
shell:
"python scripts/clean.py {input} {output}"
你看,rule后面跟规则名字,然后缩进写四个部分:input、output、params、shell(或run)。我个人习惯把input和output对齐,读起来清爽。Snakemake比较轴——它只认缩进,跟Python一样。如果你混用空格和Tab,会报IndentationError,我以前踩过这个坑,整个项目跑不起来,结果只是键盘敲歪了。
Input与Output:工作流的“契约”
每个规则声明它吃进什么、吐出什么。Snakemake就靠这个构建依赖图。注意output路径必须被后面的规则引用,否则Snakemake会觉得这文件没人要,直接跳过。
| 属性 | 作用 | 常见写法 |
|---|---|---|
| input | 声明输入文件路径 | "data/{sample}.txt" |
| output | 声明输出文件路径 | "results/{sample}.clean.txt" |
| params | 传递额外参数 | params = "threads=4" |
| log | 日志文件路径(可选) | log = "logs/my_step.log" |
我的一点经验:output里尽量用唯一路径,别跟其他规则抢名字。Snakemake一旦发现两个规则输出同一个文件,会报 AmbiguousRuleException。
通配符:以柔克刚
这才是Snakemake的杀手锏。你想对100个样本做同样操作,不可能写100条规则吧?用花括号{}做通配符:
rule clean_sample:
input:
"data/{sample}.txt"
output:
"results/{sample}.clean.txt"
shell:
"python clean.py {input} {output}"
Snakemake会从最终目标文件反推,把{sample}替换成实际字符串。比如最终要results/high_quality.clean.txt,那sample就是high_quality。它就像一个智能管道工,帮你把管子接好。
注意:通配符必须同时在input和output中出现(或者通过wildcard函数从input推导)。否则Snakemake不知道匹配什么。我曾经忘写一个,结果它用空字符串匹配,搞得文件都跑偏了。
rule all:默认目标
如果你不指定目标文件,Snakemake会执行Snakefile里第一条规则。但通常我们会用特殊的rule all作为入口:
rule all:
input:
"results/HG00096.clean.txt",
"results/HG00097.clean.txt",
"results/HG00098.clean.txt"
它只有input,没有output和shell。Snakemake一看,哦,我需要造这些文件,于是它从上往下找满足条件的规则。你想想看,这像一个任务清单。我每次写新项目,第一件事就是定义rule all,把最终产物列清楚。
参数、日志与临时文件
有些规则需要额外参数,比如线程数、平台选项。用params传:
rule align:
input:
"reads/{sample}.fq"
output:
"aligned/{sample}.bam"
params:
threads = 4
log:
"logs/align/{sample}.log"
shell:
"bwa mem -t {params.threads} ref.fa {input} > {output} 2> {log}"
日志单独拉出来,方便排查。我推荐每个非核心规则都加log,出了问题你直接看日志,不用重新跑一遍。
临时文件用temp()标记:
output:
temp("tmp/{sample}.intermediate.txt")
Snakemake会在管道完成后自动删除临时文件。省硬盘空间,这点很贴心。
规则执行方式:shell vs run
shell直接运行shell命令,run则执行Python代码块。我一般用shell,因为大部分生物信息工具是命令行。需要复杂逻辑就用run。
rule calculate_stat:
input:
"results/{sample}.counts"
output:
"results/{sample}.stat"
run:
import pandas as pd
df = pd.read_csv(input[0], sep="\t")
mean = df["value"].mean()
with open(output[0], "w") as f:
f.write(str(mean))
核心思路:Snakefile本质上是一个有向无环图(DAG)的声明文件。你定义规则,Snakemake自动推导执行顺序。不用自己写条件判断或循环,把脑力省给业务逻辑。
Snakefile规则与数据流结构
下面这张图概括了规则的关键组成和数据流动方向。我每次备课都会画一遍,帮你一眼看透。
没有规则的规则:checkpoint 和 localrule
除了普通rule,还有两种特殊形态:checkpoint和localrule。checkpoint用于动态生成新文件,让Snakemake在运行时重新构图;localrule强制在本地执行(即使有集群)。这两者属于进阶内容,但知道名字以后好搜。我很少用checkpoint,大部分场景普通的rule加通配符就够。
归纳总结
Snakefile语法就这些核心概念:
- 规则(rule):工作流的基本单元,有名字、input、output、params、log、shell/run。
- 通配符(wildcards):用花括号标记,实现批量化处理。
- 依赖推导:Snakemake根据output与input的路径匹配自动构建DAG。
- 特殊规则:rule all定义最终目标;checkpoint实现动态流程图;localrule强制本地执行。
你分析一下,几乎所有生信流程都可以拆成这样的规则组合。写Snakefile就像搭积木——一块规则处理一个变换,拼起来就是完整的分析流程。我个人建议新手上手时,先写一条手工规则的shell版本试试水,能跑过之后再加通配符,步子小一点不容易翻车。
实战建议:在项目根目录放一个Snakefile,然后用snakemake -n先干跑看流程图,确认依赖关系正确再真正执行。这样省电省时间。