10-Nextflow DSL 2

DSL 2

Nextflow提供了一个语法扩展,它实现了模块库的定义,并简化了复杂数据分析管道的编写。

要启用这个特性,你需要在你的工作流脚本的开头定义以下指令:

nextflow.enable.dsl=2

Function

Nextflow允许在工作流脚本中使用以下语法定义自定义函数:

def <function name> ( arg1, arg, .. ) {
    <function body>
}
def foo() {
    'Hello world'
}

def bar(alpha, omega) {
    alpha + omega
}

上面的代码片段定义了两个简单的函数,可以在工作流脚本中调用它们,foo()返回Hello world字符串,bar(10,20)返回两个参数的和。

Tip
函数隐式返回最后一个求值语句的结果。

关键字return可用于显式退出返回指定值的函数。例如:

def fib( x ) {
    if( x <= 1 )
        return x
    else
        fib(x-1) + fib(x-2)
}

Process

Process定义

新的DSL将流程的定义与其调用分开。流程定义遵循流程文档中描述的常用语法。唯一的区别是必须省略from和into通道声明。

然后,可以将流程作为workflow作用域中的函数调用,将预期的输入通道作为参数传递,就像它是一个自定义函数一样。例如:

nextflow.enable.dsl=2

process foo {
    output:
      path 'foo.txt'
    script:
      """
      your_command > foo.txt
      """
}

 process bar {
    input:
      path x
    output:
      path 'bar.txt'
    script:
      """
      another_command $x > bar.txt
      """
}

workflow {
    data = channel.fromPath('/some/path/*.txt')
    foo()
    bar(data)
}

Warning
流程组件在相同的工作流上下文中只能被调用一次。

Process合成

可以组合具有匹配输入-输出声明的流程,以便将第一个流程的输出作为输入传递给下一个流程。考虑到前面的过程定义,可以这样写:

workflow {
    bar(foo())
}
流程输出

还可以使用各自流程对象的out属性访问流程输出。例如:

workflow {
    foo()
    bar(foo.out)
    bar.out.view()
}

当进程定义两个或多个输出通道时,可以使用数组元素操作符(例如out[0]、out[1]等)或使用命名输出(见下文)来访问每一个输出通道。

流程输出命名

流程输出定义允许使用emit选项来定义一个名称标识符,该标识符可用于在外部作用域中引用通道。例如:

process foo {
  output:
    path '*.bam', emit: samples_bam

  '''
  your_command --here
  '''
}

workflow {
    foo()
    foo.out.samples_bam.view()
}
流程标准输出命名

进程可以使用emit选项命名标准输出:

process sayHello {
    input:
        val cheers
    output:
        stdout emit: verbiage
    script:
    """
    echo -n $cheers
    """
}

workflow {
    things = channel.of('Hello world!', 'Yo, dude!', 'Duck!')
    sayHello(things)
    sayHello.out.verbiage.view()
}

工作流

工作流定义

workflow关键字允许定义包含调用一个或多个流程和操作符的子工作流组件:

workflow my_pipeline {
    foo()
    bar( foo.out.collect() )
}

例如,上面的代码片段定义了一个名为my_pipeline的工作流组件,可以从另一个工作流组件定义中调用该组件作为任何其他函数或流程,例如my_pipeline()。

工作流参数

工作流组件可以访问外部作用域中定义的任何变量和参数:

params.data = '/some/data/file'

workflow my_pipeline {
    if( params.data )
        bar(params.data)
    else
        bar(foo())
}
工作流输入

工作流组件可以使用take关键字声明一个或多个输入通道。例如:

workflow my_pipeline {
    take: data
    main:
    foo(data)
    bar(foo.out)
}

Warning
当使用take关键字时,需要用main关键字标识工作流主体的开始部分。

然后,可以在工作流调用语句中将输入指定为参数:

workflow {
    my_pipeline( channel.from('/some/data') )
}

Note
工作流输入定义为通道数据结构。如果提供了基本的数据类型,例如。数字、字符串、列表等。

工作流输出

工作流组件可以使用emit关键字声明一个或多个输出通道。例如:

workflow my_pipeline {
    main:
      foo(data)
      bar(foo.out)
    emit:
      bar.out
}

然后,可以使用out属性访问my_pipeline执行的结果,例如:my_pipeline.out。当声明了多个输出通道时,按照Process输出定义的描述,使用数组括号符号来访问每个输出组件。

或者,可以使用在emit声明中赋值的标识符名称访问输出通道:

workflow my_pipeline {
   main:
     foo(data)
     bar(foo.out)
   emit:
     my_data = bar.out
}

然后,可以使用my_pipeline.out.my_data访问上述代码片段的结果。

隐式工作流

没有声明任何名称的工作流定义被假定为主工作流,并隐式执行。因此,它是工作流应用程序的入口点。

Note
当脚本作为模块包含时,隐式工作流定义将被忽略。这允许编写工作流脚本,它既可以用作库模块,也可以用作应用程序脚本。

Tip
另一个工作流条目可以使用-entry命令行选项指定。

工作流组合

在您的脚本中定义的工作流或由模块包含导入的工作流可以作为应用程序中的任何其他流程调用和组合。

