注意: 虽然 JavaScript 对于本网站不是必需的,但您与内容的互动将受到限制。请开启 JavaScript 以获得完整的体验。

Python 助力协同药物研发

引言

阿斯利康是全球领先的制药公司之一。公司在全球拥有超过 54,000 名员工,提供创新、有效的药物,旨在抗癌、止痛、治疗感染以及对抗心血管、中枢神经、胃肠道和呼吸系统疾病。

发现一种新药通常需要十多年时间,耗资超过 8 亿美元。在这个过程的早期,一个大问题是如何从浩瀚的潜在分子中识别出那些更有可能成为好药的候选分子。

计算化学家已经开发出许多技术来预测分子性质。这些技术可以用来评估分子在胃中(对于吞服的药片)的稳定性、在血液中传输、穿过细胞膜并最终被分解和清除的可能性,所有这些都不能对身体造成太大毒性。

如果这些计算技术足够好,就没有必要进行实际实验。但目前的计算机模型无法完全表征分子在体内的行为,也无法取代经验丰富的药物化学家的直觉。真正的分子仍然需要在实验室中进行测试,以观察它们的反应。

为了节省实验室工作的时间和金钱,实验化学家使用计算模型来缩小良好药物候选者的范围,同时验证待测试的候选者并非彼此基本化学结构的简单变体。

流程改进需求

许多药物识别工作实际上是通过分散在世界各地的许多研究小组之间的协作进行的。作为此过程的一部分,实验化学家将化合物列表发送给计算化学家,后者处理数据集并返回结果。

历史上,实验化学家不得不依赖计算化学家和其他工作人员来运行计算机预测。每种预测技术都需要运行一个单独的程序,有些是商业的,有些是由公司内部不同团队开发的,每个程序都有自己的一套输入、选项、配置和故障行为。实验化学家通常没有足够的培训来操作它们,这意味着计算化学家不得不从开发新技术的工作中抽出时间来运行常规模型。

2000 年,阿斯利康希望改进这个流程,以便实验化学家能够自行进行更好的计算预测,从而使计算化学家的研究能够更快地推进,并更快地进入实验室。

阿斯利康的首席科学家 Pierre Bruneau 在 Zeneca(后来合并成为阿斯利康)工作期间曾研究过这个问题。他开发了一个名为 H2X 的基于网络的界面,其名称来源于二战期间盟军使用的导航系统。H2X 基于一个内部分子性质计算器,名为 Drone。该系统使用一个 Perl 脚本,通过调用适当的预测程序(通常通过 Perl、csh 或领域特定控制语言编写的包装器)来计算一些更简单的分子性质。

选择 Python

使用 Drone 的 H2X 是一次成功的实验,并被许多人使用。2001 年,阿斯利康决定进一步开发它,并聘请 Andrew Dalke 作为顾问,通过使其更健壮、可扩展和可维护来改进后端代码。Andrew 是计算化学和生物学领域 Python 的知名倡导者,他 MfL 说服了团队,Python 是下一代后端(命名为 PyDrone)的合适语言。

选择 Python 来完成这项工作,是因为它是物理科学家(即没有计算机科学背景的人)可用的最佳语言之一。还有许多其他强大而富有表现力的高级语言,包括 Perl、Lisp、Scheme、Ruby、CAML 和 Haskell。在所有这些语言中,Python 是少数基于可用性研究以及使编程语言易于学习和使用的因素而设计的语言之一。然而,Python 的设计也旨在解决专业程序员面临的实际问题。结果是,这种语言可以很好地从化学家编写的小脚本扩展到软件开发人员编写的大型软件包。

Python 的错误处理提高了健壮性

PyDrone 的第一次迭代将现有的 Perl 代码重构为更合适的函数、类和模块,同时将代码库翻译成 Python。在不迁移到 Python 的情况下重构 Perl 代码会产生类似的代码架构,但 Python 明确的错误处理和更强的类型检查显著提高了代码的健壮性。

