第一个Snakemake工作流:编写简单的质控规则

终于到了动手环节。前面我们把环境搭好、概念理清,现在来写第一个真正的 Snakemake 工作流。

我个人习惯,学一个新工具时先让它跑通一个最简单的任务。不要一上来就搞复杂的全流程。先能输出一个文件,再考虑后续的批量、条件、报告。这叫「最小可行工作流」。

3.1 第一个 Snakefile 长什么样?

Snakemake 的核心文件叫 Snakefile。你想想看,它其实就是一个带规则的 Python 文件。所有规则(rule)都写在这个文件里。

我们直接来看一个最简单的 Snakefile:

rule hello:
    output:
        "hello.txt"
    shell:
        "echo 'Hello, Snakemake!' > hello.txt"

这个规则做了三件事:

  • 声明规则名:rule hello:,名字叫 hello
  • 定义输出:output: "hello.txt",告诉 Snakemake 我要生成一个文件
  • 执行命令:shell: 后面跟的就是实际要跑的 shell 命令

运行它试试看:

snakemake -j1

-j1 表示只用一个 CPU 核心。输出长这样:

Building DAG of jobs...
Using shell: /bin/bash
Provided cores: 1
Rules claiming more threads will be scaled down.
Job counts:
	count	jobs
	1	hello
	1

[Mon Jan  1 00:00:00 2024]
rule hello:
    output: hello.txt
    jobid: 0
    reason: Missing output files: hello.txt
    wildcards: --

Finished job 0.
1 of 1 steps (100%) done

看到「100% done」了吗?工作流跑完了。当前目录下多了一个 hello.txt,内容就是 Hello, Snakemake!。

个人经验:我第一次跑 Snakemake 时,不知道 -j 是干什么的。没加这个参数,结果它提醒我「No core count provided」。其实 Snakemake 默认不允许你使用所有资源,必须显式指定。这个设计挺严谨的,防止你一不小心把服务器撑爆。

3.2 编写第一条 QC 规则

光输出一个文本文件太简单了。我们用 FastQC 来做真正的质控。

假设你有一个 FASTQ 文件 sample1_R1.fastq,在项目的 data/ 目录下。我们的目标是让 Snakemake 自动对 sample1_R1.fastq 运行 FastQC,并把结果输出到 results/fastqc/。

先写好 Snakefile:

rule run_fastqc:
    input:
        "data/sample1_R1.fastq"
    output:
        "results/fastqc/sample1_R1_fastqc.html",
        "results/fastqc/sample1_R1_fastqc.zip"
    shell:
        "fastqc {input} -o results/fastqc/"

这里我加入了 input:。Snakemake 会检查输入文件是否存在。如果不存在,它会报错并停下,不会傻傻地跑出一个空结果。

运行命令不变:

mkdir -p results/fastqc
snakemake -j1
注意:results/fastqc/ 目录需要提前创建吗?答案是:不需要。Snakemake 会自动创建输出文件所需的目录。但如果你在 shell: 里用了 -o results/fastqc/,FastQC 本身会尝试写文件到这个目录。所以最好提前建好目录,或者用 mkdir -p 确保目录存在。我在项目中遇到过 FastQC 因为目录不存在而报「No such file or directory」的错,所以你现在看到我写 Snakefile 时,习惯在 shell 命令里加一行 mkdir -p。

3.3 理解 Snakemake 的输入输出模式

刚才这个规则虽然能跑,但你仔细看:input 和 output 都是硬编码的。这样写有个大问题——如果换一个样本,就要改 Snakefile。

Snakemake 真正的威力在于「自动推断」。你只需要告诉它最终想要什么,它会反向推导出需要哪些输入、跑哪些规则。

看这个更通用的写法:

rule run_fastqc:
    input:
        "data/{sample}_R1.fastq"
    output:
        "results/fastqc/{sample}_R1_fastqc.html",
        "results/fastqc/{sample}_R1_fastqc.zip"
    shell:
        "fastqc {input} -o results/fastqc/"

注意到 {sample} 了吗?这叫通配符(wildcard)。当你执行:

snakemake results/fastqc/sample1_R1_fastqc.html -j1

Snakemake 会自动把 {sample} 替换成 sample1,然后推导出输入文件是 data/sample1_R1.fastq。

概念 说明
通配符 用 {name} 表示的变量,在运行时替换为实际值
输入推断 Snakemake 根据 output 中的通配符自动匹配 input
目标文件 命令行中指定的文件,Snakemake 以它为目标反向构建

关键思维转变:写 Snakefile 时,不要想着「先做什么后做什么」。而是思考「我最终想要什么文件」以及「这些文件从哪些输入文件产生」。Snakemake 会自动帮你理顺执行顺序。

3.4 运行前检查:干运行模式

你写的规则到底对不对?Snakemake 有一个非常实用的功能——干运行(dry-run)。

snakemake -n

-n 参数表示只模拟执行,不真的跑。输出像这样:

Building DAG of jobs...
Job stats:
rule        count
run_fastqc      1

[Tue Jan  2 00:00:00 2024]
rule run_fastqc:
    input: data/sample1_R1.fastq
    output: results/fastqc/sample1_R1_fastqc.html,
            results/fastqc/sample1_R1_fastqc.zip
    jobid: 0
    reason: Missing output files: results/fastqc/sample1_R1_fastqc.html
    wildcards: sample=sample1
    resources: tmpdir=/tmp

Job counts:
	count	jobs
	1	run_fastqc
	1

你会看到它准备执行哪些规则、输入输出是什么、通配符的值是什么。这些信息在调试时非常有用。

避坑指南:我曾经花了一个下午排查一个 bug,最后发现是 input 文件名写错了。如果当时先跑一次 snakemake -n,一看它提示的 input 路径,立马就能发现问题。所以现在我写任何新规则,第一步永远是 snakemake -n。

3.5 查看结果

工作流跑完后,结果在 results/fastqc/ 目录下。

ls results/fastqc/
sample1_R1_fastqc.html  sample1_R1_fastqc.zip

你可以用浏览器打开 HTML 文件查看质控报告。如果是在远程服务器上,可以用 scp 下载到本地,或者用 python -m http.server 临时起一个 HTTP 服务再访问。

至此,你完成了第一个真正的 Snakemake 工作流。它很小,但已经包含了所有核心要素:规则、输入、输出、通配符、干运行。后面的内容就是在这个基础上不断扩展功能。

核心要点回顾:

  • Snakefile 是工作流的定义文件,使用 Python 语法
  • 每个 rule 包含 input、output、shell 三个基本部分
  • 通配符 {sample} 让规则可以批量适用于多个样本
  • 干运行 -n 是调试利器,建议每次修改后都执行一次
Snakemake 工作流核心逻辑 rule run_fastqc input output shell snakemake -n 通配符 {sample} 自动匹配 Snakemake 反向推导 从目标文件到输入文件