Есть ли способ получить доступ/изменить содержимое канала Nextflow?

#channel #nextflow

Вопрос:

У меня есть ситуация, когда мой рабочий процесс выводит основной каталог, который я выделяю из процесса, использующего DSL2. Я передаю эти выходные данные в скрипт python, который может легко перебирать подкаталоги и соответствующие им файлы, извлекая информацию и компилируя ее в файл .tsv

Две важные части информации, которые получает скрипт python, — это имя подкаталога и то, какой файл на самом деле важен в подкаталоге.

Я хотел бы взять вывод моего процесса («корневой каталог») подкаталог (из файла) важное имя файла (из файла) и преобразовать его в новый путь генератора для передачи другому процессу.

Я просто использую плохой метод? Есть ли лучший способ получить доступ к генератору? В документации я видел подписку, но мне не повезло с использованием этой функции. Заранее спасибо.

Пример файла .tsv (столбцы 1 и 3-это то, что я хочу добавить в генератор)

 GCF_000005845.2 Escherichia coli str. K-12 substr. MG1655, complete genome      GCF_000005845.2_ASM584v2_genomic.fna
GCF_000008865.2 Escherichia coli O157:H7 str. Sakai DNA, complete genome        GCF_000008865.2_ASM886v2_genomic.fna
 

Структура рабочего каталога

 ├── c6
│   └── 6598d4838f61d0421f03216990465c
│       ├── ecoli
│       │   ├── README.md
│       │   └── ncbi_dataset
│       │       ├── data
│       │       │   ├── GCF_000005845.2
│       │       │   │   ├── GCF_000005845.2_ASM584v2_genomic.fna
│       │       │   │   ├── genomic.gff
│       │       │   │   ├── protein.faa
│       │       │   │   └── sequence_report.jsonl
│       │       │   ├── GCF_000008865.2
│       │       │   │   ├── GCF_000008865.2_ASM886v2_genomic.fna
│       │       │   │   ├── genomic.gff
│       │       │   │   ├── protein.faa
│       │       │   │   └── sequence_report.jsonl
│       │       │   ├── assembly_data_report.jsonl
│       │       │   └── dataset_catalog.json
│       │       └── fetch.txt
 

Вот мой сценарий nextflow (конструктивная критика очень приветствуется):

 #!/usr/bin/env Nextflow

nextflow.enable.dsl=2

workflow {

  //ref_genome_ch = Channel.fromPath("$params.ref_genome")
  println([params.taxon, params.zipName, params.unzippedDir])
  DOWNLOAD_ZIP(params.taxon, params.zipName)
  UNZIP(DOWNLOAD_ZIP.out.zipFile)
  REHYDRATE(UNZIP.out.unzippedDir)
  COLLECT_NAMES(REHYDRATE.out.dataDir)


  // I want to get the dir name and file name out of
  // relations.txt
  //thing = Channel.from(  )
  //thing.view()
  //organism_genomes = REHYDRATE.out.dataDir.subscribe { println("$it/")}

}

process DOWNLOAD_ZIP {
  errorStrategy 'ignore'

  input:
  val taxonName
  val zipName

  output:
  path "${zipName}" , emit: zipFile

  script:
  def reference = params.reference
  """
  datasets download genome \
     taxon '${taxonName}' \
     --dehydrated \
     --filename ${zipName} \
     ${reference} \
     --exclude-genomic-cds
  """

}


process UNZIP {
  input:
  path zipFile

  output:
  path "${zipFile.baseName}" , emit: unzippedDir

  script:
  """
  unzip $zipFile -d ${zipFile.baseName}
  """

}


process REHYDRATE {
  input:
  path unzippedDir

  output:
  path "$unzippedDir/ncbi_dataset/data" , emit: dataDir

  script:
  """
  datasets rehydrate \
     --directory $unzippedDir
  """
}



process COLLECT_NAMES {
  publishDir params.results

  input:
  path dataDir

  output:
  path "relations.txt" , emit: org_names

  script:
  """
  python "$baseDir/bin/collect_org_names.py" $dataDir
  """

}
 