当前版本的 PyDrone 使用大约 20 种不同的外部二进制文件和脚本来预测各种分子性质。当外部程序正常工作时,输出很容易解析为所需结果。然而,这些程序并不总是正常工作,没有完整文档,而且从外部通常很难确定所有可能的故障情况。为了弥补这一点,PyDrone 开发人员编写了测试来预见尽可能多的错误情况,但在部署后,不可能排除额外的意外错误情况。

根据我们首先在 Perl (Drone) 中,然后是 Python (PyDrone) 中处理这个问题的经验,我们发现 Python 在捕获多种类型的错误以及管理已部署应用程序中的意外问题方面表现更好。这是两种语言处理错误处理方式的普遍结果。例如,Perl 的 I/O 例程是“安静的”,必须明确检查故障,通常使用“or die”习语。一个认真的程序员总是会添加这些,但它们会占用空间,容易忘记,而且难以测试。与此相反,Python 函数是“嘈杂的”,当代码出现问题时,几乎总是自动引发异常。

用 Python 重写后,我们最初认为这种“嘈杂”的行为很烦人,因为 Python 不断地引发异常并停止,而旧的 Perl 代码却一直在运行。然而,我们很快发现几乎每一个异常都表明了一个以前未检测到的错误情况,我们需要为其添加新的错误处理代码。Python 帮助我们发现问题所在,并阻止我们将静默错误带入我们的数据中。

Python 为我们发现的一个错误案例是由一个外部预测程序引起的,该程序通常会返回一个数字错误代码,但在某些情况下却返回字符串“error”。在 Perl 中,字符串和数字会自动转换,例如字符串"123"变为整数123。不幸的是,Perl 也会将非数字字符串,例如"error",转换为0。因此,Drone 将“error”视为成功的返回值,导致结果不正确。Python 更强的类型系统一旦执行导致它的罕见情况就发现了这个错误。

Python 帮助我们改进代码的另一种方式是,它在每个异常报告中都包含了完整的堆栈回溯。我们发现这对于帮助我们理解问题的根源非常有帮助,而无需运行调试器或添加额外的代码检测。Python 的这个特性在罕见情况的远程调试中特别有用。Andrew 在美国,Pierre 在法国。当发生错误时,Pierre 带有回溯的电子邮件通常包含足够的信息来查明并修复问题。

通过 Python 增加强大的可扩展性

PyDrone 发展的下一个阶段是提高其可扩展性。某些分子性质依赖于其他性质。例如,分子的质量取决于其组成。旧的 Drone 代码通过一组“if”语句手动维护这些依赖关系,这些语句指定在分析执行期间应该调用哪些预测例程以及以什么顺序调用。在这种方法中,添加新的依赖关系很快导致了组合上的噩梦。

为了在 Python 中解决这个问题,我们开发了一个简单的规则库,其行为类似于 Python 字典。它包含一个数据缓存和从属性名称到预测函数的映射。如果请求的属性名称(字典键)在缓存中,我们就重用它。否则,我们找到相关的函数,调用它来计算值,将结果存储在缓存中并返回它。函数本身被赋予对 Properties 管理器的引用,因此它们可以递归地请求任何额外需要的依赖项。要添加新的预测,我们在函数表中注册新函数——并让函数本身处理依赖项。需要缓存是因为某些属性计算成本很高或许多其他属性都需要它们。

Architecture of the Property Manager

属性管理器的架构 放大

由此产生的新架构对项目产生了简单而深远的影响。我们现在拥有一个单一的系统,可以适应所有当前和未来的预测方法,它只计算获得所需结果所需的最小量,并且易于理解和维护。在我们用 Python 构建它之前,公司中的几个人曾认为根本不可能构建这样的系统。

Python 类型系统的优势

PyDrone 架构可以用许多语言实现,但 Python 的动态类型使得构建我们的属性管理器变得容易得多。有些分子性质是数字,有些是字符串,有些是列表和字典,还有一些是类实例。静态类型语言将需要额外的麻烦才能允许混合返回类型插入到属性管理器中。即使是同样动态类型的 Perl,也需要某种方法来区分对$scalar, %hash@list的引用。在 Python 中,它只是工作,我们可以混合属性管理器字典中键的数据类型,而无需任何额外的努力。然而,如上所述,Python 同时提供了足够的数据类型检查,以发现许多常见的类型不匹配错误。

