5 Processes 上

Processes(进程)

我们现在知道如何创建和使用 Channels 来发送数据。现在我们将看到如何使用进程在工作流中运行任务。
进程是 Nextflow 执行的是你在命令行上运行的命令或自定义脚本。
一个进程可以被认为是工作流中的一个特定步骤,例如 RNA-seq 分析中的一个比对步骤。进程彼此独立(不需要执行任何其他进程) ,不能彼此通信/写入。数据通过输入和输出通道在进程之间传递。
例如,下面的命令行是用于创建酵母转录组索引,使用了salmon:

$ salmon index -t data/yeast/transcriptome/Saccharomyces_cerevisiae.R64-1-1.cdna.all.fa.gz -i data/yeast/salmon_index --kmerLen 31

现在我们将展示如何将其转换为一个简单的 Nextflow 进程。

进程定义

进程定义以关键字 process 开始,后跟进程名称(在本例中为 INDEX) ,最后是由花括号{}分隔的主体。进程体必须包含一个命令,或者更一般地说,表示由它执行的脚本。

process INDEX {
  script:
  "salmon index -t ${projectDir}/data/yeast/transcriptome/Saccharomyces_cerevisiae.R64-1-1.cdna.all.fa.gz -i ${projectDir}/data/yeast/salmon_index --kmerLen 31"
}

隐式变量
我们使用 Nextflow 隐式变量 ${ projectDir }来指定主脚本所在的目录。这一点很重要,因为 Nextflow 脚本是在单独的工作目录中执行的。更多的隐式变量可以参考隐式变量

要将进程添加到工作流中,需要添加一个工作流块,并像函数一样调用该进程。我们将在工作流中了解更多的内容。
注意: 由于我们正在使用 DSL2,因此需要在脚本中包含 nextflow.enable.dsl = 2

//process_index.nf
nextflow.enable.dsl=2

process INDEX {
  script:
  "salmon index -t ${projectDir}/data/yeast/transcriptome/Saccharomyces_cerevisiae.R64-1-1.cdna.all.fa.gz -i data/yeast/salmon_index --kmerLen 31"
}

workflow {
  //process is called like a function in the workflow block
  INDEX()
}

运行脚本:nextflow run process_index.nf.

定义进程体

前面的示例是一个没有定义输入和输出的只运行一次的简单进程。为了控制输入、输出和命令的执行,一个进程可能包含五个定义块:

  1. directives - 0,1,或多个:允许定义影响当前进程执行的可选设置,例如任务使用的 CPU 数量和分配的内存量。
  2. **input ** - 0,1,或多个:定义输入依赖项(通常是通道) ,它决定进程执行的次数。
  3. outputs - 0,1,或多个:定义进程用于发送结果/数据的输出通道。
  4. when 子句 - 可选:定义为执行进程而必须验证的条件。
  5. script - 必须:定义进程执行其任务时所执行的命令。

语法定义如下:

process < NAME > {
  [ directives ]        
  input:                
  < process inputs >
  output:               
  < process outputs >
  when:                 
  < condition >
  [script|shell|exec]:  
  < user script to be executed >
}

Script(脚本)

一个进程块必须包含一个脚本块。脚本块定义进程执行的命令以执行其任务。这些通常是在终端上运行的命令。
一个进程只包含一个脚本块,并且它必须是该进程包含输入和输出声明时的最后一条语句。
脚本块可以是一个简单的带引号的单行字符串。例如:

nextflow.enable.dsl=2

process PROCESSBAM {
    script:
    "samtools sort -o ref1.sorted.bam ${projectDir}/data/yeast/bams/ref1.bam"
}

workflow {
  PROCESSBAM()
}

