#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, чтобы он содержал путь к файлам. Теперь я хочу проанализировать этот файл и прочитать содержимое в канал.
Без более подробной информации о том, что это за файлы, насколько они велики, как они будут использоваться и каким должен быть тип возвращаемого значения, я действительно только предполагаю здесь. Поэтому я собрал несколько потенциальных решений, которые могут помочь вам начать:
- Это реализовано как закрытие и возвращает канал списков:
def getOrganismGenomeFiles = { reader ->
def values = []
reader.splitEachLine('t') { fields ->
values.add( fields[3] )
}
return values
}
ch.map( getOrganismGenomeFiles ).view()
- Это уменьшает количество строк, но также возвращает канал списков:
ch.map { it.readLines().collect { it.split('t')[3] } }.view()
- Это поглощает содержимое файла, разбивает его на записи с помощью оператора 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.