Правка: Пользователь @Steve рекомендовал операторов каналов. Я еще не до конца понимаю синтаксис groovy {thing -> stuff}, но я пытался это сделать:

 thing = REHYDRATE.out.dataDir.map{"$it/*"}
thing.view()
 

и я получаю

 /mnt/c/Users/mkozubov/Desktop/nextflow_tutorial/tRNA_stuff/work/d0/long_hash/ecoli/ncbi_dataset/data/*
 

напечатанный… Но когда я передаю это в процесс, у которого есть только сценарий: println(ввод) Я получаю сообщение об ошибке, в котором говорится, что выполненная команда равна нулю, вывод команды (пустой) и что цель » * » не является каталогом.

Мой вопрос в том, почему оператор .map не расширил*, как это сделал бы ввод «ПУТЬ/*» в канал?




Edit2: Я чувствую, что у меня почти что-то было. Я изменил вывод скрипта COLLECT_NAMES, чтобы он содержал путь к файлам. Теперь я хочу проанализировать этот файл и прочитать содержимое в канал. За это я и сделал

 organism_genome_files = Channel.from()
  COLLECT_NAMES.out.org_names.map {
    new File(it.toString()).eachLine { line ->
      organism_genome_files << line.split('t')[3] }
  }
 

Если я organism_genome_files << line.split('t')[3] заменю println line.split('t')[3] его на, я смогу увидеть нужный мне контент, но, похоже, я не могу найти способ извлечь эту информацию.

Я также попробовал это с помощью organism_genome_files в виде списка, но, похоже, ничего не работает, я просто не могу извлекать информацию из каналов и эффективно мутировать ее.

Метод .splitCSV() кажется, что он может быть полезен, но я все еще не понимаю, как заставить канал работать в качестве входного сигнала для другого канала 🙁

Ответ №1:

Есть ли способ получить доступ/изменить содержимое канала Nextflow?

Для этого вы можете использовать один или несколько операторов преобразования. Например, чтобы получить имя каталога и имя файла ‘relations.txt», вы могли бы использовать:

 COLLECT_NAMES.out.org_names.map { tuple( it.parent, it.name ) }.view()
 

См. Также: Проверка атрибутов файлов



Мой вопрос в том, почему оператор .map не расширил*, как это сделал бы ввод «ПУТЬ/*» в канал?

Ему было сказано только вернуть строку (на самом деле GString). Groovy не будет автоматически расширять это так же, как ваша оболочка. Я думаю, что вам нужен какой-то способ перечислить содержимое этого каталога. Для этого вы можете использовать метод listFiles() :

 REHYDRATE.out.dataDir.map { tuple( it.listFiles() ) }.view()
 

См. также: Содержимое каталога списка



Я изменил вывод скрипта COLLECT_NAMES, чтобы он содержал путь к файлам. Теперь я хочу проанализировать этот файл и прочитать содержимое в канал.

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

  1. Это реализовано как закрытие и возвращает канал списков:
 def getOrganismGenomeFiles = { reader ->
    def values = []
    reader.splitEachLine('t') { fields ->
        values.add( fields[3] )
    }
    return values
}

ch.map( getOrganismGenomeFiles ).view()
 
  1. Это уменьшает количество строк, но также возвращает канал списков:
 ch.map { it.readLines().collect { it.split('t')[3] } }.view()
 
  1. Это поглощает содержимое файла, разбивает его на записи с помощью оператора splitCsv и возвращает канал значений:

 ch.map { it.text }.splitCsv(sep: 't').map { it[3] }.view()
 
  • Примечание: Я сократил название входного канала для удобства чтения. Пожалуйста, замените ch на COLLECT_NAMES.out.org_names в приведенных выше примерах.


Моя (возможно, не столь конструктивная) критика на самом деле касается дизайна рабочего процесса, а не столько стиля, макета и т. Д. Я предпочитаю и всегда буду избегать использования какой-либо команды web get, такой как curl, wget или, в данном случае, наборов данных NCBI, внутри процесса Nextflow. Конечно, вы можете заставить все работать таким образом, но в конечном итоге вы столкнетесь с проблемами, когда позже решите поделиться своим рабочим процессом с другими. Даже если все согласятся с тем, что тратить дополнительные ресурсы на загрузку файлов нормально (чего они не будут делать, но, возможно, эти затраты незначительны в схеме вещей…), вы не обязательно можете гарантировать, что машина или узел, на который попадает ваш процесс, даже сможет разрешить указанный URL(ы). Есть способы обойти эти проблемы, но мой совет-просто позволить Nextflow локализовать необходимые файлы. Проблема в том, как это сделать. И это, конечно, зависит от того, что вы на самом деле пытаетесь сделать…

Эти файлы доступны с FTP-сайта NCBI, и их URL-адреса могут быть добавлены в вашу конфигурацию, возможно, что-то вроде:

 params {
  genomes {
    'GCF_000005845.2_ASM584v2' {
      genomic_fna = 'ftp://ftp.ncbi.nlm.nih.gov/genomes/refseq/bacteria/Escherichia_coli/reference/GCF_000005845.2_ASM584v2/GCF_000005845.2_ASM584v2_genomic.fna.gz'
      genomic_gff = 'ftp://ftp.ncbi.nlm.nih.gov/genomes/refseq/bacteria/Escherichia_coli/reference/GCF_000005845.2_ASM584v2/GCF_000005845.2_ASM584v2_genomic.gff.gz'
      protein_faa = 'ftp://ftp.ncbi.nlm.nih.gov/genomes/refseq/bacteria/Escherichia_coli/reference/GCF_000005845.2_ASM584v2/GCF_000005845.2_ASM584v2_protein.faa.gz'
    }
    'GCF_000008865.2_ASM886v2' {
      genomic_fna = 'ftp://ftp.ncbi.nlm.nih.gov/genomes/refseq/bacteria/Escherichia_coli/reference/GCF_000008865.2_ASM886v2/GCF_000008865.2_ASM886v2_genomic.fna.gz'
      genomic_gff = 'ftp://ftp.ncbi.nlm.nih.gov/genomes/refseq/bacteria/Escherichia_coli/reference/GCF_000008865.2_ASM886v2/GCF_000008865.2_ASM886v2_genomic.gff.gz'
      protein_faa = 'ftp://ftp.ncbi.nlm.nih.gov/genomes/refseq/bacteria/Escherichia_coli/reference/GCF_000008865.2_ASM886v2/GCF_000008865.2_ASM886v2_protein.faa.gz'
    }
  }
}
 

Затем, чтобы получить доступ к файлам для данного генома, используйте что-то вроде:

 genome = 'GCF_000005845.2_ASM584v2'

genomic_fna = genomes[genome].genomic_fna
genomic_gff = genomes[genome].genomic_gff
protein_faa = genomes[genome].protein_faa
 

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

1. Спасибо за ответ @Steve, операторы будут полезны, но у меня все еще возникают проблемы (я отредактирую свои вопросы).

2. Не беспокойтесь @MatthewKozubov, пожалуйста, посмотрите мой отредактированный ответ выше. Другим способом было бы просто изменить ваше объявление о выходе РЕГИДРАТАЦИИ. Если вам нужны только подкаталоги, возможно, что-то вроде: path ""$unzippedDir/ncbi_dataset/data/*", type: 'dir' ?

3. Определенно кажется, что я пытаюсь применить грубую силу к чему-то, что можно сделать проще, я попробовал что-то другое и добавил это в качестве редактирования.

4. @MatthewKozubov Я добавил пару примеров выше. Может быть, они полезны. Хотя трудно сказать, что для этого нужно. Если эти файлы невелики (например, менее 1 МБ), их, вероятно, следует удалить…

5. Спасибо, эти примеры были очень полезны! Мне нужно поближе познакомиться с Java/Groovy, чтобы определить свои собственные функции ( мне понравился этот пример ). Кроме того, что означает прихлебывание? Google был не самым полезным, когда я прихлебывал Google.