数据科学家的软件开发技能

本文翻译自 http://treycausey.com/software_dev_skills.html ,标题为Software development skills for data scientists》,感谢作者Trey Causey,同时也感谢@爱可可-爱生活 陈光老师的传送。当然,由于是第一次翻译,有疏漏或不妥善之处,还请小伙伴们提出纠正。

数据科学家的软件开发技能

2015年9月17日

数据科学家通常来自不同背景领域,并且很大程度上没有计算机科学或软件开发等方面的正规训练。也就是,大多数数据科学家在某些时候会由于一些已经被使用或即将投入生产环境的代码等原因,陷入与软件工程师的争论当中。他们的谈话很可能会是这样:

SE(软件工程师):“你没有进行代码审查,在代码主干中提交你的代码和测试,不是吗?”

DS(数据科学家):“没有经过什么,提交我的代码到哪里?”

事实表明,新的数据科学家们缺乏软件开发者理所应当具备的很多技能,甚至很可能有些技能他们还没听说过。我在Twitter上做了一项快速的调查,想了解这些技能有可能是哪些。接下来我会简述调查中最为普遍的回复,但我想说所有回复体现了一致的观点,那就是很多新的数据科学家们并不知道如何有效地进行合作。或许他们曾经是研究学者,一般独自工作或跟其他某个同事一起工作,或许他们是自学成才的。然而不管是什么原因,在实际环境中编写代码,在这个环境中通常别人(这个“别人”也会包括你在内,在未来的某些天里)会阅读你的代码,尝试去理解,并使用你的代码或代码具有的功能。

你可能已经习惯在你的硬盘或共享的Dropbox文件中保存你的代码。现在你的代码将会常规性地提交到代码仓库,任何人都能查看代码。这在一开始可能会引起一些不安,然后会慢慢促使你只想提交完美的代码。那通常会是一个糟糕的想法。

接下来的话题以某种方式阐述上面提到的想法。我并不想说数据科学家需要马上成为所有这些领域的专家,但在这些领域达到一定的专业水平迟早是很必要的事。你通常不会在“Introduction to Data Science in Python”或“Machine Learning in R”等书籍中找到这些主题——因为这些早已成为理所当然应当掌握的技能。

编写模块化,可重用的代码

很多数据科学家都是自学的程序员,或者通过研究项目学会编程。编程是人们为了达到特定的目标,比如估计回归问题或对行星的移动进行建模,或模拟大气条件等,而采用的一门工具。相比而言,“编程”是具有特定的规范和最佳实践的一项技能,然而编写代码是关于学会将正确的命令按正确的顺序执行并产生能以LaTeX形式的输出结果。

通常而言,不同的研究项目看起来差别很大,或者足够简单(代码级别)使得你每次都直接从零开始,或者仅仅从旧的项目当中复制和粘贴你需要的部分。你的代码通常是以某种样式进行组织,从头到尾阅读能够得到所要完成任务的思路。“首先加载数据,然后做这个步骤,接下来执行那个步骤,再输出结果,最后结束”

那样的时代已经结束。

你应当学习体会一个原则——DRY,那就是不要重复你自己。基本思想是很多任务可以抽象成一个函数,或是无关特定任务的可重用的代码片段。这从代码行数的角度会更加高效,同时也在一定程度上节省你的时间。它也可能会导致不合逻辑的极端,代码十分晦涩难以跟踪,但还是可以寻求合适的解决方法。一个好的经验法则是:当你发现自己每次编写同一行代码只需要做少量修改时,那么考虑将你的代码转换成特定的函数,将变化的部分作为函数的参数。避免在你的代码中使用硬编码。重新审视你过去写过的代码,看看你的代码是否能更为干净,更加有效,或者更为模块化和可重用。这称为重构。

当某天你被邀请提交代码以进行代码审查,这种机会通常是好的。这是很正常的事,也不意味着别人怀疑你的工作。很多公司组织要求代码在被合并到生产代码之前必须经过多人的审查。编写干净的,可重用的代码对任何人来说,都会使这个过程更加容易,同时根据代码审查,也会降低你重写关键部分代码的可能性。

