Snakemake如何将两个相似规则合并为单个规则?
合并Snakemake相似规则的可行方案
你有两个逻辑几乎一致的Snakemake规则,核心差异仅在于输入路径的生成逻辑和输出路径的结构(一个包含dataset+day通配符,另一个用testset通配符),想合并为单个规则但遇到输出路径无法灵活处理的问题,以下是几种解决思路:
方法1:利用lambda函数+通配符存在性判断构建输出路径
Snakemake允许在输入/输出中使用lambda函数,结合通配符的存在性来动态生成路径。可以在规则中同时声明所有可能的通配符,然后根据哪些通配符被实际使用来构建输出路径:
rule labels_unified: input: lambda wildcards: ( get_predict_data(datadir, wildcards.dataset, wildcards.day) if hasattr(wildcards, 'dataset') else datadir / config['test_sets'][wildcards.testset]['path'] ) output: lambda wildcards: ( dir / 'data' / wildcards.dataset / wildcards.day / 'labels.gz' if hasattr(wildcards, 'dataset') else dir / 'data' / wildcards.testset / 'labels.gz' ) shell: "zcat {input} | cut -f1 -d '|' | gzip > {output}"
调用这个规则时,只需要明确指定对应的通配符即可:
- 原
labels规则的调用:snakemake labels_unified --wildcards dataset=foo day=bar - 原
labels2规则的调用:snakemake labels_unified --wildcards testset=baz
注意:这种方法要求每次调用时只传入一组互斥的通配符(要么传dataset+day,要么传testset),避免冲突。
方法2:使用规则继承简化重复代码
如果不想完全合并为单个规则,也可以用Snakemake的规则继承特性,把公共的shell命令抽出来作为基础规则,子规则只定义差异的输入输出:
rule labels_base: shell: "zcat {input} | cut -f1 -d '|' | gzip > {output}" rule labels: extends: labels_base input: lambda wildcards: get_predict_data(datadir, wildcards.dataset, wildcards.day) output: dir / 'data' / '{dataset}' / '{day}' / 'labels.gz' rule labels2: extends: labels_base input: lambda wildcards: datadir / config['test_sets'][wildcards.testset]['path'] output: dir / 'data' / '{testset}' / 'labels.gz'
这种方式既减少了代码重复,又保留了原规则的清晰结构,适合需要区分两种场景的情况。
方法3:引入统一的通配符+配置映射
可以定义一个统一的通配符(比如group),然后在配置中维护group到具体参数的映射,让规则通过这个映射来生成输入输出路径:
首先在config.yaml中添加映射:
groups: foo_bar: type: predict dataset: foo day: bar baz_test: type: test testset: baz
然后规则中使用这个映射:
rule labels_unified: input: lambda wildcards: ( get_predict_data(datadir, config['groups'][wildcards.group]['dataset'], config['groups'][wildcards.group]['day']) if config['groups'][wildcards.group]['type'] == 'predict' else datadir / config['test_sets'][config['groups'][wildcards.group]['testset']]['path'] ) output: lambda wildcards: ( dir / 'data' / config['groups'][wildcards.group]['dataset'] / config['groups'][wildcards.group]['day'] / 'labels.gz' if config['groups'][wildcards.group]['type'] == 'predict' else dir / 'data' / config['groups'][wildcards.group]['testset'] / 'labels.gz' ) shell: "zcat {input} | cut -f1 -d '|' | gzip > {output}"
调用时只需指定group:snakemake labels_unified --wildcards group=foo_bar
这种方法适合场景较多、需要统一管理的情况,规则本身更简洁。
内容的提问来源于stack exchange,提问作者Pida
相关产品推荐
相关产品推荐

