第3节:Snakefile基本语法与规则定义归纳总结

讲到Snakefile,其实它就是个文本文件,你给它取名Snakefile就行。我刚开始写的时候总觉得要加什么头文件、引用库,结果发现——直接写规则就完了。Snakemake的语法继承自Python,所以里边的变量、函数、循环,你按Python写就行。但真正干活的是规则(rule),每个rule定义一个计算步骤。

这一节我们把最核心的语法掰开揉碎。嗯,很多教程直接甩一个复杂例子,新手就懵了。我习惯从最小骨架讲起,你理解了那几行,后面几百行就都能看懂。

规则的骨架

最简单的rule长这样:

rule my_step:
    input:
        "data/raw.txt"
    output:
        "results/clean.txt"
    shell:
        "python scripts/clean.py {input} {output}"

你看,rule后面跟规则名字,然后缩进写四个部分:input、output、params、shell(或run)。我个人习惯把input和output对齐,读起来清爽。Snakemake比较轴——它只认缩进,跟Python一样。如果你混用空格和Tab,会报IndentationError,我以前踩过这个坑,整个项目跑不起来,结果只是键盘敲歪了。

Input与Output:工作流的“契约”

每个规则声明它吃进什么、吐出什么。Snakemake就靠这个构建依赖图。注意output路径必须被后面的规则引用,否则Snakemake会觉得这文件没人要,直接跳过。

属性作用常见写法
input声明输入文件路径"data/{sample}.txt"
output声明输出文件路径"results/{sample}.clean.txt"
params传递额外参数params = "threads=4"
log日志文件路径(可选)log = "logs/my_step.log"

我的一点经验:output里尽量用唯一路径,别跟其他规则抢名字。Snakemake一旦发现两个规则输出同一个文件,会报 AmbiguousRuleException。

通配符:以柔克刚

这才是Snakemake的杀手锏。你想对100个样本做同样操作,不可能写100条规则吧?用花括号{}做通配符:

rule clean_sample:
    input:
        "data/{sample}.txt"
    output:
        "results/{sample}.clean.txt"
    shell:
        "python clean.py {input} {output}"

Snakemake会从最终目标文件反推,把{sample}替换成实际字符串。比如最终要results/high_quality.clean.txt,那sample就是high_quality。它就像一个智能管道工,帮你把管子接好。

注意:通配符必须同时在input和output中出现(或者通过wildcard函数从input推导)。否则Snakemake不知道匹配什么。我曾经忘写一个,结果它用空字符串匹配,搞得文件都跑偏了。

rule all:默认目标

如果你不指定目标文件,Snakemake会执行Snakefile里第一条规则。但通常我们会用特殊的rule all作为入口:

rule all:
    input:
        "results/HG00096.clean.txt",
        "results/HG00097.clean.txt",
        "results/HG00098.clean.txt"

它只有input,没有output和shell。Snakemake一看,哦,我需要造这些文件,于是它从上往下找满足条件的规则。你想想看,这像一个任务清单。我每次写新项目,第一件事就是定义rule all,把最终产物列清楚。

参数、日志与临时文件

有些规则需要额外参数,比如线程数、平台选项。用params传:

rule align:
    input:
        "reads/{sample}.fq"
    output:
        "aligned/{sample}.bam"
    params:
        threads = 4
    log:
        "logs/align/{sample}.log"
    shell:
        "bwa mem -t {params.threads} ref.fa {input} > {output} 2> {log}"

日志单独拉出来,方便排查。我推荐每个非核心规则都加log,出了问题你直接看日志,不用重新跑一遍。

临时文件用temp()标记:

output:
    temp("tmp/{sample}.intermediate.txt")

Snakemake会在管道完成后自动删除临时文件。省硬盘空间,这点很贴心。

规则执行方式:shell vs run

shell直接运行shell命令,run则执行Python代码块。我一般用shell,因为大部分生物信息工具是命令行。需要复杂逻辑就用run。

rule calculate_stat:
    input:
        "results/{sample}.counts"
    output:
        "results/{sample}.stat"
    run:
        import pandas as pd
        df = pd.read_csv(input[0], sep="\t")
        mean = df["value"].mean()
        with open(output[0], "w") as f:
            f.write(str(mean))

核心思路:Snakefile本质上是一个有向无环图(DAG)的声明文件。你定义规则,Snakemake自动推导执行顺序。不用自己写条件判断或循环,把脑力省给业务逻辑。

Snakefile规则与数据流结构

下面这张图概括了规则的关键组成和数据流动方向。我每次备课都会画一遍,帮你一眼看透。

Snakefile 规则内部结构 rule my_rule input: "data/{sample}.txt" output: "out/{sample}.clean" params: threads=4 log: "logs/{sample}.log" shell / run 执行体 通配符 {sample} 会在运行时自动替换为实际文件名 Snakemake 从最终目标反推规则,构建 DAG

没有规则的规则:checkpoint 和 localrule

除了普通rule,还有两种特殊形态:checkpoint和localrule。checkpoint用于动态生成新文件,让Snakemake在运行时重新构图;localrule强制在本地执行(即使有集群)。这两者属于进阶内容,但知道名字以后好搜。我很少用checkpoint,大部分场景普通的rule加通配符就够。

归纳总结

Snakefile语法就这些核心概念:

  • 规则(rule):工作流的基本单元,有名字、input、output、params、log、shell/run。
  • 通配符(wildcards):用花括号标记,实现批量化处理。
  • 依赖推导:Snakemake根据output与input的路径匹配自动构建DAG。
  • 特殊规则:rule all定义最终目标;checkpoint实现动态流程图;localrule强制本地执行。

你分析一下,几乎所有生信流程都可以拆成这样的规则组合。写Snakefile就像搭积木——一块规则处理一个变换,拼起来就是完整的分析流程。我个人建议新手上手时,先写一条手工规则的shell版本试试水,能跑过之后再加通配符,步子小一点不容易翻车。

实战建议:在项目根目录放一个Snakefile,然后用snakemake -n先干跑看流程图,确认依赖关系正确再真正执行。这样省电省时间。