扩展阅读: Chris DuBois on becoming a full-stack statistician,The Pragmatic ProgrammerClean Code

文档/注释

由于其他人会阅读你的代码,你需要学会在你的代码中添加有意义的,富含信息量的注释和文档。在你进行实际编码之前,写好注释和文档通常是一个很好的实践(虽然很可能你不会遵从)。这里等同于你写文章之前要列好文章的提纲。

[离题话:一些老练的程序员会主张在代码编写完整之后才加上注释,因为这会促使你编写清晰的和自解释的代码,你所需要添加注释之处只是一些比较含糊、不透明的场景。但作为一名刚入门的软件开发者,你可能需要忽略这个建议]

注释是不会执行的代码块,用来解释你在做什么,为什么要这样做。良好的注释使得代码的目标更为清晰,而不仅仅是重复说明代码里很明显的东西。如果你编写清晰的,样式规范的代码,那么你的函数、变量和对象等的命名应该都是相当能自解释的。

你可能听说过你应该多次在代码里添加注释。所以,你像下面这种做法添加注释:

# import packages
import pandas as pd

# load some data
df = pd.read_csv('data.csv', skiprows=2)

这些是比较差的注释。它们并没有添加任何额外的信息。为什么参数skiprows要设置为2?data.csv文件的开头有没有注释说明?诸如这样的注释可能更好一些:

# Data contains two lines of description text, skip to avoid errors.
df = pd.read_csv('data.csv', skiprows=2)

当你更新代码时同时更新你的注释,这是相当重要的。还是上面的例子,假设CSVs数据源发生了变化,不再有任何描述的行。那么你应该修改对read_csv函数的调用方式,但不要移除注释,像下面这样:

# Data contains two lines of description text, skip to avoid errors.
df = pd.read_csv('data.csv')

现在,如果任何阅读你代码的人对你的注释或代码的准确性全然不知的话,那么意味着他们必须执行代码来弄清楚,然后还要有效地为你调试你的代码,并没有人会愿意这样做。

当你编写函数的时候,写上文档字符串(用你所熟知的任何一种语言,对函数的功能等属性进行描述)清晰地表名这个函数是做什么的,需要什么类型的参数,返回什么类型的结果。

不像注释,文档通常是用英语来书写(或者你用的任何一种语言),而不是编程语言,用来解释你写的代码的目的,怎么操作,用例,提供支持的联系人,等等。这可以是一个简单的README文档,放在你代码对应的可打印给用户的完整手册的目录下面。

版本控制

在我的非正式Twitter调查中,版本控制(通常也成为源码或修改控制)是被提到最多的新的数据科学家需要学习的技能。版本控制也同样是最让人困惑的技能之一。在你之前的生活中,”版本控制“可能意味着你在你的硬盘里某个地方有保存着project.pyproject2.py,project3.pyproject3_final.pyproject3_final_do_not_delete_final_revised.py等project.py, project2.py,project3.py, project3_final.py, project3_final_do_not_delete_final_revised.py文件夹。

版本控制提供了一种从一人到多人同时在同个代码库里工作而不用在彼此的代码里进行修改的集中化方式。每人可以迁出代码的副本,然后在本地的分支上进行修改,并且可以把这些修改提交进一步合并到共同的代码库中。这里涉及到很多专有名词,但一段时间之后就开始理解。版本控制同样允许你轻松地恢复一些之前添加的误事的修改。

很多人使用git作为他们的版本控制系统,尽管你也可能遇到mercurial(缩写为bg)和subversion(缩写为svn)。这里的术语和实际的工作流有些许的差异,但基本的前提都是一样的。所有代码保存在一个或多个库中,在每个库中你可能有多个分支——即代码的不同版本。在每个库中,每个人对应的起始/引用点所在的分支称为主干分支。GitHub是一个拥有资源库(包括公有和私有)并提供一个工具集合和git进行交互的服务端工具。

