включая суб-Snake-файлы и генерировать их результаты - PullRequest
0 голосов
/ 08 апреля 2020

Проблема

Проблема в том, что когда я include других суб-файлов Snake-файлов в основной Snake-файл, выходные данные суб-файлов Snake никогда не генерируются. Если это помогает, я думаю об этом как о наличии дополнительных «ветвей» в конвейере - иногда мои клиенты хотят получить выходные данные определенной ветки c, а приходят клиенты - нет. Я хочу иметь возможность выбирать, когда выполнять эти дополнительные ветви. Моя версия snakemake: 5.13.0.

Идеальное решение

Я хочу include суб-Snake-файлов и генерировать выходные данные этих суб-Snakefile. Вот что я представляю:

config.yaml

module2: "yes"

Snakefile ('главный' snakefile)

# working dir
workdir: "path/to/dir/test"
configfile: "code/config.yaml"

# core rules. These rules are always run. 
include: "code/head.py"

if config["module2"] == "yes":
    include: "code/module2/Snakefile"

rule all:
    input:
        "data/output_head.txt"

Где module2 может, например, генерировать спецификацию c график, если мои клиенты запрашивают его.

Обратите внимание, что, хотя правило главного файла Snakefile не изменило все входные данные, я хотел бы сгенерировать вывод code/module2/Snakefile sub-Snakefile. Каждое содержимое файла показано в следующем разделе.

Мой (неправильный) подход

Воспроизводимый пример

├── Snakefile
├── code
│   ├── config.yaml
│   ├── head.py
│   └── module2
│       ├── Snakefile
│       └── tail.py
├── data
└── inputs
    └── test_input.txt

Snakefile («главный» файл snakefile создает основной рабочий поток)

# working dir
workdir: "path/to/dir/test"
configfile: "code/config.yaml"

# core rules
include: "code/head.py"

# module 2 rules
include: "code/module2/Snakefile"

# core output
rule all:
    input:
        "data/output_head.txt"

test_input.txt

1
2
3
4
5
6
7
8
9
10

code / head.py

rule head:
    input:
        "inputs/test_input.txt"
    output:
        "data/output_head.txt"
    shell:
        "head -3 {input} > {output} " # we expect 1 2 3 in output_head.txt

code / module2 / Snakefile («подчиненный» snakefile создает необязательные выходные файлы модуля 2)

include: "tail.py"

rule all:
    input:
        "data/output_tail.txt"

code / module2 / tail.py

rule tail:
        input:
                "inputs/test_input.txt"
        output:
                "data/output_tail.txt"
        shell:
                "tail -3 {input} > {output} " # we expect 8 9 10 in output_tail.txt

Фактическая ошибка, с которой я сталкиваюсь:

CreateRuleException in line 12 of path/to/dir/test/Snakefile:
The name all is already used by another rule

Я полагаю, это связано с тем, что rule all суб-Snake-файла конфликтует с rule all основного Snake-файла.

Воспроизводимый пример

Наконец, я заглянул на страницу Модуляризация , и под include он говорит:

Целевое правило по умолчанию ( часто называется все-правило), не будет зависеть от включения. Т.е. это всегда будет первое правило в вашем Snakefile, независимо от того, сколько включений у вас есть выше вашего первого правила.

Если я перевел это право, его можно перефразировать следующим образом: «включение файлов Snake в ваш основной файл Snakefile не приведет к добавлению новых выходных файлов, поскольку это определяется основным файлом Snakefile».

Если это правда, тогда какой смысл включать субфайлы Snake-файлов, если я не получаю их выходные файлы?

Заключение

Как лучше всего настроить конвейер для условно include sub-Snakefiles и генерировать их выходные файлы? Может быть, я читаю документы ужасно неправильно. Если да, то, пожалуйста, просветите меня.

1 Ответ

0 голосов
/ 08 апреля 2020

Не уверен, что это лучшее, но вот мое решение. В конфигурации я должен указать пользователю, какие «ветви» включить:

# config.yaml
main:
    subworkflows:
        - workflow1
        - workflow2

Основной Snakefile использует эту запись конфигурации для включения других файлов Snake с соответствующими именами. Также обратите внимание, что у меня есть дополнительные проверки, если имя не существует, и есть файлы конфигурации для каждого рабочего процесса, которые загружаются и объединяются с каким-то дополнительным сценарием.

# Snakefile
subworkflows = config['main']['subworkflows']
for subw in subworkflows:
    sub_path = 'subworkflows/{}.snake'.format(subw)
    sub_config = 'subworkflows/{}.yaml'.format(subw)
    if not os.path.exists(sub_path):
        continue

    subw_outputs_dict[subw] = []

    if os.path.exists(sub_config):
        configfile: sub_config
        paths = join_config_paths(paths, config['path'])

    include: sub_path

    if len(subw_outputs_dict[subw]) == 0:
        continue

    subw_outputs.extend(subw_outputs_dict[subw])

rule all:
    input:
        subw_outputs

И каждый подмодуль создает свой окончательный вывод в subw_outputs_dict , действуя как правило все для каждой части.

# workflow1.snake
if "subw_outputs_dict" in locals() and "ids" in locals():
    subw_outputs_dict['workflow1'] = expand(paths['my_output'], id=ids)

Подводя итог:

  • config.yaml контролирует то, что выполняется
  • Snakefile включает в себя запрошенные рабочие процессы и объединяет их в единый список файлов для правила all
  • Каждый дополнительный рабочий процесс определяет набор правил и его выводов в глобальном словаре.

Это было вдохновлено другим проектом, который я не могу вспомнить, но у меня все получилось Мне было бы интересно, что сделали другие. Могу поспорить, что есть более изящный способ использования подпроцессов, но я нахожу, что включает в себя более легкое решение и отладку.

...