Parte 3: Hello Workflow¶
Tradução assistida por IA - saiba mais e sugira melhorias
A maioria dos fluxos de trabalho do mundo real envolve mais de uma etapa. Neste módulo de treinamento, você aprenderá como conectar processos em um fluxo de trabalho de múltiplas etapas.
Isso ensinará a você a maneira Nextflow de realizar o seguinte:
- Fazer os dados fluírem de um processo para o próximo
- Coletar saídas de múltiplas chamadas de processo em uma única chamada de processo
- Passar mais de uma entrada para um processo
- Gerenciar múltiplas saídas vindas de um processo
Para demonstrar, continuaremos construindo sobre o exemplo Hello World agnóstico de domínio das Partes 1 e 2. Desta vez, faremos as seguintes alterações em nosso fluxo de trabalho para refletir melhor como as pessoas constroem fluxos de trabalho reais:
- Adicionar uma segunda etapa que converte a saudação para maiúsculas.
- Adicionar uma terceira etapa que coleta todas as saudações transformadas e as escreve em um único arquivo.
- Adicionar um parâmetro para nomear o arquivo de saída final e passá-lo como uma entrada secundária para a etapa de coleta.
- Fazer a etapa de coleta também relatar uma estatística simples sobre o que foi processado.
Como começar a partir desta seção
Esta seção do curso pressupõe que você completou as Partes 1-2 do curso Hello Nextflow, mas se você está confortável com os conceitos básicos cobertos nessas seções, pode começar a partir daqui sem fazer nada especial.
0. Aquecimento: Execute hello-workflow.nf¶
Vamos usar o script de fluxo de trabalho hello-workflow.nf como ponto de partida.
Ele é equivalente ao script produzido ao trabalhar na Parte 2 deste curso de treinamento, exceto que removemos as instruções view() e alteramos o destino de saída:
Apenas para ter certeza de que tudo está funcionando, execute o script uma vez antes de fazer quaisquer alterações:
Saída do comando
Como anteriormente, você encontrará os arquivos de saída no local especificado no bloco output.
Para este capítulo, está em results/hello_workflow/.
Conteúdo do diretório
Se isso funcionou para você, você está pronto para aprender como montar um fluxo de trabalho de múltiplas etapas.
1. Adicione uma segunda etapa ao fluxo de trabalho¶
Vamos adicionar uma etapa para converter cada saudação para maiúsculas.
Para isso, precisamos fazer três coisas:
- Definir o comando que vamos usar para fazer a conversão para maiúsculas.
- Escrever um novo processo que envolve o comando de conversão para maiúsculas.
- Chamar o novo processo no bloco de fluxo de trabalho e configurá-lo para receber a saída do processo
sayHello()como entrada.
1.1. Defina o comando de conversão para maiúsculas e teste-o no terminal¶
Para fazer a conversão das saudações para maiúsculas, vamos usar uma ferramenta UNIX clássica chamada tr para 'text replacement', com a seguinte sintaxe:
Esta é uma substituição de texto muito simples que não leva em conta letras acentuadas, então por exemplo 'Holà' se tornará 'HOLà', mas fará um trabalho bom o suficiente para demonstrar os conceitos do Nextflow e isso é o que importa.
Para testá-lo, podemos executar o comando echo 'Hello World' e direcionar sua saída para o comando tr:
A saída é um arquivo de texto chamado UPPER-output.txt que contém a versão em maiúsculas da string Hello World.
Isso é basicamente o que vamos tentar fazer com nosso fluxo de trabalho.
1.2. Escreva a etapa de conversão para maiúsculas como um processo Nextflow¶
Podemos modelar nosso novo processo no primeiro, já que queremos usar todos os mesmos componentes.
Adicione a seguinte definição de processo ao script de fluxo de trabalho, logo abaixo da primeira:
| hello-workflow.nf | |
|---|---|
Neste, compomos o segundo nome de arquivo de saída baseado no nome do arquivo de entrada, de forma similar ao que fizemos originalmente para a saída do primeiro processo.
1.3. Adicione uma chamada ao novo processo no bloco de fluxo de trabalho¶
Agora precisamos dizer ao Nextflow para realmente chamar o processo que acabamos de definir.
No bloco de fluxo de trabalho, faça a seguinte alteração de código:
| hello-workflow.nf | |
|---|---|
Isso ainda não é funcional porque não especificamos o que deve ser entrada para o processo convertToUpper().
1.4. Passe a saída do primeiro processo para o segundo processo¶
Agora precisamos fazer a saída do processo sayHello() fluir para o processo convertToUpper().
Convenientemente, o Nextflow empacota automaticamente a saída de um processo em um canal chamado <process>.out.
Então a saída do processo sayHello é um canal chamado sayHello.out, que podemos conectar diretamente na chamada para convertToUpper().
No bloco de fluxo de trabalho, faça a seguinte alteração de código:
Para um caso simples como este (uma saída para uma entrada), isso é tudo que precisamos fazer para conectar dois processos!
1.5. Configure a publicação da saída do fluxo de trabalho¶
Finalmente, vamos atualizar as saídas do fluxo de trabalho para publicar os resultados do segundo processo também.
1.5.1. Atualize a seção publish: do bloco workflow¶
No bloco workflow, faça a seguinte alteração de código:
A lógica é a mesma de antes.
1.5.2. Atualize o bloco output¶
No bloco output, faça a seguinte alteração de código:
Mais uma vez, a lógica é a mesma de antes.
Isso mostra que você pode controlar as configurações de saída em um nível muito granular, para cada saída individual. Sinta-se à vontade para tentar mudar os caminhos ou o modo de publicação para um dos processos para ver o que acontece.
Claro, isso significa que estamos repetindo algumas informações aqui, o que pode se tornar inconveniente se quisermos atualizar a localização para todas as saídas da mesma forma. Mais tarde no curso, você aprenderá como configurar essas definições para múltiplas saídas de forma estruturada.
1.6. Execute o fluxo de trabalho com -resume¶
Vamos testar isso usando a flag -resume, já que já executamos a primeira etapa do fluxo de trabalho com sucesso.
Saída do comando
Agora há uma linha extra na saída do console que corresponde ao novo processo que acabamos de adicionar.
Você encontrará as saídas no diretório results/hello_workflow conforme definido no bloco output.
Conteúdo do diretório
Isso é conveniente! Mas ainda vale a pena dar uma olhada dentro do diretório de trabalho de uma das chamadas ao segundo processo.
Conteúdo do diretório
Note que há dois arquivos *-output: a saída do primeiro processo assim como a saída do segundo.
A saída do primeiro processo está lá porque o Nextflow a preparou (staged) ali para ter tudo o que é necessário para execução dentro do mesmo subdiretório.
No entanto, é na verdade um link simbólico apontando para o arquivo original no subdiretório da primeira chamada de processo. Por padrão, ao executar em uma única máquina como estamos fazendo aqui, o Nextflow usa links simbólicos em vez de cópias para preparar arquivos de entrada e intermediários.
Agora, antes de prosseguir, pense em como tudo o que fizemos foi conectar a saída de sayHello à entrada de convertToUpper e os dois processos puderam ser executados em série.
O Nextflow fez o trabalho pesado de gerenciar arquivos individuais de entrada e saída e passá-los entre os dois comandos para nós.
Esta é uma das razões pelas quais os canais do Nextflow são tão poderosos: eles cuidam do trabalho tedioso envolvido em conectar as etapas do fluxo de trabalho.
Conclusão¶
Você sabe como encadear processos conectando a saída de uma etapa como entrada para a próxima etapa.
O que vem a seguir?¶
Aprenda como coletar saídas de chamadas de processo em lote e alimentá-las em um único processo.
2. Adicione uma terceira etapa para coletar todas as saudações¶
Quando usamos um processo para aplicar uma transformação a cada um dos elementos em um canal, como estamos fazendo aqui para as múltiplas saudações, às vezes queremos coletar elementos do canal de saída desse processo e alimentá-los em outro processo que realiza algum tipo de análise ou soma.
Para demonstrar, adicionaremos uma nova etapa ao nosso pipeline que coleta todas as saudações em maiúsculas produzidas pelo processo convertToUpper e as escreve em um único arquivo.
Sem estragar a surpresa, mas isso vai envolver um operador muito útil.
2.1. Defina o comando de coleta e teste-o no terminal¶
A etapa de coleta que queremos adicionar ao nosso fluxo de trabalho usará o comando cat para concatenar múltiplas saudações em maiúsculas em um único arquivo.
Vamos executar o comando sozinho no terminal para verificar que funciona conforme esperado, assim como fizemos anteriormente.
Execute o seguinte em seu terminal:
echo 'Hello' | tr '[a-z]' '[A-Z]' > UPPER-Hello-output.txt
echo 'Bonjour' | tr '[a-z]' '[A-Z]' > UPPER-Bonjour-output.txt
echo 'Holà' | tr '[a-z]' '[A-Z]' > UPPER-Holà-output.txt
cat UPPER-Hello-output.txt UPPER-Bonjour-output.txt UPPER-Holà-output.txt > COLLECTED-output.txt
A saída é um arquivo de texto chamado COLLECTED-output.txt que contém as versões em maiúsculas das saudações originais.
Esse é o resultado que queremos alcançar com nosso fluxo de trabalho.
2.2. Crie um novo processo para fazer a etapa de coleta¶
Vamos criar um novo processo e chamá-lo de collectGreetings().
Podemos começar a escrevê-lo com base no que já vimos antes.
2.2.1. Escreva as partes 'óbvias' do processo¶
Adicione a seguinte definição de processo ao script de fluxo de trabalho:
| hello-workflow.nf | |
|---|---|
Isso é o que podemos escrever com confiança com base no que você aprendeu até agora. Mas isso não é funcional! Deixa de fora a(s) definição(ões) de entrada e a primeira metade do comando do script porque precisamos descobrir como escrever isso.
2.2.2. Defina as entradas para collectGreetings()¶
Precisamos coletar as saudações de todas as chamadas ao processo convertToUpper().
O que sabemos que podemos obter da etapa anterior no fluxo de trabalho?
O canal de saída de convertToUpper() conterá os caminhos para os arquivos individuais contendo as saudações em maiúsculas.
Isso equivale a um slot de entrada; vamos chamá-lo de input_files por simplicidade.
No bloco de processo, faça a seguinte alteração de código:
Note que usamos o prefixo path mesmo esperando que isso contenha múltiplos arquivos.
2.2.3. Componha o comando de concatenação¶
É aqui que as coisas podem ficar um pouco complicadas, porque precisamos ser capazes de lidar com um número arbitrário de arquivos de entrada. Especificamente, não podemos escrever o comando antecipadamente, então precisamos dizer ao Nextflow como compô-lo em tempo de execução com base nas entradas que fluem para o processo.
Em outras palavras, se tivermos um canal de entrada contendo o elemento [file1.txt, file2.txt, file3.txt], precisamos que o Nextflow transforme isso em cat file1.txt file2.txt file3.txt.
Felizmente, o Nextflow fica feliz em fazer isso por nós se simplesmente escrevermos cat ${input_files} no comando do script.
No bloco de processo, faça a seguinte alteração de código:
Em teoria, isso deve lidar com qualquer número arbitrário de arquivos de entrada.
Tip
Algumas ferramentas de linha de comando exigem fornecer um argumento (como -input) para cada arquivo de entrada.
Nesse caso, teríamos que fazer um pouco de trabalho extra para compor o comando.
Você pode ver um exemplo disso no curso de treinamento Nextflow for Genomics.
2.3. Adicione a etapa de coleta ao fluxo de trabalho¶
Agora devemos apenas precisar chamar o processo de coleta na saída da etapa de conversão para maiúsculas.
2.3.1. Conecte as chamadas de processo¶
No bloco de fluxo de trabalho, faça a seguinte alteração de código:
Isso conecta a saída de convertToUpper() à entrada de collectGreetings().
2.3.2. Execute o fluxo de trabalho com -resume¶
Vamos tentar.
Saída do comando
Ele é executado com sucesso, incluindo a terceira etapa.
No entanto, olhe o número de chamadas para collectGreetings() na última linha.
Estávamos esperando apenas uma, mas há três.
Agora dê uma olhada no conteúdo do arquivo de saída final.
Oh não. A etapa de coleta foi executada individualmente em cada saudação, o que NÃO é o que queríamos.
Precisamos fazer algo para dizer ao Nextflow explicitamente que queremos que a terceira etapa seja executada em todos os elementos no canal de saída de convertToUpper().
2.4. Use um operador para coletar as saudações em uma única entrada¶
Sim, mais uma vez a resposta para nosso problema é um operador.
Especificamente, vamos usar o operador apropriadamente chamado collect().
2.4.1. Adicione o operador collect()¶
Desta vez vai parecer um pouco diferente porque não estamos adicionando o operador no contexto de uma fábrica de canal; estamos adicionando-o a um canal de saída.
Pegamos o convertToUpper.out e acrescentamos o operador collect(), o que nos dá convertToUpper.out.collect().
Podemos conectar isso diretamente na chamada do processo collectGreetings().
No bloco de fluxo de trabalho, faça a seguinte alteração de código:
2.4.2. Adicione algumas instruções view()¶
Vamos também incluir algumas instruções view() para visualizar os estados antes e depois do conteúdo do canal.
| hello-workflow.nf | |
|---|---|
As instruções view() podem ir em qualquer lugar que você quiser; as colocamos logo após a chamada para legibilidade.
2.4.3. Execute o fluxo de trabalho novamente com -resume¶
Vamos tentar:
Saída do comando
N E X T F L O W ~ version 25.10.2
Launching `hello-workflow.nf` [soggy_franklin] DSL2 - revision: bc8e1b2726
[d6/cdf466] sayHello (1) | 3 of 3, cached: 3 ✔
[99/79394f] convertToUpper (2) | 3 of 3, cached: 3 ✔
[1e/83586c] collectGreetings | 1 of 1 ✔
Antes do collect: /workspaces/training/hello-nextflow/work/b3/d52708edba8b864024589285cb3445/UPPER-Bonjour-output.txt
Antes do collect: /workspaces/training/hello-nextflow/work/99/79394f549e3040dfc2440f69ede1fc/UPPER-Hello-output.txt
Antes do collect: /workspaces/training/hello-nextflow/work/aa/56bfe7cf00239dc5badc1d04b60ac4/UPPER-Holà-output.txt
Depois do collect: [/workspaces/training/hello-nextflow/work/b3/d52708edba8b864024589285cb3445/UPPER-Bonjour-output.txt, /workspaces/training/hello-nextflow/work/99/79394f549e3040dfc2440f69ede1fc/UPPER-Hello-output.txt, /workspaces/training/hello-nextflow/work/aa/56bfe7cf00239dc5badc1d04b60ac4/UPPER-Holà-output.txt]
Ele é executado com sucesso, embora a saída de log possa parecer um pouco mais bagunçada do que isso (nós a limpamos para legibilidade).
Desta vez a terceira etapa foi chamada apenas uma vez!
Olhando para a saída das instruções view(), vemos o seguinte:
- Três instruções
Antes do collect:, uma para cada saudação: nesse ponto os caminhos dos arquivos são itens individuais no canal. - Uma única instrução
Depois do collect:: os três caminhos de arquivos agora estão empacotados em um único elemento.
Dê uma olhada no conteúdo do arquivo de saída final.
Desta vez temos todas as três saudações no arquivo de saída final. Sucesso!
Note
Se você executar isso várias vezes sem -resume, verá que a ordem das saudações muda de uma execução para outra.
Isso mostra que a ordem na qual os elementos fluem através das chamadas de processo não é garantida ser consistente.
2.4.4. Remova as instruções view() para legibilidade¶
Antes de passar para a próxima seção, recomendamos que você delete as instruções view() para evitar poluir a saída do console.
| hello-workflow.nf | |
|---|---|
Esta é basicamente a operação inversa do ponto 2.4.2.
Conclusão¶
Você sabe como coletar saídas de um lote de chamadas de processo e alimentá-las em uma etapa de análise conjunta ou soma.
O que vem a seguir?¶
Aprenda como passar mais de uma entrada para um processo.
3. Passe mais de uma entrada para um processo¶
Queremos ser capazes de nomear o arquivo de saída final com algo específico para processar lotes subsequentes de saudações sem sobrescrever os resultados finais.
Para isso, faremos os seguintes refinamentos no fluxo de trabalho:
- Modificar o processo coletor para aceitar um nome definido pelo usuário para o arquivo de saída
- Adicionar um parâmetro de linha de comando ao fluxo de trabalho e passá-lo ao processo coletor
3.1. Modifique o processo coletor¶
Vamos precisar declarar a entrada adicional e integrá-la ao nome do arquivo de saída.
3.1.1. Declare a entrada adicional¶
Boas notícias: podemos declarar quantas variáveis de entrada quisermos na definição do processo.
Vamos chamar esta de batch_name.
No bloco de processo, faça a seguinte alteração de código:
Você pode configurar seus processos para esperar quantas entradas quiser. Agora, todas estas estão configuradas para serem entradas obrigatórias; você deve fornecer um valor para o fluxo de trabalho funcionar.
Você aprenderá como gerenciar entradas obrigatórias versus opcionais mais tarde em sua jornada Nextflow.
3.1.2. Use a variável batch_name no nome do arquivo de saída¶
Podemos inserir a variável no nome do arquivo de saída da mesma forma que compusemos nomes de arquivos dinâmicos antes.
No bloco de processo, faça a seguinte alteração de código:
Isso configura o processo para usar o valor batch_name para gerar um nome de arquivo específico para a saída final do fluxo de trabalho.
3.2. Adicione um parâmetro de linha de comando batch¶
Agora precisamos de uma forma de fornecer o valor para batch_name e alimentá-lo à chamada do processo.
3.2.1. Use params para configurar o parâmetro¶
Você já sabe como usar o sistema params para declarar parâmetros CLI.
Vamos usar isso para declarar um parâmetro batch (com um valor padrão porque somos preguiçosos).
Na seção de parâmetros do pipeline, faça as seguintes alterações de código:
Assim como demonstramos para --input, você pode sobrescrever esse valor padrão especificando um valor com --batch na linha de comando.
3.2.2. Passe o parâmetro batch para o processo¶
Para fornecer o valor do parâmetro ao processo, precisamos adicioná-lo na chamada do processo.
No bloco de fluxo de trabalho, faça a seguinte alteração de código:
Você vê que para fornecer múltiplas entradas a um processo, você simplesmente as lista nos parênteses da chamada, separadas por vírgulas.
Warning
Você DEVE fornecer as entradas ao processo na MESMA ORDEM EXATA em que estão listadas no bloco de definição de entrada do processo.
3.3. Execute o fluxo de trabalho¶
Vamos tentar executar isso com um nome de lote na linha de comando.
Saída do comando
Ele é executado com sucesso e produz a saída desejada:
Agora, contanto que especifiquemos o parâmetro apropriadamente, execuções subsequentes em outros lotes de entradas não destruirão os resultados anteriores.
Conclusão¶
Você sabe como passar mais de uma entrada para um processo.
O que vem a seguir?¶
Aprenda como emitir múltiplas saídas e manuseá-las convenientemente.
4. Adicione uma saída à etapa coletora¶
Até agora estivemos usando processos que produziam apenas uma saída cada.
Conseguimos acessar suas respectivas saídas muito convenientemente usando a sintaxe <process>.out, que usamos tanto no contexto de passar uma saída para o próximo processo (por exemplo, convertToUpper(sayHello.out)) quanto no contexto da seção publish: (por exemplo, first_output = sayHello.out).
O que acontece quando um processo produz mais de uma? Como lidamos com as múltiplas saídas? Podemos selecionar e usar uma saída específica?
Todas excelentes perguntas, e a resposta curta é sim, podemos!
Múltiplas saídas serão empacotadas em canais separados. Podemos escolher dar nomes a esses canais de saída, o que torna fácil referenciá-los individualmente mais tarde, ou podemos referenciá-los por índice.
Vamos nos aprofundar com um exemplo.
Para fins de demonstração, digamos que queremos contar o número de saudações que estão sendo coletadas para um determinado lote de entradas e relatá-lo em um arquivo.
4.1. Modifique o processo para contar e gerar o número de saudações¶
Isso exigirá duas mudanças-chave na definição do processo: precisamos de uma forma de contar as saudações e escrever um arquivo de relatório, então precisamos adicionar esse arquivo de relatório ao bloco output do processo.
4.1.1. Conte o número de saudações coletadas¶
Convenientemente, o Nextflow nos permite adicionar código arbitrário no bloco script: da definição do processo, o que é muito útil para fazer coisas como essa.
Isso significa que podemos usar a função integrada size() do Nextflow para obter o número de arquivos no array input_files, e escrever o resultado em arquivo com um comando echo.
No bloco de processo collectGreetings, faça as seguintes alterações de código:
A variável count_greetings será computada em tempo de execução.
4.1.2. Emita o arquivo de relatório e nomeie as saídas¶
Em princípio, tudo o que precisamos fazer é adicionar o arquivo de relatório ao bloco output:.
No entanto, enquanto estamos fazendo isso, também vamos adicionar algumas tags emit: às nossas declarações de saída. Estas nos permitirão selecionar as saídas por nome em vez de ter que usar índices posicionais.
No bloco de processo, faça a seguinte alteração de código:
As tags emit: são opcionais, e poderíamos ter adicionado uma tag a apenas uma das saídas.
Mas como dizem, por que não ambos?
Tip
Se você não nomear as saídas de um processo usando emit:, ainda pode acessá-las individualmente usando seu respectivo índice (baseado em zero).
Por exemplo, você usaria <process>.out[0] para obter a primeira saída, <process>.out[1] para obter a segunda saída, e assim por diante.
Preferimos nomear saídas porque caso contrário, é muito fácil pegar o índice errado por erro, especialmente quando o processo produz muitas saídas.
4.2. Atualize as saídas do fluxo de trabalho¶
Agora que temos duas saídas saindo do processo collectGreetings, a saída collectGreetings.out contém dois canais:
collectGreetings.out.outfilecontém o arquivo de saída finalcollectGreetings.out.reportcontém o arquivo de relatório
Precisamos atualizar as saídas do fluxo de trabalho adequadamente.
4.2.1. Atualize a seção publish:¶
No bloco workflow, faça a seguinte alteração de código:
Como você pode ver, referir-se a saídas específicas de processos agora é trivial.
Quando formos adicionar mais um passo ao nosso pipeline na Parte 5 (Contêineres), poderemos facilmente nos referir a collectGreetings.out.outfile e passá-lo ao novo processo (spoiler: o novo processo se chama cowpy).
Mas por enquanto, vamos terminar de atualizar as saídas no nível do fluxo de trabalho.
4.2.2. Atualize o bloco output¶
No bloco output, faça a seguinte alteração de código:
Não precisamos atualizar a definição de saída collected já que esse nome não mudou.
Só precisamos adicionar a nova saída.
4.3. Execute o fluxo de trabalho¶
Vamos tentar executar isso com o lote atual de saudações.
Saída do comando
Se você olhar no diretório results/hello_workflow/, encontrará o novo arquivo de relatório, trio-report.txt.
Abra-o para verificar que o fluxo de trabalho relatou corretamente a contagem de saudações que foram processadas.
Sinta-se à vontade para adicionar mais saudações ao CSV e testar o que acontece.
Conclusão¶
Você sabe como fazer um processo emitir múltiplas saídas nomeadas e como manuseá-las apropriadamente no nível do fluxo de trabalho.
De forma mais geral, você entende os princípios-chave envolvidos em conectar processos de formas comuns.
O que vem a seguir?¶
Faça uma pausa extra longa, você a merece.
Quando estiver pronto, passe para Parte 4: Olá Módulos para aprender como modularizar seu código para melhor manutenibilidade e eficiência de código.
Quiz¶
Como você acessa a saída de um processo no bloco de fluxo de trabalho?
O que determina a ordem de execução de processos no Nextflow?
Quando você deve usar o operador collect()?
Como você acessa uma saída nomeada de um processo?
Qual é a sintaxe correta para nomear uma saída em um processo?
Ao fornecer múltiplas entradas a um processo, o que deve ser verdadeiro?