第2章 规则(Rule)基础:规则定义、输入输出、运行命令
Snakemake 的核心就是规则。说白了,每条规则告诉系统:你要用哪些输入文件,生成哪些输出文件,中间跑什么命令。
我刚开始接触时,觉得这和 Makefile 差不多嘛,但用着用着就发现,它比 Makefile 聪明太多了。
2.1 一条规则长什么样
一条完整的规则,至少包含三样东西:规则名字、输入文件、输出文件。
如果再给个运行命令,它就能干活了。
rule fastqc:
input:
"data/sample1.fastq"
output:
"results/fastqc/sample1_fastqc.html"
shell:
"fastqc {input} -o results/fastqc/"
这里 rule fastqc 是规则名,input 和 output 分别指定了输入输出路径,shell 里写的是要执行的命令。{input} 和 {output} 是 Snakemake 自动解析的占位符,会自动替换成实际文件路径。
run_fastqc、trim_adapters,这样一看就知道它在干啥。
2.2 输入输出:不仅仅是路径
input 和 output 支持多种写法。最常见的自然是字符串路径,但遇到多个文件时,就得用列表或字典了。
rule multiqc:
input:
"results/fastqc/sample1_fastqc.zip",
"results/fastqc/sample2_fastqc.zip",
"results/fastqc/sample3_fastqc.zip"
output:
"results/multiqc/multiqc_report.html"
shell:
"multiqc {input} -o results/multiqc/"
如果输入文件有几百个,手动写列表就太傻了。这时候我会用 expand() 函数批量生成路径。
SAMPLES = ["sample1", "sample2", "sample3"]
rule multiqc:
input:
expand("results/fastqc/{sample}_fastqc.zip", sample=SAMPLES)
output:
"results/multiqc/multiqc_report.html"
shell:
"multiqc {input} -o results/multiqc/"
expand() 会遍历所有样本名,拼接出完整列表。后面讲到通配符时,你会发现它和通配符是一对好搭档。
2.3 运行命令的三种方式
Snakemake 提供三种方式来执行规则里的计算:
- shell:直接写 shell 命令,适合快速调用现成工具
- run:用 Python 代码块,适合需要逻辑处理的场景
- script:调用外部脚本(R、Python 等),适合已经写好的分析脚本
看看它们的具体用法:
# shell 方式 - 调用外部工具
rule cutadapt:
input:
"data/{sample}.fastq"
output:
"results/trimmed/{sample}.fastq"
shell:
"cutadapt -a AGATCGGAAGAGC -o {output} {input}"
# run 方式 - Python 内联处理
rule count_reads:
input:
"data/{sample}.fastq"
output:
"results/counts/{sample}.txt"
run:
total = 0
with open(input[0]) as f:
for i, line in enumerate(f):
if i % 4 == 1:
total += 1
with open(output[0], "w") as out:
out.write(f"{total}\n")
# script 方式 - 调用外部 R 脚本
rule plot_distribution:
input:
"results/counts/{sample}.txt"
output:
"results/plots/{sample}_dist.pdf"
script:
"scripts/plot_dist.R"
run 时忘记写 input 和 output 的索引。注意,input 和 output 在 run 块里是列表或字典,要用 input[0] 或 output["key"] 来取具体文件。直接写 input 会报错!
2.4 规则的执行顺序与依赖
Snakemake 会自动分析规则之间的输入输出依赖,确定执行顺序。举个例子:
rule fastqc:
input:
"data/{sample}.fastq"
output:
"results/fastqc/{sample}_fastqc.html"
shell:
"fastqc {input} -o results/fastqc/"
rule multiqc:
input:
expand("results/fastqc/{sample}_fastqc.html", sample=SAMPLES)
output:
"results/multiqc/multiqc_report.html"
shell:
"multiqc {input} -o results/multiqc/"
Snakemake 看到 multiqc 需要 fastqc 的输出,就会先执行所有 fastqc 任务,再执行 multiqc。你不需要手动排序,它自己会推算。
但有一点要留心:如果两个规则的输出文件名相同,Snakemake 会报错。所以输出路径一定要有区分度,我习惯用 results/工具名/样本名.后缀 这种模式。
2.5 规则结构全景图
下面这张图把规则的核心元素串在了一起,你看看就明白了:
一句话总结:规则定义输入输出,Snakemake 自己跟踪依赖,你只管写命令。这就是它比脚本批处理的高明之处。
2.6 常见小坑与习惯
| 问题 | 表现 | 解决 |
|---|---|---|
| 输出路径写错 | 规则永远不执行,说输出已经存在 | 检查 output 里的目录是否已创建,Snakemake 不会自动建目录 |
| 通配符没用对 | 报错 "Wildcards in input not in output" | 所有出现在 input 里的通配符,output 里必须有 |
| shell 命令太长 | 可读性差,不好维护 | 用反斜杠换行,或者写到单独的脚本里用 script 调用 |
我个人习惯,每写一条新规则都先跑 snakemake -n 做一次 dry-run,看看 Snakemake 解析出来要执行哪些步骤。能提前发现输入输出不匹配的问题。
规则的基础就这些。你想想看,当输入输出定义清楚后,Snakemake 就能自动帮你管理数据流和任务并行。后面章节我们会往规则里加入通配符、线程、资源控制,让它真正强大起来。不过,先把今天这三板斧练熟——规则名、输入输出、运行命令——你已经能写出可用的工作流了。