Nextflow生物信息流程(二):从入门到放弃

2023-12-11 14:26:32 浏览数 (1)

什么鬼?把简单的事情弄复杂,连多年的生信老司机都给整不会了。还社区驱动?无力吐槽。没入门,已放弃,白白浪费几十分钟。

既然其官网说了, Linux 是数据科学的通用语言 。为何 Nextflow 搭建的流程没有多少 Linux 的影子?

把简单的生信流程,弄成一个堪比 IT 的大工程?

槽点一:过度包装,徒增复杂性

我们就以其官网提供的核心流程 RNA-seq 为例,来看看这东西到底有多复杂。下面是流程目录。

代码语言:javascript复制
rnaseq/
├── assets
├── bin
├── CHANGELOG.md
├── CITATIONS.md
├── CODE_OF_CONDUCT.md
├── conf
├── docs
├── lib
├── LICENSE
├── main.nf
├── modules
├── modules.json
├── nextflow.config
├── nextflow_schema.json
├── pyproject.toml
├── README.md
├── subworkflows
├── tower.yml
└── workflows

内容很多,其中:

代码语言:javascript复制
workflows/
└── rnaseq.nf

是主流程。在其中,引入子流程 subworflows 和模块 modules ,如下图:

在这一套体系中,模块是最小的单位,每一个软件的具体操作,被包装为模块。然后在模块之上,再封装成子流程。最后,由主流程将各子流程串起来,成为完整流程。

这样看似很有道理,模块化,增加代码的可重用性。实则是过度包装,一行 Shell 代码能完成的功能,硬是包装出了几百行代码(可查看 hisat2 比对软件的包装逻辑)。功能没增强多少,复杂性呈指数级上升,有害无益。

如果你曾经学习过 Nextflow 却没有学懂,那不是你的错。这套东西没几个人能看懂,更何况用它搭建流程了。

槽点二:语法怪异,晦涩难懂

语法中有大量生造的符号,仅举一例便可窥其全貌,如将多个 FASTQ 文件 cat 在一起的命令:

代码语言:javascript复制
CAT_FASTQ (
        ch_fastq.multiple
    )
    .reads
    .mix(ch_fastq.single)
    .set { ch_cat_fastq }
    ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first().ifEmpty(null))

其中.reads / .mix / .set 等,啥意思,就一个简单的cat命令,需要弄这么复杂吗?

结论是,用这套符号系统搭流程,还不如直接用 C ,Java 呢,起码是正规的编程语言,参考资料众多。更不用说像 Python 或 Perl 这种通用的脚本语言,天然适合流程搭建。

我将流程搭建分为这样几个境界:

  1. Shell 脚本
  2. Makefile
  3. Python / Perl
  4. Python / Perl Makefile

这足以应付绝大多数的生信数据分析场景,没必要把事情搞得那么复杂。

那些年,我们踩过的坑

好的生信团队都是用自己的生信框架。不会用社区的,如WDL,snakemake,nextflow等,我们好多年前就放弃了。不为别的,因为吃过亏。

还记得曾经大火的 WDL,许多知名生信机构都在推,我们也热情拥抱社区。原因很简单:社区驱动,资源共享,一套流程开发出来,所有团队都能使用,这将会极大地避免重复劳动,提高资源利用率。

于是当时部门的生信流程尽量都用 WDL 搭建。没想到这货非常不稳定,总是莫名其炒地报错,非常难以排查原因。

痛定思痛之后,我们放弃了 WDL,回到了做生信最开始的地方:老老实实写 Shell,然后用 Python 串起来。

到后来,随着搭建的流程越来越多,我们经验也越来越丰富。最后将所有流程通用的部分,抽离出来,形成了一个通用的组学数据分析框架。于量,我们也定义了一门域语言(DSL)。

不过,我们并没有“屠龙少年终变成恶龙”,我们的框架依然保持简单而高效。它没那么华丽,但刚好够用。

一个好的生信流程框架应该是怎样的?

我认为,好的生信流程框架,应该有这样几个特点:

  1. 批量任务,能同时分析任意数量样本的任务。
  2. 既支持单样本,也支持配对样本的数据分析。
  3. 能轻松定义任务之间的依赖关系,最终纳入有向无环图(DAG)管理。
  4. 每一步任务可以定义 CPU 和内存资源需求。
  5. 能适应各种计算环境,如单机,以及各种集群。这可以分两步实现,第一步生成 Shell 脚本,第二步再将 Shell 脚本组织成符合集群任务投递的文件。
  6. 任务命令调用,尽量用 Linux Shell 脚本描述,这样方便提前测试命令的正确性,因为 Linux 是数据科学的通用语言,Shell 命令是软件调用最自然的方式。
  7. 简约而不简单。在保证能够满足绝大多数应用场景的情况下,新手也能简单上手。

那有人可能要问了,这样一套流程,实现应该很复杂吧?其实不然,只需要 500 行 Python 代码即可。

欲知究竟如何通过 500 行 Python 代码就能实现这样一套堪称完美的框架模型,且听下回分解。

0 人点赞