Как обрабатывать переменные числа повторов в snakemake

#python #snakemake #fastq

#python #snakemake #fastq

Вопрос:

Я обрабатываю большое количество файлов fastq. У меня есть несколько папок, по одной для каждого образца, каждая из которых содержит несколько копий (repl), например, папка может содержать:

 Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R{r}_{repl}.fastq.gz
{r} = 1 or 2
{repl} = [1...n 1] is a three digit integer with leading zeros
 

в каждой папке разное количество файлов, например, n может быть 13, например, в этом случае будут следующие файлы:

 Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_001.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_002.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_003.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_004.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_005.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_006.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_007.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_008.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_009.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_010.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_011.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_012.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_013.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R1_014.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_001.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_002.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_003.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_004.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_005.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_006.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_007.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_008.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_009.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_010.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_011.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_012.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_013.fastq.gz
Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001_R2_014.fastq.gz
 

Мне нужно передать salmon в моей командной оболочке r1 и r2, где r1 — это список файлов для образца, содержащего ‘R1’ в имени файла, аналогично для R2.

Поскольку я не знаю, какие могут быть реплики в каждой папке, я подумал проверить, существует ли файл, и вернуть список из моей собственной функции repl_expand . Однако он создает список всех файлов во всех образцах, а не ограничивается для каждого образца, поскольку ему передается список expand(["{sample}"], sample=DATASETS) .

Казалось бы, я мог бы просто передать «{sample»} в свою функцию, но это всего лишь строка… замена {sample} фактическим именем образца, по-видимому, выполняется snakemake после вызова моей функции, но перед вызовом, если я использую функцию расширения snakemake.

Чтобы было ясно, я хочу, чтобы salmon запускался три раза, каждый раз, когда r1 является списком файлов ‘R1’ для образца, аналогично для r2.

Поэтому я хотел бы иметь возможность передавать только одно повторяющееся имя образца в мою функцию в каждом из трех вызовов, чтобы создать r1 и r2 для моей командной строки salmon для этого образца.

Вот мой Snakefile, который производит три прогона лосося, но все они одинаковы и содержат данные из всех выборок.

 import os

MAX_REPLICATES = 30
DATASETS = ["Sample_AJL-0704-ME/AJL-0704-ME_GCCAAT_L001",
 "Sample_BG-1058-ME/BG-1058-ME_ACAGTG_L002",
 "Sample_BO-0712-ME/BO-0712-ME_GTGAAA_L002"]
# and many more...

def repl_expand(templates, direction):
    ss = []
    for templ in templates:
        templ = templ   "_R{dir}_{repl:03d}.fastq.gz"
        s = []
        for i in range (1, MAX_REPLICATES):
            name = templ.format(dir=direction, repl=i)
            if os.path.exists(templ.format(dir=direction, repl=i)):
                s.append(name)
        ss.append(s)
    return ss

SALMON = "/path/to/salmon/salmon-1.4.0_linux_x86_64/salmon-latest_linux_x86_64/bin/salmon"

rule all:
    input: expand(["quants/{dataset}.sf"], dataset=DATASETS)


rule salmon_quant:
    input:
        r1 = repl_expand(expand(["{sample}"], sample=DATASETS), 1),
        r2 = repl_expand(expand(["{sample}"], sample=DATASETS), 2),
        index = "path/to/salmon/salmon_sa_index"
    output:
        "quants/{sample}.sf"
    params:
        dir = "quants",
        libtype = 'A',
        threads = 12
    shell:
        "{SALMON} quant 
         -i {input.index} 
         --libtype {params.libtype} 
         --threads {params.threads} 
         --validateMappings 
         --gcBias 
         -o {params.dir} 
         -1 {input.r1} -2 {input.r2}"
 

Я новичок в snakemake, поэтому, возможно, подходил к этому совершенно неправильно и был бы рад, если бы меня поставили на правильный путь…

Спасибо

Ответ №1:

Первое замечание: вам не нужно предоставлять список expand функции. Эта функция принимает строку шаблона и возвращает список строк как результат подстановки переменных. Итак, правило all должно быть:

 rule all:
    input: expand("quants/{dataset}.sf", dataset=DATASETS)
 

Следующее замечание касается именования подстановочных знаков. Однако Snakemake не волнует, вызываете ли вы один и тот же объект с разными именами в разных правилах, я бы рекомендовал вам быть последовательным и явным, чтобы избежать ошибок, поэтому вывод rule salmon_quant может звучать как:

 rule salmon_quant:
    output:
        "quants/{dataset}.sf"
 

Интересно, что вы используете имя «{sample}» в своем rule salmon_quant в двух разных контекстах, поэтому они означают совершенно разные вещи. {sample} В разделе вывода (тот, который я переименовал) является шаблоном, в то время {sample} как на входе является переменной для expand функции. Вот почему вы наблюдаете эту «аномалию» подстановки.

Если вы хотите, чтобы подстановочный знак соответствовал символу из вашего раздела вывода, вы должны сделать:

 rule salmon_quant:
    input:
        r1 = lambda wildcards: repl_expand([wildcards.dataset], 1),
        r2 = lambda wildcards: repl_expand([wildcards.dataset], 2),
        index = "path/to/salmon/salmon_sa_index"
    output:
        "quants/{dataset}.sf"
 

На самом деле вам не нужен этот уродливый список из одного элемента, если вы упростите реализацию repl_expand удаления ненужного цикла for templ in templates: .

Комментарии:

1. Спасибо! предложенные вами изменения решили проблему, и ваше объяснение действительно проясняет ситуацию.