workflow flow1 {
    take: data
    main:
        foo(data)
        bar(foo.out)
    emit:
        bar.out
}

workflow flow2 {
    take: data
    main:
        foo(data)
        baz(foo.out)
    emit:
        baz.out
}

workflow {
    take: data
    main:
      flow1(data)
      flow2(flow1.out)
}

Note
嵌套的工作流执行确定了隐式作用域。因此,相同的流程可以在两个不同的工作流作用域中调用,例如上面的代码片段中的foo在flow1和flow2中使用。工作流执行路径以及进程名定义了进程的完全限定名,用来区分两个不同的进程调用,如上面例子中的flow1:foo和flow2:foo。

Tip
流程完全限定名可以用作下一个流中的有效流程选择器。配置文件,它的优先级高于进程简单名称。


模块

新的DSL允许在工作流应用程序之间包含和共享定义模块脚本。

模块可以包含上述部分所述的功能、过程和工作流定义。

模块的include

模块脚本中定义的组件可以使用include关键字导入到另一个Nextflow脚本中。
例如:

include { foo } from './some/module'

workflow {
    data = channel.fromPath('/some/data/*.txt')
    foo(data)
}

上面的代码片段包括一个名为foo的进程,它在主执行上下文中的模块脚本中定义,因此它可以在工作流范围内调用。
Nextflow隐式查找脚本文件./some/module.nf,根据包含的脚本位置解析路径。

Note
相对路径必须以./前缀开头。

多个include

Nextflow脚本允许包含任意数量的模块。当需要从某个模块脚本中包含多个组件时,可以在同一个包含中使用花括号符号指定组件名称,如下所示:

include { foo; bar } from './some/module'

workflow {
    data = channel.fromPath('/some/data/*.txt')
    foo(data)
    bar(data)
}
模块别名

当包含一个模块组件时,可以指定一个名称别名。这允许在脚本中使用不同的名称多次包含和调用相同的组件。例如:

include { foo } from './some/module'
include { foo as bar } from './other/module'

workflow {
    foo(some_data)
    bar(other_data)
}

当包括来自相同模块脚本的多个组件时,情况也是一样的,如下所示:

include { foo; foo as bar } from './some/module'

workflow {
    foo(some_data)
    bar(other_data)
}
模块参数

模块脚本可以使用与Nextflow工作流脚本相同的语法定义一个或多个参数:

params.foo = 'Hello'
params.bar = 'world!'

def sayHello() {
    println "$params.foo $params.bar"
}

然后,从包含上下文继承参数。例如:

params.foo = 'Hola'
params.bar = 'Mundo'

include {sayHello} from './some/module'

workflow {
    sayHello()
}

上面的代码片段打印:

Hola Mundo

Note
该模块继承include语句之前定义的参数(即截止调用这个模块,使用最新定义的参数),因此以后设置的任何参数都将被忽略。

Tip
在脚本的开头,在任何include声明之前定义所有管道参数

addParams选项可用于扩展模块参数而不影响外部作用域。例如:

include {sayHello} from './some/module' addParams(foo: 'Ciao')

workflow {
    sayHello()
}

上面的代码片段打印:

Ciao world!

最后,include选项参数允许指定一个或多个参数,而不从外部环境继承任何值。


通道分叉

使用新的DSL,当连接两个或多个consumers时,Nextflow通道将自动分叉。

Channel
    .from('Hello','Hola','Ciao')
    .set{ cheers }

cheers
    .map{ it.toUpperCase() }
    .view()

cheers
    .map{ it.reverse() }
    .view()

这同样适用于流程执行的结果(通道)。因此,流程输出可以由两个或多个流程使用,而不需要使用into操作符将它们派生出来,从而使工作流脚本的编写更加流畅和可读。


管道

管道操作符

Nextflow流程和操作符可以使用|管道操作符组合。例如:

process foo {
    input: val data
    output: val result
    exec:
    result = "$data world"
}

workflow {
   channel.from('Hello','Hola','Ciao') | foo | map { it.toUpperCase() } | view
}

与操作符

&和操作符允许输入两个或多个具有相同通道内容的进程,例如:

process foo {
  input: val data
  output: val result
  exec:
    result = "$data world"
}

process bar {
    input: val data
    output: val result
    exec:
      result = data.toUpperCase()
}

workflow {
   channel.from('Hello') | map { it.reverse() } | (foo & bar) | mix | view
}

在上面的代码片段中,发送Hello的通道使用了映射管道,该映射可以反转字符串值。然后,结果被传递给并行执行的foo和bar进程。结果是一对通道,使用mix操作符将其内容合并到单个通道中。最后,使用视图操作符打印结果。

Tip
break-line操作符\可用于在多行上分割长管道连接。

上面的代码片段可以写成如下所示:

workflow {
   channel.from('Hello') \
     | map { it.reverse() } \
     | (foo & bar) \
     | mix \
     | view
}

DSL2变动注意点

见(https://www.nextflow.io/docs/latest/dsl2.html#dsl2-migration-notes)[https://www.nextflow.io/docs/latest/dsl2.html#dsl2-migration-notes]

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容