或者,对于跨多行的命令,可以使用三重引号"""。例如:

//process_multi_line.nf
nextflow.enable.dsl=2

process PROCESSBAM {
    script:
    """
    samtools sort -o ref1.sorted.bam ${projectDir}/data/yeast/bams/ref1.bam
    samtools index ref1.sorted.bam
    samtools flagstat ref1.sorted.bam
    """
}

workflow {
  PROCESSBAM()
}

默认情况下,process 命令被解释为 Bash 脚本。然而,任何其他的脚本语言都可以简单地用相应的 Shebang 声明来启动脚本。例如:

//process_python.nf
nextflow.enable.dsl=2

process PYSTUFF {
  script:
  """
  #!/usr/bin/env python
  import gzip

  reads = 0
  bases = 0

  with gzip.open('${projectDir}/data/yeast/reads/ref1_1.fq.gz', 'rb') as read:
      for id in read:
          seq = next(read)
          reads += 1
          bases += len(seq.strip())
          next(read)
          next(read)

  print("reads", reads)
  print("bases", bases)
  """
}

workflow {
  PYSTUFF()
}

R:

//process_rscript.nf
nextflow.enable.dsl=2

process RSTUFF {
  script:
  """
  #!/usr/bin/env Rscript
  library("ShortRead")
  countFastq(dirPath="data/yeast/reads/ref1_1.fq.gz")
  """
}

workflow {
  RSTUFF()
}

允许使用不同的编程语言,这样可能更适合一个特定的工作。但是,对于大块代码,建议将它们保存到单独的文件中,并从进程脚本调用它们。

nextflow.enable.dsl=2

process PYSTUFF {

  script:
  """
  myscript.py
  """
}

workflow {
  PYSTUFF()
}

上面示例中的 myscript.py 脚本可以存储在与调用它们的 Nextflow 工作流脚本相同目录级别的 bin 文件夹中,并且给与执行权限。Nextflow 会自动将此文件夹添加到 PATH 环境变量

脚本参数

可以使用 Nextflow 变量(例如 ${ projectDir })动态定义脚本块中的命令。

与 bash 脚本类似,Nextflow 使用$字符来引入变量替换,要展开的变量名称可以括在大括号{ variable_name }中。

在下面的示例中,变量 kmer 被设置为值31。在脚本块中的多行字符串语句中使用 $kmer 语法引用该变量。Nextflow 变量可以在脚本块中多次使用。

//process_script.nf
nextflow.enable.dsl=2

kmer = 31

process INDEX {

  script:
  """
  salmon index \
  -t $projectDir/data/yeast/transcriptome/Saccharomyces_cerevisiae.R64-1-1.cdna.all.fa.gz \
  -i index \
  --kmer $kmer
  echo "kmer size is $kmer"
  """
}

workflow {
  INDEX()
}

在下面的示例中,我们在 Nextflow 脚本中定义了默认值为31的变量 params.kmer。

//process_script_params.nf
nextflow.enable.dsl=2

params.kmer = 31

process INDEX {

  script:
  """
  salmon index \
  -t $projectDir/data/yeast/transcriptome/Saccharomyces_cerevisiae.R64-1-1.cdna.all.fa.gz \
  -i index \
  --kmer $params.kmer
  echo "kmer size is $params.kmer"
  """
}

workflow {
  INDEX()
}

我们可以使用下面的命令运行 Nextflow 脚本,将 kmer 的默认值更改为11。

nextflow run process_script_params.nf --kmer 11

Bash 变量

Nextflow 使用相同的 Bash 语法进行变量替换,即 $variable。但是,Bash 变量需要使用转义 \$variable
在下面的示例中,我们将 bash 变量 KMERSIZE 设置为 $params.kmer 的值,然后在脚本块中使用 KMERSIZE

//process_escape_bash.nf
nextflow.enable.dsl=2

process INDEX {

  script:
  """
  #set bash variable KMERSIZE
  KMERSIZE=$params.kmer
  salmon index -t $projectDir/data/yeast/transcriptome/Saccharomyces_cerevisiae.R64-1-1.cdna.all.fa.gz -i index --kmer \$KMERSIZE
  echo "kmer size is $params.kmer"
  """
}

params.kmer = 31

workflow {
  INDEX()
}

Shell

另一种选择是使用 shell 块定义而不是脚本script。当使用 shell 语句时,通常以 $my_bash_variable 的方式引用 bash 变量。但是,shell 语句对 Nextflow 变量替换使用了不同的语法:!{nextflow_variable}
例如,在下面使用 shell 语句的脚本中,我们将 Nextflow 变量引用为!{ projectDir }!{ params.kmer } ,Bash 变量为 ${ KMERSIZE }

//process_shell.nf
nextflow.enable.dsl=2

params.kmer = 31

process INDEX {

  shell:
  '''
  #set bash variable KMERSIZE
  KMERSIZE=!{params.kmer}
  salmon index -t !{projectDir}/data/yeast/transcriptome/Saccharomyces_cerevisiae.R64-1-1.cdna.all.fa.gz -i index --kmer ${KMERSIZE}
  echo "kmer size is !{params.kmer}"
  '''
}

workflow {
  INDEX()
}

条件语句

有时,我们希望根据某些条件更改进程的运行方式。在 Nextflow 脚本中,我们可以使用条件语句,例如 if 语句。

if

If 语句使用与其他编程语言(如 Java、 C、 JavaScript 等)相同的语法。

if( < boolean expression > ) {
    // true branch
}
else if ( < boolean expression > ) {
    // true branch
}
else {
    // false branch
}

例如:

//process_conditional.nf
nextflow.enable.dsl=2

params.aligner = 'kallisto'
params.transcriptome = "$projectDir/data/yeast/transcriptome/Saccharomyces_cerevisiae.R64-1-1.cdna.all.fa.gz"
params.kmer = 31

process INDEX {
  script:
  if( params.aligner == 'kallisto' ) {
    """
    echo indexed using kallisto
    kallisto index -i index -k $params.kmer $params.transcriptome
    """
  }  
  else if( params.aligner == 'salmon' ) {
    """
    echo indexed using salmon
    salmon index -t $params.transcriptome -i index --kmer $params.kmer
    """
  }  
  else {
    """
    echo Unknown aligner $params.aligner
    """
  }  
}

workflow {
  INDEX()
}

输入(Inputs)

进程彼此隔离,但可以通过 Nextflow 通道从输入和输出块发送值和文件进行通信。
输入块定义进程期望从哪个通道接收输入。输入通道中元素的数量决定了进程依赖关系和进程执行的次数。
input
一次只能定义一个输入块,它必须包含一个或多个输入声明。
输入块语法如下:

input:
  <input qualifier> <input name>

<input qualifier>声明要接收的数据类型,取值类型如下:

  • val:通过名称作为进程脚本中的变量访问接收到的输入值。
  • env:使用输入值设置一个名为指定输入名称的环境变量。
  • path:将接收到的值作为文件处理,并在执行上下文中正确地分段处理该文件。
  • stdin:将接收到的值转发到进程 stdin 特殊文件。
  • tuple:处理具有上述限定符之一的一组输入值。
  • each:为输入集合中的每个条目执行流程。
//process_input_value.nf
nextflow.enable.dsl=2

process PRINTCHR {

  input:
  val chr

  script:
  """
  echo processing chromosome $chr
  """
}

chr_ch = Channel.of( 1..22, 'X', 'Y' )

workflow {

  PRINTCHR(chr_ch)
}

在上面的示例中,该进程执行了24次; 每次从队列通道 chr_ch 接收到一个值时,就用它来运行该进程。

通道保证按照发送的顺序交付项目,但是由于进程是以并行方式执行的,因此不能保证按照与接收项目相同的顺序处理项目。

输入文件

当需要处理文件作为输入时,需要使用path限定符。
例如,在下面的脚本中,我们使用path限定符将变量名称读取到输入文件。使用脚本块中的变量替换语法 ${ read }引用该文件:

//process_input_file.nf
nextflow.enable.dsl=2

process NUMLINES {
    input:
    path read

    script:
    """
    printf '${read} '
    gunzip -c ${read} | wc -l
    """
}

reads_ch = Channel.fromPath( 'data/yeast/reads/ref*.fq.gz' )

workflow {
  NUMLINES(reads_ch)
}
结合输入通道

进程的一个关键特性是处理来自多个通道的输入的能力。例如:

//process_combine.nf
nextflow.enable.dsl=2

process COMBINE {
  input:
  val x
  val y

  script:
  """
  echo $x and $y
  """
}

num_ch = Channel.of(1, 2, 3)
letters_ch = Channel.of('a', 'b', 'c')

workflow {
  COMBINE(num_ch, letters_ch)
}

进程被执行3次,结果如下:

2 and b

1 and a

3 and c

当不是所有通道都有相同数量的元素时会发生什么?
例如:

//process_combine_02.nf
nextflow.enable.dsl=2

process COMBINE {
  input:
  val x
  val y

  script:
  """
  echo $x and $y
  """
}

ch_num = Channel.of(1, 2)
ch_letters = Channel.of('a', 'b', 'c', 'd')

workflow {
  COMBINE(ch_num, ch_letters)

在上面的示例中,进程只执行两次,因为当队列通道没有更多的数据要处理时,它将停止进程的执行。

输出:
2 and b
1 and a

与队列通道不同,值通道可以使用多次:

//process_combine_03.nf
nextflow.enable.dsl=2

process COMBINE {
  input:
  val x
  val y

  script:
  """
  echo $x and $y
  """
}
ch_num = Channel.value(1)
ch_letters = Channel.of('a', 'b', 'c')

workflow {
  COMBINE(ch_num, ch_letters)
}

本例中进程将会执行3次:

1 and b
1 and a
1 and c

我们在前面看到,默认情况下,进程运行的次数由具有最少项的队列通道决定。each限定符允许为列表或队列通道中的每一项重复执行进程。例如:

//process_repeat.nf
nextflow.enable.dsl=2

process COMBINE {
  input:
  val x
  each y

  script:
  """
  echo $x and $y
  """
}

ch_num = Channel.of(1, 2)
ch_letters = Channel.of('a', 'b', 'c', 'd')

workflow {
  COMBINE(ch_num, ch_letters)
}

运行$ nextflow run process_repeat.nf -process.echo
进程将会运行8次:

2 and d
1 and a
1 and c
2 and b
2 and c
1 and d
1 and b
2 and a

总结

  • Nextflow 进程是工作流中的独立的步骤。
  • 进程包含多达五个定义块,包括: 指令、输入、输出、 when 子句和最后一个脚本块。
  • 脚本块包含要运行的命令。
  • 一个进程应该有一个脚本,但是其他四个块是可选的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值