工作流基础:规则定义、输入输出文件、运行工作流
一个可复现的工作流,核心就是「规则」。你要是理解了规则怎么写,Snakemake 就学会了一半。我自己刚开始接触时,总觉得语法麻烦,后来才发现——说白了,规则就是告诉 Snakemake:你要什么,我有什么,用什么工具来干。
1. 规则的骨架
每个规则由 rule 关键字开头,后面跟一个名字。名字随你取,但别用中文,也别带空格。我习惯用工具名+步骤,比如 bwa_mem、samtools_sort,这样一眼能看出它在干嘛。
一个最精简的规则长这样:
rule bwa_mem:
input:
"data/sample_R1.fastq",
"data/sample_R2.fastq",
"refs/genome.fa"
output:
"results/sample.sam"
shell:
"bwa mem {input[2]} {input[0]} {input[1]} > {output}"
看到了吗?input 和 output 是必须写的(除非你用 checkpoint 之类的高级功能,那是后话)。shell 里写你要执行的命令行,用花括号引用输入输出文件。顺序?没有严格的顺序要求,但我建议按使用习惯排好,方便别人读你的 Snakefile。
2. 输入输出文件:规则的“护照”
Snakemake 靠输入输出文件来推导依赖关系。你想想看,它怎么知道先跑哪个规则、后跑哪个?就是看输出文件是不是另一个规则的输入文件。这个推导机制,我称之为“文件的护照”——只有匹配上了,工作流才走得通。
| 特性 | 说明 | 常见写法 |
|---|---|---|
| 单个文件 | 直接写路径字符串 | "data/sample.fastq" |
| 多个文件 | 用逗号分隔,Snakemake 会自动打包为列表 | "a.fq", "b.fq" |
| 命名输入 | 用 键 = 值 的形式,shell 里通过 {input.键} 引用 | reads = "sample.fq", ref = "genome.fa" |
| 通配符输出 | 用 {sample} 等方式动态匹配文件名 | "results/{sample}.bam" |
我遇到过有人把 input 写成了 inputs(多了一个 s),结果 Snakemake 直接报错——语法检查和类型检查都不放过。所以,要注意变量名严格匹配文档。
这里有个小技巧:如果你有大量相同前缀的输入文件,可以用 expand() 函数批量生成:
rule merge_bams:
input:
expand("results/{sample}.bam", sample=["A", "B", "C"])
output:
"results/merged.bam"
shell:
"samtools merge {output} {input}"
3. 运行工作流:蛇的第一次爬行
写好了 Snakefile,怎么跑?在终端里执行 snakemake 命令就行。最简单的用法:
snakemake -j1
-j 代表并行任务数,1 就是一次只跑一个规则。我第一次跑的时候没加 -j,默认把 CPU 全部占满,搞得电脑卡死……嗯,还是老实点,先从 -j1 开始。
如果你想指定生成某个文件,直接把文件名作为参数传给 snakemake:
snakemake results/sample.bam -j1
这样 Snakemake 就会自动分析依赖,找出需要运行的所有规则,执行一条完整的流水线。它不会多干也不偷懒。哇,这种确定性我最喜欢了。
/home/user/data/xxx,结果换到服务器跑就炸了。建议始终使用相对路径。
4. 规则执行流程的直观理解
我画了一张图,帮你理解 Snakemake 是怎么把规则串起来的。下面这个例子有两个规则:bwa_mem 和 samtools_sort。第一个生成 .sam,第二个把它排成 .bam。
当你执行 snakemake sample.bam 时,它先找 sample.bam 是不是某个规则的输出。找到了 samtools_sort,发现它需要 sample.sam;接着找 sample.sam 是 bwa_mem 的输出,而 bwa_mem 的输入文件已经存在磁盘上。好,那就先跑 bwa_mem,再跑 samtools_sort。整个过程不需要你手动排序,Snakemake 自动搞定。
5. 实战中的小坑
我曾经 犯过一个错误:在规则 A 里输出 "sample.bam",规则 B 的输入写成了 "sample.bam "(末尾多了一个空格)。当时怎么找都报错说文件不存在,后来用 ls | cat -A 才揪出来。空格和小写大小写在 Snakemake 里就是天壤之别,千万别不信。
还有,output 文件所在的目录如果不存在,Snakemake 会自动创建目录。但它不会替你生成文件本身——文件得靠 shell 或 run 里的命令去创建。所以别以为写个 output 路径就完事了,命令里忘了写重定向,运行完发现 output 文件是空的(或者干脆不存在),Snakemake 就会报错退出。
snakemake -n 做 dry-run,看推导对不对。等确认无误后再 snakemake -j1 跑一次,顺便用 touch 测试一下增量更新。这样能省很多调试时间。
6. 小结
这一章的内容很基础,但非常关键。规则、输入输出文件、运行命令,这三者构成了 Snakemake 工作流的最小闭环。我个人觉得:只要你把输入输出写清楚,Snakemake 像个聪明的管家,它会自己安排干活顺序。后面几章会在这个基础上加进阶玩法,但那之前,先把基础练扎实了。
嗯,留个小练习:试着写一个 Snakefile,包含两个规则——download_data(用 wget 下载一个文件)和 count_lines(统计行数)。然后运行 snakemake count_lines.txt -j1,看看是不是自动推导出先下载后统计。如果跑通了,恭喜你,Snakemake 的入门关已经过了。