Nexflow: структурированные входные данные с файлами

#nextflow

#nextflow

Вопрос:

У меня есть массив структурных данных, похожих на:

 - name: foobar
  sex: male
  fastqs:
  - r1: /path/to/foobar_R1.fastq.gz
    r2: /path/to/foobar_R2.fastq.gz
  - r1: /path/to/more/foobar_R1.fastq.gz
    r2: /path/to/more/foobar_R2.fastq.gz
- name: bazquux
  sex: female
  fastqs:
  - r1: /path/to/bazquux_R1.fastq.gz
    r2: /path/to/bazquux_R2.fastq.gz
  

Обратите внимание, что fastq приходят парами, и количество пар на «образец» может быть переменным.

Я хочу написать process в nextflow, который обрабатывает один образец за раз.

Для того, чтобы исполнитель nextflow правильно маршалировал файлы, они должны каким-то образом быть напечатаны как path (или file ). Набрав таким образом, исполнитель скопирует файлы на вычислительный узел для обработки. Просто введя пути к файлам var как, пути будут обрабатываться как строки, и никакие файлы не будут скопированы.

Тривиальный пример path ввода из документов:

 process foo {
  input:
    path x from '/some/data/file.txt'
  """
    your_command --in $x
  """
}
  

Как мне объявить process входные данные, чтобы файлы были правильно маршалированы для вычислительного узла? До сих пор я не нашел в документах никаких примеров того, как обрабатывать структурированные входные данные.

Ответ №1:

Ваши структурированные данные очень похожи на YAML. Если вы можете включить объект верхнего уровня, чтобы ваш файл выглядел примерно так:

 samples:
- name: foobar
  sex: male
  fastqs:
  - r1: ./path/to/foobar_R1.fastq.gz
    r2: ./path/to/foobar_R2.fastq.gz
  - r1: ./path/to/more/foobar_R1.fastq.gz
    r2: ./path/to/more/foobar_R2.fastq.gz
- name: bazquux
  sex: female
  fastqs:
  - r1: ./path/to/bazquux_R1.fastq.gz
    r2: ./path/to/bazquux_R2.fastq.gz
  

Затем мы можем использовать -params-file опцию Nextflow для загрузки параметров при запуске нашего рабочего процесса. Мы можем получить доступ к объекту верхнего уровня из параметров, что дает нам список, который мы можем использовать для создания канала с использованием fromList фабричного метода. В следующем примере используется новый DSL 2:

 process test_proc {

    tag { sample_name }

    debug true
    stageInMode 'rellink'

    input:
    tuple val(sample_name), val(sex), path(fastqs)

    """
    echo "${sample_name},${sex}:"

    ls -g *.fastq.gz
    """
}

workflow {

    Channel.fromList( params.samples )
        | flatMap { rec ->
            rec.fastqs.collect { rg -> 
                readgroup = tuple( file(rg.r1), file(rg.r2) )

                tuple( rec.name, rec.sex, readgroup )
            }
        }
        | test_proc
}
  

Результаты:

 $ mkdir -p ./path/to/more
$ touch ./path/to/foobar_R{1,2}.fastq.gz
$ touch ./path/to/more/foobar_R{1,2}.fastq.gz
$ touch ./path/to/bazquux_R{1,2}.fastq.gz

$ nextflow run main.nf -params-file file.yaml 
N E X T F L O W  ~  version 22.04.0
Launching `main.nf` [desperate_colden] DSL2 - revision: 391a9a3b3a
executor >  local (3)
[ed/61c5c3] process > test_proc (foobar) [100%] 3 of 3 ✔
foobar,male:
lrwxrwxrwx 1 users 35 Oct 14 13:56 foobar_R1.fastq.gz -> ../../../path/to/foobar_R1.fastq.gz
lrwxrwxrwx 1 users 35 Oct 14 13:56 foobar_R2.fastq.gz -> ../../../path/to/foobar_R2.fastq.gz

bazquux,female:
lrwxrwxrwx 1 users 36 Oct 14 13:56 bazquux_R1.fastq.gz -> ../../../path/to/bazquux_R1.fastq.gz
lrwxrwxrwx 1 users 36 Oct 14 13:56 bazquux_R2.fastq.gz -> ../../../path/to/bazquux_R2.fastq.gz

foobar,male:
lrwxrwxrwx 1 users 40 Oct 14 13:56 foobar_R1.fastq.gz -> ../../../path/to/more/foobar_R1.fastq.gz
lrwxrwxrwx 1 users 40 Oct 14 13:56 foobar_R2.fastq.gz -> ../../../path/to/more/foobar_R2.fastq.gz

  

