Snakemake规则中如何通过script关键字条件性调用多个脚本
我完全理解你的痛点——不想大改已经写好的Python和R脚本(毕竟它们都依赖snakemake.input这类变量),但又想在同一个Snakemake规则里根据条件额外运行R脚本。确实,Snakemake的script:关键字本身只支持指定单个脚本路径,没法直接写条件判断或者多个脚本,但有两个不用修改原有脚本的办法可以解决这个问题:
方法一:用snakemake --script在shell中调用多个脚本
Snakemake提供了--script命令行参数,它可以让你在shell环境中运行脚本,同时自动把当前规则的snakemake上下文(包括input、output、params等变量)注入到脚本里,这样你的原有脚本完全不用改动。
你只需要把规则改成用shell:关键字,先调用Python脚本,再根据条件调用R脚本:
rule merge_results: input: [...] # 保持你的原有输入定义 output: path_results # 保持你的原有输出定义 params: cond = your_condition # 这里替换成你的实际条件,比如config["run_r_script"]或者某个输入是否存在 shell: """ # 先运行Python脚本,--script会自动传递snakemake上下文 snakemake --script merge_results.py # 根据条件判断是否运行R脚本 if {params.cond}; then snakemake --script my_script.R fi """
这个方案的好处是完全不用新增脚本,直接在规则里写shell逻辑就行,原有Python和R脚本的代码可以原封不动。
方法二:写一个轻量的Python调度脚本作为入口
如果更习惯用Python来处理逻辑,可以写一个简单的wrapper脚本,作为script:关键字的入口,在这个脚本里先执行原有Python脚本,再根据条件调用R脚本。
首先创建一个wrapper.py:
import subprocess import sys # 直接执行原有Python脚本,这样它能直接访问当前的snakemake对象 exec(open("merge_results.py").read()) # 根据条件判断是否运行R脚本,这里的cond从规则的params里获取 if snakemake.params.cond: # 调用R脚本,subprocess会继承当前环境变量,所以R脚本能拿到snakemake上下文 subprocess.run(["Rscript", "my_script.R"], check=True, stderr=sys.stderr, stdout=sys.stdout)
然后修改你的规则:
rule merge_results: input: [...] output: path_results params: cond = your_condition script: "wrapper.py"
这个方案里,原有Python脚本通过exec直接在wrapper的上下文里运行,完全能访问snakemake变量;R脚本通过subprocess调用时,会继承wrapper的环境变量,而wrapper是作为Snakemake脚本运行的,所以R脚本里的snakemake包依然能正常读取到input、output等信息,不需要修改任何原有代码。
备注:内容来源于stack exchange,提问作者Kiffikiffe

