4、第一个Snakemake工作流:编写简单的规则,从FASTQ文件开始,运行工作流并理解输出
搞定了环境和安装,咱们终于要亲手写第一个Snakefile了。说实话,我个人觉得这一步是Snakemake从“看着还行”到“确实好用”的转折点。你不需要懂得多高深的概念,只要跟着敲几个规则,就能看到一个真正的工作流跑起来。
4.1 准备工作:创建项目目录和测试数据
我在项目中习惯用tree命令预览目录结构。咱们先搭好实验场:
mkdir -p snakemake_qc/data/raw_fastq
cd snakemake_qc
然后快速造两个微型FASTQ文件用于测试。别纠结数据质量,咱们要的是让工作流转起来:
echo -e "@read1\nACTGACTGACTG\n+\nIIIIIIIIIIII" > data/raw_fastq/sample1.fastq
echo -e "@read2\nTGACTGACTGAC\n+\nIIIIIIIIIIII" > data/raw_fastq/sample2.fastq
4.2 编写第一个Snakefile
核心文件就是Snakefile。你想想看,一个工作流入口文件,里面放的是规则,它告诉Snakemake要做什么、依赖什么、输出什么。
在项目根目录下新建一个空文件,命名为Snakefile,然后写入下面内容:
rule fastqc:
input:
"data/raw_fastq/sample1.fastq"
output:
"results/fastqc/sample1_fastqc.html",
"results/fastqc/sample1_fastqc.zip"
shell:
"fastqc {input} -o results/fastqc"
这个规则很简单。我刚开始写Snakefile的时候,第一反应就是:这不就是在描述我要干啥嘛?对,就是这么回事。
4.3 拆解规则:看看每行在做什么
| 元素 | 作用 | 注意 |
|---|---|---|
rule fastqc: |
定义一个规则,名字叫fastqc | 规则名在同一个文件里别重复 |
input: |
规则需要的输入文件 | 可以写多个,每个一行 |
output: |
规则产生的输出文件 | Snakemake根据输出判断是否要执行 |
shell: |
实际执行的shell命令 | {input}和{output}会自动替换 |
说白了,Snakemake就是读你的input和output,如果输出文件不存在,或者输入文件比输出文件新,它就执行shell里的命令。这一点我在做生产环境QC时体会很深——它能避免重复计算,节省大量时间。
4.4 运行工作流
先创建输出目录,让fastqc写得到文件:
mkdir -p results/fastqc
这条命令有点特殊,我建议你放在Snakefile外面处理好。有时候我自己写规则时会在shell里加一句mkdir -p results/fastqc,但更好的做法是让工作流依赖目录自动创建。
运行命令:
snakemake -c1
你会看到类似这样的输出:
Building DAG of jobs...
Nothing to be done (all files are up-to-date).
等等,为什么什么都不做?因为你已经手动创建了输出目录,且输出文件还未生成……等一下,我好像忘了让Snakemake知道目标文件不存在。试试这样:
snakemake results/fastqc/sample1_fastqc.html -c1
这次输出应该不一样:
Building DAG of jobs...
Using shell: /bin/bash
Provided cores: 1
Rules claiming more threads will be scaled down.
Job stats:
job count min threads max threads
------- ----- ----------- -----------
fastqc 1 1 1
total 1 1 1
[Mon Jan 20 10:00:00 2025]
rule fastqc:
input: data/raw_fastq/sample1.fastq
output: results/fastqc/sample1_fastqc.html, results/fastqc/sample1_fastqc.zip
jobid: 0
reason: Missing output files: results/fastqc/sample1_fastqc.html
resources: tmpdir=/tmp
Running fastqc…
Analysis complete for sample1.fastq
[Mon Jan 20 10:00:05 2025]
Finished job 0.
1 of 1 steps (100%) done
输出里清晰展示了规则名、输入、输出、触发原因(Missing output files),最后告诉你跑完了。
4.5 理解Snakemake执行逻辑:依赖图是关键
第一个工作流跑通了,但只有一个规则,你感受不到工作流的真正威力。我们再加入第二个样本,看看Snakemake如何同时处理多个文件。
把Snakefile改成:
rule fastqc:
input:
"data/raw_fastq/sample1.fastq",
"data/raw_fastq/sample2.fastq"
output:
"results/fastqc/sample1_fastqc.html",
"results/fastqc/sample1_fastqc.zip",
"results/fastqc/sample2_fastqc.html",
"results/fastqc/sample2_fastqc.zip"
shell:
"fastqc {input} -o results/fastqc"
不过这写法太“硬”了。假如有20个样本,你打算写20组output吗?不,我们马上要讲通配符。但先别急,先理解这个规则:现在它输入两个文件,输出四个文件,Shell命令本身会一次处理所有输入。
我经常会遇到新手问:“只有一个rule fastqc,它怎么同时处理两个样本?”其实Snakemake把这两个样本看作一个job,在job内部一次性跑完fastqc。当然,这不是最优的并行方式。
4.6 输出目录结构:你期望的是什么样?
跑完上面的规则,目录结构应该是:
snakemake_qc/
├── Snakefile
└── data/
└── raw_fastq/
├── sample1.fastq
└── sample2.fastq
└── results/
└── fastqc/
├── sample1_fastqc.html
├── sample1_fastqc.zip
├── sample2_fastqc.html
└── sample2_fastqc.zip
我第一次跑Snakemake时,对它自动把输出文件归到目标目录这一点感到很爽。传统脚本经常把中间产物和最终结果混到一起,但Snakemake强迫你养成好的组织习惯,这一点在长期维护中能省不少心。
4.7 常见运行问题与排查
- “Nothing to be done”:输出文件已经存在且比输入新。手动删除输出文件或用
-f强制重跑。 - “Missing input files”:输入路径写错了,检查
data/raw_fastq下是否真有文件。 - “wildcards”相关的报错:当你开始使用通配符(下一章会讲)时,通配符不匹配就会报这个。当前还没涉及。
sample1.fastq,但在Snakefile里写的是sample1.fq。Snakemake找不到输入,直接报错退出,花了我十分钟才找到这个拼写错误。建议把文件名和路径的拼写检查当作调试第一步。
还有一个容易忽略的点——Shell命令里不要硬编码路径。上面例子中-o results/fastqc是我手动写的,但更好的做法是用{output}自动推导目录。我们等到讲通配符时再深入优化。
4.8 把上一节的知识点串起来
你看上面这张图,整个工作流就这么几步:
- Snakemake读取Snakefile,解析
rule fastqc的输入和输出。 - 自动构建一个依赖图——目前只有一个节点。
- 检查输出文件是否存在。不存在?那就执行
shell里的fastqc命令。 - 命令执行完毕后,输出文件落入
results/fastqc/目录。
这个流程虽然简单,却是Snakemake一切高级功能的基石。我带的团队里,新成员只要把这个流程跑通一次,后面理解通配符、配置文件和并行执行就顺多了。