Ir para o conteúdo

9. Operadores

Operadores são métodos para conectar canais, transformar valores emitidos por canais ou executar regras próprias em canais.

Existem sete grupos de operadores descritos em detalhe na Documentação do Nextflow, estes são:

  1. Operadores de filtragem
  2. Operadores de transformação
  3. Operadores de divisão
  4. Operadores de combinação
  5. Operadores de bifurcação
  6. Operadores matemáticos
  7. Outros operadores

9.1 Exemplo básico

Clique no ícone para ver explicações do código.

1
2
3
nums = Channel.of(1, 2, 3, 4) // (1)!
quadrados = nums.map { it -> it * it } // (2)!
quadrados.view() // (3)!
  1. Cria um canal de fila que emite quatro valores
  2. Cria um novo canal, transformando cada número ao quadrado
  3. Imprime o conteúdo do canal
432nums11694quadrados1map

Para implementar funcionalidades específicas operadores também podem ser encadeados. Então, o código anterior também pode ser escrito assim:

1
2
3
4
Channel
    .of(1, 2, 3, 4)
    .map { it -> it * it }
    .view()

9.2 Operadores básicos

Agora iremos explorar alguns dos operadores mais comuns.

9.2.1 view()

O operador view imprime os itens emitidos por um canal para o terminal, acrescentando um caractere de quebra de linha após cada item. Por exemplo:

1
2
3
Channel
    .of('foo', 'bar', 'baz')
    .view()
Output
foo
bar
baz

Você também pode especificar uma clausura para personalizar como os itens são impressos. Por exemplo:

1
2
3
Channel
    .of('foo', 'bar', 'baz')
    .view { "- $it" }
Output
- foo
- bar
- baz

9.2.2 map()

O operador map aplica uma função de sua escolha em cada item emitido por um canal e retorna os items obtidos como um novo canal. A função aplicada é chamada de função de mapeamento e é expressa com uma clausura, como demonstrado no exemplo abaixo:

1
2
3
4
Channel
    .of('olá', 'mundo')
    .map { it -> it.reverse() }
    .view()

Um map pode associar uma tupla genérica a cada elemento e pode conter qualquer tipo de dado.

1
2
3
4
Channel
    .of('olá', 'mundo')
    .map { palavra -> [palavra, palavra.size()] }
    .view { palavra, comprimento -> "$palavra contém $comprimento letras" }

Exercise

Use fromPath para criar um canal emitindo os arquivos fastq que correspondam à expressão data/ggal/*.fq, então use map para retornar um par contendo o nome e o caminho para o arquivo, e, por fim, use view para imprimir o canal resultante.

Solution
1
2
3
4
Channel
    .fromPath('data/ggal/*.fq')
    .map { arquivo -> [arquivo.name, arquivo] }
    .view { nome, arquivo -> "> $nome : $arquivo" }

9.2.3 mix()

O operador mix combina os itens emitidos por dois (ou mais) canais em um único canal.

1
2
3
4
5
6
7
meu_canal_1 = Channel.of(1, 2, 3)
meu_canal_2 = Channel.of('a', 'b')
meu_canal_3 = Channel.of('z')

meu_canal_1
    .mix(meu_canal_2, meu_canal_3)
    .view()
Output
1
2
a
3
b
z

Warning

Os itens no canal resultante possuem a mesma ordem dos seus respectivos canais originais. No entanto, não há garantia que o elemento do segundo canal é acrescentado ao final dos elementos do primeiro canal. Como se pode observar acima, o elemento a foi impresso antes de 3.

9.2.4 flatten()

O operador flatten transforma um canal de maneira que cada tupla é achatada, isto é, cada entrada é emitida como um único elemento pelo canal resultante.

1
2
3
4
5
6
7
foo = [1, 2, 3]
bar = [4, 5, 6]

Channel
    .of(foo, bar)
    .flatten()
    .view()
Output
1
2
3
4
5
6

9.2.5 collect()

O operador collect coleta todos os itens emitidos por um canal em uma lista e retorna o objeto como uma única emissão.

1
2
3
4
Channel
    .of(1, 2, 3, 4)
    .collect()
    .view()

Isto imprime o valor:

Output
[1, 2, 3, 4]

Info

O resultado do operador collect é um canal de valor.

9.2.6 groupTuple()

O operador groupTuple coleta as tuplas (ou listas) de valores emitidos pelo canal de entrada, agrupando os elementos que possuem a mesma chave. Por fim, ele emite uma nova tupla para cada chave distinta.

Por exemplo:

1
2
3
4
Channel
    .of([1, 'A'], [1, 'B'], [2, 'C'], [3, 'B'], [1, 'C'], [2, 'A'], [3, 'D'])
    .groupTuple()
    .view()
Output
[1, [A, B, C]]
[2, [C, A]]
[3, [B, D]]

Esse operador é útil para processar um grupo, juntando elementos que possuem uma propriedade ou uma chave em comum.

Exercise

Use fromPath para criar um canal emitindo todos os arquivos no diretório data/meta/, então use map para associar o prefixo baseName a cada arquivo. Por fim, agrupe todo os arquivos que possuem o mesmo prefixo.

Solution
1
2
3
4
5
Channel
    .fromPath('data/meta/*')
    .map { arquivo -> tuple(arquivo.baseName, arquivo) }
    .groupTuple()
    .view { baseName, arquivo -> "> $baseName : $arquivo" }

9.2.7 join()

O operador join cria um canal que combina os itens emitidos por dois canais que possuam uma chave em comum. Por padrão, a chave é definida como o primeiro elemento em cada item emitido.

1
2
3
esquerda = Channel.of(['X', 1], ['Y', 2], ['Z', 3], ['P', 7])
direita = Channel.of(['Z', 6], ['Y', 5], ['X', 4])
esquerda.join(direita).view()
Output
[Z, 3, 6]
[Y, 2, 5]
[X, 1, 4]

Note

Perceba como P está ausente no resultado final.

9.2.8 branch()

O operador branch permite que você envie os itens emitidos por um canal de entrada para um ou mais canais de saída.

O critério de seleção de cada canal de saída é definido especificando uma clausura que forneça uma ou mais expressões booleanas, cada uma das quais é identificada por um rótulo único. Para a primeira expressão verdadeira, o item é ligado a um canal nomeado com o rótulo. Por exemplo:

Channel
    .of(1, 2, 3, 40, 50)
    .branch {
        pequeno: it < 10
        grande: it > 10
    }
    .set { resultado }

resultado.pequeno.view { "$it é pequeno" }
resultado.grande.view { "$it é grande" }

Info

O operador branch retorna um objeto multi-canal (isto é, uma variável que possui mais de um canal).

Note

No exemplo acima, o que aconteceria com um valor igual a 10? Para lidar com isso, você pode usar >=.

9.3 Outros recursos

Veja a documentação de operadores no site oficial do Nextflow.