在你作为数据科学家的生涯里,只会有三种必然的事:死亡,税收,还有不可避免的git问题。你将会发现自己在一次次叹息的时候输入git reset --hard和敲击回车键。那就对了。

扩展阅读:Code School.

测试

如果你没有正规的计算机科学训练,你甚至不知道当我说“测试”的时候到底我指的是什么,这有很大的可能性。我想谈论的是编写能够在各种各样的情况下检查你的代码中的错误。你编写的测试代码中最为基础的类型是单元测试。

在过去,你可能可以交互式地运行大部分代码,或者一行行地输入,或者通过写一段脚本然后将部分脚本发送到某些解析器。假设你现在处于代码在运行时,你可能还没睡醒的情形。或许你在构建一个推荐系统,你想在每个晚上产生批量的推荐以便用户隔天可以浏览。因此,你写了一段脚本在凌晨2点运行并将产生的推荐写入数据库。

当你用来产生推荐的产品列表存在错误返回很少的列,那将会发生什么?当某一列是整数型的,突然变成浮点数类型,又会怎样?你想成为第二天数据库里没有推荐信息的罪魁祸首吗?

你应该编写测试代码来描述代码执行的预期行为,还有当预期行为没有发生导致执行失败的情形。我在另一篇帖子中讨论了数据科学家的代码测试问题,因此我不会在这里太深入地探讨,但这是一个相当重要的话题。你的代码将要跟其他代码及杂乱的数据进行交互。你需要清楚知道当程序现在运行的数据跟你原始数据不同的时候,将会发生什么情况。

扩展阅读:Improve Your Python: Understanding Unit-Testing,pytestThoughtful Machine Learning

日志

在上述的场景中,你代码在凌晨2点执行,你又不能看到当(明确是 当,而不是 如果)它改变时发生了什么。为了应对这种情形,你需要记录日志。日志只是对代码执行过程中发生了什么做的记录。它包括你的程序中哪部分成功执行、哪些失败了的信息,还有你希望看到的其他诊断信息。像注释、文档和测试代码,这些是除了实际可执行代码之外的额外代码,需要你添加上去的,但记录日志完全是值得的。

当你早上去上班发现你代码出问题的时候,你直接想知道到底发生了什么错误而不是通过重新运行所有代码——同时重新运行代码也不保证能重现错误,导致程序错误的可能是某个数据出了差错,但刚好数据又因此正确了。日志能让你立刻发现问题的根源(当然,你记录日志的代码要写的很好才有这种效果),然后迅速找到如何处理这种问题的办法。

  • 举个例子,当你的日志告诉你代码因为包含产品信息的文件找不到而无法运行时,你马上知道去尝试和检查文件是否被删除了,或者网络是否被隔断了,等等。如果代码部分运行了然后在某个特定的产品或客户失败了,那么你可以去检查这些特定的数据,然后修复你代码使错误不再发生。

磁盘空间是廉价的,所以慷慨地记录日志吧。比起在大型的代码库或数据集重现一个不寻常的错误,在大量的日志记录中更容易(更快)捕获错误信息。让日志为你工作所用——比你想到的更多去记录你所需要的日志。在函数调用的时候,聪明地记录日志,记录程序执行的步骤。

扩展阅读:Logging HOWTO (Python)

结论

还有其他很多主题我并没有在文中涉及,比如:

  • 如何执行代码审查
  • 重构代码
  • 在类unix终端来操作浏览,添加你的ssh keys,搭建开发环境
  • 在分布式资源平台工作,如AWS云平台
  • 集成开发环境的选择
  • 编程范式(函数式、面向对象等)

类似本文主题的帖子通常又会涉及一长串列表的技能和语言,任何人想要掌握所有这些技能看起来几乎是不可能的事。我尽量避免这些讨论,并聚焦于能助你写出更好的代码,更好和软件工程师进行交流,同时能极大程度节约你的时间,省去你很多头痛的事等方面。你并不需要在工作的第一天掌握所有这些技能,同时某些技能在这家公司很重要,但在其他公司并没有,但是在未来的某个时间,你终将会遇到接触所有这些技能。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值