稳健的研究在很大程度上是采取一系列方案,这些方案共同累积了可靠度,我们的目的是防止“无声”的错误混淆结果。
01 重视实验设计
稳健的研究始于良好的实验设计。不幸的是,再多精彩的分析也救不了一个设计糟糕的实验。
我见过项目落在我的桌子上准备进行分析,花了数万元测序,但当数据放在这时,其实已经完全“死”了。无论如何,在实验结束后,我们都无法挽救设计糟糕的实验。
大多数统计学入门课程和书籍都涵盖了实验设计中的基本方法。不过,基因组学实验中的实验设计是另一回事,并且正在积极研究和改进。确保你的数万元的实验达到它的潜力的最好方法是,看看你这种类型的项目同行中做的最好的是什么样的。咨询您当地友好的统计学家,了解任何实验设计问题或您在计划实验时可能存在的问题。
02 为人类编写代码,为计算机编写数据
调试的难度是最初编写代码的两倍。生物信息学项目可能涉及堆积如山的代码,而我们针对bug的最好防御就是为人类,而不是为计算机编写代码。人类是进行调试的人,因此编写简单、清晰的代码可以使调试变得更容易。
代码应该是可读的,分解为小的包含组件(模块化),并且可重用(因此不需要重写代码来一遍又一遍地执行相同的任务)。这些实践在编程圈中是至关重要的,也应该应用于生物信息学工作中。注释代码和遵守代码风格指南是提高代码可读性的简单方法。
为什么代码的可读性如此重要?首先,可读代码使项目更具可重复性,因为其他人可以更容易地理解脚本做什么以及它们是如何工作的。其次,在可读的、注释良好的代码中查找和更正软件错误要比混乱的代码容易得多。第三,当代码注释良好且编写清晰时,将来重新访问代码总是更容易。编写模块化和可重用代码只是需要多加练习而已。
与代码相反,数据的格式应该便于计算机读取。很多时候,我们人类记录数据的方式最大限度地提高了数据对我们人类的可读性,但在计算机处理数据之前需要进行大量的整理工作。计算机可读的数据越多,我们就越能利用计算机来处理这些数据。
03 让你的电脑替你工作
人们做死记硬背的事情往往会犯很多错误。让你的工作更健壮的最简单的方法之一就是,让你的电脑尽可能多地做这种死记硬背的工作。这种自动化任务的方法更稳健,因为它降低了犯小错误的可能性(如意外遗漏文件或输出文件命名错误)。
例如,通过单独键入每个命令在20个不同的文件上运行程序是脆弱的--每处理一个文件,发生粗心错误的可能性都会增加。在生物信息学工作中,应该养成让计算机为你做这种重复性工作的习惯。与其粘贴相同的命令20次,不如编写一个脚本来完成这项工作,只需更改输入和输出文件。这不仅更容易并且不太可能导致错误,而且还增加了可重复性,因为脚本记录下了对每个文件所做的事情。
04 在代码和方法中进行断言并“发出声音”
当我们写代码时,我们倾向于对我们的数据有隐含的假设。例如,我们期望只有三个DNA链选项(正向,反向和未知),基因的起始位置小于结束位置,并且我们不能有负位置。我们对数据所做的这些隐含的假设会影响我们编写代码的方式;例如,如果我们假设某种情况不会发生,我们可能不会考虑在代码中处理它。然而,这可能导致可怕的静默错误:我们的代码或程序接收到超出我们预期的值,但仍然在没有警告的情况下返回输出。防止此类错误的最佳方法是使用断言语句(如Python的assert()和R的stopifnot())显式声明,测试我们关于代码中数据的假设。
几乎每种编程语言都有自己的Assert函数版本。这些Assert函数的操作方式类似:如果语句的计算结果为false,则Assert函数将停止程序并引发错误。它们可能很简单,但这些断言函数在稳健研究中是必不可少的。在我学术生涯的早期,一位导师激励我养成了非常宽松地使用断言的习惯--即使看起来似乎绝对不可能有问题--但我一直惊讶于这些断言有多少次捕捉到了微妙的错误。在生物信息学(以及所有领域)中,至关重要的是我们尽可能地将可怕的“无声”错误转变为“响亮”的错误。
05 测试代码,或者让代码测试代码
程序猿们是一群聪明的人,他们把让自己的计算机做工作的想法提升到一个新的水平,让代码测试其他代码,而不是手工完成。测试代码的一种常见方法称为单元测试。在单元测试中,我们将代码分解为单独的模块化单元(这也可以提高可读性的),并编写测试此代码的附加代码。在实践中,这意味着如果我们有一个名为add()的函数,我们就会编写一个名为test_add()的附加函数(通常在单独的文件中)。这个test_add()函数将调用具有特定输入的add()函数,并测试输出是否如预期的那样。在Python中,这可能类似于:
```
EPS = 0.00001 # 比较浮点值时使用的较小数字
def add(x, y):
"""Add two things together."""
return x + y
def test_add():
"""测试add()函数是否适用于各种数值类型。"""
assert(add(2, 3) == 5)
assert(add(-2, 3) == 1)
assert(add(-1, -1) == -2)
assert(abs(add(2.4, 0.1) - 2.5) < EPS)
```
test_add()函数的最后一行看起来比其他行更复杂,因为它比较的是浮点值。很难在计算机上比较浮点值,因为存在表示和舍入误差。然而,这是一个很好的提醒,我们总是受到我们的机器所能做的事情的限制,我们必须在分析中记住这些限制。
与软件行业相比,单元测试在科研编码中的使用要少得多,尽管科研代码更有可能包含错误(因为我们的代码通常只运行一次以生成出版物的结果,并且科研代码中的许多错误都是沉默的)。可以称为科研编码的悖论:科研编码容易出错的本质意味着我们应该使用与软件行业一样多或更多的测试,但实际上我们做的测试要少得多。这是令人遗憾的,因为现在许多科学结论都是堆积如山的代码的结果,但这些代码却没有经过充分测试。
虽然测试代码是发现、修复和防止软件错误的最好方法,但测试并不便宜。测试代码使我们的结果变得健壮,但它也占用了我们相当多的时间。对于研究人员来说,为他们编写的每一段代码编写单元测试需要花费太多的时间。科学发展迅速,在编写和执行单元测试所需的时间内,研究可能会过时或被抢发。更明智的策略是在每次编写代码时考虑三个重要变量:
此代码被其他代码调用了多少次?
如果此代码错误,对最终结果会有多大危害?
如果发生错误,错误会有多明显?
测试一段代码的重要性与前两个变量成正比,与第三个变量成反比(如果bug非常明显,就没有必要为它编写测试)。
07 将数据处理为只读
许多科学家花费大量时间使用Excel,眼都不眨一下,就会改变单元格中的值并保存结果。我强烈反对以这种方式修改数据。相反,更好的方法是将所有数据视为只读,并且只允许程序读取数据并创建新的、额外的结果文件。
为什么在生物信息学中将数据作为只读处理很重要?首先,就地修改数据很容易遇到崩溃的结果。例如,假设您编写了一个直接修改文件的脚本。在处理大型文件的过程中,脚本遇到错误并崩溃。因为您已经修改了原始文件,所以无法撤销更改并重试(除非您有备份)!本质上,此文件已损坏,无法再使用。
其次,当我们就地修改文件时,很容易忘记我们是如何更改它的。与每个步骤都有一个输入文件和一个输出文件的工作流不同,就地修改的文件不会给出我们对它所做的任何指示。如果我们忘记了我们是如何更改文件的,并且没有原始数据的备份副本,那么我们的更改基本上是不可重复的。
对于熟悉在Excel中工作的科学家来说,将数据视为只读似乎有违直觉,但这对于稳健的研究是必不可少的(并可防止灾难,并有助于重复性)。最初的困难是值得的;除了保护数据不受损坏和不正确的更改之外,它还促进了可重复性。此外,分析的任何步骤都可以很容易地重做,因为程序不会改变输入数据。
08 花时间将常用脚本开发为工具
在您作为一名高技能生物信息学家的整个开发过程中,您最终将创建一些反复使用的脚本。这些可能是从数据库下载数据的脚本,或者处理某种类型的文件,或者可能只是生成相同的漂亮图形。这些脚本可以与实验室成员共享,甚至可以跨实验室共享。您应该付出额外的努力和关注,使这些高使用率或高度共享的脚本尽可能健壮。我认为这个过程是将一次性脚本变成工具。
与脚本不同的是,工具被设计为可以一遍又一遍地运行。它们有良好的文档记录,具有显式的版本控制,具有可理解的命令行参数,并保存在共享的版本控制存储库中。
重复应用于大量数据集的脚本会影响更多结果,因此应该进行更多开发,以使它们更加健壮和用户友好。与其他研究人员共享的脚本尤其如此,这些研究人员需要能够查阅文档并将您的工具安全地应用于他们自己的数据。虽然开发工具比编写一次性脚本更费力,但从长远来看,它可以节省时间并防止头痛。
09 让数据证明它的高质量
当科学家考虑分析数据时,他们通常会考虑分析实验数据以得出生物学结论。然而,为了进行稳健的生物信息学工作,我们实际上需要分析的不仅仅是实验数据。这包括检查和分析关于实验数据质量的数据,来自生物信息学程序的中间输出文件,以及可能的模拟测试数据。这样做可以确保我们的数据处理功能如我们预期的那样运行,并体现了生物信息学的黄金法则:永远不要相信你的工具或数据。
永远不要假设数据集是高质量的,这一点很重要。相反,数据的质量应该通过探索性数据分析(称为EDA)来证明。EDA既不复杂也不耗时,并且将使您的研究对大型数据集中潜伏的惊喜更加健壮。
文章部分来源于Bioinformatics Data Skills.
·end·
超乎想象的干货分享