Как сделать ввод Snakemake необязательным, но не пустым?

Dec 10 2020

Я создаю сценарий SQL из текстовых данных. Сценарий (часть) должен состоять из CREATE TABLEоператора и необязательного INSERT INTOоператора. Значения для INSERT INTOоператора берутся из списка файлов, каждый может существовать, а может и не существовать; все значения существующих файлов объединяются. Важнейшей частью является то, что INSERT INTOоператор должен пропускаться, если ни один файл данных не существует.

Я создал сценарий в Snakemake, который это делает. Есть два неоднозначных правила, которые создают сценарий: одно, которое создает сценарий для пустых данных, и другое, которое создает таблицу, но вставляет данные (неоднозначность разрешается с помощью ruleorderоператора).

Интересная часть - это правило, объединяющее значения из файлов данных. Он должен создавать выходные данные всякий раз, когда присутствует хотя бы один вход, и это правило не должно рассматриваться иначе. Есть две трудности: сделать каждый ввод необязательным и запретить Snakemake использовать это правило, когда файлы не существуют. Я сделал это с помощью трюка:

def require_at_least_one(filelist):
    existing = [file for file in filelist if os.path.isfile(file)]
    return existing if len(existing) else "non_existing_file"

rule merge_values:
    input: require_at_least_one(expand("path_to_data/{dataset}/values", dataset=["A", "B", "C"]))
    output: ...
    shell: ...

require_at_least_oneФункция принимает список имен файлов, и отфильтровывает те имена файлов , которые не представляют файл. Это позволяет сделать каждый ввод необязательным. В крайнем случае, когда ни один файл не существует, эта функция возвращает специальное значение, представляющее несуществующий файл. Это позволяет обрезать эту ветку и предпочесть ту, которая создает скрипт без INSERTоператора.

Мне хочется изобретать велосипед, к тому же трюк с "non_existing_file" выглядит немного грязно. Есть ли лучшие и идиоматические способы сделать это в Snakemake?

Ответы

silence Feb 11 2021 at 16:24

Моим решением было бы что-то вроде того, что вы не должны заставлять snakemake использовать или не использовать правило внутри правила, но укажите, какие выходные данные вам нужны, и snakemake решит, нужно ли использовать правило. Итак, для вашего примера я бы сделал что-то как:

def required_files(filelist):
    return [file for file in filelist if os.path.isfile(file)]

rule what_to_gen:
    input: 
        merged = [] if required_files(expand("path_to_data/{dataset}/values", dataset=["A", "B", "C"])) else 'merged_files.txt'

rule merge_values:
    input: required_files(expand("path_to_data/{dataset}/values", dataset=["A", "B", "C"]))
    output: 'merged_files.txt'
    shell: ...

Это приведет к выполнению правила merge_values, только если required_files не пусто.