Как и было запрошено, вот решение, которое выполняется для каждого примера. Проблема, с которой мы сталкиваемся, заключается в том, что мы не можем просто ввести список списков, используя path квалификатор (поскольку ArrayList не является допустимым значением пути). Мы могли бы сгладить () список пар файлов, но это затрудняет доступ к каждой из пар файлов, если они нам понадобятся. Возможно, вам не обязательно нужны отношения пары файлов, но, предполагая, что вы это сделаете, я думаю, что правильным решением будет передать файлы R1 и R2 отдельно (т. Е. Используя спецификатор пути для R1 и другой спецификатор пути для R2). В следующем примере анализируется тип экземпляра для (повторного) создания списка групп чтения. Мы можем использовать stageAs опцию для локализации файлов в постепенно индексируемые подкаталоги, поскольку некоторые файлы в YAML имеют идентичные имена.

 process test_proc {

    tag { sample_name }

    debug true
    stageInMode 'rellink'

    input:
    tuple val(sample_name), val(sex), path(r1, stageAs:'*/*'), path(r2, stageAs:'*/*')

    script:
    if( [r1, r2].every { it instanceof List } )
        readgroups = [r1, r2].transpose()
    else if( [r1, r2].every { it instanceof Path } )
        readgroups = [[r1, r2], ]
    else
        error "Invalid readgroup configuration"

    read_pairs = readgroups.collect { r1, r2 -> "${r1},${r2}" }

    """
    echo "${sample_name},${sex}:"
    echo ${read_pairs.join(' ')}

    ls -g */*.fastq.gz
    """
}
  
 workflow {

    Channel.fromList( params.samples )
        | map { rec ->

            def r1 = rec.fastqs.r1.collect { file(it) }
            def r2 = rec.fastqs.r2.collect { file(it) }

            tuple( rec.name, rec.sex, r1, r2 )
        }
        | test_proc
}
  

Результаты:

 $ nextflow run main.nf -params-file file.yaml 
N E X T F L O W  ~  version 22.04.0
Launching `main.nf` [berserk_sanger] DSL2 - revision: 2f317a8cee
executor >  local (2)
[93/6345c9] process > test_proc (bazquux) [100%] 2 of 2 ✔
foobar,male:
1/foobar_R1.fastq.gz,1/foobar_R2.fastq.gz 2/foobar_R1.fastq.gz,2/foobar_R2.fastq.gz
lrwxrwxrwx 1 users 38 Oct 19 13:43 1/foobar_R1.fastq.gz -> ../../../../path/to/foobar_R1.fastq.gz
lrwxrwxrwx 1 users 38 Oct 19 13:43 1/foobar_R2.fastq.gz -> ../../../../path/to/foobar_R2.fastq.gz
lrwxrwxrwx 1 users 43 Oct 19 13:43 2/foobar_R1.fastq.gz -> ../../../../path/to/more/foobar_R1.fastq.gz
lrwxrwxrwx 1 users 43 Oct 19 13:43 2/foobar_R2.fastq.gz -> ../../../../path/to/more/foobar_R2.fastq.gz

bazquux,female:
1/bazquux_R1.fastq.gz,1/bazquux_R2.fastq.gz
lrwxrwxrwx 1 users 39 Oct 19 13:43 1/bazquux_R1.fastq.gz -> ../../../../path/to/bazquux_R1.fastq.gz
lrwxrwxrwx 1 users 39 Oct 19 13:43 1/bazquux_R2.fastq.gz -> ../../../../path/to/bazquux_R2.fastq.gz

  

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

1. Здесь вы демонстрируете отдельный процесс для каждой пары fastqs. Не могли бы вы вместо этого продемонстрировать, как запускать один процесс для каждого образца , сохраняя при этом копирование всех файлов, необходимых для каждого образца? По сути, ввод в test_proc должен допускать список пар путей (групп чтения) вместо одной группы чтения.

2. @GordonBean Пожалуйста, посмотрите мою правку выше. Неясно, как именно вам нужно упорядочить пары файлов для команды, которую вам нужно запустить. Но вы должны быть в состоянии переработать вышесказанное, чтобы получить то, что вы хотите, без особых проблем, я надеюсь. Пожалуйста, дайте мне знать, если вам нужно что-нибудь еще. Приветствия.