使我们的属性管理器如此成功的一个因素是 Python 允许用户定义的类型模仿内置类型的行为。我们的属性管理器很像一个将属性名称映射到值的查找表,因此我们将其设计为模仿 Python 字典。在 Python 中,这是通过实现特定的特殊方法(例如__getitem__(), __setitem__()等等)来完成的。通过模仿字典,几乎所有其他操作字典的 Python 函数都可以与我们的管理器一起工作。它还使代码更容易理解和调试,因为语法和调用点使用方式与人们的期望自然契合。

Python 还以其他方式促进了我们的属性管理器实现。PyDrone 的一个用户要求的功能是能够使用基于现有属性的方程式来描述新的预测。例如,一个方程式可能看起来像

0.34 + (0.78*CLOGP) - (0.018*HBA) - (0.015*HB_TOT) - (0.11*MM_HADCA) - (0.017*MM_QON) + (0.012*VDW_POL_AREA)

其中变量是属性管理器中的键。这在 Python 中实现起来非常容易,我们很难找到一种能让它更容易的语言。Python 的数学表达式几乎与科学中使用的标准形式相同,因此我们可以使用 Python 的“eval”语句来解析和评估用户定义的表达式。由于我们的属性管理器类似于 Python 字典,因此它可以(至少在理论上)直接提供给 eval 语句作为表达式评估期间用于变量查找的本地字典。

事实证明,出于性能原因,Python 中的eval()实现只接受内置字典类型,而不接受模拟映射类型,因此我们不得不采取一些额外的技巧,使我们的按需依赖系统与方程式一起工作。尽管如此,整个实现还是相当容易的。

结果

PyDrone 大约花费了 3 个月的开发时间,3 个月的质量保证,以及 3 周的文档编写时间,最终产生了大约 5,600 行完成的 Python 代码。

总体而言,PyDrone 对阿斯利康来说取得了巨大的成功。由于使用了 Python,我们能够快速轻松地开发出一个功能强大且易于使用、能够很好地适应新预测方法的工具。

我们使用 Python 遇到的最大问题是,阿斯利康内部很少有人将其用于开发。IT 部门更喜欢 Perl(系统人员)或 Java(架构人员),因此我们偶尔会收到将项目部分用这些语言之一重写的请求。即便如此,我们发现开发人员对学习 Python 感兴趣,特别是当他们看到开发时间与精力、最终代码大小和其他指标的比较时。

关于作者

Scott Boyer 是阿斯利康探索研发部(瑞典默恩达尔)赋能科学与技术部门的首席科学家。Scott 获得了科罗拉多大学博尔德分校的博士学位,并曾在辉瑞和阿斯利康工作,从事与建立最佳“药物样性质”相关的实验质谱和核磁共振研究。大约四年前,他转向了计算化学的“黑暗面”,现在负责内部项目,旨在为阿斯利康所有 10 个探索研究站点的台式化学家提供更多建模工具。

Andrew Dalke 是 Dalke Scientific Software, LLC 的创始人,这是一家位于美国新墨西哥州圣达菲的软件咨询和合同编程公司。Andrew 自 1992 年以来一直从事计算化学和生物学软件的开发。他的主要重点是结合可用性设计和软件工程,开发科学家既喜欢使用又乐于使用的软件工具。他如此喜欢 Python 不足为奇。

Pierre Bruneau 是阿斯利康探索部(法国兰斯)癌症和感染研究领域的首席科学家。在斯特拉斯堡国立高等化学学院学习化学后,他最初加入了 Organon R&D,随后又加入了阿斯利康(前身为 ICI Pharma,后为 Zeneca)在法国兰斯的分公司。在担任药物化学家多年后,Pierre 现在领导法国实验室的当地物理化学和计算机小组,同时保持对开发预测物理化学性质和建立潜在药物分子结构-活性关系的方法和工具的兴趣。