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
小技巧:生产环境中FASTQ文件往往非常大,做测试时务必用小文件。我曾有一次用全基因组数据试Snakemake语法,结果等输出等了半小时……

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),最后告诉你跑完了。

关键观察:日常工作流我很少手动指定目标文件,而是把run-all的规则写在文件顶部。你还记得刚才的“Nothing to be done”吗?这是因为Snakemake没找到默认目标文件。只要把第一条规则改成目标规则,情况就不同了。

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”相关的报错:当你开始使用通配符(下一章会讲)时,通配符不匹配就会报这个。当前还没涉及。
避坑:我曾经把FASTQ文件名拼写为sample1.fastq,但在Snakefile里写的是sample1.fq。Snakemake找不到输入,直接报错退出,花了我十分钟才找到这个拼写错误。建议把文件名和路径的拼写检查当作调试第一步。

还有一个容易忽略的点——Shell命令里不要硬编码路径。上面例子中-o results/fastqc是我手动写的,但更好的做法是用{output}自动推导目录。我们等到讲通配符时再深入优化。

第一个Snakemake工作流:执行流程 FASTQ输入文件 Snakefile规则定义 构建DAG图 执行fastqc规则 输出:HTML + zip structured results/ 规则解析输入→输出→shell 检查文件是否存在

4.8 把上一节的知识点串起来

你看上面这张图,整个工作流就这么几步:

  1. Snakemake读取Snakefile,解析rule fastqc的输入和输出。
  2. 自动构建一个依赖图——目前只有一个节点。
  3. 检查输出文件是否存在。不存在?那就执行shell里的fastqc命令。
  4. 命令执行完毕后,输出文件落入results/fastqc/目录。

这个流程虽然简单,却是Snakemake一切高级功能的基石。我带的团队里,新成员只要把这个流程跑通一次,后面理解通配符、配置文件和并行执行就顺多了。

小结:你现在已经掌握了Snakemake的核心抽象——规则。接下来的所有内容,都是在这个框架上做扩展:让规则更通用(通配符)、让规则更灵活(配置文件)、让规则更快(并行/集群)。嗯,打好这个基础,后面的内容其实就是在给这个“骰子”加点功能。