数据科学思维-全-

数据科学思维(全)

原文:Think Like a Data Scientist

译者:飞龙

协议:CC BY-NC-SA 4.0

第一部分. 准备和收集数据和知识

数据科学的过程始于准备阶段。你需要确定你所知道的,你所拥有的,你可以得到的,你目前的位置,以及你希望达到的位置。最后这一点至关重要;数据科学项目需要有一个目的和相应的目标。只有当你有明确的目标时,你才能开始调查可用的资源以及实现这些目标的所有可能性。

本书的第一部分从讨论我的以过程为导向的数据科学项目视角的章节开始。之后,我们继续进行一个重要且故意的步骤,即为项目设定良好的目标。接下来的三个章节涵盖了过程中最重要三个以数据为中心的步骤:探索、整理和评估。在这一部分的最后,你将熟悉你拥有的数据和你可以获取的相关数据。更重要的是,你会知道它是否以及如何帮助你实现项目的目标。

第一章. 数据科学的哲学

本章涵盖

  • 数据科学家的角色以及它与软件开发者的角色有何不同

  • 数据科学家最大的资产是意识,尤其是在存在重大不确定性的情况下

  • 阅读此书的前提:软件开发和统计学的基本知识

  • 在考虑大局的同时为项目设定优先级

  • 最佳实践:在项目期间可以使生活更轻松的技巧

在接下来的几页中,我将数据科学介绍为一套过程和概念,这些过程和概念作为在以数据为中心的项目中进行进步和决策的指南。这与将数据科学视为一套统计和软件工具以及使用它们的知识的观点形成对比,在我的经验中,这种观点在关于数据科学的对话和文本中更为普遍(参见图 1.1 以幽默的方式看待数据科学的观点)。我并不是说这两种观点相互矛盾;它们是互补的。但是忽视其中之一而偏爱另一个将是愚蠢的,因此在这本书中,我讨论了较少讨论的方面:实践和思想中的过程。

图 1.1. 对数据科学的某些典型观点

与木工相比,知道如何使用锤子、钻头和锯子并不等同于知道如何制作一把椅子。同样,如果你知道制作椅子的过程,这并不意味着你在使用过程中可能会用到的锤子、钻头和锯子方面有任何优势。要制作一把好椅子,你必须知道如何使用这些工具,以及具体如何一步步地使用它们。在这本书中,我试图充分讨论工具,以便建立对它们如何工作的理解,但我更关注它们何时应该使用,以及如何和为什么使用。我不断地提出并回答一个问题:接下来应该做什么?

在本章中,使用相对高级的描述和示例,我讨论了数据科学家的思维过程为什么可能比使用的具体工具更重要,以及某些概念几乎渗透到数据科学工作的所有方面。

1.1. 数据科学与本书

数据科学作为一个研究领域或职业追求的起源,介于统计学和软件开发之间。统计学可以被视为蓝图,而软件则被视为机器。数据通过这两者流动,无论是概念上还是实际上,也许只是在最近几年,从业者才开始将数据置于首位,尽管数据科学在很大程度上受益于任何将统计学和软件结合在一起的老牌领域,如运筹学、分析和决策科学。

除了统计学和软件之外,许多人说数据科学还有一个第三大组成部分,那就是类似专业知识或领域知识的东西。虽然在你尝试解决问题之前理解问题确实很重要,但一个好的数据科学家可以转换领域,并相对很快地开始做出贡献。就像一个好的会计可以快速学习新行业的财务细微差别,一个好的工程师可以掌握设计各种类型产品的具体细节一样,一个好的数据科学家可以转换到一个全新的领域,并在短时间内开始做出贡献。这并不是说领域知识没有价值,但与软件开发和统计学相比,特定领域的知识通常需要最短的时间来学习,以便足够好地帮助解决涉及数据的问题。它也是三个组成部分中可以互换的组成部分。如果你能做数据科学,你可以走进一个全新的以数据为中心的项目规划会议,房间里几乎每个人都会有你需要的领域知识,而几乎没有人会拥有编写良好分析软件的技能,这种软件能够工作。

在整本书中——也许你已经注意到了——我选择使用“以数据为中心”这个术语,而不是更流行的“以数据驱动”,来描述软件、项目和问题,因为我认为数据“驱动”任何这些概念是一个误导性的观点。只有当软件明确用于移动、存储或以其他方式处理数据时,数据才应该驱动软件。旨在解决项目或商业目标的软件不应该由数据驱动。那将是本末倒置。问题和目标独立于任何数据、软件或其他资源存在,但那些资源可以用来解决问题和实现目标。“以数据为中心”这个术语反映了数据是解决方案的一个组成部分,我相信使用它而不是“以数据驱动”承认了我们需要从目标和数据可以帮助我们解决的问题的角度来看待问题,而不是从数据的角度来看待问题。

在这本书中,关于正确视角的陈述很常见。在每一章中,我都试图让读者关注最重要的事情,在项目结果不确定的时候,我会尝试提供一些指导方针,帮助你决定哪些是最重要的事情。从某种意义上说,我认为定位并保持对项目最重要方面的关注是我在这几页中试图教授的最有价值的技能之一。数据科学家必须具备许多硬技能——其中就包括软件开发和统计学知识——但我发现,对于大多数我所知道的数据科学家来说,保持适当的视角和对任何以数据为中心的问题中众多变动部分的意识是一种非常困难但非常有价值的软技能。

有时数据质量成为一个重要问题;有时主要问题是数据量、处理速度、算法参数、结果的可解释性,或者问题的许多其他方面。在这些问题变得重要时忽视任何一点都可能损害或完全无效化后续结果。作为一名数据科学家,我的目标是确保项目的任何重要方面都不会被忽视而出现问题。当出现问题——这是不可避免的——我希望能够注意到它,以便我可以修复它。在整个章节和整本书中,我将继续强调保持对项目所有方面的意识的重要性,尤其是那些关于潜在结果不确定性的方面。

数据科学项目生命周期可以分为三个阶段,如图 1.2 图 1.2 所示。本书围绕这些阶段组织。第一部分涵盖准备阶段,强调在项目开始时花点时间和精力收集信息可以让你在以后避免大麻烦。第二部分涵盖为客户构建产品,从规划到执行,使用第一部分学到的知识以及统计学和软件可以提供的所有工具。第三部分和最后一部分涵盖完成项目:交付产品、获取反馈、进行修订、支持产品以及整洁地结束项目。在讨论每个阶段时,本书包括一些自我反思,即它经常要求你,作为读者,重新考虑你在之前步骤中做过的事情,如果看起来是个好主意,就有可能以某种其他方式重新做。到本书结束时,你可能会希望对这些思考过程和作为希望使用数据获得有价值结果的科学家在做出决策时的考虑有一个牢固的把握。

图 1.2. 数据科学过程

图 1.2

1.2. 意识到这一点很有价值

如果每次有软件开发者告诉我一个分析软件工具“不起作用”,我都会有一笔财富。这并不是说我认为所有的分析软件工具都工作得很好或根本不起作用——这绝对不是事实——但我认为这促使我们讨论数据科学家和所谓的“纯”软件开发者(通常不与原始或“未整理”的数据互动)之间最普遍的差异之一。

这种差异的一个很好的例子发生在一个初出茅庐的创业公司创始人向我提出一个问题时。任务是提取与即将到来的旅行相关的电子邮件中的姓名、地点、日期和其他关键信息,以便这些数据可以用于一个跟踪用户旅行计划的移动应用程序。创始人遇到的问题很常见:电子邮件和其他文档形式多样,大小不一,解析它们以获取有用信息是一个挑战。当来自不同航空公司、酒店、预订网站等的电子邮件格式不同时,提取这些特定的与旅行相关的数据就更加困难,更不用说这些格式经常发生变化。谷歌和其他公司似乎在自己的应用程序中提供了很好的提取此类数据的工具,但这些工具通常不会对外部开发者开放。

创始人和我都知道,通常有两大策略可以应对这个挑战:手动暴力破解和脚本编写。我们也可以将这两种方法结合起来使用。鉴于暴力破解需要为每种电子邮件格式创建一个模板,以及每次格式更改时都需要一个新的模板,我们都不想走这条路。一个能够解析任何电子邮件并提取相关信息脚本听起来很棒,但它听起来也非常复杂,几乎不可能编写。两种极端方法之间的折衷方案似乎是最好的,正如它通常所做的那样。

在与创始人和主要软件开发者交谈时,我建议他们在暴力破解和纯脚本编写之间找到一个折衷方案:为最常见的格式开发一些简单的模板,检查相似性和常见的结构模式,然后编写一个简单的脚本,该脚本可以匹配新电子邮件中熟悉的模板 HTML 或文本的块,并从这些块中提取已知位置的数据。我当时称之为算法模板化,不管好坏。这个建议显然不能完全解决问题,但它会在正确的方向上取得一些进展,更重要的是,它将揭示最常见的格式中的常见结构模式,并突出一些尚未知晓但可能容易解决的特定挑战。

软件开发人员提到,他已经开始使用一种流行的自然语言处理(NLP)工具构建解决方案,该工具能够识别和提取日期、姓名和地点。然后他说,他仍然认为 NLP 工具能够解决这个问题,并在完全实施后告诉我。我告诉他,自然语言解析和分析非常复杂,我对 NLP 工具的信心不如他那么高,但我希望他是正确的。

几周后,我又与创始人以及软件开发者进行了交谈,被告知自然语言处理(NLP)工具无法正常工作,并再次请求他们的帮助。该 NLP 工具能够识别大多数日期和地点,但,为了转述一个问题,"大多数情况下,在涉及航班预订的电子邮件中,预订日期通常出现在邮件的第一位,然后是出发日期,到达日期,然后可能是返程日期。但在某些 HTML 电子邮件格式中,预订日期出现在出发日期和到达日期之间。那么我们该怎么办呢?"

NLP 工具无法解决 100%的问题这一点是显而易见的。但它确实解决了某些中间问题,例如识别姓名和日期,即使它无法在旅行计划本身中精确地放置它们。我不想曲解开发者的原话或脱离上下文;这对数据科学家来说是一个难题,对其他人来说则是一个非常难题。第一次尝试未能解决问题几乎不能算作彻底失败。但在这个项目中,我们三个人尝试了几个星期,试图找到一个有足够时间来尝试帮助解决这个具体问题的经验丰富的数据科学家,这一部分工作因此停滞了几个星期。这样的延误对初创公司——或者任何公司来说——都是代价高昂的。

通过这样的经历,我学到的教训是,在处理涉及数据的问题时,意识极其宝贵。一个优秀的开发者使用优秀的工具来解决看似非常容易解决的问题,如果他们没有考虑到代码开始处理数据时可能发生的许多可能性,他们可能会遇到麻烦。

不确定性是冷静逻辑算法的对手,意识到这些算法在异常情况下可能会崩溃,可以加快在问题发生时解决问题的过程——它们会发生。数据科学家的主要责任是尝试想象所有可能性,解决那些重要的可能性,并在成功和失败发生时重新评估它们。这就是为什么——无论我写了多少代码——意识和对不确定性的熟悉是我作为数据科学家能提供的最有价值的东西。有些人可能会告诉你不要在工作时做白日梦,但如果你能利用它来为可能出错的事实做好准备,想象力可以成为数据科学家的最佳朋友。

1.3. 开发者与数据科学家

优秀的软件开发者(或工程师)和优秀的数据科学家有几个共同点。两者都擅长设计和构建由许多相互连接的部分组成的复杂系统;两者都熟悉构建这些系统所需的各种工具和框架;两者都擅长在系统实现之前预见潜在的问题。但一般来说,软件开发者设计由许多定义良好的组件组成的系统,而数据科学家则与至少一个组件在构建之前没有明确定义的系统打交道,而这个组件通常与数据处理或分析密切相关。

软件开发者和数据科学家的系统可以分别与数学概念中的逻辑和概率相对应。逻辑陈述“如果 A,则 B”可以很容易地用任何编程语言编码,从某种意义上说,每个计算机程序都包含大量这样的陈述,这些陈述在不同的上下文中。概率性陈述“如果 A,则可能B”则远非那么直接。任何好的以数据为中心的应用都包含许多这样的陈述——考虑一下谷歌搜索引擎(“这些可能是最相关的页面”),亚马逊网站上的产品推荐(“我们认为你可能会喜欢这些商品”),网站分析(“你的网站访客可能来自北美,每个访客大约查看*三个页面”)。

数据科学家专门从事创建依赖于关于数据和结果概率性陈述的系统。在之前提到的在电子邮件中查找旅行信息的系统案例中,我们可以做出如下陈述:“如果我们知道电子邮件包含出发日期,NLP 工具可能能够提取它。”对于一个优秀的 NLP 工具,经过一点调整,这个陈述很可能是正确的。但如果我们过于自信,在陈述中省略了可能这个词,那么这个新的陈述很可能是不正确的。它可能在某些时候是正确的,但肯定不会在所有时候都是正确的。这种将概率与确定性混淆的困惑正是大多数软件开发者在开始数据科学项目时必须克服的挑战。

当作为一个软件开发者,你从一个软件规范、文档齐全或开源代码库、产品特性要么工作要么不工作(“报告一个错误!”)的世界中来时,软件中的不确定性概念可能看起来很陌生。软件可以比作一辆汽车:粗略地说,如果你有所有正确的部件,并且以正确的方式将它们组合在一起,汽车就会工作,如果你按照手册操作,它就会带你到你想要去的地方。如果汽车不能正常工作,那么字面上来说,确实有什么东西坏了,可以修复。对我来说,这直接类似于纯软件开发。另一方面,建造一辆自动驾驶汽车在沙漠中自主赛车,则更像数据科学。我并不是说数据科学像自动驾驶沙漠赛车那样酷得离谱,而是你永远不能确定你的汽车能否到达终点线,或者任务是否可行。有如此多的未知和随机变量在起作用,以至于绝对无法保证汽车会停在何处,甚至无法保证任何汽车能够完成比赛——直到有一辆车做到了。

如果一辆自动驾驶汽车在完成 90%的路程时被暴雨冲进沟里,那么说这辆自动驾驶汽车不工作显然是不恰当的。同样,如果汽车没有真正越过终点线,而是绕过它继续行驶了 100 英里,这也是不恰当的。此外,将专为道路设计的自动驾驶轿车带入沙漠比赛,并在它卡在沙丘上时宣称汽车不工作,这也是不恰当的。这正是当有人将专为特定目的设计的以数据为中心的工具应用于不同目的时我的感受;他们得到的是不良结果,并宣称它不工作。

为了更具体的例子,假设一个网站所有者告诉你,“典型的用户在离开之前会访问我们网站的四个页面。” 假设你对网站使用的新数据集进行了分析,发现平均用户在离开之前会访问八个页面。这意味着有错误吗?当你应该使用中位数用户时,你却在使用平均值用户吗?这些新数据是否包括不同类型的用户或使用方式?这些问题通常是由数据科学家而不是软件开发者回答的,因为它们涉及数据探索和不确定性。基于这些问题及其答案实施软件解决方案当然可以受益于软件开发者的专业知识,但探索本身——必然涉及统计学——完全属于数据科学家的领域。在第五章中,我们将探讨数据评估和评估作为预防、诊断问题以及帮助避免看似完成的软件产品在某些方面失败的实用工具。

值得注意的是,尽管我似乎把数据科学家和软件开发者对立起来,但这种冲突(如果我可以称之为冲突的话)也可以存在于一个人身上。在从事数据科学项目时,我经常发现自己从数据科学家的帽子换成了软件开发者的帽子,尤其是在编写生产代码时。我把它们看作是两顶不同的帽子,是因为有时可能会有利益冲突,因为两个领域的优先级可能不同。像我在这本书中这样做,公开讨论这些冲突,可以帮助说明这些差异的解决,无论它们是发生在两个人或更多人之间,还是发生在可能戴这两顶帽子之一的个人身上。

1.4. 我需要成为一名软件开发者吗?

之前,我讨论了数据科学家和软件开发者之间的区别,常常好像只有这两个选项。当然,并非如此。而且,你不需要成为其中任何一个才能从这本书中获得收益。

掌握统计软件工具的知识是进行实际数据科学工作的先决条件,但这可以简单到只是一个常见的电子表格程序(例如,虽然存在争议但几乎无处不在的微软 Excel)。从理论上讲,一个人可以不接触电脑或其他设备就成为一名数据科学家。理解问题、数据和相关的统计方法可能就足够了,只要有人能理解你的意图并编写代码。在实践中,这种情况并不常见。

或者,你可能是一个经常与数据科学家合作的人,你希望了解这个过程,而无需 necessarily 理解技术。在这种情况下,本书中也有适合你的内容。我的主要目标之一是列举在解决以数据为中心的问题时必须考虑的许多因素。在许多情况下,我将在这本书中针对我过去和现在的一些半虚构同事进行解释:生物学家、金融高管、产品所有者、经理或其他可能给我数据并问我一个简单问题的人:“你能为我分析这个吗?”对于最后一个案例,也许如果我在这里详细地写下来,并附上大量的例子,我就不必(再次)重复说这并不那么简单。分析需要一个问题,我将在这些页面上深入讨论这两个问题。

本书是关于思考和实践数据科学的过程,但显然软件不能被忽视。软件——作为一个行业及其产品——是数据科学家的工具箱。工艺的工具是超越人类心智和身体能力的工作的推动者。但在本书中,我将只涵盖必要的软件,以便探索现有软件工具的优势和局限性,并提供它们使用的具体例子以供说明。否则,我将尝试以抽象的方式撰写关于软件的内容——而不失实用性——以便尽可能多的人能够理解,无论是技术人员还是非技术人员,而且多年以后,这些解释可能仍然有价值,即使我们已经开始转向更新(和更好?)的软件语言和产品。

1.5. 我需要了解统计学吗?

与软件一样,对统计学的专业知识当然有帮助,但并非必要。本质上,我是一个数学家和统计学家,因此我可能会在这些领域过于技术性地偏离主题。但我比大多数人更讨厌行话和假设知识,所以我将尽力包括对统计概念的易于理解的概念解释;希望这些解释对任何有想象力且坚持不懈的读者来说都是足够的。在我不足之处,我会尽力引导你到一些提供更详尽解释的资源。一如既往,我倡导使用网络搜索来查找你感兴趣主题的更多信息,但在某些情况下,在深入统计学网页的兔子洞之前,至少先跟我读几页可能更好。

同时,为了在概念上帮助你入门,请考虑统计学领域作为你每天遇到的数据生成过程的理论体现。一个匿名的网站用户是一个随机变量,他们可能会根据他们头脑中的情况点击任何数量的事物。社交媒体数据反映了公众的思想和担忧。消费品购买既取决于消费者的需求,也取决于商品的市场营销活动。在这些情况下,你必须理论化无形的思想、需求和反应最终如何转化为可测量的行动,从而创建数据。统计学为此理论化提供了一个框架。本书将花较少的时间在统计模型的复杂理论依据上,而更多地关注制定数据生成过程的心理模型,并将这些心理模型转化为统计术语、方程式,最终是代码。

1.6. 优先级:知识优先,技术其次,意见第三

这个章节标题是我的一个格言。我使用它来帮助解决数据科学项目中各种关注点之间永无止境的斗争——例如,软件与统计学、不断变化的企业需求与项目时间表、数据质量与结果准确性。随着项目的进展,每个关注点都会对其他关注点施加推拉,每当两个关注点在行动方案上意见不一致时,我们就被迫做出选择。我为此开发了一个简单的框架来帮助处理这些问题。

知识、技术和意见通常是任何项目开始时你所拥有的东西;它们是将数据转化为答案的三个要素。“知识”是你确信无疑知道的东西。“技术”是你可用的工具集。“意见”是你想要考虑为真的小事实,但还不完全确定。为你的思维过程建立层次结构很重要,这样不那么重要的事情就不会因为它们更容易或更受欢迎,或者因为有人有预感而压倒更重要的事情。

在实践中,层次结构看起来是这样的:

  • 知识第一—— 在做任何事情之前,了解你的问题、你的数据、你的方法和你的目标,并将这些放在首要位置。

  • 技术第二—— 软件是为你服务的工具。它既使你能够,也限制了你的能力。除非在特殊情况之下,它不应该规定你解决问题的方法。

  • 观点第三—— 意见、直觉和愿望都是作为指向可以证明正确的理论的指南来使用的,而不应该成为任何项目的焦点。

我并不是在提倡在每一个决策中知识都应该优先于技术——同样,技术也应该优先于观点——但如果要颠倒这个顺序,你应该是有意为之,并且有非常好的理由。例如,假设你拥有大量数据和一个你希望估计参数的统计模型。此外,你已经有一个工具可以将这些数据加载到执行一种称为最大似然估计(MLE)的统计参数优化的系统中。你知道你的数据和统计模型可能足够复杂,可以生成许多合理的参数值,因此使用 MLE 来找到一个最可能的参数值可能会产生不可预测的结果。存在更稳健的替代方案,但你目前还没有实现。你有两个选择:

  1. 构建一个能够进行稳健参数估计的新工具。

  2. 使用你已有的工具来进行最大似然估计(MLE)。

你的知识告诉你应该做 A,如果你有无限的时间和资源,但技术表明你应该做 B,因为 A 需要巨大的资源投入。务实的选择可能是 B,但这颠倒了顺序。如前所述,你可以这样做,但只能是有意为之,并且有非常好的理由。这个好理由可能是你在 A 和 B 上需要花费的时间和金钱的差异。通过有意为之,我的意思是说你不应该轻率地做出决定,也不应该忘记它。如果你选择 B,你应该将任何结果与你在质量上做出的牺牲(为了节省成本)的知识一起传递,并在文档和技术报告中注明这一点。你应该适当地加强质量控制,并执行专门检查 MLE 容易出现的优化错误/偏差的测试。做出决定然后忘记推理是导致令人失望或误导性结果的道路。

观点带来的挑战更加模糊。有时人们会被找到真正惊人结果的潜力所蒙蔽,忘记考虑如果这些结果在数据中不明显会发生什么。在大数据繁荣时期,无数软件初创公司试图利用社交媒体——尤其是 Twitter 及其“消防水龙”——来确定各个商业市场的趋势,他们常常遇到比预期更大的障碍。计算和数据的规模、在仅 140 个字符内解析自然语言,以及在混乱数据上推断随机变量都常常涉及其中。只有这些公司中的佼佼者才能从数据中提取有意义的、重要的知识并从中获利。其余的被迫放弃或改变他们的重点。每个这些初创公司,在某个时候,都必须决定他们是否愿意花费更多的时间和金钱去追逐一个主要基于希望而不是基于证据的目标。我相信他们中的许多人当他们决定放弃时,一定会后悔他们走了多远以及他们花了多少钱。

人们常常被他们认为可能的事情所蒙蔽,忘记考虑它可能不可能实现,或者它可能比预期的要昂贵得多。这些都是观点——猜测——而不是知识,它们不应当在数据分析产品开发中扮演主要角色。目标并不一定能够实现,但任何项目都需要目标,因此,不要理所当然地认为目标及其可实现性是必要的。你应该始终首先考虑当前的知识,并寻求逐步扩展这一知识,直到你实现目标或被迫放弃它。我曾提到这种可实现性的不确定性是软件开发和数据科学哲学之间一个特别鲜明的差异。在数据科学中,目标不太可能以原始形式实现。在一个充满软件开发人员或缺乏经验的数据科学家的房间里,特别要警惕那些没有证据就预设目标 100%可实现的人。

记住:先知识,再技术,最后是观点。这不是一个完美的框架,但我发现它很有帮助。

1.7. 最佳实践

在我作为应用数学家、博士研究生、生物信息学软件工程师、数据科学家或我所拥有的其他任何头衔的这些年里,我遇到了一些涉及我自身项目管理不善的问题。当我独自工作多年,我的研究项目无人触及或查看时,我经常设法将文档不充分的代码放置很长时间,以至于忘记了它是如何工作的。我也忘记了哪个版本的结果是最新的。我在离开那个职位后,几乎让任何人都无法接手我的项目。这一切都不是故意的;这主要是疏忽,也是对人们确保项目材料和代码能够在书架上或他人手中长期存活的通常方式的忽视。

当在一个团队中工作时——特别是经验丰富的软件开发团队——通常有人已经建立了一套关于文档和项目材料及代码保存的最佳实践。通常,确保团队中的每个人都遵守这些策略是很重要的,但在没有团队策略的情况下,或者如果你是独立工作,你可以做一些事情来使你的数据科学家生涯在长远来看更加轻松。以下小节涵盖了保持组织的一些建议。

1.7.1. 文档

你能想象如果你的同伴在你突然离开时接管你的项目,他们可能需要经历什么吗?接管你的项目会是一个糟糕的经历吗?如果你回答是,请为这些未来的同伴——以及你自己——行个方便,保持你的文档更新。以下是一些建议:

  • 对你的代码进行注释,以便不熟悉你工作的人可以理解代码的功能。

  • 对于一个完成的软件作品——即使是简单的脚本——写一个简短的说明,说明如何使用它,并将其放在与代码相同的地方(例如,相同的文件文件夹)。

  • 确保所有东西——文件、文件夹、文档等——都有一个有意义的名称。

1.7.2. 代码仓库和版本控制

一些软件产品是专门为了包含和管理软件源代码而构建的。这些被称为源代码仓库(或repos),它们可以从多个方面极大地帮助你。

首先,大多数现代仓库都是基于版本控制系统的,这也是很好的。版本控制系统跟踪你在代码中做出的更改,允许你创建和比较代码的不同版本,一旦你习惯了,通常会使编写和修改代码的过程变得更加愉快。仓库和版本控制的缺点是它们需要时间来学习和融入你的正常工作流程。然而,两者都值得花费时间。在撰写本文时,Bitbucket.orgGitHub.com 都提供免费的代码仓库网络托管服务,尽管这两个网站都托管了公共和私有仓库,所以请确保你不会不小心将所有源代码公开。Git 是目前最受欢迎的版本控制系统,并且很好地融入了上述提到的仓库托管服务。你可以在托管者的网页上找到如何开始的教程。

我发现远程仓库托管服务几乎不可或缺的另一个原因是它充当备份的作用。即使我的电脑不小心被一辆自主沙漠赛车压碎(尽管这还没有发生在我身上),我的代码也会安全。我养成了几乎每天都将最新的代码更改推送(发送或上传)到远程主机的习惯,或者大约像在标准数据备份服务上安排自动备份那样频繁。

一些代码托管服务提供了查看代码历史、各种版本、开发状态等功能出色的网页界面,这已成为团队合作和大型项目的标准实践。对于个人和小型项目来说,这也很有帮助,尤其是在回到一个旧项目或试图弄清楚自上次特定时间以来你做了哪些更改时。

最后,远程仓库允许你从任何有网络访问的地方访问你的代码。你不需要一台装有适当编辑器和环境的电脑来浏览代码。不,你通常不能做很多除了浏览代码(也许还能进行简单的编辑)之外的事情,但最好的那些有良好的语言特定代码高亮和其他一些使浏览变得容易和有用的功能。

这里有一些关于仓库和版本控制的提示:

  • 使用远程源代码仓库现在是大多数编写代码的团队的常规做法;使用一个!

  • 学习 Git 或其他版本控制系统绝对值得付出努力;学习它!

  • 经常将代码更改提交到仓库,可能是每天或每当完成一个特定任务时。将这些更改推送到远程仓库,以便它们得到备份并与团队中的其他人共享。

  • 如果你的下一次代码更改会破坏某些东西,请在不会影响生产版本或其他团队成员开发的地方进行工作。例如,你可以在 Git 中创建一个新的分支。

  • 使用版本控制、分支和分叉(互联网上有很多教程)而不是从一处复制粘贴代码到另一处,因此需要在多个地方维护/修改相同的代码。

  • 大多数软件团队都有一个 Git 大师。每当你有关于最佳实践的问题时,就向他们请教;现在花在学习上的时间最终会得到回报。

1.7.3. 代码组织

许多书籍详细介绍了良好的编码实践,我并不打算在这里复制或取代它们。但一些指南可能非常有帮助,尤其是在你尝试共享或重用代码时。大多数有一定编程经验的人都会熟悉这些,但我发现许多人——尤其是在学术和其他没有太多编码协作的工作环境中——并不总是了解它们或遵守它们。

这里有一些指南:

  • 尽量使用特定编程语言的常见编码模式。例如,Python、R 和 Java 在开发者通常组织代码的方式上都有显著差异。任何流行的语言资源都包含此类编码模式的示例和指南。

  • 为变量和其他对象使用有意义的名称。这使得你的代码对新合作者和未来的你来说更容易理解。

  • 使用大量的信息性注释,正如前面提到的理由。

  • 不要复制粘贴代码。在两个地方都有相同的代码意味着当你想要更改它时,你需要做的工作量加倍。将这段代码封装在函数、方法、对象或库中,确保后续的修改只在一个地方发生。

  • 尝试以具有特定功能的方式编写代码块。对于脚本,这意味着拥有注释良好的代码片段(可选地放在单独的文件或库中),每个片段都完成特定的任务。对于应用程序,这意味着拥有相对简单的函数、对象和方法,以便每个都能完成特定的事情。一个很好的通用规则是,如果你不能以某种方式命名你的代码片段、函数或方法,使得名称通常描述了代码块所完成的一切,那么你可能需要将代码块拆分成更小、更简单的代码块。

  • 不要过早优化。这是程序员普遍遵循的一个常见格言。确保你的算法代码逻辑清晰、连贯,只有在你发现你的实现效率低下时,才尝试减少计算周期和内存字节。

  • 假设将来会有人加入你的项目。问问自己,“他们能读懂我的代码并弄清楚它在做什么吗?”如果不能,那么尽早花点时间组织和注释,而不是拖到后来。

1.7.4. 提问

这可能听起来很显然或微不足道,但它如此重要,以至于我把它包括在这里。我已经讨论过,意识是优秀数据科学家最大的优势之一;同样,不愿意通过任何和所有方式获得意识可能是一个巨大的弱点。内向的学术人士因为害羞而不愿意寻求帮助的刻板印象你可能很熟悉,但你听说过那种自以为是的博士数据科学家,因为太骄傲而不承认自己不知道某些事情吗?我确实听说过。如今,骄傲可能比害羞是更大的陷阱,但你应该对两者都保持警惕。

软件工程师、商业策略师、销售主管、市场营销人员、研究人员以及其他数据科学家,他们对各自领域或项目了解的都比你要多,因为害羞、骄傲或其他任何原因而忽视他们所拥有的丰富知识,这将是件遗憾的事情。在一个几乎每个人都扮演着与你不同角色的商业环境中,你有很多机会了解公司和行业。这就是我之前提到的主题专业知识或领域知识。根据我的经验,非技术性的商业人士往往倾向于把你,数据科学家,看作是谈话中的智者,但不要忘记他们往往比你更了解项目目标和商业问题,这两个概念极其重要。不要犹豫与了解项目商业面和你在处理的问题的人进行长时间的讨论。我经常发现这样的对话以新的方式照亮了项目,有时导致策略的改变,但总是有助于我成功完成项目所需的领域知识。

1.7.5. 紧密关注数据

“紧密关注数据”意味着确保你应用到的数据方法和算法不会过于复杂以至于掩盖了数据本身。另一种表述方式可能是“不要使用比必需更复杂的方法,并且始终意识到出错的可能性。”

许多人会反对这条建议,我同意这些批评者,许多复杂的方法已经证明了自己的价值。机器学习领域就是这样一个方法来源的完美例子。在复杂方法(在某些情况下称为“黑盒”方法)能带来相当大的优势的情况下,接近数据的概念可以适应:确保复杂方法的一些结果可以通过接近数据的一些简单方法来验证、证明或支持。这可能包括一种夸张的抽查形式,你可以随机选择一些结果,提取与这些结果相关的原始数据,并使用逻辑和/或简单的统计方法来确保结果在直观上是有意义的。在没有安全线的情况下远离数据可能会让你陷入麻烦,因为这些问题是诊断起来最困难的。在整个书中,以及在每个例子中,我都会回到这个概念,并给出更具体的说明。

1.8. 阅读这本书:我是如何讨论概念的

我的经验——一次又一次地——是,复杂的概念在你开始了解它们的时候看起来比后来理解它们的时候要复杂得多。这不仅是因为一旦你开始理解它们,所有概念看起来都变得不那么复杂,而且也因为人们,总的来说,他们可能正在向你解释一个概念或正在写关于它的内容,他们沉醉于他们所理解的复杂性,而对于那些不理解的人来说,他们往往缺乏耐心或同情。例如,大多数统计学教授很难向一个外行人解释一个简单的统计测试。我发现,这种无法用简单术语解释的能力在所有领域和行业中都存在。这个问题的部分原因在于,大多数人喜欢使用行话,并且喜欢证明他们对这些行话的了解。这可能也许是人类本性的一个缺陷。但从此我学会了在学习一个新、复杂的概念的初期忽略我的挫败感,并且不要在了解整个概念之前纠结于任何具体点。

在整本书中,我试图用简单的术语解释新概念,然后再深入具体细节。这是我偏好的学习方式和解释方式。尽管如此,你仍然会遇到一些难以理解的时刻。我恳请你不要气馁,坚持看到章节的结尾,然后整体回顾这个概念。在那个时刻,如果仍有不明白的地方,也许重新阅读有疑问的段落会有所帮助,如果还不行,请随时查阅其他资源。我使用概念性思维过程,并打算在分析部分之前先关注整体。提前了解这一点可能会在阅读时对你有所帮助。

在下一章以及更深入地探讨数据科学过程的具体步骤之前,我想指出,这本书旨在让数据科学、软件和统计学非专业人士也能轻松阅读。如果你不是初学者,你可能会偶尔发现一些涵盖你已经了解的内容的部分。我希望这本书的每一部分都能为读者提供有用的甚至新颖的视角,即使是对专家来说也是如此;但如果你时间紧迫,请随意跳到对你更有用的部分。另一方面,我建议在首次阅读这本书时不要跳过整个章节。每一章都描述了数据科学过程中的一个步骤,跳过任何一个都可能会打断过程的连续性和展示方式。与其跳过,不如至少阅读每一章的开头和结尾,并逐节浏览,以便为后续章节获得重要背景信息。

作为最后的声明,我在整本书中引用了许多来自我自己的经验实例。我的早期、形成性的数据科学项目主要集中在生物信息学领域,因此有时我会深入讨论遗传学、RNA 和其他生物学概念。这可能对一些人来说显得有些复杂,但没关系。只要你能理解数据导向的方面,如项目目标、数据来源和统计分析,就不需要了解生物学。这也适用于其他领域的例子,但我特别依赖生物信息学,因为我还能清楚地记得在我职业生涯早期遇到各种数据科学挑战时的感受。此外,我不会回避使用高度具体的例子,因为对于数据科学家来说,学习新领域的基础知识并尝试将经验和知识应用于其中是一个很好的实践。然而,我会尽量以任何人都能够理解的方式呈现所有例子。

摘要

  • 意识力可能是优秀数据科学家的最大优势,尤其是在面对不确定性时。

  • 应对不确定性通常是区分数据科学家角色和软件开发者角色的关键。

  • 使用一个有助于确定优先级并平衡限制和要求的有用框架。

  • 使用我建议的一些最佳实践可以让你以后避免头痛。

  • 我是一个喜欢在深入实际例子之前先抽象地谈论事物的概念学习者兼教师,所以请记住这一点,贯穿整本书。

第二章:通过提出好问题来设定目标

本章涵盖

  • 站在客户的立场上思考

  • 向数据提出具体、有用的问题

  • 理解数据在回答这些问题时的优势和局限性

  • 将这些问题和答案与项目目标联系起来

  • 从期望的目标逆向规划,而不是从数据和软件工具开始向前规划

图 2.1 展示了我们在数据科学过程中的位置:设定目标,这是准备阶段的第一步。在数据科学项目中,就像在许多其他领域一样,主要目标应该在项目开始时设定。在设定目标之后,你所做的一切工作都是利用数据、统计学和编程来朝着并实现这些目标。本章强调了这一初始阶段的重要性,并提供了一些关于如何以有用方式制定和陈述目标的指导。

图 2.1. 数据科学过程准备阶段的第一步:设定目标

2.1. 倾听客户

数据科学中的每个项目都有一个客户。有时客户是支付你或你的公司进行项目的人——例如,客户或承包机构。在学术界,客户可能是一位要求你分析他们数据的实验室科学家。有时客户是你、你的老板或另一位同事。无论客户是谁,他们对你,即被分配项目的数据科学家,都有一些期望。通常,这些期望与以下内容相关:

  • 需要回答的问题或需要解决的问题

  • 一个有形的最终产品,例如报告或软件应用

  • 前期研究或相关项目及产品的摘要

期望可以来自几乎任何地方。有些是希望和梦想,而另一些则是从经验或对类似项目的知识中汲取而来。但典型的期望讨论归结为两个方面:客户想要什么与数据科学家认为可能什么。这可以描述为愿望与实用主义,客户描述他们的愿望,而数据科学家根据明显的可行性予以批准、拒绝或限定每一个。另一方面,如果你愿意将自己视为数据科学家,一个愿望的授予者,那么你不会是唯一这样做的人!

2.1.1. 解决愿望与实用主义

关于客户的愿望,它们可以从完全合理到完全荒谬,这是可以接受的。商业发展和硬科学的大部分内容都是由直觉驱动的。CEO、生物学家、营销人员和物理学家都使用他们的经验和知识来发展关于世界如何运作的理论。其中一些理论有可靠的数据和分析支持,但其他理论则更多地来自直觉,这是个人在广泛从事其领域工作时发展起来的概念框架。许多领域与数据科学的一个显著区别是,在数据科学中,如果客户有一个愿望,即使是经验丰富的数据科学家也可能不知道这是否可行。而软件工程师通常知道软件工具能够执行的任务,生物学家也大致知道实验室能够做什么,但尚未见过或处理过相关数据的数据科学家面临着大量的不确定性,主要关于具体可用的数据以及它能够提供多少证据来回答任何给定的问题。不确定性再次是数据科学过程中的一个主要因素,在谈论客户愿望时应该始终将其放在首位。

例如,在我与生物学家和基因表达数据共事的几年里,我开始形成自己关于 RNA 如何从 DNA 翻译以及 RNA 链如何在细胞中漂浮并与其他分子相互作用的观念。我是一个视觉型的人,所以我经常想象一条由数百或甚至数千个核苷酸组成的 RNA 链,每个核苷酸看起来像代表一种碱基化合物的四个字母之一(A、C、G 或 T;为了方便,我将用T代替U)并且整个链看起来像一条长而灵活的链,只有细胞内部的机器才能理解这个句子。由于 RNA 及其核苷酸的化学性质,互补序列倾向于相互结合;A 喜欢与 T 结合,C 喜欢与 G 结合。当两条 RNA 链含有近互补序列时,它们很可能粘在一起。如果一条 RNA 链足够灵活并且包含相互互补的序列,它也可能折叠并粘附在自己身上。我已经多次使用这个概念框架来猜测当许多 RNA 在细胞中漂浮时可能发生的事情。

当我开始处理 microRNA 数据时,对我来说,microRNA——大约由 20 个核苷酸组成的短序列——可能绑定到基因 mRNA 序列的一部分(这是直接从对应特定基因的 DNA 链(通常要长得多)翻译过来的 RNA),从而阻止其他分子与基因的 mRNA 相互作用,实际上使该基因序列变得无用。对我来说,一个 RNA 片段可以粘附到遗传 RNA 的一部分,并最终阻止另一个分子粘附到同一部分,这个概念在概念上是合理的。这一概念得到了科学期刊文章和硬数据的支持,这些数据表明,如果它们有互补序列,microRNA 可以抑制遗传 mRNA 的表达或功能。

我曾与一位生物学家教授合作,他有一个更加细腻的概念框架,描述了他如何看待基因、microRNA 和 mRNA 这个系统。特别是,他几十年来一直在研究小鼠的生物学——一种常见的小鼠——并能列出任何数量的显著基因、它们的功能、相关基因和物理系统,以及如果开始进行敲除这些基因的实验,会受到可测量的影响的特征。因为这位教授对小鼠遗传学的了解比我多得多,而且他不可能与我分享他所有的知识,所以在我们投入太多时间在项目的任何方面之前,讨论项目的目标和期望非常重要。没有他的意见,我就会猜测生物相关的目标是什么。如果我是错的,这很可能是,那么这项工作就会白费。例如,某些特定的 microRNA 已经被充分研究,并且已知它们在细胞内完成基本功能,而几乎没有其他功能。如果项目的目标之一是发现研究较少的 microRNA 的新功能,我们可能会希望排除某些 microRNA 家族的分析。如果我们不排除它们,它们很可能会增加细胞内已经非常嘈杂的遗传对话的噪音。这只是教授知道而我不知道的大量重要事情之一,因此在认真开始项目之前,进行关于目标、期望和警告的长时间讨论是必要的。

在某种意义上,这是一种非常普遍的看法,一个项目只有在客户对结果满意的情况下才能被认为是成功的。虽然有一些例外,但无论如何,在数据科学项目的每个步骤中始终牢记期望和目标是很重要的。不幸的是,根据我的经验,期望通常在项目一开始时并不清晰或明显,或者它们不容易简洁地表达。我已经形成了一些习惯,这些习惯帮助我确定合理的目标,这些目标可以指导我完成涉及数据科学项目的每个步骤。

2.1.2. 客户可能不是数据科学家

关于客户期望的一个有趣之处在于,它们可能并不合适。这并不总是——甚至通常不是——客户的错,因为数据科学所解决的问题本质上是复杂的,如果客户完全理解他们自己的问题,他们可能不需要数据科学家来帮助他们。这就是为什么当客户在语言或理解上不清楚时,我总是给他们一些宽容,并将设定期望和目标的过程视为一种类似冲突解决或关系治疗的联合练习。

你——数据科学家——和客户都对项目成功完成有着共同的兴趣,但你们两个可能有着不同的具体动机、不同的技能,最重要的是,不同的视角。即使你是客户,你也可以将自己视为有两个部分,一个(数据科学家)专注于获得结果,另一个(客户)专注于利用这些结果做些实际的事情,或者超出项目本身的事情。这样,数据科学项目就从寻找两个性格、两个视角之间的共识开始,即使它们并不冲突,至少也是不同的。

虽然严格来说,你和客户之间没有冲突,但有时在你们都朝着一些看似可实现(对数据科学家来说)且有益(对客户来说)的目标摸索的过程中,可能会感觉像是那样。而且,正如在冲突解决和关系治疗中一样,情感是存在的。这些情感可能是意识形态的,由个人经验、偏好或意见驱动,可能对另一方来说没有意义。一点耐心和理解,不要过多评判,对你们两个人,更重要的是对项目,都极为有益。

2.1.3. 提出具体问题以揭示事实,而非意见

当客户在描述你即将调查的系统中的理论或假设时,他们几乎肯定是在表达事实和观点的混合体,区分这两者可能很重要。例如,在关于小鼠癌症发展的研究中,生物教授告诉我,“众所周知哪些基因与癌症相关,这项研究只关注那些基因以及抑制它们的 microRNAs。”有人可能会倾向于直接接受这个陈述并仅分析与癌症相关基因的数据,但这可能是一个错误,因为陈述中存在一些模糊性。原则上,不清楚其他所谓的非癌症相关基因是否可以参与由实验引发的复杂反应的辅助角色,或者是否众所周知且已证明癌症相关基因的表达完全独立于其他基因。在前一种情况下,忽略与非癌症相关基因对应的数据可能不是一个好主意,而在后一种情况下,可能是一个好主意。如果不解决这个问题,就不知道哪种选择是合适的。因此,询问是很重要的。

同样重要的是,问题本身要以客户理解的方式提出。例如,问“我应该忽略非癌症相关基因的数据吗?”是不明智的。这是一个关于特定案例中数据科学实践的问题,属于你的领域,而不是生物学家。你更应该问类似的问题,比如,“你是否有任何证据表明癌症相关基因的表达通常独立于其他基因?”这是一个关于生物学的问题,希望生物教授能够理解。

在他的回答中,区分他认为的和已知的是很重要的。如果教授只是认为这些基因的表达与其他基因无关,那么在整个项目中保持这一点当然是有意义的,但你不应该基于此做出任何重要决定——例如忽略某些数据。另一方面,如果教授可以引用支持他主张的科学研究,那么建议使用这个事实来做出决定。

在任何项目中,您作为数据科学家,是统计学和软件工具的专家,但主要领域的专家通常不是您,就像涉及生物教授的案例一样。在向这个领域的专家学习时,您应该提出一些问题,这些问题能给您一些关于正在研究的系统如何工作的直观感觉,以及一些试图区分事实、观点和直觉的问题。基于事实做出实际决策总是好主意,但基于观点则可能很危险。这里适用的格言是“信任但核实”。如果我忽略了数据集中的任何基因,我可能真的会错过癌症实验中各种 RNA 之间复杂相互作用的关键方面。结果证明,癌症在遗传水平上以及医学水平上都是一个非常复杂的疾病。

2.1.4. 提出交付成果:猜测并检查

您的客户可能并不了解数据科学及其所能带来的功能。询问他们“您希望在最终报告中看到什么?”或者“这个分析应用应该做什么?”很可能会得到“我不知道”或者更糟糕的,一个不合理的建议。数据科学不是他们的专业领域,他们可能并不完全清楚软件和数据的可能性和局限性。通常最好通过一系列建议来提出关于最终产品的问题,然后记录客户的反应。

我最喜欢问客户的问题之一是:“您能给我一个例子,说明您希望在最终报告中看到的句子吗?”我可能会得到这样的回答:“我想看到类似这样的句子,比如‘MicroRNA-X 似乎显著抑制基因 Y’”,或者‘基因 Y 和基因 Z 似乎在所有测试样本中表达水平相同’。”这样的回答为构思最终产品的格式提供了一个很好的起点。如果客户能提供这样的种子想法,您就可以在此基础上提出最终产品的建议。然后您可能会问:“如果我给您一个表格,显示特定 miRNA 和遗传 mRNA 之间最强的相互作用,会怎样?”也许客户会认为这很有价值——也许不会。

然而,客户可能做出更不明确的陈述,例如:“我想知道哪些 miRNA 在癌症发展中很重要。”为了成功完成项目,您需要对此进行澄清。在生物学意义上,“重要”是什么意思?这种重要性如何在现有数据中体现?在继续之前,得到这些问题的答案至关重要;如果您不知道 miRNA 的重要性如何在数据中体现,您如何知道何时找到了它?

许多人和我偶尔犯的一个错误是将相关性等同于显著性。有些人谈论相关性混淆因果关系;以下是一个例子:戴头盔的自行车事故发生率高于不戴头盔的自行车。可能会诱使人们得出结论说头盔导致事故,但这可能是错误的。头盔和事故之间的相关性并不意味着头盔导致事故;也不意味着事故导致人们戴头盔(直接)。实际上,在更繁忙、更危险的路上骑行的自行车手更有可能戴头盔,也更有可能发生事故。在更危险的路上骑行这一行为同时导致了这两者。在头盔和事故的问题上,尽管存在相关性,但没有直接的因果关系。因果关系反过来又是一个相关性可能重要的例子。如果你正在进行关于头盔使用和事故率的调查,那么即使没有因果关系,这种相关性也可能很重要。我必须强调,我所说的显著性是由项目目标决定的。关于头盔-事故相关性的知识可能导致考虑(并模拟)每个道路的交通和危险水平作为项目的一部分。显著性也不一定由相关性保证。我相当确信更多的自行车事故发生在晴朗的日子里,但这是因为在晴朗的日子里路上有更多的自行车手,而不是因为任何其他显著的关系(除了雨天)。我并不清楚如何利用这些信息进一步实现我的目标,因此我不会花太多时间探索它。在这种情况下,相关性似乎没有任何意义。

在基因/RNA 表达实验中,仅在 10-20 个生物样本中就测量了成千上万的 RNA 序列。这种分析变量(每个 RNA 序列或基因的表达水平)远多于数据点(样本)的情况被称为高维或通常称为欠确定,因为变量太多,其中一些可能只是随机机会相关联,因此说它们在真实生物学意义上相关是错误的。如果你向生物学教授展示一系列与生物学相关的强相关性,他立刻会指出你报告的一些相关性并不重要,或者更糟糕的是,与既定研究相悖,你将不得不回去做更多分析。

2.1.5. 基于知识而非愿望迭代你的想法

在你获得的专业知识领域内,区分事实和观点很重要,同样重要的是避免过度乐观使你忽视障碍和困难。我长期以来认为,优秀数据科学家的一项宝贵技能是预见潜在困难并留出绕过它们的路径。

在当今软件行业,在分析功能仍在开发中时宣称其能力是很常见的。据我所知,这是一种销售策略,对于年轻的初创公司来说,在竞争激烈的市场中取得领先似乎是一种必要手段。当我与一家初创公司合作时,如果我的同事正在积极推销一款我声称可以构建但并不完全确定能否按计划工作的分析软件,这总是让我感到紧张,因为考虑到我所拥有的数据的某些限制。当我关于一个假设产品做出大胆声明时,我会尽可能地将其保持在几乎可以确定我能做到的事情的范围内。如果我不能做到,我会尝试制定一个不涉及原始计划中最复杂部分的备用计划。

假设你想开发一个总结新闻文章的应用程序。你需要创建一个算法,该算法可以解析文章中的句子和段落并提取主要观点。编写这样的算法是可能的,但尚不清楚其性能如何。总结可能在某些意义上对大多数文章都有效,但 51%的成功率和 99%的成功率之间有很大的差距,而且你只有在至少构建了一个版本之后才能知道你的特定算法在这个范围内处于什么位置。盲目地推销和狂热地开发这个算法可能看起来是最好的主意;辛勤的工作会得到回报,对吧?也许吧。这项任务很难。完全有可能,尽管你尽力了,但你永远无法超过 75%的成功率,而且从商业角度来看,这可能还不够好。那么你该怎么办?你是放弃并关闭店铺吗?你是在这次失败之后才开始寻找替代方案的吗?

良好的数据科学家在开始之前就知道一项任务有多难。句子和段落是复杂的、随机的变量,它们往往似乎专门设计来挫败你可能会向它们投掷的任何算法。在失败的情况下,我总是回到第一原理,从某种意义上说。我会问自己:我试图解决什么问题?除了总结之外,最终目标是什么?

如果目标是构建一个使阅读新闻更高效的产品,也许有另一种方法来解决效率低下的新闻读者的难题。也许更容易的做法是将类似的文章聚合在一起并一起呈现给读者。也许可以通过更友好的设计或通过整合社交媒体来设计一个更好的新闻阅读器。

没有人愿意宣布失败,但数据科学是一个充满风险的业务,假装失败从未发生本身就是一种失败。总有多种方法可以解决问题,制定一个承认可能遇到障碍和失败的计划,可以让你在实现主要目标之前,从微小的成功中获得价值。

一个更大的错误是忽视失败的可能性,以及测试和评估应用程序性能的需要。如果你假设产品几乎完美无缺,但实际上并非如此,将产品交付给客户可能是一个巨大的错误。你能想象如果你开始销售一个据说总结新闻文章但很快用户开始抱怨总结完全错误的应用程序吗?不仅应用程序会失败,你和你的公司可能会因为软件不工作而获得坏名声。

2.2. 提出好的问题——针对数据

初看起来,这个部分似乎可以与前一部分合并,我甚至提到了几种向客户提出好问题的方法。但在这个部分,我讨论的问题不仅是对客户知识的探究,也是对数据能力的探究。数据集所能告诉我们的不会超过我们向它提出的问题,而且即使如此,数据也可能无法回答问题。这是两个最危险的陷阱:

  • 期望数据能够回答它无法回答的问题

  • 对数据提出的问题不能解决原始问题

提出能够引导出有信息量答案并随后改善结果的问题是一个重要且复杂的问题,它值得比通常所得到的更多讨论。我在前几节中提到的良好或至少有帮助的问题在措辞和范围上相当具体,即使它们可以应用于许多类型的项目。在接下来的小节中,我试图定义并描述一个好问题,目的是提供一个框架或思维过程,用于为任意项目生成好问题。希望你能看到,通过提出一些问题,你可能会得到一些有用的、好的问题,你可以向数据提出这些问题。

2.2.1. 好问题的假设是具体的

没有任何问题像基于错误假设的问题那样难以回答。但基于不明确假设的问题紧随其后。每个问题都有假设,如果这些假设不成立,可能会对你的项目造成灾难。考虑你问题的假设并决定这些假设是否安全是很重要的。而且为了你能判断假设是否安全,它们需要是具体的,这意味着定义明确且能够被测试。

简短地来说,我曾经在一家对冲基金工作过。我在量化研究部门,我们的主要目标是,就像任何对冲基金一样,寻找金融市场中可能被利用以获得货币收益的模式。我工作的交易算法的一个关键方面是模型选择的方法。模型选择对于数学建模来说,就像试穿裤子购物一样:我们尝试了很多,评判它们,然后选择一个或几个看起来对我们很有用的,希望它们在未来能为我们服务得很好。

在我开始在这家对冲基金工作几个月后,又聘请了一位刚从研究生院毕业的数学家。她开始直接参与算法中的模型选择方面。有一天,在去吃午饭的路上,她开始向我描述,许多商品市场的数学模型已经开始与它们长期平均成功率大相径庭。例如,假设模型 A 在过去三年中有 55%的时间正确预测了原油日价的涨跌。但在过去的四周里,模型 A 只有 32%的时间是正确的。我的同事告诉我,由于模型 A 的成功率已经低于其长期平均水平,它必然会在接下来的几周内恢复,我们应该赌模型 A 的预测。

实话实说,我对我的同事感到失望,但她的错误很容易犯。当一个特定的数量——在这个例子中是模型 A 的成功率——通常返回其长期均值时,这被称为均值回归,并且它是许多现实生活系统的著名争议假设之一,其中最不多的就是世界金融市场。

这个世界上无数的系统都不遵循均值回归。抛一枚标准硬币就是其中之一。如果你抛硬币 100 次,只看到 32 次正面,你认为在接下来的 100 次投掷中你会看到超过 50 次正面吗?我肯定不会,至少不会到愿意赌这个程度。一枚(公平的)硬币被抛掷的历史不会影响硬币的未来,商品市场通常也是这样。诚然,许多基金在金融市场中发现了可利用的模式,但这些只是例外而不是规则。

均值回归的假设是你在问数据时可能遇到的错误假设的一个很好的例子。在这种情况下,我的同事问的是“模型 A 的成功率会回到其长期平均水平吗?”基于均值回归的假设,答案将是肯定的:均值回归意味着当模型 A 最近连续出现错误时,它将更频繁地正确。但如果你在这个情况下不假设均值回归,答案将是“我不知道。”

承认你的假设——总是有假设——并且确保它们是真实的,或者至少确保如果假设被证明是错误的,你的结果不会被破坏,这一点非常重要。但这说起来容易做起来难。实现这一目标的一种方法是将你分析到结论之间的所有推理分解成具体的逻辑步骤,并确保所有差距都被填补。在我以前同事的例子中,原始的推理步骤如下:

  1. 模型 A 的成功率最近相对较低。

  2. 因此,模型 A 在近期内的成功率将相对较高。

数据告诉你 1,然后 2 是你得出的结论。如果这里缺失的逻辑步骤不明显,那么当你用可能随时间上升或下降的任意数量 X 替换模型 A 的成功率时,可能更容易看到它:

  1. X 最近已经下降。

  2. 因此,X 将很快上升。

想想 X 可能是什么:股价、降雨量、学校的成绩、银行账户余额。对于这些中的哪一个,前面的逻辑是有意义的?是否有缺失的步骤?我会争辩说确实有一个缺失的步骤。逻辑应该是这样的:

  1. X 最近已经下降。

  2. 因为 X 总是会纠正自己,朝着某个值 V,

  3. X 将很快上升,朝着 V 方向。

注意数据已经告诉了你 1,就像之前一样,你希望能够在 3 中得出结论,但 3 依赖于 2 是真实的。2 是真实的吗?再次,想想 X 可能是什么。显然,对于银行账户余额或降雨量来说,2 不是真实的,所以它不可能总是真实的。你必须问自己,对于你正在检查的特定数量来说,它是否是真实的:你有什么理由相信,在任意时间段内,模型 A 的预测应该有 55% 的时间是正确的?在这种情况下,你唯一能证明模型 A 有 55% 的时间是正确的证据是,模型 A 历史上有 55% 的时间是正确的。这就像循环推理一样,这不是足够的真实证据来证明假设。均值回归不应该被视为真理,而且模型 A 在近期内应该有 55% 或更多的时间是正确的这一结论是没有根据的。

作为一名数学家,我受过训练,将所有分析、论证和结论都分成逻辑步骤,这种经验在通过数据科学做出和证明现实生活中的结论和预测方面证明了自己是无价的。在大学数学课程工作中学到的技能中,形式推理可能是我最重视的。关于推理的一个重要事实是——再次强调我在本节中试图说明的观点——一个错误或不清晰的假设会让你从一个可疑的地方开始,你应该尽一切努力避免依赖这样的错误假设。

2.2.2. 好的答案:可衡量的成功,成本不高

也许将重点转移到对好问题的答案上,可以更清楚地了解好问题包含的内容,以及帮助你决定何时你的答案足够。好问题的答案应该在某种程度上可衡量地改善项目的状况。关键是,你应该提出无论答案如何都能使你的工作变得更容易,并使你更接近实际结果的问题。

你如何知道回答一个问题是否会让你更接近有用的、实际的结果?让我们回到这样的想法:数据科学家最宝贵的特质之一是他们对可能发生的事情的意识以及他们为这种情况做准备的能力。如果你能想象所有(或至少大多数)可能的结果,那么你可以从它们中得出逻辑结论。如果你知道逻辑结论——你可以从新的结果中推断出的额外知识——那么你可以弄清楚它们是否会帮助你实现项目的目标。

可能会有广泛的结果,其中许多是有帮助的。尽管这不是一个详尽的列表,但如果你提出并回答导致正面或负面结果、消除可能的路径或结论或提高情境意识的问题,你就可以更接近你项目的目标。

正面和负面结果都可以是有帮助的。我所说的正面结果是指那些在你最初提问时你怀疑和/或希望得到的结果。这些结果显然是有帮助的,因为它们符合你对项目的思考过程,并且直接推动你朝着目标前进。毕竟,目标还是未实现的正面结果,如果得到证实,会给你的客户提供一些实际的好处。

负面结果是有帮助的,因为它们告诉你,你原本认为可能正确的事情实际上是不正确的。这些结果通常感觉像是挫折,但从实际的角度来看,它们是所有可能结果中最具信息量的。假设你发现尽管有所有相反的历史证据,太阳明天不会升起,这会是什么情况?这是一个极端的例子,但你能想象如果它被证实为真,会有多具信息量吗?这会改变一切,而且你很可能会是少数几个知道这一点的人之一,因为它是如此反直觉的。从这种意义上说,负面结果可能是最有帮助的,尽管它们通常需要你根据新的信息重新调整目标。至少,负面结果迫使你重新思考你的项目,以考虑这些结果,这个过程会导致更明智的选择和更现实的项目路径。

如我在第一章中提到的,数据科学充满了不确定性。通往解决方案的路径总是很多,通往失败的道路也很多,甚至通往成功与失败之间的灰色地带的路径更多。对于任何这些可能路径或结论的不可能性证据或直接排除,都可能有助于通知和聚焦项目的下一步。一条路径可以通过多种方式被排除或被认为是不可能的,这可能包括以下几种:

  • 新信息使一条路径的可能性大大降低

  • 新信息使其他路径的可能性大大增加

  • 技术挑战使得探索某些路径非常困难或不可能

如果排除一条路径似乎没有帮助——也许它是唯一可能成功的路径之一——请记住,无论如何你的情况已经变得简单了,这可能是好事。或者,利用这个机会重新思考你的路径集合和项目知识。也许有更多数据、更多资源,或者你还没有想到的其他东西,这些都可能帮助你从新的角度看待挑战。

在数据科学中,提高情境意识总是好的。你不知道的东西可能会伤害你,因为一个未知量会悄悄地进入你项目的某个方面并破坏结果。如果一个问题有助于你深入了解一个系统是如何工作的,或者哪些外围事件正在发生,这些事件会影响数据集,那么这个问题就是好的。如果你在某些时候发现自己说“我想知道如果...”,或者如果同事也有同样的想法,问问自己这个想法是否与一个问题相关,这个问题可以帮助你为项目获得一些背景信息——如果不是回答一个更大、更直接的问题。以这种方式进行内省,为通常模糊的任务寻找好的结果带来了一些正式性和程序性。

2.3. 使用数据回答问题

你有很好的问题,现在你想要答案。毕竟,提供解决问题的答案是整个项目的目标。从数据科学项目中获得答案通常看起来像图 2.2 中的公式或食谱。尽管有时其中一个成分——好的问题、相关数据或深入的分析——比其他成分更容易获得,但三者对于获得有用的答案都是至关重要的。此外,我选择的四个形容词(每个成分中有一个:好的相关的有洞察力的)和结果中的一个(有用的)不应被忽视,因为没有它们,公式并不总是有效。任何旧问题、数据和分析的产物并不总是答案,更不用说有用的答案了。值得重申的是,你需要在项目的每个步骤中都深思熟虑,而这个公式的元素也不例外。例如,如果你有一个好的问题但数据不相关,找到答案将很困难。

图 2.2. 数据科学项目中有用答案的食谱

图片

2.3.1. 数据是否相关且足够?

并非总是容易判断数据是否相关。例如,假设你正在构建一个啤酒推荐算法。用户会选择一些他们喜欢的啤酒,算法将推荐他们可能喜欢的其他啤酒。你可能假设啤酒爱好者通常喜欢某些类型的啤酒而不喜欢其他类型的啤酒,因此他们的喜好倾向于聚集在他们最喜欢的啤酒类型中。这是一个好问题:啤酒爱好者是否对某些类型的啤酒比对其他类型的啤酒有显著偏好?你有一个来自流行的啤酒评分网站的数据集,由数千名网站用户的一到五星级评分组成。你希望使用这些数据来测试你的假设;这些数据是否相关?

很快你就会意识到数据集是一个只包含三个列的 CSV 文件:USER_NAMEBEER_NAMERATING。(糟糕!没有啤酒类型。)之前看似极其相关的数据集现在对这个问题来说似乎不那么相关了。当然,对于一个关于啤酒类型的问题,你需要知道每款啤酒的类型。因此,为了回答这个问题,你必须找到匹配啤酒和啤酒类型的数据集,或者尝试从你已有的数据中推断啤酒类型,可能基于啤酒的名称。

无论哪种方式,都应该很明显,最初看起来能够完美回答问题的数据集需要一些额外的资源才能做到这一点。一个有远见和意识的数据科学家可以在这些问题耗费你或你的同事时间或金钱之前预见这些问题。第一步是具体说明数据将如何帮助你回答你的问题。在调查对啤酒类型的亲和力的情况下,这样的声明可能就足够了:

为了找出啤酒爱好者是否对某些类型的啤酒比对其他类型的啤酒有显著偏好,我们需要包含啤酒名称、啤酒类型、用户名以及他们对啤酒的个别评分的数据。有了这些数据,我们可以进行统计分析,例如方差分析(ANOVA),将每种啤酒类型作为一个变量,并检查啤酒类型是否对个别用户的评分有显著影响。

忽略统计测试描述中缺乏具体细节的问题(在这里并不重要,但我们在后面的章节中会回到这个问题),你这里有一个基本的大纲,说明如何使用你认为可用的数据来回答问题。可能有其他类似的方案也能同样好或更好地达到目的,但一个好的大纲会说明你需要哪些数据以及你将如何使用这些数据来回答问题。通过说明数据集应具有的具体属性,你或你合作的人可以快速检查数据集(或多个数据集)是否满足要求。

我(就像我认识的大多数人一样)有时会基于我认为拥有的数据而不是实际拥有的数据来创建算法。当我意识到我的错误时,我已经在毫无价值的代码上浪费了一些时间(幸运的是,不多)。通过简要概述你打算如何使用数据来回答问题,你就可以很容易地检查确保你打算使用的数据集包含了你列出的所有必要信息——在你开始编码之前。如果数据集缺少关键信息,你可以调整你的计划,要么在其他地方找到缺失的部分,要么制定另一个不需要它的计划/大纲。在数据科学项目的这个阶段进行规划可以节省你以后大量的时间和精力。在项目后期大量修改代码或完全废弃它通常不是有效利用时间的方式。

在以下小节中,我概述了一些你可以使用的步骤,以制定一个坚实而详细的计划来寻找和使用数据来回答特定的问题。在后面的章节中,我将详细讨论收集和使用数据,但在这里我想介绍在整个项目中需要注意和牢记的基本思想。你通常有很多选择,如果这些选择后来证明效率低下——或者完全错误——那么记录下你可以做出的其他选择会很有帮助。

2.3.2. 以前有人做过吗?

这应该是制定计划的第一个步骤:在网上搜索博客文章、科学文章、开源项目、大学的研究描述或任何你可以找到的与你要开始的项目相关的其他内容。如果其他人已经做了类似的事情,你可能会对尚未遇到的挑战和能力获得很多洞察。再次强调,意识是非常有帮助的。

关于其他人已经完成的项目,你可能会在你的项目中遇到类似的问题和类似的解决方案,因此最好尽可能多地了解你应该注意什么以及如何处理这些问题。

有时,一点搜索可能会带你找到你之前未见过的有用数据集,你可能没有考虑过的分析方法,或者最好的是,你可以用于你自己的项目的结果或结论。假设分析是严谨的——但总是最好进行验证——其他人可能已经为你做了很多工作。

2.3.3. 确定可以使用哪些数据和软件

现在你已经搜索了相关信息,包括先前的类似项目和相关的数据集,你应该盘点一下你有什么,你还需要什么。

数据

如果你想象出一个可以极大地帮助你数据集,但这个数据集在任何地方都不存在,那么在将来你意识到可以做什么的时候,记下这一点通常很有帮助。例如,如果你在啤酒评分数据集中缺少啤酒类型标签,你可能会发现许多酿酒厂在他们的网站上列出了这些类型,或者在其他网页上。这为你提供了自己收集它们的机会,可能需要时间和精力。如果你记下这个需求和这个潜在解决方案,那么在未来的任何时候,你都可以重新评估成本和收益,并决定如何继续前进。由于与以数据为中心的项目相关的许多不确定性,可能选择的成本和收益几乎在任何时候都可能发生变化。

软件

大多数数据科学家都有一个用于数据分析的 favorite 工具,但这个工具可能并不总是适合你打算做的事情。在尝试将这个概念与可以将概念转化为现实的软件相匹配之前,考虑你想要进行的分析的概念通常是一个好主意。你可能决定,如果数据支持,统计测试可以提供好的答案,机器学习可以找出你需要分类,或者简单的数据库查询可以回答问题。在任何这些情况下,你可能知道如何使用你喜欢的工具来执行分析,但首先你可能想要考虑你数据的格式,可能需要进行的任何数据转换,你拥有的数据量,将数据加载到分析工具中的方法,以及最后在工具中的分析方法。在执行这些步骤之前思考所有这些步骤,并考虑它们在实际中可能如何工作,当然可以导致软件工具选择的更好选择。

如果你不太熟悉许多软件工具和技术,你现在可以跳过这一步,继续阅读,因为我在后面的章节中会涵盖这些内容。但就目前而言,我只想强调在做出任何选择之前,有意识地思考各种选项的重要性。这个决定可能具有重大影响,不应轻率对待。

2.3.4. 预期实现所有愿望的障碍

在项目的这个规划阶段,你可能想要问自己以下问题:

  • 数据是否容易访问和提取?

  • 你是否知道关于数据的一些可能重要的信息,但你可能不知道?

  • 你是否有足够的数据?

  • 你是否有太多的数据?处理这些数据是否会花费太多时间?

  • 是否有缺失的数据?

  • 如果你正在组合多个数据集,你是否确定一切都会正确集成?两个数据集中名称、ID 和代码是否相同?

  • 如果你的统计测试或其他算法没有给出你预期的结果,会发生什么?

  • 你有方法来检查你的结果吗?如果检查显示某处有错误怎么办?

其中一些可能看起来很明显,但我已经看到足够多的人——数据科学家、软件工程师以及其他一些人——忘记考虑这些事情,并且他们在后来的忽视中付出了代价。最好是在一开始就提醒你要持怀疑态度,这样任务的不可确定性就不会给你带来太多的损失。

2.4. 设定目标

我在本章中多次提到了目标,但尚未直接讨论它们。尽管你可能已经带着一些目标开始了这个项目,但现在是一个很好的时机,在预期的疑问、数据和答案的背景下评估它们。

通常,初始目标是在考虑一些商业目的的情况下设定的。如果你不在商业领域——比如你在研究——那么目的通常是外部使用结果,比如在特定领域推进科学知识或为其他人提供分析工具。尽管目标起源于项目本身之外的环境,但每个目标都应该根据数据科学进行实用筛选。这个筛选包括提出以下问题:

  • 什么是可能的?

  • 什么是有价值的?

  • 什么是高效的?

将这个筛选应用于所有潜在目标,考虑到好问题、可能的答案、可用数据和预见的障碍,可以帮助你确定一个坚实的目标集,这些目标既可能实现,又具有价值,并且是高效的。

2.4.1. 什么是可能的?

有时很明显什么是可能的,但有时并不明显。在接下来的章节中,我将描述一些看似简单的任务实际上并不简单。例如,找到合适的数据,处理数据,利用数据来获取答案,设计软件进行分析,以及面对任何其他障碍,如我在上一节中提到的那些,都可能影响你实现某个目标的能力。任务越复杂,你对任务事先了解得越少,任务实现的可能性就越小。例如,如果你认为某个数据集存在,但你还没有确认,那么实现任何需要该数据集的目标可能是不可能的。对于任何具有不确定性的目标,考虑实现目标可能是不可能的。

2.4.2. 什么是有价值的?

一些目标比其他目标带来更多的利益。如果资源稀缺,在其他条件相同的情况下,追求更有益的目标会更好。在商业领域,这可能意味着追求预期将带来最高利润增长的目标。在学术界,这可能意味着追求最具影响力的科学出版物。正式考虑实现目标预期的价值,正如我这里所建议的,为项目规划创造了一个有意识的背景和框架。

2.4.3. 什么是高效的?

在考虑了可能性和价值之后,你可以考虑实现每个目标可能需要的努力和资源。然后你可以通过以下方程式来近似效率:

效率 = 价值 / 努力 * 可能性

实现目标的整体效率是实现它的价值除以实现它所需的努力(每单位努力获得的价值)乘以可能性(实现它的概率)。效率随着目标价值的增加而提高,如果需要更多的努力,则效率会下降,如果目标看起来不太可能实现,则效率也会下降。这只是一个粗略的计算,它在概念上对我更有意义,但在实际应用中帮助不大,但我确实觉得它很有帮助。

2.5. 计划:保持灵活

考虑到你对项目的所有了解,你迄今为止所做的研究,以及你对自己可能使用的关于数据和软件工具的假设性问题,现在是制定计划的时候了。这不应该是一个包含事先假设结果的连续步骤的计划。数据和数据科学的不可确定性几乎保证了事情不会按照你预期的方向发展。考虑几种可能实现目标的不同方法是一个好的策略。甚至目标本身也可以是灵活的。

这些替代路径可能代表不同的总体策略,但在大多数情况下,计划中的两条路径会在预期存在不确定性的地方分叉,即两个最可能的情况表明两种不同的策略来应对不确定性的结果。从一开始就制定一个计划到第一个主要不确定性是非常明智的。如果你想在那里停下来,这可能会节省你一些计划时间,但最好还是规划出所有最可能的路径,特别是如果你与多个人合作。这样,每个人都可以看到项目将走向何方,并且从一开始就知道会有问题和绕路,但你还没有知道它们会是哪些。这就是数据科学家的生活!

最后,你在这里制定的计划将在整个项目(以及本书)中定期回顾,因此计划的早期阶段是最重要的。一个好的目标是为下一步制定计划,以便你在下次回顾计划和目标时能获得充分的信息。增加知识和减少不确定性总是好事。

练习

考虑以下场景:

你在一家为顾客整合个人财务数据的公司工作,这些顾客主要是个人消费者。让我们称这家公司为 Filthy Money, Inc,简称 FMI。通过 FMI 的主要产品,一个网络应用程序,客户可以在一个地方查看他们所有的数据,而不是需要分别登录他们每个财务账户的网站。典型的客户已经将他们的几个账户,如银行账户和信用卡,连接到了 FMI 应用程序。

FMI 的主要产品设计师要求你帮助构建一个名为“脏钱预测”的新产品组件,该组件旨在根据用户的消费和收入习惯,为 FMI 应用的用户提供其账户和整体财务状况的短期预测。产品设计师希望与你合作,找出可能性和一些可能的好产品特性:

1.

你会向产品设计师提出哪三个问题?

2.

你可能会对数据提出哪三个好问题?

3.

项目可能的三项目标是什么?

摘要

  • 保持警觉:经验、领域专家和其他相关项目的知识有助于你在问题出现之前进行规划和预测。

  • 注意客户的角度和可能缺乏数据科学知识。

  • 确保项目专注于回答好问题。

  • 花时间思考所有可能的路径来回答那些好问题。

  • 使用客户想要、你已开发的问题以及获取答案的可能路径的实用视角来设定目标。

第三章. 我们周围的数据:虚拟荒野

本章涵盖

  • 发现你可能需要的数据

  • 在各种环境中与数据交互

  • 结合不同的数据集

本章讨论数据科学家研究的主要物种:数据。拥有数据——即有用的数据——通常被视为理所当然,但通常不主张做这样的假设。与任何值得科学研究的主题一样,数据可能难以找到和捕捉,并且很少完全被理解。对你拥有的或希望拥有的数据集的任何错误观念都可能导致代价高昂的问题,因此在本章中,我讨论了将数据作为科学研究对象的处理方式。

3.1. 数据作为研究对象

近年来,关于数据科学领域是否仅仅是大数据时代一些较老领域(如运筹学、决策科学、分析、数据挖掘、数学建模或应用统计学等)的转世或分支的讨论似乎永无止境。与任何流行术语或话题一样,关于其定义和概念的讨论只有在术语的流行度下降后才会停止。我认为我无法比之前那些定义数据科学的人做得更好,所以这里引用维基百科(en.wikipedia.org/wiki/Data_science)的定义,稍作改写,就足够了:

数据科学是从数据中提取知识。

虽然足够简单,但这个描述并没有区分数据科学与其他许多类似术语,除了可能声称数据科学是一个涵盖所有这些术语的总称。另一方面,这个数据科学时代有一个以前时代所不具备的特性,对我来说,这是一个相当有说服力的理由来为新术语命名,这些术语用于描述数据科学家所做的事情,而以前的应⽤统计学家和数据导向的软件工程师并没有做这些事情。这个理由帮助我强调了数据科学中经常被忽视但非常重要的一个方面,如图 3.1figure 3.1 所示。

图 3.1. 数据科学过程准备阶段第二步:探索可用数据

图片

3.1.1. 计算机和互联网的用户成为数据生成者

在最近的历史中,计算机在计算能力、存储能力和完成以前闻所未闻的任务的通用能力方面取得了惊人的进步。自近一个世纪前现代计算机发明以来,每一代计算机都见证了机器的体积不断缩小,其性能比上一代最强大的超级计算机高几个数量级。

包括二十世纪后半叶至二十一世纪初以及现在的这个时期,通常被称为信息时代。信息时代,以计算机和互联网的普及为特征,可以细分为几个更小的转变,这些转变与数据分析有关。

首先,早期的计算机主要用于进行以前需要不合理时间的计算。破解军事密码、船舶导航以及在应用物理学中进行模拟是早期计算机执行的计算密集型任务。

第二,人们开始使用计算机进行沟通,互联网在规模和容量上得到了发展。数据和信息可以轻松地发送到很远的距离。这使得数据分析师能够在一个地方收集更大、更多样化的数据集进行研究。在 20 世纪 90 年代,发达国家的普通人的互联网接入量大幅增加,使数十亿人能够访问发布的信息和数据。

第三,虽然早期互联网的使用主要由公众消费发布的内容和与他人沟通组成,但很快许多网站和应用的所有者意识到,他们的用户行为的聚合提供了对其自身产品成功以及有时是普遍人类行为的宝贵见解。这些网站开始收集用户数据,形式包括点击、输入文本、网站访问以及其他用户可能采取的任何行动。用户开始产生的数据比他们消费的数据更多。

第四,移动互联网和智能手机的出现,使得收集用户数据的数量和具体性有了巨大的进步。在任何时刻,您的移动设备都能够记录和传输其传感器所能收集的每一比特信息(位置、移动、摄像头图像和视频、声音等),以及您在使用设备时故意采取的每一个动作。如果您启用或允许其收集,这可能会是一大批信息。

第五——尽管这并不一定是个人移动设备出现之后的直接结果——是几乎在所有电子产品中包含数据收集和互联网连接。通常被称为物联网(IoT),这可以包括从您的汽车到您的手表,再到您办公楼顶部的气象传感器。当然,从设备收集和传输信息开始的时间远早于 21 世纪,但其普遍性相对较新,互联网上以各种形式(处理过的或原始的,免费或出售)可用的结果数据也是如此。

通过计算设备和互联网增长的这些阶段,网络世界不仅仅是一个消费信息的地方,本身也成为了一个数据收集工具。我在 1990 年代末的高中时期有一个朋友,他建立了一个网站,提供电子贺卡作为收集电子邮件地址的幌子。他以几十万美元的价格出售了由此产生的数百万个电子邮件地址列表。这是一个用户数据价值的原始例子,其用途与网站本身完全无关,也是我年轻时很遗憾错过的一个完美例子。到 2000 年代初,类似规模的电子邮件地址列表不再值这么多钱,但其他类型的用户数据变得非常受欢迎,同样可以卖出高价。

3.1.2. 数据本身的价值

当人们和企业意识到用户数据可以以相当大的金额出售时,他们开始不加区分地收集它。大量数据开始堆积在各个数据存储库中。在线零售商开始存储您购买的所有商品,以及您查看的每一项商品和点击的每一个链接。视频游戏存储了您的角色形象所走过的每一步以及它击败的每一个对手。各种社交网络存储了您和您的朋友所做的一切。

收集所有这些数据的初衷并不总是为了出售,尽管这种情况经常发生。因为几乎每个主要网站和应用都使用自己的数据来优化用户体验和效果,因此网站和应用发布者通常在数据的销售价值和使用内部价值之间犹豫不决。许多出版商害怕出售他们的数据,因为这可能会让其他人发现用它赚钱的方法。他们中的许多人将数据保留在自己手中,为未来储备,届时他们将有足够的时间榨取数据的全部价值。

互联网巨头 Facebook 和 Amazon 每天每分钟都在收集大量的数据,但据我估计,他们所拥有的数据大部分尚未得到充分利用。Facebook 专注于营销和广告收入,而他们拥有全球人类行为数据中最大的数据集之一。产品设计师、市场营销人员、社会工程师和社会学家如果能够访问 Facebook 的数据,很可能会在他们的学术和工业领域取得重大进展。反过来,如果 Amazon 的数据被学术机构利用,可能会颠覆许多受喜爱的经济原则,并创造几个新的原则。或者它可能能够改变整个行业中零售、制造和物流的工作方式。

这些互联网巨头知道他们的数据很有价值,并且他们自信没有其他人拥有如此规模或质量相似的数据集。无数的公司愿意为访问数据支付高价,但 Facebook 和 Amazon——我推测——有自己使用数据的最大潜力的抱负,因此不希望其他人抢走由此产生的利润。如果这些公司拥有无限资源,他们肯定会试图从每个字节的数据中榨取每一美元。但无论他们多么庞大和强大,他们仍然在资源上有限,并且被迫专注于那些最直接影响他们底线的数据用途,而排除了一些其他有价值的工作。

另一方面,一些公司已经选择提供对其数据的访问权限。Twitter 是一个显著的例子。通过付费,你可以访问 Twitter 平台上的完整数据流,并将其用于自己的项目。围绕数据销售的中介行业已经发展起来,以盈利为目的。这一领域的突出例子是来自各个主要证券交易所的数据市场,这些数据长期以来一直可供购买。

学术和非营利组织通常会将数据集免费公开提供,但可能对如何使用它们有所限制。由于即使在单个科学领域内数据集的差异,数据集的地理位置和格式的合并趋势已经出现。几个主要领域已经创建了组织,其唯一目的是维护包含尽可能多该领域数据集的数据库。通常,科学文章的作者在发表作品之前必须将这些数据提交给这些经典数据存储库之一。

不论以何种形式,数据现在无处不在,它已经不再仅仅是分析师用来得出结论的工具,而成为了一个目的本身。公司现在似乎收集数据作为一种目的,而不是手段,尽管他们中的许多人声称计划在未来使用这些数据。独立于信息时代的其他定义特征,数据已经获得了自己的角色、自己的组织和自己的价值。

3.1.3. 数据科学家作为探险家

在 21 世纪,数据以前所未有的速度被收集,在许多情况下,收集数据并不是为了特定的目的。无论是私人、公共、免费、出售、结构化、非结构化、大数据、常规大小、社交、科学、被动、主动,还是任何其他类型,数据集无处不在地积累。而几个世纪以来,数据分析师收集自己的数据或被分配一个数据集来工作,历史上第一次,许多来自多个行业的人首先收集数据,然后问,“我能用这个做什么?”还有其他人问,“有没有现成的数据可以解决我的问题?”

以这种方式,数据——无处不在的数据,作为一个假设的集合——已经成为一个值得研究和探索的实体。在过去,数据集通常是有意收集的,因此它们代表了现实世界的一些有意测量。但最近,互联网、无处不在的电子设备和对于错过数据中潜在价值的潜在恐惧,导致我们尽可能多地收集数据,通常基于一个松散的前提,即我们可能会在将来使用它。

图 3.2 展示了计算历史中四种主要创新类型的解释:计算能力本身、计算机之间的网络和通信、大数据的收集和使用,以及大数据的严格统计分析。我所说的“大数据”仅仅是指最近捕捉、组织和使用任何可能的数据的运动。每一种计算创新都是从需要解决的一个问题开始的,然后经历四个发展阶段,这个过程类似于卡洛塔·佩雷斯(Carlota Perez)的技术革命与金融资本(Technological Revolutions and Financial Capital,Edward Elgar Publishing,2002)的技术激增周期,但重点在于计算创新及其对计算机用户和公众的影响。

图 3.2。我们目前处于大数据收集和使用的完善阶段,以及大数据统计分析的广泛应用阶段。

图片

图中包含的每一项创新都包含五个阶段:

  1. 问题—— 存在着计算机可以以某种方式解决的问题。

  2. 发明—— 可以解决该问题的计算技术被创造出来。

  3. 证明/认可—— 有人在有意义的方面使用计算技术,其价值得到证明或至少被一些专家认可。

  4. 采用—— 新近证明的技术在工业中得到广泛应用。

  5. 完善—— 人们开发新的版本,更多功能,更高效率,与其他工具的集成等等。

由于我们目前处于大数据收集的完善阶段和该数据统计分析的广泛应用阶段,我们创造了一个完整的数据生态系统,其中提取的知识只是包含的总知识的一小部分。不仅许多知识尚未被提取,而且在许多情况下,数据集的全面范围和属性甚至没有人理解,除非是设置系统的少数软件工程师;可能只有那些可能太忙或过于专业而无法利用它的人才能理解数据中包含的内容。所有这些未充分利用或理解不佳的数据的聚合对我来说就像一个全新的大陆,上面有许多未发现的植物和动物物种,一些完全陌生的生物,以及可能是一些由久远文明留下的遗产结构。

这种描述有例外。谷歌、亚马逊、Facebook 和 Twitter 是走在曲线前面的公司的良好例子。在某些情况下,它们正在进行与创新后期阶段相匹配的行为。例如,通过允许访问其整个数据集(通常需要付费),Twitter 似乎正在大数据收集和使用的完善阶段运营。世界各地的人们都在试图从用户的推文中榨取每一丝知识。同样,谷歌似乎在以严谨的统计方式分析其数据方面做得很好。它在图像搜索、谷歌分析甚至其基本的文本搜索方面的工作都是大规模坚实统计的良例。然而,可以轻松地争论说,谷歌还有很长的路要走。如果今天的数据生态系统就像一个很大程度上未被探索的大陆,那么数据科学家就是其探险家。就像著名的早期欧洲美洲或太平洋岛屿的探险家一样,一个好的探险家擅长几件事情:

  • 进入有趣领域

  • 认识到新和有趣的事物

  • 认识到可能有趣的事物即将出现的迹象

  • 处理新、不熟悉或敏感的事物

  • 评估新和不熟悉的事物

  • 在熟悉的事物和陌生的事物之间建立联系

  • 避免陷阱

一个在南美洲丛林中的探险家可能会用一把大刀砍开丛林灌木,偶然发现几块松散的石头,推断出一个千年古庙就在附近,找到古庙后,从废墟中了解到古代部落的宗教仪式。数据科学家可能会编写一个脚本,从公共 API 中提取一些社交网络数据,意识到少数人构成了主要的社会活动中心,发现这些人经常在他们社交网络上的帖子中提到一个新的照片分享应用,从照片分享应用的公共 API 中获取更多数据,然后通过一些统计分析将两个数据集结合起来,了解在线社区中网络影响者的行为。这两种情况都揭示了关于社会运作的先前未知信息。像探险家一样,现代数据科学家通常必须调查地形,仔细注意周围环境,四处走走,然后深入一些不熟悉的领域看看会发生什么。当他们发现有趣的东西时,他们必须检查它,弄清楚它能做什么,从中学习,并且能够将这种知识应用于未来。尽管数据分析不是一个新领域,但数据无处不在的存在——通常无论是否有人在使用它——使我们能够将科学方法应用于对现有数据世界的发现和分析。对我来说,这就是数据科学与所有其前辈之间的区别。数据如此之多,没有人可能全部理解,所以我们将其视为一个独立的世界,值得探索。

将数据视为一片荒野的想法,是使用“数据科学”这一术语而不是其任何对应术语的最有说服力的理由之一。要从数据中获得真实的事实和有用的答案,我们必须使用科学方法,或者在我们这个案例中,是“数据科学方法”:

  1. 提出一个问题。

  2. 对问题的答案提出一个假设。

  3. 提出一个可测试的预测,如果正确,将提供支持假设的证据。

  4. 通过涉及数据的实验来测试预测。

  5. 通过对实验结果的分析得出适当的结论。

以这种方式,数据科学家只是在做科学家几个世纪以来一直在做的事情,尽管是在数字世界中。今天,我们的一些最伟大的探险家把时间花在虚拟世界中,我们可以在不离开电脑的情况下获得强大的知识。

3.2 数据可能存在的地方以及如何与之互动

在我们深入探讨当今数据状态的未知领域之前,我想讨论数据可能采取的形式,这些形式的意义,以及我们最初如何处理它们。平面文件、XML 和 JSON 是一些数据格式,每种格式都有其自身的特性和独特之处。有些比其他更简单,或者更适合某些用途。在本节中,我将讨论几种格式和存储方法,它们的一些优点和缺点,以及你如何利用它们。

尽管很多人可能会反对这一点,但我决定在本节中包含对数据库和 API 的讨论。将文件格式的讨论与数据存储的软件工具结合起来对我来说是有意义的,因为在数据科学项目的初期,这些格式或数据源中的任何一个都是对“数据现在在哪里?”这一问题的有效回答。文件、数据库或 API,数据科学家需要知道的是“我如何访问和提取我需要的数据?”这就是我在这里的目的。

图 3.3 展示了数据科学家可能访问数据的三种基本方式。这可能是一个文件系统上的文件,数据科学家可以将文件读入他们最喜欢的分析工具中。或者数据可能存储在数据库中,数据库也位于文件系统中,但为了访问数据,数据科学家必须使用数据库的接口,这是一个帮助存储和提取数据的软件层。最后,数据可能位于应用程序编程接口(API)之后,这是数据科学家和可能完全未知或陌生的系统之间的软件层。在这三种情况下,数据可以以本节中讨论的任何格式或任何其他格式存储和/或交付给数据科学家。在某些系统中,存储和交付数据紧密交织在一起,因此我选择将它们视为一个单一的概念:将数据放入你的分析工具中。

图 3.3. 数据科学家可能访问数据的三种方式:从文件系统、数据库或 API

图片 3.3

我并不声称涵盖所有可能的数据格式或系统,也不会列出所有技术细节。我的主要目标是提供描述,让读者感到舒适地讨论和接近每一个。我仍然记得,从传统数据库中提取数据对我来说曾经是令人畏惧的,通过本节,我希望让即使是初学者也能感到轻松。只有在你对这些基本的数据存储和访问形式相当熟悉的情况下,你才能继续前进到数据科学最重要的部分:数据能告诉你什么。

3.2.1. 平面文件

平面文件 是纯文本数据集,在没有人为努力去实现其他格式的情况下,大多数情况下的默认数据格式。平面文件是自包含的,你不需要任何特殊的程序来查看其中包含的数据。你可以使用通常称为文本编辑器的程序打开平面文件进行查看,并且每个主要操作系统都有许多文本编辑器可用。平面文件包含 ASCII(或 UTF-8)文本,每个文本字符使用(很可能)8 位(1 字节)的内存/存储。只包含单词 DATA 的文件大小将是 32 位。如果在 DATA 单词之后有一个行结束(EOL)字符,文件大小将是 40 位,因为需要一个 EOL 字符来表示一行已经结束。我的解释可能对许多人来说看起来很简单,但即使是一些这些基本概念,在我们讨论其他格式时也会变得很重要,所以我感觉最好概述一下平面文件的一些基本属性,这样我们就可以在以后比较其他数据格式。

平面文件主要有两种子类型:纯文本和分隔符。纯文本 是键盘上输入的单词。它可能看起来像这样:

This is what a plain text flat file looks like. It's just plain ASCII text. Lines don't really end unless there is an end-of-line character, but some text editors will wrap text around anyway, for convenience.
Usually, every character is a byte, and so there are only 256 possible characters, but there are a lot of caveats to that statement, so if you're really interested, consult a reference about ASCII and UTF-8.

这个文件将包含七行,或者技术上说是八行,如果文本最后一行的末尾有一个行结束字符。纯文本平面文件是一系列字符存储在两种(或更多)非常常见的格式之一中。这不同于存储在文字处理程序格式中的文本文档,例如 Microsoft Word 或 Open-Office Writer。(参见“常见错误格式”小节。)文字处理程序文件格式可能包含更多信息,包括如样式格式和有关文件格式本身的元数据等开销,以及可能被插入到文档中的图像和表格等对象。纯文本是包含单词和仅单词的最小文件格式——没有样式,没有花哨的图像。数字和一些特殊字符也可以。

但是如果你的数据包含大量条目,分隔符文件可能是一个更好的选择。分隔符文件 是纯文本,但有一个规定,即文件中每隔一段时间会出现一个分隔符,如果你正确地对齐分隔符,你可以制作出类似表格的东西,有行、列和标题。分隔符文件可能看起来像这样:

NAME      ID   COLOR     DONE
Alison    1    'blue'    FALSE
Brian     2    'red'     TRUE
Clara     3    'brown'   TRUE

让我们称这个表格为 JOBS_2015,因为它代表了一组虚构的 2015 年开始的房屋粉刷工作,包括客户姓名、ID、油漆颜色和完成状态。

这个表格恰好是制表符分隔的——或者说制表符分隔值(TSV)——这意味着列由制表符字符分隔。如果在一个文本编辑器中打开,这样的文件通常会像这里一样显示,但可能会选择性地显示文本\t,而制表符本应出现的位置。这是因为制表符,就像行尾字符一样,可以用单个 ASCII 字符表示,而这个字符通常用\t表示,除非以可变长度的空白字符渲染,以将字符对齐到表格格式。

如果JOBS_2015以逗号分隔值(CSV)格式存储,在标准文本编辑器中它将看起来像这样:

NAME,ID,COLOR,DONE
Alison,1,'blue',FALSE
Brian,2,'red',TRUE
Clara,3,'brown',TRUE

逗号取代了制表符字符,但数据仍然是相同的。在任何情况下,您都可以看到文件中的数据可以解释为一组行和列。行代表AlisonBrianClara的每个工作,而标题(第一)行上的列名是NAMEIDCOLORDONE,给出了表中包含的工作的详细信息类型。

大多数程序,包括电子表格和一些编程语言,要求每行(除可能的首行外)具有相同数量的定界符,以便在它们尝试读取文件时,列数保持一致,并且每行恰好向每列贡献一个条目。一些软件工具不需要这样做,并且它们各自有处理每行条目数量变化的具体方式。

我应该在这里指出,定界符分隔的文件通常被解释为表格,就像电子表格一样。此外,由于纯文本文件可以使用文字处理程序进行读取和存储,因此定界符分隔的文件通常可以加载到电子表格程序,如 Microsoft Excel 或 OpenOffice Calc 中。

任何用于操作文本或表格的常见程序都可以读取平面文件。所有流行的编程语言都包括可以读取此类文件的功能和方法。我最熟悉的两种语言,Python(csv包)和 R(read.table函数及其变体),都包含可以将 CSV 或 TSV 文件轻松加载到这些语言中最相关数据类型的方法。对于纯文本,Python(readlines)和 R(readLines)也有按行读取文件的方法,允许通过您认为合适的方法解析文本。这两种语言以及许多其他语言的包提供了加载相关类型文件的功能,我建议查看最近的语言和包文档,以了解是否有其他文件加载方法更适合您的需求。

不压缩文件时,平面文件几乎是文本或表格中最小和最简单的常见文件格式。其他文件格式包含有关文件格式或数据结构的特定信息。由于它们是最简单的文件格式,因此通常最容易阅读。但是,由于它们非常精简,除了显示数据外,它们不提供任何其他功能,因此对于较大的数据集,平面文件变得效率低下。使用 Python 这样的语言扫描包含数百万行文本的平面文件可能需要几分钟或几小时。在读取平面文件太慢的情况下,有设计用来快速解析大量数据的替代数据存储系统。这些被称为数据库,将在后面的章节中介绍。

3.2.2. HTML

标记语言是用标签或特别指定的指令标记的纯文本,这些指令说明了文本应该如何被解释。非常流行的超文本标记语言(HTML)在互联网上被广泛使用,一个片段可能看起来像这样:

<html>
    <body>
        <div class="column">
            <h1>Column One</h1>
            <p>This is a paragraph</p>
        </div>

        <div class="column">
            <h1>Column Two</h1>
            <p>This is another paragraph</p>
        </div>
    </body>
</html>

HTML 解释器知道,在<html></html>标签之间的所有内容都应该被视为 HTML 并读取。同样,在<body></body>标签之间的所有内容将被视为文档的主体,这在 HTML 渲染中具有特殊含义。大多数 HTML 标签的格式为<TAGNAME>以开始注释,</TAGNAME>以结束注释,其中 TAGNAME 是任意名称。现在,两个标签之间的所有内容都被视为由 TAGNAME 注释,解释器可以使用它来渲染文档。示例中的两个<div>标签展示了如何标记两块文本和其他内容,并且将一个名为column的类应用于div,允许解释器以特殊方式处理column实例。

HTML 主要用于创建网页,因此它通常看起来更像文档而不是数据集,具有标题、主体以及一些样式和格式化信息。HTML 通常不用于存储原始数据,但它当然能够做到这一点。实际上,网络爬虫的概念通常涉及编写能够抓取和读取网页、解释 HTML 并抓取 HTML 页面中感兴趣的具体部分的代码。

例如,假设我们感兴趣的是收集尽可能多的博客文章,并且一个特定的博客平台使用 <div class="column"> 标签来表示博客文章中的列。我们可以编写一个脚本,系统地访问一个博客,解析 HTML,寻找 <div class="column"> 标签,捕获它和相应的 </div> 标签之间的所有文本,然后丢弃其他所有内容,然后再去访问另一个博客做同样的事情。这是网络爬虫,如果所需数据不包含在其他更友好的格式中,这可能会很有用。网络爬虫有时被网站所有者禁止,因此最好小心行事,在爬虫之前检查网站的版权和服务条款。

3.2.3. XML

可扩展标记语言(XML)看起来很像 HTML,但通常更适合存储和传输文档和数据,而不仅仅是网页。前面的 HTML 片段可以是有效的 XML,尽管大多数 XML 文档都是以一个声明特定 XML 版本的标签开始的,如下所示:

<?xml version="1.0" encoding="UTF-8"?>

这个声明有助于确保 XML 解释器以适当的方式读取标签。否则,XML 的工作方式与 HTML 类似,但没有与网页相关的许多开销。XML 现在用作离线文档的标准格式,例如 OpenOffice 和 Microsoft Office 格式。由于 XML 规范旨在机器可读,它也可以用于数据传输,例如通过 API。例如,许多官方财务文件以可扩展商业报告语言(XBRL)的形式公开,而 XBRL 是基于 XML 的。

这是对表格 JOBS_2015 的前两行的 XML 表示:

<JOB>
     <NAME>Alison</NAME>
     <ID>1</ID>
     <COLOR>'blue'</COLOR>
     <DONE>FALSE</DONE>
</JOB>
<JOB>
     <NAME>Brian</NAME>
     <ID>2</ID>
     <COLOR>'red'</COLOR>
     <DONE>TRUE</DONE>
</JOB>

您可以看到,表格的每一行都由一个 <JOB> 标签表示,并且在每个 JOB 中,表格的列名已被用作标签来表示各种信息字段。显然,以这种格式存储数据比标准表格占用更多的磁盘空间,因为 XML 标签占用磁盘空间,但 XML 的灵活性要大得多,因为它不受行和列格式的限制。因此,它在需要这种灵活性的应用程序和文档中变得流行。

3.2.4. JSON

虽然不是标记语言,但 JavaScript 对象表示法(JSON)在功能上相似,至少在存储或传输数据时是这样。JSON 通常描述的更像是一种数据结构,例如在许多流行的编程语言中的列表、映射或字典。以下是表格 JOBS_2015 的前两行的 JSON 数据:

[
     {
          NAME: "Alison",
          ID: 1,
          COLOR: "blue",
          DONE: False
     },
     {
          NAME: "Brian",
          ID: 2,
          COLOR: "red",
          DONE: True
     }
]

在结构方面,这种 JSON 表示形式看起来与您已经看到的 XML 表示形式非常相似。但 JSON 表示形式在表达数据所需的字符数量上更为精简,因为 JSON 被设计用来表示数据对象,而不是作为文档标记语言。因此,在传输数据方面,JSON 已经变得非常流行。JSON 的一个巨大好处是它可以直接作为 JavaScript 代码读取,许多流行的编程语言,包括 Python 和 Java,都有对 JSON 作为原生数据对象的自然表示。对于编程语言之间的互操作性,JSON 几乎无与伦比,其易用性极高。

3.2.5. 关系数据库

数据库 是一种数据存储系统,它被优化以在各种环境中尽可能高效地存储和检索数据。从理论上讲,关系数据库(最常见的数据库类型)包含的不仅仅是表格的集合,这些表格同样可以用已讨论的分隔文件来表示:行和列的名称以及每一行-列对的单一数据点。但数据库被设计为搜索——或者用常见的术语来说,查询——表格条目中的特定值或值范围。

例如,让我们回顾一下 JOBS_2015 表:

NAME      ID     COLOR     DONE
Alison    1     'blue'     FALSE
Brian     2     'red'      TRUE
Clara     3     'brown'    TRUE

但这次假设这个表格是存储在数据库中的许多表格之一。一个数据库查询可以用简单的英语表述如下:

From JOBS_2015, show me all NAME in rows where DONE=TRUE

这个查询应该返回以下内容:

Brian
Clara

这是一个基本的查询,尽管许多数据库都有自己的语言来表述这样的查询,尽管许多数据库共享相同的查询语言基础,最常见的是结构化查询语言(SQL)。

现在想象一下,如果表格包含数百万行,并且你想执行与刚才显示的类似的查询。通过一些软件工程的技巧,这里不会详细讨论,一个设计良好的数据库能够比扫描平面文件更快地检索出符合某些标准(查询)的表格行集。这意味着如果你正在编写一个需要经常搜索特定数据的应用程序,如果你使用数据库而不是平面文件,你可能会将检索速度提高几个数量级。

数据库擅长快速检索特定数据的主要原因在于数据库索引。一个 数据库索引 本身就是一个数据结构,它帮助数据库软件快速找到相关数据。它就像数据库内容的结构图,已经以巧妙的方式排序和存储,并且可能需要在数据库中的数据发生变化时更新。然而,数据库索引并非通用,这意味着数据库管理员需要选择哪些表格列应该被索引,如果默认设置不合适的话。被选为索引的列是查询将最有效的列,因此索引的选择对于使用该数据库的应用程序的效率来说是一个重要的选择。

除了查询之外,数据库通常擅长的另一个操作是连接表。查询和连接并不是数据库擅长的唯一两个操作,但它们是使用数据库而不是其他数据存储系统的最常见原因。在数据库术语中,“连接”意味着将两个数据表合并,以创建一个新的表,该表包含两个原始表的一些信息。

例如,假设你有一个名为 CUST_ZIP_CODES 的以下表格:

CUST_ID  ZIP_CODE
1        21230
2        45069
3        21230
4        98033

你想调查 2015 年中哪些油漆颜色被用于哪些 ZIP 代码。因为各种工作中的颜色在 JOBS_2015 表中,而客户的 ZIP 代码在 CUST_ZIP_CODES 表中,你需要连接这两个表以匹配颜色和 ZIP 代码。可以使用以下简单的英语描述内连接,匹配 JOBS_2015 表的 IDCUST_ZIP_CODES 表的 CUST_ID

JOIN tables JOBS_2015 and CUST_ZIP_CODES where ID equals CUST_ID, and
show me ZIP_CODE and COLOR.

你是在告诉数据库首先匹配两个表中的客户 ID 号码,然后只显示你关心的两列。请注意,两个表之间没有重复的列名,所以没有命名上的歧义。但在实践中,你通常需要使用类似 CUST_ZIP_CODES.CUST_ID 的表示法来表示 CUST_ZIP_CODES 表的 CUST_ID 列。这里我使用简短版本以节省篇幅。

连接的结果看起来可能如下所示:

ZIP_CODE    COLOR
21230       'blue'
45069       'red'
21230       'brown'

如果原始表很大,连接可能是一个非常庞大的操作。如果每个表都有数百万个不同的 ID,那么对所有这些 ID 进行排序和匹配可能需要很长时间。因此,如果你要连接表,你应该尽量减小这些表的大小(主要是行数),因为数据库软件将不得不根据连接标准对两个表的所有行进行重新排序,直到在新表中创建所有适当的行组合。连接应该谨慎且小心地进行。

如果你要进行查询和连接操作,一个好的通用规则是先查询数据,然后再进行连接。例如,如果你只关心 ZIP_CODE 21230 中的 COLOR,通常最好是先查询 CUST_ZIP_CODES 表中的 ZIP_CODE=21230,然后将结果与 JOBS_2015 表连接,而不是先连接然后查询。这样,可能需要匹配的记录会少得多,整体操作执行速度也会更快。关于优化数据库操作的信息和指导,你可以在市面上找到许多实用的数据库书籍。

你可以将数据库一般地看作是有良好组织的图书馆,它们的索引就像是优秀的图书管理员。图书管理员可以在几秒钟内找到你需要的那本书,而你可能需要花很长时间才能找到它。如果你有一个相对较大的数据集,并且发现你的代码或软件工具在任意时刻都在花费大量时间搜索所需数据,那么设置数据库绝对值得考虑。

3.2.6. 非关系型数据库

即使你没有表格数据,你也可能仍然能够利用数据库索引的效率。一类名为 NoSQL(通常解释为“不仅限于 SQL”)的数据库允许在更传统的 SQL 风格的关系数据库之外使用数据库模式。图数据库和文档数据库通常被归类为 NoSQL 数据库。

许多 NoSQL 数据库以熟悉的格式返回查询结果。例如,Elasticsearch 和 MongoDB 返回的结果以 JSON 格式(在第 3.2.4 节中讨论)。Elasticsearch 尤其是一个面向文档的数据库,在索引文本内容方面非常出色。例如,如果你正在处理大量的博客文章或书籍,并且执行诸如计算每篇博客文章或书籍中单词出现次数的操作,那么如果正确索引,Elasticsearch 通常是一个不错的选择。

一些 NoSQL 数据库的另一个可能优势是,由于模式的高度灵活性,你可以几乎将任何东西放入 NoSQL 数据库而不会遇到太多麻烦。字符串?映射?列表?当然!为什么不呢?例如,MongoDB 非常容易设置和使用,但这样你可能就会失去通过设置更严格的索引和适用于你数据的模式所获得的一些性能。

总的来说,如果你正在处理大量非表格数据,那么很可能有人已经开发了一个擅长索引、查询和检索你这类数据的数据库。在网上快速查找一下,看看别人在类似情况下使用什么,这绝对值得。

3.2.7. API

一种 应用程序编程接口 (API) 在其最常见的形式中是一套与软件进行通信的规则。在数据方面,可以将 API 视为一个网关,通过它你可以发出请求并接收数据,使用一组定义良好的术语。数据库有 API;它们定义了你在查询中必须使用的语言,例如,为了接收你想要的数据。

许多网站也有 API。例如,Tumblr 就有一个公开的 API,允许你请求并接收有关 Tumblr 某类内容的详细信息,以 JSON 格式。Tumblr 拥有包含其博客服务上托管的所有数十亿条帖子的巨大数据库。但它已经决定了作为公众成员的你可以在数据库中访问什么以及不能访问什么。访问方法和限制由 API 定义。

Tumblr 的 API 是一个可通过 HTTP 访问的 REST API。我从未发现 REST API 的技术定义很有帮助,但这是一个当人们讨论可通过 HTTP 访问的 API 时使用的术语——这意味着您通常可以从网页浏览器中访问它们,并且以熟悉的格式返回信息。例如,如果您在 Tumblr 上注册为开发者(免费),您可以得到一个 API 密钥。这个 API 密钥是一个只属于您的字符串,它告诉 Tumblr 每当您发起请求时,您正在使用 API。然后,在您的网页浏览器中,您可以粘贴 URL api.tumblr.com/v2/blog/good.tumblr.com/info?api_key=API_KEY,这将请求有关 Tumblr 上特定博客的信息(用您获得的 API 密钥替换 API_KEY)。按下 Enter 后,响应应该出现在您的浏览器窗口中,看起来像这样(经过一些重新格式化):

{
    meta:
    {
        status: 200,
        msg: "OK"
    },
    response:
    {
        blog:
        {
            title: "",
            name: "good",
            posts: 2435,
            url: "http://good.tumblr.com/",
            updated: 1425428288,
            description: "<font size="6">
                        GOOD is a magazine for the global citizen.
                        </font>",
            likes: 429
        }
    }
}

这是在 description 字段中包含一些 HTML 的 JSON。它包含有关请求状态的元数据,然后是一个 response 字段,包含请求的数据。假设您知道如何解析 JSON 字符串(以及类似地 HTML),您可以使用它以编程方式。如果您对 Tumblr 博客的点赞数感兴趣,您可以使用此 API 请求有关任何数量博客的信息,并比较它们收到的点赞数。但是,您不应该从您的浏览器窗口中这样做,因为这会花费很长时间。

为了以编程方式捕获 Tumblr API 的响应,您需要在您喜欢的编程语言中使用 HTTP 或 URL 包。在 Python 中有 urllib,在 Java 中有 HttpUrlConnection,而 R 有 url,但每种语言都有许多执行类似任务的包。无论如何,您都需要组装请求 URL(作为一个字符串对象/变量),然后将该请求传递给适当的 URL 获取方法,该方法应返回一个类似于上一个响应的响应,可以捕获为另一个对象/变量。以下是一个 Python 中的示例:

import urllib

requestURL = \
    'http://api.tumblr.com/v2/blog/good.tumblr.com/info?api_key=API_KEY'

response = urllib.urlopen(requestURL)

运行这些代码后,变量 response 应该包含一个类似于之前显示的 JSON 字符串。

我记得我是从 Python 中学习如何使用这样的 API 的,一开始我有点困惑和不知所措。如果从各个部分(例如,基本 URL、参数、API 密钥等)以编程方式组装请求 URL,那么得到完全正确的请求 URL 可能有点棘手。但熟练使用这样的 API 可以成为数据收集中最强大的工具之一,因为通过这些网关可以获取到大量数据。

3.2.8. 常见错误格式

我不是办公室软件套件的粉丝,这并不是什么秘密:文字处理程序、电子表格、邮件客户端。幸运的是,我很少需要使用它们。只要可能,我都会避免使用它们,尤其是在做数据科学时。这并不意味着我不会处理这些文件;相反,我不会放弃免费的数据。但我确保尽快摆脱任何不便的格式。除非我使用为它们专门构建的高度专业化的程序,否则通常没有很好的方法与它们交互。这些程序通常无法进行数据科学家通常需要的分析。我不记得上一次我是如何(或看到)在 Microsoft Excel 中做(或看到)一段扎实的科学数据;对我来说,Excel 的分析方法有限,界面对于查看表格以外的任何东西来说都不方便。但我知道我有偏见,所以如果你确信你可以在电子表格内进行严谨的分析,请不要介意我。OpenOffice Calc 和 Microsoft Excel 都允许您将电子表格中的单个工作表导出为 CSV 格式。如果 Microsoft Word 文档包含我想使用的文本,我会将其导出为纯文本、HTML 或 XML 格式。

PDF 也可能是一个棘手的东西。我已经从 PDF 中导出了很多文本(或复制粘贴)到纯文本文件中,然后我将这些文件读入 Python 程序。这是我最喜欢的数据整理例子之一,我将在整个章节中讨论这个话题,所以现在只需说,在可能的情况下,从 PDF(如果可能)导出或抓取文本通常是一个好主意,无论你想要分析该文本。

3.2.9. 不寻常的格式

这是所有我不熟悉的数据格式和存储系统的总类别。各种格式都可用,我相信有人出于很好的理由开发了它们,但出于某种原因,它们对我来说并不熟悉。有时它们是过时的;也许它们被另一种格式取代了,但一些遗留数据集尚未更新。

有时格式非常专业。我曾经参与过一个项目,探索一个化合物的化学结构和它与化合物气味(其香气)的联系。RDKit包(www.rdkit.org)提供了大量有用的功能,用于解析化学结构和子结构。但其中许多功能高度特定于化学结构和其符号。此外,该包对化学结构的某些方面的相当复杂的二进制表示进行了大量使用,这大大提高了算法的计算效率,但也使得它们极其难以理解。

当我遇到一个与我之前所见完全不同的数据存储系统时,我会这样做:

  1. 在线搜索并搜索(再搜索)几个与我想要做的事情类似的人的例子。将这些例子适应我的需求可能会有多难?

  2. 决定我有多想得到这些数据。这是否值得麻烦?有哪些替代方案?

  3. 如果值得,我会尝试从找到的类似例子中归纳总结。有时,通过调整参数和方法,我可以逐渐从例子中扩展。我会尝试几件事情,看看会发生什么。

处理完全陌生的数据格式或存储系统可能是其自身的一种探索类型,但请放心,某处有人之前已经访问过这些数据。如果没有人访问过这些数据,那么在最初创建数据格式时,有人肯定犯了完全的错误。如有疑问,发送几封电子邮件,尝试找到能帮助你的人。

3.2.10. 决定使用哪种格式

有时候你并没有选择。数据以某种格式传入,你必须处理它。但如果你发现这种格式效率低下、难以操作或不受欢迎,你通常可以自由地设置一个二级数据存储,这可能使事情变得更容易,但代价是设置二级数据存储所需的时间和精力。对于访问效率至关重要的应用,这种成本可能是值得的。对于较小的项目,可能就不一定了。当你到达那里时,你必须跨过那座桥。

我将以一些关于在可以选择的情况下以及特别是当你打算从编程语言访问数据时使用哪些数据格式的通用规则来结束本节。表 3.1 给出了与特定类型数据交互的最常见优秀格式。

表 3.1. 一些常见的数据类型和适合存储它们的格式
数据类型 优秀的,常见的格式
表格数据,数量较少 分隔的平面文件
表格数据,大量且需要大量搜索/查询 关系型数据库
纯文本,数量较少 平面文件
纯文本,大量 具有文本搜索功能的关系型数据库
在组件之间传输数据 JSON
传输文档 XML

这里还有一些关于选择或转换数据格式的指南:

  • 对于电子表格和其他办公文档,导出!

  • 更常见的格式通常更适合你的数据类型和应用。

  • 不要花太多时间从某种格式转换到你的首选格式;首先权衡成本和收益。

现在我已经涵盖了数据可能呈现给你的许多形式,希望你在关于数据格式、存储和 API 的高级对话中会感到有些舒适。一如既往,不要犹豫,向别人询问你之前没听过的术语或系统的详细信息。新的系统正在不断开发中,根据我的经验,最近了解过某个系统的人通常很乐意帮助他人了解它。

3.3. 数据侦察

上一节讨论了许多数据常见的格式,从文件格式到数据库再到 API。我的目的是使这些数据形式更容易接近,并提高您对寻找数据方式的意识。找到数据并不难,就像在特定气候中找到一棵树或一条河一样简单。但找到能帮助您解决问题数据则是另一回事。或者,也许您已经从内部系统中获得了数据。这些数据似乎可以回答您项目的重大问题,但您不应该想当然。也许某个数据集可以完美地补充您已经拥有的数据,并极大地提高结果。互联网和其他地方有如此多的数据;其中一部分应该能够帮助您。即使不能,快速搜索也绝对值得,即使是对可能性很小的搜索也是如此。

在本节中,我讨论了寻找可能帮助您项目的数据的行为。这正是我在本章开头所提到的探索。现在,您已经对上一节中常见的数据形式有所了解,您可以减少对格式的关注,更多地关注内容和它是否能够帮助您。

3.3.1. 第一步:谷歌搜索

这可能看起来很明显,但我仍然觉得有必要提一下:谷歌搜索并不完美。为了使它们尽可能有效,您必须知道要搜索什么以及您在搜索结果中寻找什么。鉴于上一节对数据格式的介绍,您现在在谷歌搜索中比以前有更多的“弹药”。

谷歌搜索“Tumblr 数据”和搜索“Tumblr API”的结果不同。鉴于我目前没有涉及 Tumblr 的具体项目,我不确定我更喜欢哪一个。前者返回涉及 Tumblr 帖子中使用的术语数据以及第三方销售历史 Tumblr 数据的结果。后者返回几乎完全与官方 Tumblr API 相关的结果,其中包含有关 Tumblr 帖子的最新信息。根据您的项目,一个可能比另一个更好。

但确实值得记住的是,诸如数据API之类的术语在网页搜索中确实会产生影响。尝试搜索“社交网络”和“社交网络 API”。结果有明显的差异。

因此,在搜索与您的项目相关的数据时,务必包括如历史API实时等修饰词,因为它们确实会产生影响。同样,在搜索结果中也要注意它们。这可能看起来很明显,但它对您找到所需内容的能力有相当大的影响,因此值得重复。

3.3.2. 版权和许可

我已经讨论了搜索、访问和使用数据,但还有一个非常重要的关注点:您是否允许使用它?

就像软件许可证一样,数据可能存在许可、版权或其他限制,这可能会使得在特定目的下使用数据成为非法行为。例如,如果数据来自学术来源(如大学、研究机构等),那么通常会有一个限制,即数据不能用于盈利。像 Tumblr 或 Twitter 这样的专有数据,通常带有限制,即你不能使用数据来复制平台本身提供的功能。你可能无法制作一个与标准 Tumblr 平台做同样事情的 Tumblr 客户端,但如果你提供平台不包括的其他功能,可能就不会有限制。这样的限制很棘手,最好阅读数据提供者提供的任何法律文件。此外,通常好的做法是搜索其他人或公司以类似方式使用数据的例子,看看是否有任何关于法律问题的参考。先例并不能保证特定数据的使用在法律上是合理的,但它可能在你是否使用数据的决定上提供指导。

总而言之,你应该敏锐地意识到,大多数不属于你或你所在组织的数据集都带有使用限制。如果不确认你的使用案例是合法的,你仍然面临失去数据访问权或更糟糕的是,面临诉讼的风险。

3.3.3. 你拥有的数据:是否足够?

假设你已经找到了数据,并确认你可以为你的项目使用它。你应该继续寻找更多数据,还是应该立即处理你拥有的数据?这个问题的答案——就像数据科学中的几乎所有事情一样——很棘手。在这种情况下,答案之所以棘手,是因为数据集并不总是像它们看起来或你希望它们看起来的那样,或者你希望它们成为的那样。以 Uber 为例,这是一家出租车服务应用发布商。我最近读到,Uber(在败诉后)被迫(在上诉失败后)向纽约市的出租车和豪华轿车委员会(TLC)提交行程数据。假设你是 TLC 的员工,你希望比较 Uber 与传统出租车服务在许多特定路线上的乘客行程数量。鉴于你拥有 Uber 和传统出租车服务的数据,似乎很容易比较两种汽车服务之间相似路线的行程数量。但一旦你开始分析,你就会意识到 Uber 提供的是以 ZIP 代码为单位的接送地点,而这恰好是 TLC 所要求的最低具体性。ZIP 代码可以覆盖很大的区域,尽管在纽约市可能不如其他地方那么大。从数据分析的角度来看,地址或至少城市街区会更好,但要求这样的具体性会带来关于出租车服务用户个人隐私的法律问题,因此这是可以理解的。

那么,你应该怎么做呢?在最初的失望情绪消退之后,你可能需要检查你的数据是否足够,或者你是否需要补充这些数据以及/或者修改你的项目计划。通常,有一种简单的方法可以完成这个任务:你能通过几个你打算分析的特定例子来运行一下,看看是否会产生显著的影响吗?

在这个出租车与优步的例子中,你想要找出 ZIP 代码的相对非特异性是否仍然可以为你想要评估的许多路线提供一个有用的近似值。选择一个特定的路线,比如说从时代广场(ZIP 代码:10036)到布鲁克林音乐学院(ZIP 代码:11217)。如果一辆车在 10036 和 11217 之间行驶,那么乘客可能还乘坐了哪些其他特定的路线?在这种情况下,那些相同的 ZIP 代码也可能描述从无畏号海、空与太空博物馆到格兰德军营广场的行程,或者同样是从地狱厨房的一家餐厅到帕克斜坡的一套公寓的行程。这些可能对纽约市以外的人来说意义不大,但就我们的目的而言,可以说这些其他地点距离所选路线的起点和终点最多一公里,这个距离大约是十分钟的步行路程,按照纽约的标准,并不是很短。决定这些在同一 ZIP 代码中的其他地点是否足够接近或太远于它们的目标,这取决于你,作为数据科学家。这个决定反过来应该基于项目的目标和(来自第二章)你希望回答的精确问题。

3.3.4. 合并数据源

如果你发现你的数据集不足以回答你的问题,而且你找不到足够的数据集,那么仍然有可能通过合并数据集来找到答案。这看似是一个显而易见但值得提及的观点,因为它的重要性,以及可能出现的几个棘手问题。

将两个(或更多)数据集合并可能就像拼拼图一样。如果这个拼图在比喻意义上是你希望拥有的完整数据集,那么拼图的每一块——即数据集——都需要精确地覆盖其他拼图块所没有的部分。当然,与拼图块不同,数据集在某种程度上可以重叠,但所有现有拼图块组合后留下的任何间隙都是一个需要克服或绕过的障碍,无论是通过改变计划还是通过重新评估你将如何回答你的问题。

你的多个数据集可能以多种格式到来。如果你擅长操作这些格式中的每一个,这通常不会造成问题,但如果它们以截然不同的形式存在,那么理解数据集之间如何相互关联可能会很困难。对我来说,数据库表和 CSV 文件很相似——它们都有行和列——所以我通常可以想象它们可能如何结合在一起,就像本章前面提到的数据库示例中,一个数据集(一个表)提供客户的颜色选择,另一个数据集(另一个表)提供客户的 ZIP 代码。这两个可以很容易地结合起来,因为这两个数据集都是基于相同的客户 ID 集。如果你能想象如何将两个数据集中的客户 ID 匹配起来,然后结合相关信息——在数据库术语中称为“连接”——那么你可以想象如何有用地结合这两个数据集。

另一方面,合并数据集可能并不那么简单。在我担任巴尔的摩一家分析软件公司首席数据科学家期间,我参与了一个项目,我们的团队在法律调查中分析电子邮件数据集。这些电子邮件以几十个 PST 格式的文件形式交付给我们,这是微软 Outlook 的存档格式。我之前见过这种格式,因为我之前曾与现在公开且广泛研究的安然电子邮件数据集合作过。每个存档文件包含一个人的电脑上的电子邮件,由于被调查的人经常互相发送电子邮件,数据集存在重叠。除了已删除的电子邮件外,每封电子邮件都存在于发送者和接收者的存档中。人们可能会觉得将所有电子邮件存档合并成一个文件很容易——我选择了一个简单的大型 CSV 文件作为目标格式,然后分析这个文件。但事实并非如此。

从每个存档中提取单个电子邮件并将它们转换为 CSV 文件的一行相对容易。我很快意识到,困难的部分是确保我能清楚地记录所有发送者和接收者。实际上,电子邮件发送者和接收者字段中列出的名称并不标准化——当你发送电子邮件时,SENDER字段中显示的内容并不总是与某人给你发送电子邮件时RECIPIENT字段中显示的内容相同。事实上,即使在每个这些字段中,名称也不一致。如果尼古拉·特斯拉给托马斯·爱迪生(为保护无辜者,姓名已更改)发送电子邮件,SENDERRECIPIENT字段可能是以下任何一种:

SENDER                             RECIPIENT
Nikola Tesla <nikola@ac.org>       Thomas Edison, CEO <thomas@coned.com>
Nikola <nikola.tesla@ac.org>       thomas.edison@dc.com
ntesla@gmail.com                   tommyed@comcast.com
nikola@tesla.me                    Tom <t@coned.com>
wirelesspower@@tesla.me            litebulbz@hotmail.com

其中一些可能被识别为特斯拉或爱迪生,即使在没有上下文的情况下也是如此,但其他一些则不然。为了确保每封电子邮件都归因于正确的人,你还需要一个与正确姓名匹配的电子邮件地址列表。我没有那个列表,所以我尽我所能,做出了一些假设,并使用了一些模糊字符串匹配和抽查(在下一章关于数据整理中讨论)来尽可能多地匹配电子邮件与适当的姓名。我认为多个电子邮件数据集可以很好地合并在一起,但很快我就发现情况并非如此。

数据集可以以任何数量的方式不同;格式、命名法、范围(地理、时间等)只是其中的一些。正如第 3.3.3 节中提到的,在花费太多时间处理多个数据集或进行深入分析之前,了解你的数据是否足够,通常是非常有帮助和有信息的,可以在小规模上检查几个数据点并尝试快速分析。在电子邮件示例中快速查看几个 PST 文件让我意识到文件和字段之间不同的命名方案,并使我能够在项目中计划额外的时间和不可避免的匹配错误。

现在想象一下,将这个电子邮件数据集与内部聊天消息(可能包含不同的用户名)以及专有日历格式的活动/预约结合起来——将它们组装成一个具有明确用户名的单一时间线并不是一个简单的任务,但通过小心谨慎和意识到潜在的风险,这可能是有可能的。

3.3.5. 网络爬虫

有时候,你可以在互联网上找到你需要的信息,但这并不是传统意义上的数据集。社交媒体资料,如 Facebook 或 LinkedIn 上的资料,是互联网上可查看但不易以标准数据格式获取的数据的好例子。因此,有些人选择从网络上爬取这些数据。

我必须明确指出,网络爬虫在很多网站的条款服务中都是被禁止的。有些网站设置了防护措施,一旦检测到爬虫,就会关闭你的访问权限。有时他们能检测到你,因为你访问网页的速度比人类快得多,比如几分钟内访问几千页,甚至几小时。无论如何,人们已经使用爬虫技术收集了他们原本无法获得的有用数据,在某些情况下,通过调整爬虫的行为使其更像人类来绕过任何防护措施。

一个网络爬虫必须做好的两件重要事情是程序化地访问大量 URL 并从页面中捕获正确的信息。如果你想知道你在 Facebook 上的朋友网络,理论上你可以编写一个脚本,访问你所有朋友的 Facebook 个人资料,保存个人资料页面,然后解析页面以获取他们朋友的列表,访问他们朋友的个人资料,等等。这仅适用于允许你查看他们个人资料和好友列表的人,而对于私人资料则不起作用。

2014 年初流行的一个网络爬虫的例子是数学家克里斯·麦金莱(Chris McKinlay)的例子,他使用网络爬虫从流行的约会网站 OKCupid 上的数千个个人资料中捕获数据。他使用收集到的信息——主要是女性对该网站上多项选择题的回答——将女性分为几种类型,并随后为每种他认为有吸引力的类型优化了自己的个人资料。因为他为每种女性集群/类型优化了个人资料,所以该集群中的女性与相应个人资料有很高的匹配分数(根据 OKCupid 自己的算法),因此更有可能与他交谈,并最终与他出去约会。这似乎对他很有帮助,在他遇到想要开始一夫一妻关系的那位女性之前,他已经有数十次约会了。

关于构建网络爬虫的实用性,请参阅您喜欢的编程语言的 HTTP 和 HTML 相关实用程序的文档,以及任何数量的在线指南,特别是关于数据格式第 3.2 节的讨论,尤其是关于 HTML 的讨论。

3.3.6. 自己测量或收集事物

与我在本章中展示数据的主要方式相反——这是一种想要数据本身的文化产物,无论某人是否打算使用它——你有时有机会以传统方式收集数据。方法可能简单到亲自数一数在特定人行横道处穿越的人数,或者也许给一群感兴趣的人发送调查问卷。当你开始一个新项目时,如果你曾问自己,“我需要的数据存在吗?”并且发现答案是“不存在”或“存在,但我无法获取它”,那么也许问一问“数据可以存在吗?”会有所帮助。

“数据可以存在吗?”这个问题旨在引起人们对你可以采取的简单措施的关注,这些措施可以创建你想要的数据集。以下是一些包括在内的措施:

  • 现实生活中测量事物——使用卷尺、计数、亲自提问等方法可能看起来过时,但它们通常被低估。

  • 在线测量事物— 在互联网上四处点击,计算相关网页、相关谷歌搜索结果的数量以及某些维基百科页面上某些术语的出现次数等,都可以对你的项目有所帮助。

  • 脚本和网页抓取— 在一定时间内重复 API 调用或抓取某些页面可能是有用的,当 API 或网页中的某些元素不断变化,但你无法访问历史记录时。

  • 数据收集设备— 今天的物联网概念在很大程度上得益于其从物理设备中创建数据的价值,其中一些设备能够记录物理世界——例如,摄像头、温度计和陀螺仪。你有没有一个设备(比如你的手机)能帮助你?你能买一个吗?

  • 日志文件或存档— 日志文件有时被俗称为“数字足迹”或“残骸”,它们是由许多软件应用程序留下的(或可以留下的)。它们通常在非常特殊的情况下才会被投入使用(崩溃!错误!)!你能在你的项目中将它们用于良好的用途吗?

对于最后一个要点,与网络爬虫类似,主要任务是手动识别日志文件中是否以及在哪里包含有助于你的数据,并学习如何从包含大量你永远不会需要的数据的日志文件集中程序化地提取这些有用的数据。这可能就是数据荒野的前沿:使用为完全不同的目的而存在的其他数据创建概念上全新的数据集。我相信有人提出了“数据炼金术”作为这种现象的可能名称,但我会让你自己判断你自己的数据提取和转换是否值得这样一个神秘的称号。

3.4. 示例:miRNA 和基因表达

当我还是一名博士生时,我的大部分研究都与基因表达的定量建模相关。我之前提到过我在遗传学方面的工作,但直到现在我才深入探讨。我发现这是一个极其有趣的领域。

遗传学是研究构成所有生物的代码的科学。这个代码存在于每个生物体的基因组中,由 DNA 或 RNA 组成,并且在每个细胞中都存在其副本。如果一个生物体的基因组已经被测序,那么它的基因组已经被解析成基因和其他类型的非基因序列。在这里,我只关注基因及其表达。生物学家对基因表达的概念涉及生物样本中已知基因的活动,我们使用任何可以测量与这些基因直接相关的特定 RNA 序列片段的拷贝数或浓度的工具来测量基因表达。如果一个 RNA 片段包含一个已知与某个基因匹配但与其他基因不匹配的序列,那么这个序列可以用作该基因表达的指标。如果这个 RNA 序列在生物样本中非常频繁地出现(高拷贝数或浓度),那么相应基因的表达就被认为是高的,而出现频率低的序列则表明其相关的基因表达水平低。

两种称为微阵列测序的技术是测量生物样本中 RNA 序列浓度或拷贝数的常用方法,以测量基因表达。现在测序通常更受欢迎,但在我的博士研究时期,我正在分析微阵列的数据。这些数据是由马里兰大学医学院的一位合作者提供的,他一直在研究Mus musculus(一种常见的老鼠)在各个发育阶段的干细胞。在最早阶段,干细胞被知为一种可以随后发展成为多种分化或特殊细胞类型的通用类型。细胞通过这些未分化阶段和随后特定分化干细胞类型的进展并不完全理解,但我的合作者和其他人已经假设,一类称为微 RNA 的特殊 RNA 序列可能参与其中。

微 RNA(或 miRs)是一类短的 RNA 序列(大约 20 个碱基对,远短于大多数基因),已知几乎存在于所有生物体中。为了确定 miRs 是否有助于调节干细胞的发育和分化,我的合作者使用微阵列测量了基因和 miRs 在干细胞发育早期阶段的表达。

数据集包括七个干细胞类型中每个基因和 miR 的微阵列数据。单个微阵列可以测量几千个基因,或者,也可以测量几百个 miR。对于每种干细胞类型,有两个到三个重复,这意味着每个生物样本都使用两个到三个基因导向的微阵列和两个到三个 miR 导向的微阵列进行分析。重复有助于分析样本之间的差异以及识别异常值。考虑到有 7 种干细胞类型,每种类型基因和 miR 各有 2 到 3 个重复,我总共有了 33 个微阵列。

因为 miR 被认为主要是抑制基因的表达——它们显然与遗传 RNA 的互补部分结合并阻止该 RNA 被复制——我对数据集的主要问题是“我能否找到任何特定 miR 抑制特定基因表达的证据?”当特定 miR 的表达量高时,任何特定基因的表达是否通常都很低?此外,我还想知道任何 miR 的表达和抑制活性是否可以与干细胞发育和分化的特定阶段高度相关。

虽然没有人之前研究过这个具体主题——miR 在小鼠干细胞发育中的影响——但已经在相关主题上做了相当多的工作。特别值得注意的是,一类统计算法试图仅根据序列信息来描述一个特定的 miR 是否会靶向(抑制)遗传 RNA 的特定部分。如果一个 miR 的碱基序列看起来像这样

ACATGTAACCTGTAGATAGAT

(再次,为了方便,我用 T 代替 U),那么一个完美的互补遗传 RNA 序列会是

TGTACATTGGACATCTATCTA

因为,在 RNA 序列中,核苷酸 A 与 T 互补,C 与 G 互补。由于这些 miR 在细胞的细胞质中漂浮,就像遗传 RNA 序列一样,因此即使是一个完美的匹配也不能保证会结合并抑制基因表达。在完美的条件下,这些互补序列会结合,但生物学中没有什么是完美的。据说是这样,一个 miR 和它的完美匹配就像两艘在夜幕下错过的船一样漂浮过去。此外,RNA 序列的一个有趣的特性是,有时它们会弯曲得太多而粘附在自己身上——对于 miR 来说,结果是被称为发夹,因为这种形状很容易想象。无论如何,完美互补序列的结合并不是必然的;同样,不完美的匹配也不会结合。许多研究人员已经探索了这个问题,并开发了基于序列互补性和其他特征的算法,为 miR-基因对分配匹配分数。这些通常被称为目标预测算法,我在我的工作中使用了两个这样的算法:一个叫做 TargetScan (www.targetscan.org),另一个叫做 miRanda (www.microrna.org)).

TargetScan 和 miRanda 都被广泛视为坚实科学研究的产物,这两个算法及其预测结果都可以在互联网上免费获取。对于我的微阵列数据集中的任何 miR-基因对,我至少有两个目标预测分数,表明 miR 是否可能抑制基因的表达。我从 TargetScan 获得的数据文件看起来像这样(为了清晰起见,删除了一些列):

Gene ID    miRNA         context+ score    percentile
71667      xtr-miR-9b    -0.259            89
71667      xtr-miR-9     -0.248            88
71667      xtr-miR-9a    -0.248            88

如您所见,对于每个基因和 miR/miRNA,TargetScan 都给出一个分数,表示 miR 靶向遗传 RNA 的可能性。miRanda 提供了类似的文件。这些分数被认为是并不完美,但它们是有信息的,所以我决定将它们包括作为 miR 抑制基因表达的证据,但不是确凿的证据。

我的主要数据集仍然是我从合作者的实验室获得的微阵列数据集,从这些数据中我可以分析所有基因和 miR 的表达值,并确定它们之间的正负相关性。此外,我还可以将目标预测作为支持某些 miR-基因靶对进一步的证据。在贝叶斯统计框架内——在第八章中讨论得更多——目标预测可以被视为先验知识,我可以根据我收集的新数据调整这种知识——即我从合作者那里收到的新的微阵列数据。这样,预测和噪声数据集都不被视为真理,但它们都为最终估计哪些 miR-基因对最可能是真正的靶向相互作用提供了信息。

到目前为止,在本节中,我谈到了将基因表达数据与 microRNA 数据相结合,以搜索它们之间的靶向相互作用,并分析 miRs 对干细胞发育的影响。此外,我还包括了两个靶标预测数据集作为进一步证据,表明某些 miRs 靶向某些基因。在我基于这些数据集完成分析后,我需要能够表明我的模型指示与干细胞发育相关的 miRs 和基因在某种程度上是有意义的。我可能会以两种方式来做这件事:要求我的生物学家合作者在实验室中测试我的部分结果以确认它们是正确的,或者在网上找到一些已经验证并支持我的结果的数据集。

如果我没有与这类数据工作的经验,我可能会在谷歌上搜索“已验证的 microRNA 靶向”或“干细胞发育基因注释”,但由于我知道从过去的项目中,一个名为基因本体(GO)术语的大规模公共基因注释集是可用的,以及一个已报道在科学出版物中的已验证的 miR-基因靶向相互作用数据库,所以我无需搜索太多。GO 术语注释可以通过几个基于网络的工具(geneontology.org)以及 R 语言的包等访问。我之前曾使用这些注释来分析基因组,看看它们是否有共同之处。在这个项目中,如果任何在干细胞发育方面具有显著性的基因组在我的模型中也有与干细胞和干细胞发育相关的显著数量的 GO 注释,这将有助于证实我的结果。

此外,我显然更倾向于认为,我模型发现的任何具有显著性的 miR-基因靶标对,已经通过其他可靠的方式进行过验证。这就是www.microrna.org已验证的靶向相互作用数据集的作用所在。这当然是一个有用的数据集,但其中一点重要之处在于,尽管一些 miR-基因靶标对已经得到确认,但这并不意味着那些尚未得到确认的对就不是真正的靶标对。如果我的模型发现某个特定的靶标对具有显著性,但尚未经过验证,这并不表明模型是错误的。另一方面,如果一个已验证的靶标对根据我的模型没有显示出显著性,那么这就有一些值得关注的理由。总的来说,在我的项目验证步骤中,我希望所有或大多数已验证的靶标对根据模型显示出显著性,但我不一定需要看到我最重要的结果都有验证。

最后,我的合作者对哪些 miRNA 家族(具有部分匹配序列的 miRs 组)对干细胞发育的哪些阶段做出了贡献很感兴趣。结果发现,TargetScan 提供了一个很好地匹配 miRs 及其家族的格式化文件。除了基因表达微阵列、miRNA 表达微阵列、两个目标预测算法的结果、一组基因注释和一些已验证的 miR-gene 目标对之外,我还添加了一个 miR 家族数据集。

不言而喻,这个项目中有很多环节。此外,在学术界,这种情况相当常见,最终的科学论文无法包含每一项分析。有一篇论文描述了模型及其在公共数据集上的应用(“通过部分有序样本的配对表达数据和外源性预测算法推断 mRNA 的 miRNA 调控”,PloS ONE,2012 年)和另一篇论文描述了其在小鼠数据集上的应用(“小鼠造血干细胞和祖细胞亚群的 miR-mRNA 表达相关特征预测‘干性’和‘髓性’相互作用网络”,PLoS ONE,2014 年)。

我不会在这里详细描述所有结果,但我对这个项目非常满意。在将 miRs 及其各自的表达数据集中的预测目标对与 TargetScan 和 miRanda 的预测目标对匹配后,我通过一个包含所有这些数据的贝叶斯模型分析了它们,并使用 GO 注释和已知的目标对验证来验证它,还附加了一些 miR 家族分析。结果并不完美;生物信息学非常复杂,数据质量也不完美。但大多数已验证的目标对都是显著的,一些相关的 GO 注释在显著的基因组中过度表示。在后面的章节中,我将更深入地探讨统计模型、其重要性以及从结果中得出结论,但现在我想把这个例子作为一个例子,其中各种数据集以使新的分析成为可能的方式被结合在一起。

练习

继续上一章练习中的 Filthy Money Forecasting 个人财务应用场景,尝试以下练习:

1.

列出三个你预期对这个项目有益的潜在数据来源。对于每个来源,也列出你预期如何访问数据(例如,数据库、API 等)。

2.

考虑到上一章练习 3 中列出的三个项目目标。为了实现这些目标,你需要哪些数据?

摘要

  • 数据科学将数据视为可能独立于任何特定目的存在的东西。就像自然本身,以及其中存在的所有已发现或未发现的生物和物种一样,数据领域值得探索和研究。

  • 数据可以存在于许多地方,其可访问性、复杂性和可靠性各不相同。

  • 最好先熟悉数据可能采取的一些形式,以及如何查看和操作这些形式。

  • 在假设数据集包含你想要的内容之前,最好评估数据的范围和质量。

  • 找到和识别对项目有用的数据集并不总是直接的,但一些初步调查可能会有所帮助。

第四章. 数据整理:从捕获到驯化

“本章涵盖”

  • 整理数据的方法

  • 有用的工具和技术

  • 一些常见的陷阱

对“整理”的一个定义是“长期且复杂的争论。”听起来很贴切。

“数据整理”是将数据和信息从困难、非结构化或任意格式中提取出来,并转换为传统软件可以使用的过程。像数据科学的其他许多方面一样,它与其说是一个过程,不如说是一系列策略和技术,这些策略和技术可以在整体项目策略的背景下应用。整理不是一个可以事先精确规定的步骤任务。每个案例都不同,需要一些问题解决技巧才能取得好结果。在我讨论数据整理的具体技术和策略之前,如图 4.1 所示,我将介绍一个案例研究,我将使用这个案例研究来展示本章中提到的那些技术和策略。

图 4.1. 数据科学过程准备阶段第三步:数据整理

图片

4.1. 案例研究:田径运动史上最佳表现

当我在研究生院学习时,牙买加短跑运动员尤塞恩·博尔特开始以 100 米和 200 米赛跑中惊人的表现震惊世界。在北京 2008 年奥运会上,博尔特在 100 米赛跑中跑出了 9.69 秒的世界纪录,尽管他在冲过终点线之前就伸展开双臂庆祝胜利。几天后,博尔特在 200 米决赛中的 19.30 秒打破了迈克尔·约翰逊的世界纪录,这个纪录被广泛认为是整个田径运动中最好的成绩之一。尤塞恩·博尔特已经确立了自己作为历史上最伟大的短跑运动员的地位,但他仍在不断进步。

在 2009 年柏林世界锦标赛上,尤塞恩·博尔特打破了自身的两项世界纪录。他在 100 米比赛中跑出了 9.58 秒,在 200 米比赛中跑出了 19.19 秒,使得所有竞争对手,世界上最快的人,看起来都变得平庸。具体来说,在 100 米比赛中,美国运动员泰森·盖伊打破了国家纪录——而美国传统上擅长短跑——但在终点线落后博尔特数米。我对博尔特的表现印象深刻,关于“尤塞恩·博尔特的成绩与其他世界纪录相比如何?”和“没有其他运动员像博尔特那样统治过”的讨论无处不在,因此我决定量化博尔特所做的事情的不可能性。我想为所有那些坐在家里对田径充满热情的人解决这个账目,他们大多数只是猜测,使用轶事和启发式方法来展示某个特定表现是多么罕见。

4.1.1. 常见启发式比较

坐在家里的田径爱好者倾向于通过世界纪录的年龄或其他人接近打破它们的程度来比较不同项目的世界纪录。迈克尔·约翰逊的 200 米世界纪录在尤塞恩·博尔特打破它时已经 12 年了,而 100 米世界纪录在博尔特在 2008 年初第一次打破它时不到一年,之后又在 2008 年奥运会和 2009 年世界锦标赛上再次打破。世界纪录的年龄可能确实表明了记录的一些强度,但这绝对不是完美的衡量标准。博尔特的 200 米 19.19 秒的成绩比他的 19.30 秒成绩差,仅仅是因为之前的记录更年轻吗?当然不是。

有时候人们会引用一个分数相对于第二高分分数的百分比提升作为它是好的证据。捷克运动员扬·泽利兹尼的世界纪录标枪投掷成绩为 98.48 米,比他的第二高分投掷远了 2.9%,比任何其他人的最高成绩远了 5.8%。牙买加运动员尤塞恩·博尔特目前保持的世界纪录 9.58 秒,比他的第二快成绩快了 1.1%,比另一位运动员的最好成绩快了 1.3%,而这位运动员恰好是同一比赛中的第二名,他就是泰森·盖伊。再次强调,这确实是良好表现的合理指标,但由于这些第二高分表现的高变异性,它远非完美。如果,出于某种原因,第二高分表现从未发生过,百分比可能会发生剧烈变化。

4.1.2. 国际田联评分表

在我 2009 年开始这个项目的时候,存在更复杂的方法,但它们也有其不足之处。教练、粉丝以及许多人普遍接受的方法是,在田径项目中比较不同项目表现的是一套称为国际田联田径评分表的点数表。通常每几年,国际田联就会发布一套更新的点数表。国际田联还发布了用于多学科赛事(如男子十项全能和女子七项全能)的综合项目点数表,在这些赛事中,选手在每个项目中的表现都会得到分数,而获胜者是总分最高的运动员。在综合项目中,点数表是比赛本身的基础。个人项目的评分表对比赛的影响很小,除了某些基于表格颁发奖品的田径会议。

2008 年的评分表,在尤塞恩·博尔特以 9.58 秒完成 100 米赛跑时,当时最新的表格,给世界纪录表现评分为 1374 分(表格中列出的最高分数是 1400 分)。在评分表的下一轮更新中,2011 年,尤塞恩·博尔特 2009 年的表现得到了 1356 分的评分。这种评分的巨大变化在大多数项目中都没有发生。为了参考,根据 2008 年的表格,9.58 秒的 100 米成绩相当于 42.09 秒的 400 米赛跑,而根据 2011 年的表格,它相当于 42.37 秒的 400 米。在奥运会的 400 米决赛中,0.28 秒的差距很容易就是金牌和没有奖牌的区别。根据评分表,尤塞恩·博尔特的世界纪录正在变差。

这是有原因的。国际田联的综合评分表众所周知是基于每个项目相对较小的一组最佳成绩。这组小数据可能包括前 10 名或前 25 名,但我只是猜测,因为方法并没有完全公开。如果表格高度依赖于一小组顶尖表现,那么这一组的变化将对下一次更新表格的分数产生显著影响。2008 年和 2009 年的田径赛季产生了一些令人难以置信的 100 米成绩,即使没有尤塞恩·博尔特。这些成绩影响了 2011 年发布的下一组分数。从某种意义上说,通过评分表,尤塞恩·博尔特的世界纪录表现以及他对手的强劲表现最终变得不那么令人印象深刻。

通过回顾这一切,我的意思是只想展示基于太少数据构建模型如何会显著扭曲结果和结论。在评分表中,过去两三年内的一些出色表现极大地改变了我们对这些表现有多好的印象。我的目标是使用我能找到的所有数据来生成一种评分方法,这种方法不仅对最佳表现的改变不太敏感,而且还能很好地预测未来表现水平,这代表了一组良好的外样本数据。

4.1.3. 使用所有可用数据比较表现

我开始时的最大疑问:我能找到多少数据?各种互联网搜索和询问朋友让我得出结论,alltime-athletics.com 提供了最完整的精英田径表现数据集。该网站提供了所有奥运田径项目的最佳表现列表;对于某些项目,列表中包含数千次表现。我觉得,如果我能使用所有这些数据和一点统计知识,我就能提高 IAAF 评分表的鲁棒性和预测能力。

第一步是整理数据。最佳表现列表在网站上,但不是以 CSV 等方便的格式提供,所以我必须通过某种方式进行网络抓取。此外,我还需要将我的分数和结果与我的主要竞争对手 IAAF 评分表进行比较,这些评分表仅以 PDF 格式提供。网页和 PDF 都不是程序解析的理想选择,考虑到网页中的 HTML 结构和 PDF 中的页眉、页脚和数字,两者都可能变得相当杂乱。简单来说,这将需要一些整理。

在接下来的几节中,我将继续提到这个田径项目中的两个整理任务:

  • 从网站 alltime-athletics.com 整理最佳表现列表

  • 从包含它们的 PDF 中整理 IAAF 评分表

4.2. 准备开始整理数据

有些人喜欢立即跳进去,开始随意处理数据,但那不是我的风格。我更倾向于谨慎行事。我喜欢在编写任何代码或尝试任何策略之前先四处看看。我会做一些事情来收集一些关于数据的良好信息,这有助于我更有效地整理数据。

在本节中,首先我会向你展示整理前可能遇到的杂乱数据的样子。然后我会列出一些在你开始整理之前可以采取的步骤,这些步骤有助于你弄清楚你有什么,你应该做什么,以及你可能遇到多少麻烦。

4.2.1. 一些杂乱数据类型

关于杂乱的数据集,每个数据集都有其独特的方式。如果所有杂乱的数据看起来都一样,我们就会找到一种方法来解析它并快速高效地使用它。尽管我无法一一列举数据杂乱和需要整理的每一种方式,但我可以描述几种方式,并希望这能帮助你了解在深入研究新数据集时可能会出现的问题,以及你如何为此做好准备。

如果你已经在数据科学领域工作了几年,我敢肯定你一定见过这样的案例——或者更糟。截至 2016 年,我们还没有进入“干净数据时代”,我开始怀疑我们是否能够实现。

需要爬取的数据

我在第三章中提到了网络爬虫,但你也可以从 PDF 和其他非传统来源中爬取数据。“爬取”是指从未设计为程序访问的来源中程序性地提取选定元素的过程。要爬取的数据通常结构不佳。但如果你编写一个复杂的爬虫,数据最终可能会变得整洁有序。

损坏的资料

有时候你会发现数据不仅格式不佳,而且直接损坏。这通常意味着文件的一些方面要么缺失,要么由于磁盘错误或其他低级问题而被隐藏。这可以像丢失制作模型飞机的说明或意外混淆应该按顺序排列的一叠索引卡一样。常见的损坏文件格式是 PST,一种电子邮件存档。但幸运的是,我们有许多工具可以从这样的损坏文件中恢复大部分数据。(这是文件格式经常损坏的少数好处之一:有人开发了一个反损坏工具!)

设计不良的数据库

数据库也有成长的烦恼,有时这意味着原本不打算一起使用的两个数据库现在成为了你获取信息的最佳来源。错误来源包括数据库值或键不匹配以及范围、深度、API 或模式的不一致。

4.2.2. 假装你是一个算法

我已经讨论了一般性的网络爬虫,鉴于我为我田径项目所需的数据在网站上可用,爬虫似乎是一个不错的选择。在 Google Chrome 浏览器中,我使用了“查看页面源代码”选项来查看构成列出史上所有男子 100 米短跑最佳成绩页面原始 HTML。这就是程序化爬虫将要读取的内容。

有时候解析 HTML 以提取所需元素很容易,但有时候需要一点想象力。我倾向于角色扮演;假装我是一个整理脚本——一段将杂乱数据程序性地转换为整洁数据的代码——这有助于我思考如何编写脚本,并给我一个很好的想法,了解这项任务可能有多难以及我可能会遇到什么问题。

整理的第一步,以及假装成脚本,是查看原始数据(例如,使用网页上的“查看页面源代码”或使用文本编辑器查看 HTML)。你编写的任何代码都将看到这些,所以在考虑如何处理它时,你应该查看这些内容。页面顶部的某些标题行和其他材料,以及包含男子 100 米冲刺数据的页面部分,看起来是这样的:

...
<A HREF="#7">faulty wind gauge - possibly wind assisted</a><br></FONT>
<center><p>
<A name="1"><H1>All-time men's best 100m </H1></a><P>
<PRE>
1    9.58    Usain Bolt    JAM    Berlin      16.08.2009
2    9.63    Usain Bolt    JAM    London      05.08.2012
3    9.69    Usain Bolt    JAM    Beijing     16.08.2008
3    9.69    Tyson Gay     USA    Shanghai    20.09.2009
3    9.69    Yohan Blake   JAM    Lausanne    23.08.2012
6    9.71    Tyson Gay     USA    Berlin      16.08.2009
...

这是页面中开始列出最佳男子 100 米成绩的部分。因为我是在 2016 年写这篇文档的,所以现在列表中有一些在 2011 年我首次分析这些数据时并不存在的成绩。但格式是一样的。

当我扮演一个整理脚本的角色时,我会想象如果我遇到这一块 HTML 会怎么做。这个部分之前的内容对我没有用,所以主要目标是从此处开始捕捉最高分。我认出包含尤塞恩·博尔特 9.58 秒世界纪录的那一行,这是我要捕捉的数据的开始,但脚本将如何识别它呢?

识别运动员成绩的一种方法是对文件的每一行进行测试,看它是否看起来像这样:

[INTEGER]    [NUMBER]    [NAME]    [COUNTRY]    [CITY]    [DATE]

任何试图匹配此 HTML 每一行的方法都必须能够识别整数、数字、名称、国家等等。这项任务可能比最初看起来要复杂得多。例如,你将如何测试第三列中的名称?你会测试首字母大写的单词吗?由空格分隔的两个单词?Joshua J. Johnson是否符合你的标准?又比如Leonard Miles-Mills或者城市Kuala Lumpur?所有这些都在列表中。这个过程看起来并不简单,所以我通常在求助于模式识别之前尝试不同的策略。

文档结构,尤其是在 HTML 或 XML 中,可以为整理脚本提供关于有价值数据开始位置的良好线索。数据之前是什么?在这种情况下,数据是由一个<PRE>标签 precedes。不管这个标签代表什么,都值得检查它是否经常出现在页面上,或者是否只在数据集开始之前出现。调查这个,我发现在这个页面上,<PRE>标签第一次出现是在数据之前。而且,幸运的是,这一点对所有田径项目的页面都是适用的,所以我可以安全地使用<PRE>标签来表示每个事件数据集的开始。

将自己想象成一个整理脚本,我会逐行读取 HTML,并寻找<PRE>标签。当我找到一个<PRE>标签时,我知道下一行将是特定表格格式中的数据行,其中第二列是测量的性能,第三列是姓名,第四列是国家,等等。你喜欢的脚本语言中的文本解析器可以读取这一行,使用制表符或多个连续空格将列分离成字段,并将每个文本字段存储在变量或数据结构中。

4.2.3. 持续想象:可能遇到的障碍和不确定性有哪些?

现在你已经知道每个 HTML 页面中宝贵数据开始的位置以及如何开始捕获数据行,你仍然需要在心里继续扮演整理脚本的角色,因为它会逐行通过文件。

当我向下滚动查看男子 100 米表现的原始 HTML 时,大多数行看起来都应该是这样的:性能、姓名、国家等,都在它们适当的位置。但时不时地会出现一些奇怪的字符序列。有时在城市列中,我看到一些字母之间有文本&uuml;&eacute;。这些条目看起来很奇怪,我担心脚本(等等,那是我!)可能不知道该怎么办。我查找渲染 HTML(网页本身)中的相应位置,并意识到这些字符序列分别是ü和é的 HTML 表示。我也可以进行网络搜索来弄清楚这些字符序列的含义。

苏黎世和圣何塞在顶级表现地点列表中出现的频率相当高,所以很明显我需要担心 HTML 中表示特殊字符序列的字符。还有我没有见过的其他字符吗?每当任何字段中出现&符号时,我应该小心吗?对这些问题的答案可能是肯定的,但我想要强调的是我还没有弄清楚

这是在数据整理中的一个重要观点:可能会发生很多你预料不到的事情。因此,如果有一个整理原则,那就是:

双重检查一切

建议进行第二次手动检查,但如果小心操作,程序性检查也可以工作。如果我没有大量的数据,作为第二次检查,我喜欢浏览经过整理后的、理论上干净的数据文件。有时候快速浏览就能发现一些明显的错误,这些错误可能需要一小时的软件调试才能发现。如果数据列移动了位置怎么办?一个额外的制表符字符可能会搞乱很多解析算法,包括一些标准的 R 包和偶尔的 Excel 导入等。

在尝试以编程方式整理数据时,还可能发生什么问题?文件(们)中有什么看起来有点奇怪的地方?在整理之后,你可以进行哪些检查,可能会发现整理过程中的一些重要错误?每个案例都是不同的,但每个案例都值得仔细和深思熟虑地考虑任何可能的解析错误。再次强调,意识是数据科学家在这个阶段最重要的东西。

4.2.4. 查看数据末尾和文件

如果我假装自己是一个整理脚本,我会从文件的开始处开始,到文件的末尾结束。在中间可能会发生许多意外的事情,所以我无法保证我会以预期的状态到达文件的末尾。我可能在城市的位置整理了人们的名字,在原产国的位置整理了日期。谁知道呢,除非我有写出一个无错误的脚本的惊人运气,而这个数据集没有不规则性?这样的脚本或这样的数据集存在吗?假设情况并非如此——并且假设我在当前对整理脚本的构想中至少犯了一个错误——我可能应该检查整理后的数据文件(们)的开始、末尾以及至少中间的一些地方。

要找到不属于数据集的第一行 HTML,需要浏览男子 100 米成绩列表的末尾。在我意识到页面的底部有额外的、非标准的最佳成绩列表之前,我花了令人尴尬的长的时间。对于男子 100 米页面,除了主列表之后,还有起跑和手动计时的列表。这些列表看起来与主列表相同,但通过几个 HTML 标签与主列表分开。从主列表到第一个辅助列表的过渡看起来是这样的:

...
2132  10.09    Richard Thompson    TTO    Glasgow      11.07.2014
2132  10.09    Kemarley Brown      JAM    Sundsvall    20.07.2014
2132  10.09    Keston Bledman      TTO    Stockholm    21.08.2014
</pre></center>
2401 total
<p><center>
<A name="2"><H3>rolling start</H3></A><P>
<PRE>
1     9.91    Dennis Mitchell      USA    Tokyo        25.08.1991
2     9.94    Maurice Greene       USA    Berlin       01.09.1998
3     9.98    Donovan Bailey       CAN    Luzern       27.06.2000
...

表示所需数据开始的 HTML 标签<PRE>在主列表的末尾用</pre>标签关闭。(注意,HTML 标签通常不区分大小写。)这将是一个结束数据集解析的好方法。作为一个整理脚本,我会这样做,除非我想同时捕获辅助列表。我这样做吗?在这种情况下,不,我不这样做。我只想要完全合法的标记,这些标记遵守所有世界纪录资格规则,因为我想要比较世界纪录以及所有接近世界纪录的表现。考虑在非标准且本质上与合法表现核心集不同的条件下进行的表现可能并不有用。

如果我的整理脚本忽略了有用数据集的末尾,在这个例子中是</pre>标签的闭合,并假设数据一直持续到文件末尾,那么脚本可能会收集页面底部的非标准结果。或者,当数据不再符合已建立的适当列格式时,脚本可能不知道该怎么做。在这种情况下和许多其他情况下,查看整理后的数据文件末尾对于确定数据整理是否成功至关重要。这取决于你,作为数据科学家,决定整理的哪些方面最重要,并确保这些方面得到适当完成。但几乎总是很重要,要扫描文件到末尾,至少要确保在脚本完成之前没有发生任何奇怪的事情。

4.2.5. 制定计划

现在我已经讨论了想象自己是一个整理脚本的流程,解析原始数据并提取所需的部分,下一步是考虑你迄今为止收集到的所有信息,并制定整理计划。

根据你在田径数据中看到的内容,一个不错的选择是下载包含所有奥运田径赛事的所有网页,并像我们讨论的那样解析它们,适当使用 HTML 结构,并仔细检查每一项。我还没有提到的这个问题的复杂性之一是——如果你打算使用纯网页抓取策略——你需要一个包含所有单个赛事网页的网址列表,以便程序化地下载所有这些网页。有时生成这样的网址列表很容易,但每个 48 个单独的页面(每个奥运项目男女各 24 项)都有一个唯一的地址需要手动复制或输入。因此,你可能需要手动创建一个包含 48 个需要抓取的网页地址的列表,此外,你还需要编写解析 HTML 的脚本,正如已经讨论的那样。这是可能整理所需数据的计划之一。但是,由于每个页面地址都需要手动复制或输入,与手动下载每个页面相比,我认为通过程序化访问手动输入的每个页面地址并不会节省多少时间。那两个都是不错的选择,但它们并不是整理所需数据的唯一方法。

最后,我决定不采用网络爬虫的方式。正如你所见,解析 HTML 结构并不会特别困难,但我意识到还有另一种方法。在alltime-athletics.com上的页面并没有大量使用 HTML。网页上作为渲染 HTML 显示的 HTML 和文本并没有太大的区别。页面上没有太多样式,尤其是在包含所需数据的页面部分。在两种情况下,最佳表现都以空格分隔的表格形式给出,每行一个表现。因此,原始 HTML 和渲染 HTML 之间几乎没有区别。然而,正如我已经提到的,一些个别字符在原始 HTML 中的显示方式与在渲染 HTML 中的显示方式不同。例如,在原始 HTML 中,一个城市可能显示为Z&uuml;rich,但在渲染 HTML 中则显示为Zürich

我决定采用已经渲染好的网页版本,这样我就不必担心字符渲染或其他 HTML 解析问题。我需要从 48 个页面中获取数据,而不是编写(或假装是)一个脚本,该脚本将下载每个页面并解析每个奥运项目的 HTML,我决定亲自从每个网页上复制文本。

我会用 Chrome 浏览器访问这 48 个网页,按下 Ctrl-A 选择每个页面的所有文本,按下 Ctrl-C 复制文本,然后按下 Ctrl-V 将每个事件页面的文本粘贴到一个单独的平面文件中。复制页面是一项手动工作,但我不必担心翻译 HTML 或编写下载页面的脚本。这就是我最终确定计划:跳过 HTML 解析,手动复制所有页面,然后编写一个简单的脚本,将田径表现数据拉入可用的格式。这可能不是每个项目的最佳计划,但在这个场景中确实很好。

通常情况下,数据清洗计划的选取应主要依赖于你在初步调查数据时发现的所有信息。如果你能想象以某种假设的方式解析数据或访问它——我试图扮演一个清洗脚本的角色——那么你可以编写一个执行相同操作的脚本。假装你是一个清洗脚本,想象一下你的数据可能会发生什么,然后稍后编写脚本。数据清洗是一个如此不确定的过程,因此最好先探索一番,并根据你所看到的情况制定一个清洗计划。

4.3. 技术和工具

数据整理是一个极其抽象的过程,几乎在每一步都有不确定的结果。没有一种方法或一种工具可以完成使杂乱数据变得干净的目标。如果有人告诉你他们有一个可以整理任何数据的工具,那么这个工具要么是编程语言,要么他们在撒谎。许多工具适合做很多事情,但没有一种工具可以整理任意数据。坦白说,我认为这永远也不可能实现,尽管我们可以不断制造出更好的工具。数据以如此多种形式和如此多目的存在,以至于可能没有一种应用程序能够读取具有任意目的的任意数据。简单来说,数据整理是一项不确定的事情,需要根据特定情况使用特定的工具来完成工作。

4.3.1. 文件格式转换器

在 HTML、CSV、PDF、TXT 以及其他可能使用的任何常见文件格式中,了解是否可以直接从一种文件格式转换为另一种文件格式是有帮助的。

国际田联(IAAF)的评分表以 PDF 格式发布,这种格式不利于数据分析。但有一些文件格式转换器可以将 PDF 转换为其他文件格式,例如文本文件和 HTML。Unix 应用程序pdf2txt可以从 PDF 中提取文本并将其保存到文本文件中。pdf2html是另一个有用的格式转换器,可能对大多数数据科学家都有用。

许多文件格式转换器可供使用——其中许多是免费或开源的——当然值得进行一次谷歌搜索,以确定您拥有的格式是否可以轻松转换为所需的格式。

4.3.2. 专有数据整理器

到 2016 年,愿意为您整理数据的公司并不短缺——而且往往是要收费的。从某种意义上说,许多软件产品声称能够做到这一点,而我知道从个人经验来看,其中许多产品功能严重受限,这让人感到不安。另一方面,其中一些产品效果很好。

如果您能找到可以将您拥有的数据转换为所需数据的专有产品,那么它们值得考虑。如果您的项目可以提前完成,那么在这些专有工具上花费一点钱可能是值得的,但这个行业很年轻,变化太快,我无法在这里提供任何有意义的调查。

4.3.3. 脚本:使用计划,然后猜测并检查

在本章前面,我谈到了假装我是一个整理脚本,并描述了在这个角色中我会做什么。这正是编写真实数据整理脚本的依据。

当您想象自己是一个脚本,阅读文件,识别有用数据的开始和结束,以及如何解析这些数据时,您应该对任务的复杂性和主要功能有一个概念。对于更简单的任务,简单的工具通常很有用。例如,Unix 命令行对于这些任务很有帮助:

  • 从包含特定单词的文件中提取所有行(grep就是这样的工具之一)

  • 将某个单词或字符的出现转换为另一个(trsedawk

  • 将文件切割成更小的块(split

这些只是一些,但如果你的清洗计划包括几个这样的简单步骤,命令行可能就是最佳选择。

但如果你有一系列更复杂的操作要执行,而且上述文件格式转换器或专有清洗工具都不符合要求,那么脚本语言通常是你的最佳选择。Python 和 R 都是编写清洗脚本的常见语言,通常最好选择你最熟悉的语言,因为你可能会在短时间内尝试几种不同的技术。

编写数据清洗脚本,至少是第一版,通常不是一个精心安排的过程。实际上,它与当前对“黑客”一词的使用非常契合,即尝试许多事情直到找到几个能完成任务的方法。能够快速加载、操作、编写和转换数据是你选择脚本语言或工具时应追求的最重要能力。

因为你已经想象自己是一个脚本并解析数据,你应该对最终脚本将做什么有一个很好的总体概念,但我肯定一些细节仍然不清楚。你可以就如何最好地进行清洗做出一些明智的决定,或者你可以猜测并检查,如果这样做似乎更节省时间。现在也是重新审视手动与自动问题的好时机:你能否在比编写脚本更短的时间内手动清洗?你还会重用脚本吗?继续选择似乎能带来最大回报、成本最低的时间和精力的路径。

在编写数据清洗脚本时,保持警觉性非常重要。这包括关注数据、脚本、结果的状态,明确目标,以及了解每个可能的大量清洗步骤和工具能为你带来什么。

4.4. 常见陷阱

在处理杂乱的数据时,任何仅基于查看数据的一部分而构思的数据清洗脚本肯定会有遗漏重要内容的情况。即使你在考虑所有可能性时非常警觉和彻底,仍然存在出错的风险。在本节中,我概述了一些这些陷阱,并给出了一些明显的症状,表明你的数据清洗脚本可能已经陷入其中。

4.4.1. 注意 Windows/Mac/Linux 问题

当我在 2005 年左右开始处理数据时,最流行的操作系统之间的大数据不兼容性问题是我认为在 2016 年仍然存在的一个问题。但三大操作系统仍然没有就文本文件中行结束的表示达成一致。任何一天,所有文本文件的规范可能都会神奇地跨操作系统统一,但在此之前,从一种操作系统的文件转换到另一种操作系统将会有问题。

从古至今(自 20 世纪 70 年代以来),Unix 以及后来的 Linux 通过继承,都使用换行符(LF)来表示新行,而版本在 9.0 之前的 Mac OS 使用回车符(CR)来表示新行。自 1999 年以来,Mac OS 已经与其他 Unix 衍生版本一起使用换行符来表示换行,但 Microsoft Windows 仍然继续使用混合的 CR+LF 行结束符。

我不会尝试在这里解决任何具体问题;它们可能各不相同,且复杂多变。但我应该强调的是,它们可能会突然出现在你面前。不正确地解析行结束符可能会导致各种问题,因此要小心处理解析的输出。除了这一点和其他操作系统复杂性之外,每种编程语言都有自己读取不同类型文件的能力。根据你选择的包或本地方法来读取文件,确保你注意它如何解析文本、行结束符和其他特殊字符。这些信息应该包含在语言文档中。

当查看你整理好的新数据时,以下是一些可能表明你遇到了操作系统文件格式问题的迹象:

  • 你似乎有比你认为的更多的文本行。

  • 你似乎有太多的文本行。

  • 文件中穿插着一些看起来奇怪的字符。

4.4.2. 转义字符

在处理文本时,一些字符具有特殊含义。例如,在最常见的 Unix(或 Linux)shell(命令行解释器)中,字符*是一个通配符,代表当前目录中的所有文件。但如果你在它前面加上常见的转义字符\,如\*,那么特殊含义就会消失,只代表简单的星号字符。该行

rm *

删除当前目录中的所有文件,而

rm \*

如果存在,则删除名为*的文件。在这种情况下,反斜杠字符被说成是转义通配符。

这样的转义字符可能出现在编程语言中的文本文件和文本/字符串变量中。例如,假设你想要在一个文本编辑器中读取一个看起来像这样的文本文件:

this is line 1
this is line 2
A    B    C    D

这是三行文本,后面跟着一个没有任何字符的行。第三行包含字母之间的制表符。

许多常见的编程语言,包括 Python 和 R,在用最简单的文件读取器(或文件流)读取此文件时,会看到以下内容:

this is line 1\nthis is line 2\nA\tB\tC\tD\n

注意,行断点已被\n替换,制表符已被\t替换。这包含与之前相同的信息,但它被编码在一个单独的字符串中,只需使用 ASCII 字符(nt)和转义字符(反斜杠)来表示空白元素,如换行符和制表符。

另一个复杂的问题是:在 Python 或 R 等语言中创建字符串变量时,需要使用引号,而引号可能会以各种方式影响转义。让我们将之前的字符串放在双引号中,将其分配给 Python 中的一个变量(>>> 代表 Python 解释器提示符),然后检查变量的内容,首先只输入变量名本身,然后使用 print 命令:

>>> s = "this is line 1\nthis is line 2\nA\tB\tC\tD\n"

>>> s
'this is line 1\nthis is line 2\nA\tB\tC\tD\n'

>>> print s
this is line 1
this is line 2
A    B    C    D

注意,对变量内容的两次检查确认了它是一个字符串(最初以单引号显示)并且当使用 print 命令时,这个字符串会以原始文件中的方式渲染,其中转义字符被转换为适当的换行符和制表符字符。这样,一个单独的字符串变量可以表示整个文件的数据,每行由转义换行符分隔。

当在引号内使用转义字符或在引号内使用引号时,乐趣就开始了。假设你有一个第二个文件,在文本编辑器中看起来像这样:

I call this "line 1"
I call this "line 2"
There are tabs in this quote: "A    B    C"

将此文件存储在单个 Python 变量中,然后检查其内容看起来像这样:

>>> t = "I call this \"line 1\"\nI call this \"line 2\"\nThere are Tabs in this quote: \"A\tB\tC\""

>>> t
'I call this "line 1"\nI call this "line 2"\nThere are Tabs in this quote: "A\tB\tC"'

>>> print t
I call this "line 1"
I call this "line 2"
There are Tabs in this quote: "A    B    C"

这显然比之前的例子复杂一些。因为文本本身包含引号,所以这些引号需要在字符串变量中进行转义。

作为转义字符的最终示例,让我们假设你有一个包含一些电子邮件的数据集。人们可以在电子邮件中写下任何他们想写的内容,包括引号、制表符或其他任何内容。由于某种未指定原因,你想要将电子邮件数据存储在文本文件中,每行一个电子邮件。一个电子邮件的内容如下:

Dear Edison,

I dislike "escaped characters" .

-N

这封电子邮件可以编码为以下字符串:

Dear Edison,\n\nI dislike "escaped characters".\n\n-N\n

它可以像这样存储在 Python 中的字符串变量中,转义内部引号:

>>> s = "Dear Edison,\n\nI dislike \"escaped characters\".\n\n-N\n"

现在你想要将这封电子邮件写入文件的一行,因此你检查变量的内容,然后打印/渲染它们(print 命令在终端中复制的将是要写入文件的内容):

>>> s
'Dear Edison,\n\nI dislike "escaped characters".\n\n-N\n'

>>> print s
Tesla,Edison,Dear Edison,

I dislike "escaped characters".

-N

这不是你想要的结果。print 过程正在渲染换行符,这些数据不再位于单行上。看起来你不得不再次转义换行符,如下所示:

>>> s = "Dear Edison,\\n\\nI dislike \"escaped characters\".\\n\\n-N\\n"
>>> print a
Dear Edison,\n\nI dislike "escaped characters".\n\n-N\n

现在它按预期打印在单行上,因为使用了双反斜杠。记住,在变量 s 中,序列 \\n 代表一个字面上的反斜杠(只有一个——成对中的第一个是转义的那个)和字符 n。有时这可能会令人困惑,因此处理复杂的转义时最好小心。

不必只限于两个反斜杠。反斜杠是一个常见的转义字符,如果你使用的是智能文件读取器和写入器,你可能不必太担心它,但如果你使用了很多嵌套引号和新行,转义可能会成为问题。有一次在 R 中,我因为一些细微的转义而连续使用了五个或六个反斜杠。我意识到我必须使用\\\\\\n来完成我的任务,这对我来说相当令人惊讶,因为文件读取器的所有参数似乎都没有正确处理一切。

你在转义时可能遇到的问题的迹象可能包括以下内容:

  • 一些行或字符串过长或过短。

  • 你试图逐行读取文件,但最终得到的是一行很长的文本。

  • 你在引号内发现了一些不属于那里的文本。

  • 在读取或写入文件时遇到错误。

4.4.3. 异常值

有时候,一个数据点从逻辑角度来看可能包含一个完全有效的值,但从主题专家的角度来看,这个值并不现实。在我们的田径例子中,在 100 米短跑最佳时间列表中,某个时刻可能出现了 8.9 这个数字。现在,从统计角度来看,8.9 是一个完全合理的数字,但任何了解田径的人都知道,没有人曾在 100 米短跑中跑出 8.9 秒。可能是因为 8.9 在某个地方出现了打字错误,或者可能是因为某一行格式不正确,或者任何其他可能导致数据点出现在错误位置的可想象原因。但关键是,有时错误的数据可能会悄无声息地进入你的项目,而不会引起错误或使其明显。这就是总结统计和探索性图表可以发挥作用的地方。

在这种情况下,仅仅检查值的范围——从最小值到最大值——就可能捕捉到错误。在这个项目中,我绘制了所有数据的直方图,不仅是为了检查错误,也是为了对数据集的外观有一些了解。虽然直方图可能会突出这种数据错误,但通常花点时间生成一些总结——无论是统计的还是视觉的——即使它们看起来并不必要。它们可以防止错误并提高意识。我在第五章中涵盖了数据评估,包括基本描述性统计、总结和诊断,其中一些技术也可以在这里使用,以确保数据整理成功。

4.4.4. 在整理者营火旁的恐怖故事

任何经验丰富的数据科学家都有几个故事可以讲述:困扰数据科学家多年的数据幽灵故事,那个“溜走的故事”,灾难即将发生时,通过最后一刻发现“虫子”而避免的险情,以及“数据太糟糕了...(糟糕到什么程度?)”的那个时刻。在这里我没有什么更多要说的;你将在本书中了解到我的大部分故事,但不要害怕向你的当地资深数据科学家询问他们的故事。这些故事可能很“书呆子”,但嘿,我们难道不都是吗?

练习

在先于第二章中首次描述的 Filthy Money Forecasting 个人理财应用场景的基础上继续,并与前几章的练习相关,想象以下场景:

1.

你即将开始从 FMI 的内部数据库中提取数据,这是一个关系型数据库。列出你在访问和使用数据时应该警惕的三个潜在问题。

2.

在内部数据库中,对于每个你找到的金融交易,一个名为 description 的字段似乎包含一些有用的信息,该字段包含一个字符串(纯文本)。但这个字符串似乎没有从一条记录到另一条记录的固定格式,而且你交谈过的任何人也无法告诉你该字段是如何生成的或更多关于它的信息。你将如何制定从该字段中提取信息的策略?

摘要

  • 数据处理是在一些不良条件下捕获原始和杂乱无章数据的有用部分的过程。

  • 想象自己是一个计算机程序,以便创建一个良好的数据处理脚本。

  • 熟悉一些典型工具和策略以及一些常见的处理陷阱会有所帮助。

  • 良好的处理取决于在处理之前进行扎实的规划和一些猜测与检查,以查看哪些方法有效。

  • 在数据处理上花点额外的时间可以让你以后少受很多痛苦。

第五章. 数据评估:探索和检查

本章涵盖

  • 描述性统计和其他了解数据的技术

  • 检查你对数据和其内容的假设

  • 在你的数据中筛选出你想要找到的事物

  • 在花费大量时间进行软件或产品开发之前进行快速、粗略的分析以获得洞察

图 5.1 展示了我们在数据科学流程中的位置:评估可用的数据和到目前为止的进展。在前几章中,我们搜索、捕获和处理了数据。你很可能在这个过程中学到了很多,但你仍然没有准备好将数据扔给问题并希望问题得到解答。首先,你必须尽可能多地了解你所拥有的:其内容、范围和限制,以及其他特征。

图 5.1. 数据科学流程准备阶段的第四步和最后一步:评估现有数据和到目前为止的进展

图片 5.1

很容易一上来就尽快开发以数据为中心的产品或复杂的统计方法,但了解数据的益处完全值得牺牲一点时间和精力。如果你对你的数据了解得更多——如果你保持对其以及你如何分析它的意识——你将在数据科学项目的每个步骤中做出更明智的决定,并在以后收获好处。

5.1. 示例:恩隆电子邮件数据集

在我的第一份软件公司工作——在多年的以研究为导向的学术数据科学之后——我正在帮助构建分析大型、高度监管的组织员工沟通模式的软件,以检测异常或问题行为。我的雇主,一家巴尔的摩初创公司,开发了帮助理解大量员工数据的软件,在许多情况下,根据现行法律,这些数据必须存档多年,并且常常包含可以用于调查已知违规行为以及检测未知违规行为的证据。两个潜在的顾客例子是大金融机构的合规官员和政府机构的安保部门。这两者都有明确的责任防止任何人泄露或不当处理机密或秘密信息。监管机构通常要求或强烈建议监控员工对内部网络的的使用。不用说,我们需要对员工沟通和其他活动进行彻底的统计分析,同时还要在伦理和隐私方面极为小心。

但对于我们最初用于演示目的的第一个数据集来说,隐私并不是一个很大的问题。2000 年代初恩隆丑闻后,调查人员收集的电子邮件集合现在是公开记录的一部分,并且已经被研究人员广泛记录和研究(www.cs.cmu.edu/~./enron)。因为它是最全面和相关的公共数据集之一,我们想使用恩隆电子邮件来测试和展示我们软件的功能。

可用的恩隆数据集版本有几个,包括 CSV 格式的文本版本以及原始的专有格式 PST,后者可以通过 Microsoft Outlook 生成。第四章介绍了数据整理的基础知识,其中描述的所有问题和警告在这里当然也适用。根据我们从哪个版本的数据集开始,可能已经为我们完成了各种预处理和整理步骤。大多数情况下,这是一件好事,但我们始终必须警惕在数据到达我们之前可能已经做出的错误或非标准选择。

因此,以及无数其他原因,我们需要将我们的数据集视为一个不熟悉的生物。就像一个新发现的动物物种一样,我们以为的可能是我们并没有的。我们的初始假设可能并不正确,即使它们是正确的,在(比喻的)物种内部,个体之间可能存在巨大的差异。同样,即使你自信你的数据集包含你所认为的内容,数据本身肯定是从一个数据点到另一个数据点的变化。没有初步评估,你可能会遇到异常值、偏差、精度、特异性或其他数据固有的众多方面的问题。为了揭示这些并更好地了解数据,数据整理后的数据分析的第一步是计算一些描述性统计。

5.2. 描述性统计

描述性统计可能正如你所想:

  • 数据集的描述

  • 数据集的摘要

  • 最大值

  • 最小值

  • 平均值

  • 可能值的列表

  • 数据集覆盖的时间范围

  • 以及更多

这些是例子;对于定义,让我们看看维基百科上的一个例子:

描述性统计是定量描述信息集合的主要特征或定量描述本身的学科。

描述性统计既是一套技术,也是使用这些技术生成的数据集的描述。

讨论描述性统计时,往往难以不提及推断性统计。推断性统计是使用你拥有的数据来推断——或者推断——你没有直接测量或数据的知识或数量。例如,在政治选举中对 1000 名选民进行调查,然后试图预测整个选民群体的结果(可能远大于 1000 人)就使用了推断性统计。描述性统计只关注你拥有的数据,即 1000 份调查回应。在这个例子中,从样本到总体的推广步骤将这两个概念分开。

关于数据集,你可以这样说:

  • 描述性统计问的是,“我有什么?”

  • 推断性统计问的是,“我能得出什么结论?”

虽然描述性统计和推断性统计可以被视为两种不同的技术,但它们之间的界限通常是模糊的。在选举调查的情况下,就像在许多其他情况下一样,你必须对 1000 个数据点进行描述性统计,以便推断出关于未调查的其余投票人群的任何信息,而且并不总是清楚描述在哪里结束,推断在哪里开始。

我认为大多数统计学家和商人都同意,要得出大多数有趣的结论需要推断统计:世界人口何时达到顶峰然后开始下降,病毒流行病将如何快速传播,股市何时会上涨,Twitter 上的人们对某个话题是否普遍持有积极或消极的观点,等等。但描述性统计在使这些结论成为可能方面发挥着极其重要的作用。了解你拥有的数据和它能为你做什么是有益的。

5.2.1. 保持接近数据

我在第一章中也提到了保持接近数据,但这当然值得重复,也许在这里提到更为重要。在数据科学项目的这个阶段计算描述性统计的目的,是为了了解你的数据集,以便你了解其能力和局限性;在这个阶段尝试做任何除了了解数据之外的事情都是错误的。像机器学习、预测分析和概率建模等复杂的统计技术,目前完全不适合。

有些人可能会和我争论,说直接跳入并应用一些机器学习(例如)到你的数据中是可以的,因为你在进行的过程中会逐渐了解数据,如果你足够敏锐,你会在问题出现时识别并纠正它们。我完全不同意。像今天大多数机器学习中使用的复杂方法一样,这些方法并不容易分解,甚至难以理解。随机森林、神经网络和支持向量机等,虽然在理论上可以理解,但每个这些方法都有许多动态部分,一个人(或一个团队)不可能完全理解获得单个结果所涉及的特定部分和值。因此,当你注意到一个错误的结果,即使是一个极其错误的结果时,从复杂的模型中直接提取出导致这种严重错误的特定部分并不简单。更重要的是,涉及一些随机性(再次强调,大多数机器学习技术)的复杂模型可能不会在重新运行算法时重现特定的错误。这种在复杂统计方法中的不可预测性也表明,在允许任何随机过程或黑盒为你得出结论之前,你应该先了解自己的数据。

我对“接近数据”的定义是这样的:

如果你正在计算可以手动验证或可以使用另一个统计工具完全复制的统计量,那么你就接近数据了.

在这个项目阶段,你应该计算可以通过其他方式轻松验证的描述性统计,在某些情况下,你应该进行验证以确保正确性。因此,因为你正在进行简单的计算并双重检查,你可以几乎 100%地确信结果是正确的,而你积累的接近数据的描述性统计数据集成为了一种关于你的数据集的知识不可侵犯的规范,这在以后将非常有用。如果你遇到的结果与这些结果相矛盾或似乎与这些结果不太可能相关,那么你可以几乎肯定你在产生这些结果的过程中某个时候犯了一个重大错误。此外,哪些规范内的结果被矛盾可以极大地有助于诊断错误。

保持接近数据确保你可以非常确信这些初步结果,并在整个项目中保持一组良好的描述性统计数据,这为你提供了一个容易参考的参考,可以与随后的更相关但更抽象的结果进行比较,这些结果是你的项目真正的焦点。

5.2.2. 常见的描述性统计

有用的和有信息量的描述性统计方法包括但不限于平均值、方差、中位数、总和、直方图、散点图、表格摘要、分位数、最大值、最小值和累积分布。这些中的任何一个或所有都可能在你下一个项目中有所帮助,而决定你可能会计算哪些以实现你的目标,在很大程度上是偏好和相关性的问题。

在安然电子邮件数据集中,以下是我想到的第一个统计问题:

  1. 有多少人?

  2. 有多少条消息?

  3. 个人写了多少条消息?

一篇简短的论文,名为“介绍安然语料库”(2004),由 Brian Klimt 和 Yiming Yang 撰写,对这些问题给出了很好的总结。

在安然电子邮件语料库中,有来自 158 名员工账户的 619,446 条消息。但通过移除大量电子邮件和在多个账户中出现的重复电子邮件,Klimt 和 Yang 将数据集减少到 200,399 条干净的电子邮件。在干净的版本中,平均用户发送了 757 封电子邮件。这些是有用的信息。如果没有这些信息,后来如果某个统计模型表明大多数人每天发送几十封电子邮件,可能不会很明显地发现问题。因为这个数据集跨越了两年,粗略地说(另一个描述性统计!),我们知道每天两到三封电子邮件要典型得多。

说到时间范围,在 Enron 数据集和其他数据集中,我见过日期报告错误的情况。由于日期的格式方式,一个损坏的文件可以轻易地将日期报告为 1900 年、1970 年或其他年份,这显然是错误的。Enron 直到很久以后才存在,同样,我们所知道的电子邮件也是。如果你想在后续分析中将时间作为一个重要变量,那么在其余电子邮件一个世纪之前的几封电子邮件可能是一个大问题。在整理数据时,如第四章所述,识别这些问题会有所帮助,但这些问题可能被忽视,一些描述性统计可以帮助你现在捕捉到它。

例如,假设你对分析每年发送的电子邮件数量感兴趣,但你跳过了描述性统计,直接编写了一个从最早电子邮件年份(约 1900 年)开始,到最新年份(约 2003 年)结束的统计应用程序。你的结果会受到该范围内中间多年没有消息的严重影响。你可能会在早期发现这个错误,并且在这个过程中不会浪费太多时间,但对于更大、更复杂的分析,你可能不会这么幸运。在事先比较实际日期范围和假设的日期范围之前,可能会更快地发现错误的日期。在今天的海量数据世界中,有人编写一个分析数十亿封电子邮件随时间发送量的应用程序并不罕见——这可能需要使用计算集群,并且每次运行可能需要花费数百或数千美元。在这种情况下,不做作业——描述性统计——可能会代价高昂。

5.2.3. 选择要计算的特定统计数据

在描述 Enron 语料库的论文中,Klimt 和 Yang 明确指出,他们主要关注将电子邮件分类到主题或其他组别。在他们的情况下,日期和时间不如主题、词频和电子邮件线程重要。他们选择的描述性统计反映了这一点。

我们主要关注用户随时间的行为,因此我们计算了如下描述性统计:

  • 每月发送的电子邮件总数

  • 最活跃的电子邮件发送者及其发送的电子邮件数量

  • 最活跃发送者每月发送的电子邮件数量

  • 最活跃的电子邮件接收者及其接收的电子邮件数量

  • 最活跃的发件人和收件人配对以及他们交换的电子邮件数量

并非总是明显知道哪些统计数据最适合你的特定项目,但你可以问自己几个问题,这些问题将帮助你做出有用的选择:

  1. 有多少数据,其中有多少是相关的?

  2. 与项目相关的数据中,最相关的一两个方面是什么?

  3. 考虑最相关的方面,典型的数据点是什么样的?

  4. 考虑到最相关的方面,最极端的数据点看起来是什么样的?

问题 1 通常很容易回答。对于安然数据集,你将找到电子邮件的总数或电子邮件账户的总数,这两点我之前已经提到。或者,如果项目只关注数据的一个子集——例如,涉及肯·莱(Ken Lay)的电子邮件,他是后来因多项欺诈指控而被定罪的 CEO,或者可能只是 2001 年发送的电子邮件——那么你也应该找到该子集的总数。你是否有足够的相关数据来完成项目目标?始终要警惕,之前的数据整理可能并不完美,因此获取精确的子集可能不像看起来那么容易。姓名或日期格式错误等问题可能会引起问题。

问题 2 关注项目的重点。如果你在研究安然作为一个组织的兴衰,那么时间就是数据的一个相关方面。如果你主要关注电子邮件分类,就像 Klimt 和 Yang 所做的那样,那么电子邮件文件夹、电子邮件的主题和正文文本都很重要。在这个阶段,单词计数或其他语言特征可能是有信息的。想想你的项目,看看几个个别数据点,然后问自己,“我最关心的是哪个部分?”

对于问题 3,将问题 2 的答案用于计算与那些方面相对应的值的某些汇总统计。如果你认为时间变量很重要,那么计算数据集中所有电子邮件时间戳的平均值、中位数以及一些分位数(别忘了将时间戳转换为数值——例如,Unix 时间——然后再转换回来进行合理性检查)。你也可以计算每周、每月或每年发送的电子邮件数量。如果你认为电子邮件分类很重要,那么将每个文件夹中出现的电子邮件数量加起来,并找出包含最多电子邮件的文件夹。或者看看不同的人在不同文件夹中电子邮件的数量和百分比有何不同。这些结果中有任何让你感到惊讶的吗?根据你的项目目标,你能预见分析过程中可能出现的任何问题吗?

问题 4 与问题 3 类似,但不是查看典型值,而是查看极端值,如最大值和最小值。最早和最晚的时间戳,以及一些极端分位数,如 0.01 和 0.99,可能很有用。对于电子邮件分类,你应该查看包含最多电子邮件的文件夹以及可能包含少量电子邮件的文件夹——很可能许多文件夹只包含一个或几个电子邮件,并且可能对分析大部分无用。也许在项目的后期阶段,你会考虑排除这些文件夹。在查看极端值时,是否存在某些过高或过低的值没有意义?有多少值超出了合理范围?对于分类或其他非数值数据,最常见的和最不常见的类别是什么?所有这些对后续分析都有意义且有用吗?

5.2.4. 在适当的情况下制作表格或图表

除了计算这些统计量的原始值之外,你可能还会发现将一些数据格式化为表格很有价值,例如,对于最多产的各类别的电子邮件数量,或者作为图表,例如发送和接收的电子邮件每月数量的时间线。

表格和图表有时比纯文本更全面、更快地传达信息。在整个项目中制作表格和图表并保留它们以供参考是个好主意。

图 5.2 展示了从 Klimt 和 Yang 的论文中摘录的两个图表。它们是描述性统计的图形表示。第一个显示了用户在数据集中发送的消息数量与累积分布的关系。第二个则显示了 Enron 员工收件箱中的消息数量与这些电子邮件账户中存在的文件夹数量之间的关系。如果你对各种员工发送的电子邮件数量或员工如何使用文件夹感兴趣,保留这些图表并与所有后续结果进行比较可能是个好主意。它们可能会帮助你验证你的结果是合理的,或者表明你的结果不合理,并帮助你诊断问题。

图 5.2. 从 Klimt 和 Yang 的《The Enron Corpus:用于电子邮件分类研究的新数据集》(由 Springer 在Machine Learning: ECML 2004出版)中重新绘制的两个图表。

图片

适合你项目的描述性图表或表格的类型可能与这些不同,但它们同样应该解决与你的目标和希望回答的问题相关的数据方面。

5.3. 检查关于数据的基本假设

无论我们是否愿意承认,我们都会对数据集做出假设。正如前文所暗示的,我们可能会假设我们的数据包含在特定的某个时间段内。或者我们可能会假设包含电子邮件的文件夹名称是这些电子邮件主题或分类的适当描述符。这些关于数据的假设可能是期望或希望,有意识的或无意识的。

5.3.1. 关于数据内容的假设

让我们考虑安然数据中的时间元素。当我开始查看数据时,我确实假设电子邮件会跨越 20 世纪 90 年代末电子邮件出现和 21 世纪初公司倒闭之间的几年。我会犯错误,因为已经提到的日期格式潜在的错误或损坏。实际上,我看到了远超出我假设的范围的日期,以及一些可疑的日期。我对日期范围的假设当然需要检查。

如果你想要使用电子邮件账户中的文件夹名称来告知你电子邮件的内容,那么就隐含了一个假设,即这些文件夹名称确实是有信息的。你肯定希望检查这一点,这可能会涉及到相当多的手动工作,比如阅读大量的电子邮件,并使用你最好的判断来决定文件夹名称是否描述了电子邮件的内容。

需要特别注意的一个具体问题是缺失数据或占位符值。大多数人往往会假设——至少是希望——数据中的所有字段都包含可用的值。但通常电子邮件没有主题,或者发件人字段中没有姓名,或者在 CSV 数据中可能有NANaN或空白空间,本应填写数字的地方。始终检查这类占位符值是否频繁出现到足以引起问题的程度是个好主意。

5.3.2. 关于数据分布的假设

除了数据和范围的内容之外,你可能会对其分布有进一步的假设。坦白说,我知道许多统计学家会对本节标题感到兴奋,但对其内容感到失望。统计学家喜欢检查分布假设的适当性。尝试在谷歌上搜索“正态性检验”或直接访问维基百科页面,你就会明白我的意思。似乎有数百万种方法来测试你的数据是否呈正态分布,而这只是统计分布中的一种。

我可能因为写这篇文章而被所有未来的统计学会议禁止参加,但我的通常标准并不那么严格。通常,使用直方图或散点图绘制数据可以告诉你你想要做出的假设是否合理。例如,图 5.3 是我从我的研究论文中摘取的一张图表,其中我分析了田径运动的表现。图中展示的是所有时间以来最佳男子 400 米成绩的直方图(在取对数后),并且在其上叠加的是正态分布曲线。顶级表现符合正态分布的尾部是我研究的关键假设之一,因此我需要证明这个假设。我没有使用任何用于检验正态性的统计测试,部分原因是我处理的是分布的尾部——只有最佳表现,而不是历史上的所有表现——但同时也因为我打算使用正态分布,除非显然不适用于数据。对我来说,将直方图与正态分布图进行视觉比较就足以作为假设的验证。对于我的目的来说,直方图与钟形曲线足够相似。

图 5.3. 所有时间以来最佳男子 400 米成绩的对数似乎符合正态分布的尾部。

图片

尽管我在处理田径数据分布时可能不够统计严谨,但我并不想轻视检查数据分布的价值。如果你假设数据是正态分布的,而实际上不是,那么会发生不好的事情。假设正态分布的统计模型处理异常值不佳,而绝大多数流行的统计模型都做出了某种正态性的假设。这包括最常见的线性回归类型,以及 t 检验。当你的数据根本不符合正态分布时,假设正态性可能会让你的结果看起来具有显著性,而实际上它们可能是无关紧要的或完全错误的。

这最后一点适用于任何统计分布,而不仅仅是正态分布。你可能有一些你认为均匀分布的类别数据,而实际上某些类别出现的频率远高于其他类别。例如,从安然数据集中计算出的社交网络统计数据——一天发送的电子邮件数量、一天联系的人数等等——都是非正态的。它们通常是指数分布或几何分布,这两种分布你都应该在假设它们之前对数据进行检查。

总的来说,虽然可能可以跳过检查数据是否符合特定分布的统计测试,但一定要小心,并确保你的数据至少大致符合任何假设的分布。跳过这一步可能会对结果造成灾难性影响。

5.3.3. 一个揭示你假设的实用技巧

如果你觉得自己没有假设,或者你不确定你的假设是什么,甚至如果你认为你知道所有的假设,尝试这样做:向朋友描述你的数据和项目——数据集中有什么,你打算用它做什么——并写下你的描述。然后,剖析你的描述,寻找假设。

例如,我可能这样描述我的原始项目,涉及安然数据:“我的数据集是一堆电子邮件,我打算使用社会网络分析技术来建立整个网络的人员行为模式。我想得出关于员工响应性以及向上层级的沟通,包括老板在内的结论。”

在剖析这个描述时,你应该首先识别短语,然后思考可能隐藏在它们背后的假设,如下所示:

  • 我的数据集是一堆电子邮件— 这可能是真的,但检查一下是否可能存在其他非电子邮件数据类型,比如聊天消息或通话记录。

  • 组织范围— 组织是什么?你认为它是明确界定的,还是存在模糊的边界?运行一些关于组织边界的描述性统计可能有所帮助,比如拥有特定电子邮件地址域的人或写了超过一定数量信息的人。

  • 行为模式— 你对构成行为模式有哪些假设?是否每个人都需要进行相同的行为才能被宣布为模式,还是你有一套模式,你正在寻找符合这些模式的个别例子?

  • 人员网络— 网络中的每个人都需要连接吗?是否可以有不连接的人?你计划从社会网络分析文献中假设某种统计模型吗?这需要某些假设吗?

  • 响应性— 你认为这个术语意味着什么?你能用统计数据来定义它,并通过使用基本定义和一些描述性统计来验证数据支持这样的定义吗?

  • 层级结构— 你是否假设你对组织的层级结构有完整的了解?你认为它是僵化的,还是它会变化?

通过剖析你的项目描述并提出这样的问题,意识到你在做出假设,可以帮助你避免许多后续问题。你不想在完成分析、发现奇怪的结果后,再回去调查时发现一个关键假设是错误的。更重要的是,你不想一个关键假设是错误的,却从未注意到。

5.4. 寻找特定内容

数据科学项目有多种目标。一个常见的目标是能够在你的数据集中找到与某种概念描述相匹配的实体。在这里,我使用术语实体来表示你数据集中表示的任何独特个体。实体可能是一个特定的人、地点、日期、IP 地址、遗传序列或其他独特项目。

如果你从事在线零售,你可能将客户视为主要实体,并可能希望识别那些可能购买新视频游戏系统或特定作者的新书的人。如果你从事广告,你可能正在寻找最有可能对特定广告做出反应的人。如果你从事金融,你可能正在寻找即将价格上涨的股票市场股票。如果能够对这些描述进行简单的搜索,工作就会变得容易,你就不需要数据科学或统计学。但尽管这些描述不是数据固有的(你能想象一个告诉你即将上涨的股票吗?),当你看到它们时,你通常可以识别出来,至少在事后。这类数据科学项目的主要挑战是及时找到这些有趣的实体。

在安然电子邮件数据集中,我们正在寻找可能与我们现在知道在公司发生的非法活动有关的可疑行为。尽管电子邮件数据集中的可疑行为可以采取多种形式,我们可以列举一些:一般来说,员工讨论非法活动,试图掩盖某些事情,与可疑人物交谈,或以其他异常方式沟通。

我们已经有了跨社会/组织网络的通信统计模型,我们希望将其应用于安然数据集,但我们有无数种方式可以配置模型及其参数,以找到各种形式的可疑行为。我们无法保证我们会找到我们正在寻找的那种,也无法保证我们会找到任何可疑行为。我们可能找不到任何可疑行为的一个原因可能是实际上根本不存在可疑行为。

5.4.1. 寻找一些示例

如果你正在寻找相当具体的东西,你数据中的某些有趣的东西,试着找到一些。手动浏览数据或使用一些简单的搜索或基本统计来定位这些有趣事物的示例。你应该靠近数据,并能够验证这些示例确实有趣。如果你有大量数据——即使浏览都很困难——可以取数据的一个子集,并在那里寻找一些好的示例。

如果你找不到任何有趣的示例,你可能遇到麻烦了。有时,有趣的事情很少见或者根本不存在于你认为它们存在的那种形式。事实上,根据公布的情况,安然数据不包含任何线索或任何类型的烟幕弹。深入挖掘、改变你的搜索方式、以不同的方式思考数据和你要找的东西,或者以其他方式穷尽所有可能找到数据中好例子、有趣例子的方法,通常是有帮助的。

有时候暴力搜索是有效的。理论上,一个由几个人组成的小组可以在几天内阅读完所有的安然邮件。这不会很有趣——我相信不止几个律师做过这件事——但它是有可能的,并且将是找到你想要的东西的最彻底的方法。我相当有信心地说,在我与安然数据一起工作的几个月里,我阅读了该数据集中大部分的邮件。如果项目的目标没有远远超出安然数据集,这将是一个数据科学项目出错的特征。我们正在开发软件,其整个目的就是使阅读所有邮件变得不必要。我们想使用安然数据来描述可疑通信的样子,以便我们可以使用这些描述在其他数据集中找到这样的通信。这就是为什么在当时对我们来说暴力搜索是有意义的。根据你的数据,手动查看所有数据可能对你来说也是有意义的。

你也可能在你的数据子集上使用暴力搜索。可以这样想:如果 1000 个实体(数据点、人、天、消息,等等)中有 1 个是有趣的,那么如果你手动查看由一百万个实体组成的数据集的超过 0.1%,你应该能找到它。

你可能需要查看更多的数据以确保你没有运气不好,但如果你已经覆盖了 1%的数据而你还没有找到任何,你就知道有趣的实体比你想的更稀少或者根本不存在。你可以调整这些百分比以适应更稀少或更常见的实体。

如果最终你无法通过任何方式找到任何有趣的实体,唯一的选择是寻找另一种有趣的事物,遵循你项目中的另一个目标,或者回到所有数据源并找到另一个包含你感兴趣的事物痕迹的数据集。这样做并不有趣,但在实践中这是一个真实可能性,并不罕见。不过,让我们保持乐观,假设你在数据集中成功找到了一些有趣的实体。

5.4.2. 描述示例:它们有什么不同?

一旦你找到了一些有趣事物的例子,仔细观察它们在你的数据中的表示方式。在这个步骤中的目标是找出哪些数据特征和属性可以帮助你实现找到更多这类有趣事物的目标。通常,你可能会通过简单的检查识别出有趣例子共享的一些模式或值,这些数据点的某些方面可以用来识别它们并将它们与其他数据集区分开来。

对于电子邮件数据,是有趣内容的电子邮件文本,还是发送电子邮件的时间?或者,可能是发送者和接收者本身很有趣?对于其他数据类型,查看各种字段和值,并注意那些似乎在不同程度上区分有趣事物与其他事物的值。毕竟,这是大多数统计(特别是机器学习)项目的基础:区分两个(或更多)事物组。如果你能大致了解如何手动完成这项工作,那么创建统计模型并在代码中实现它来帮助你找到更多这样的例子就简单多了,这些内容我在后面的章节中会介绍。

通常,一个显著或容易与典型数据点区分开的数据点并没有什么量化特征,但仍然很有趣。以安德鲁·费斯托(Andrew Fastow)为例,他是安然公司在其最后几年中的首席财务官,也是后来使他和其他人入狱的欺诈活动的主要肇事者之一。在数据集中,费斯托的邮件中没有任何欺诈或秘密的迹象,但有趣的是,在整个语料库中只有他九封邮件。作为首席财务官,人们可能会认为他的角色应该包括与其他人更频繁的沟通,而不仅仅是每隔几个月一次。因此,他要么避免使用电子邮件,要么他很好地删除了所有服务器和其他人的个人收件箱和存档中的邮件。无论如何,费斯托的邮件可能之所以引人注目,并不是因为任何固有的信息,而仅仅是因为它的罕见性。

以类似的方式,你数据中的有趣事物可能可以通过它们的上下文或我可能称之为它们的邻域来描述。"邻域"这个术语是从拓扑学中借用的,拓扑学是数学的一个分支:

一个数据点的 邻域,从广义上讲,是一组与所讨论的点相似或位于该点附近的其他点。

相似性和位置可以有多种含义。对于安然数据,我们可以定义一个特定邮件的一个邻域类型为“由这个邮件的发送者发送的邮件集合。”或者我们可以定义一个邻域为“在同一周内发送的邮件集合。”这两种定义都包含相似性或邻近性的概念。根据第一种定义,安德鲁·费斯托夫发送的邮件确实有一个很小的邻域:只有八封其他邮件。根据邻域的第二种定义,邻域要大得多,通常在给定的一周内有几百封邮件。

除了数据点的本身属性外,你还可以使用其邻域来帮助描述它。如果你对安德鲁·费斯托夫的邮件感兴趣,也许所有很少写邮件的人发送的邮件都很有趣。在这种情况下,对“有趣”的一个定量描述使用相同的发送者定义的邻域,可以表述如下:

如果邮件是由很少发邮件的人发送的,那么这封邮件可能很有趣。

你可以将这个陈述纳入统计模型中。它是定量的(稀少性可以量化)并且可以通过你拥有的数据集中的信息来确定。

同样,你可以使用基于时间的邻域来创建对“有趣”的另一种描述。也许,假设你在搜索过程中发现了一封在半夜发送的邮件,从工作账户发送到私人电子邮件地址,要求在一个通宵餐厅见面。在安然数据集中不存在这样的邮件,但我喜欢想象这个情况比实际情况要戏剧化得多——可能是数据科学版的神秘和间谍活动。

这种关于半夜或奇怪时间点的概念可以通过几种方式来量化。一种方式是选择代表半夜的时段。另一种方式是将奇怪时间点定义为几乎没有人发邮件的时段。你可以使用几个小时的时段来描述一些有趣的邮件,例如:

如果问题邮件前后两小时内发送的其他邮件很少,那么这封邮件可能很有趣。

这种描述,就像之前的一个,既是定量的(“少”可以通过经验来量化)又是在你拥有的数据集中可回答的。

一个有趣的实体或数据点的良好描述是它既定量,又存在于或可从你的数据中计算出来,并且在某种程度上有助于将其与正常数据区分开来,即使只是略微不同。我们将在后续的初步分析和选择统计模型部分中使用这些描述,并进一步讨论。

5.4.3. 数据挖掘(或不是)

有些人可能会把在数据中四处窥探、找到你感兴趣的东西的例子,然后调整后续分析以适应这些例子称为数据调查。有些人可能会说,这会不公平地偏差结果,使它们看起来比实际情况更好。例如,如果你正在寻找你所在社区中蓝色皮卡车的数量,你碰巧知道通常有几辆蓝色皮卡车停在你家附近几条街外,你可能会朝那个方向走去,沿途数着卡车,而你已经知道的那辆卡车可能会略微偏向上调你的结果。或者,在最坏的情况下,你会随机走动,但如果你家附近并且在你典型的路线上,你更有可能经过它。

你希望避免结果中的重大偏差,所以你需要注意不要让我建议的初步描述影响这一点。数据调查可能是一个问题,而且敏锐的批评家正确地指出,有时你应该避免它。但是,只有在你评估结果的准确性或质量时,调查才是一个问题。特别是,如果你已经知道你的方法试图再次发现的一些事情,你可能会在这些情况下取得成功,你的结果将是不公平地好。

但你还没有到达评估阶段。现在,当你试图在数据集中找到和描述有趣且罕见的数据点、实体和其他事物时,你应该尽一切努力取得成功,因为这是一项艰巨的任务。然而,稍后,所有这些有用的调查可能会使结果评估复杂化,所以我现在提出这一点,让你注意潜在的复杂性和解决可能批评你不应该在数据中四处窥探的人。

5.5. 粗略的统计分析

在本章中,我已经讨论了基本描述性统计、验证假设以及描述你正在寻找的一些有趣事物的类型。现在,从统计复杂性来看,是时候将分析提升一个层次,但不是两个。我在第七章(第七章)中涵盖了完整的统计建模和分析,但在你达到那个阶段之前,最好只朝那个方向迈出一小步,看看结果如何。

大多数复杂的统计算法需要一段时间来实现;有时它们运行或计算所有数据需要花费一些时间。正如我之前提到的,其中许多在理解它们如何以及为什么给出特定结果,以及是否正确时相当脆弱或困难。这就是为什么我更喜欢缓慢且谨慎地处理这种复杂的分析,尤其是在处理新的或不熟悉的数据集时。

如果本节中的一些统计概念对你来说不熟悉,现在可以随意跳过,并在你完成本书的其余部分——或者至少第七章之后回到这一节。如果你已经熟悉大多数复杂的统计方法,本节可以帮助你决定你计划的统计方法是否是一个好选择。或者如果你还不知道你可能会使用什么方法,本节可以帮助你弄清楚。

5.5.1. 简化

大多数统计方法都可以翻译成简化的版本,与完整方法相比,可以在更短的时间内实现和测试。在你开始全面实施和分析之前,尝试其中的一些方法,可以为你提供关于哪些统计方法对你有用的巨大洞察,以及如何使用它们。

如果你最终的分析只是进行线性回归或 t 检验,那就直接进行吧。本节主要关注那些可能包括某种形式的分类、聚类、推断、建模或其他具有多个参数(固定或变量)的统计方法的那些项目。

分类

如果你计划在分析中做一些分类,有许多统计模型是为这项任务设计的,从随机森林到支持向量机再到梯度提升。但分类中最简单的方法之一是逻辑回归。

分类任务在其最简单形式上,是根据你选择并从数据中计算出的实体特征集,将两个类别标签之一分配给实体。通常,标签是 0 和 1,其中 1 代表有趣,与之前我使用的意义相同,而 0 是正常。你可以有更多类别和更复杂的分类,但我将留到以后再讲。

分类中最复杂的方法有很多可变部分,因此它们有可能比逻辑回归表现得更好。但正如我之前提到的,它们理解和调试起来要困难得多。逻辑回归是一种相对简单的方法,它的工作方式类似于线性回归,只不过输出值(对新数据的预测)介于 0 和 1 之间。

与机器学习的分类方法相比,逻辑回归的计算速度要快得多,几乎不需要你调整的参数。另一方面,它包含一些假设——例如某种类型的正态性——所以如果你有严重偏斜或其他奇怪的数据值,它可能不是最佳选择。

如果你有一个你认为可以帮助分类未知实体的实体特征,尝试将其作为逻辑回归模型中唯一的特征/参数。你的软件工具可以告诉你该特征是否确实有帮助,然后你可以尝试另一个特征,无论是单独尝试还是与第一个特征一起尝试。从简单开始通常是最佳选择,然后增加复杂性,并检查它是否有所帮助。

在 Enron 数据集中寻找可疑电子邮件的有用特征的一个良好候选是发送电子邮件的时间。深夜发送的电子邮件可能证明是可疑的。另一个特征可能是电子邮件的收件人数。

另一种更通用的方法来调查特征对分类的有用性是查看两个类别(0 或 1)中每个类别的特征值分布。通过几个图表,你可以看到两个类别的特征值之间是否似乎存在显著差异。图 5.4 是三个类别(由形状指定)的数据点的二维图。x 轴和 y 轴代表数据点的两个特征值。如果你的目标是开发出一种可以找到正方形数据点的统计软件——而不需要知道其真实形状——你可能做得很好;正方形数据点具有高的 x 和 y 值。找到正确识别正方形数据点的统计模型可能很容易。困难的部分可能在于找到当绘制时能给出这样整齐分组类别的特征。创建图表可以帮助你通过给你一个关于类别在所有数据点空间中的位置的感觉来找到那些好的、有用的特征,并帮助你弄清楚如何开发或调整特征以使其更好。

图 5.4。由形状给出的三个类别的二维图^([1])

¹

来自 en.wikipedia.org/wiki/Cluster_analysis (公有领域)

图 5.4

最后,如果你熟悉并理解你喜欢的统计分类方法,并且知道如何在统计软件中调整其参数以使其简单易懂,那么这也是进行快速粗略分类的好方法。例如,你可能使用一个有 10 棵树的随机森林,最大深度为 2。或者,如果你了解其背后的理论,具有线性核函数的支持向量机可能也相对容易理解。如果你熟悉这些技术的工作方式,这两个都可能是不错的选择。如果你选择这条路线,重要的是你要理解如何评估你方法的结果,检查各种特征的贡献,并确保该方法按预期工作。

聚类

聚类在概念上与分类有很多相似之处——存在具有特征值的实体,这些特征值旨在落入不同的组中——但是没有明确的、已知的标签。聚类过程通常被称为无监督学习,因为结果是相似实体的组——但是由于没有标签,可能一开始并不清楚每个组或聚类代表什么。通常需要手动检查或描述性统计来弄清楚每个聚类中包含哪些类型的实体。

作为聚类的粗略版本,我喜欢绘制与实体相关的各种值,并使用直观的视觉检查来确定实体是否倾向于形成聚类。对于具有许多方面和值的数据和实体,可能需要花费一些时间来直观检查一个或两个维度/变量的图。但是,如果你认为一些关键特征应该区分不同实体组,你应该能在二维图中看到这一点。如果你看不到,你可能需要重新审视你做出的某些假设。仅仅因为两个实体落入同一个聚类就盲目地假设它们相似,可能会在以后导致问题。例如,即使没有标签/颜色,图 5.4 中的数据点似乎很好地聚成了三个组。如果你知道有三个聚类,聚类算法应该能够找到它们。另一方面,如果你的数据没有很好地分组,聚类可能会给出较差的结果。

除了直观检查之外,最简单的聚类版本包含的变量和聚类很少(大多数情况下,你必须事先设置聚类数量)。如果你可以选择,比如选择三个或四个你最喜欢的实体特征,并且它们使用最简单的聚类算法(如 k-means)很好地聚类,那么你就已经迈出了良好的第一步,可以继续使用更复杂的聚类方法或配置。在你的软件工具中绘制聚类算法的结果也可能有所帮助,以确保一切看起来都有意义。

推断

统计推断是对你没有直接观察到的定量值的估计。例如,在我提到的安然项目中,我们曾经想要估计每个员工向他们的老板发送电子邮件的概率,而不是向任何其他潜在收件人的概率。我们打算将这个概率作为一个潜在变量包含在通信的统计模型中,这是一个复杂的模型,其最优参数值只能通过复杂的优化技术找到。对于大量数据,这可能很慢。

但我们可以通过计算每个员工向老板发送电子邮件的次数以及他们没有发送的次数来近似这个特定潜在参数值的推断。这是一个粗略的近似,但后来,如果完整模型的最佳参数被发现与相当不同,我们会知道可能出了问题。如果这两个值不同,并不意味着肯定出了问题,但如果我们不能理解和无法找出它们不同的原因,这确实表明我们不知道模型是如何在我们数据上工作的。

我们可以用同样的方式处理统计模型中的其他潜在变量:找到一种方法来获得粗略的近似,并为此做笔记,以便稍后与完整模型中的估计值进行比较。这不仅是一个检查可能错误的好方法,而且它还告诉我们关于我们的数据的事情,这些事情在我们之前讨论的计算描述性统计时可能没有学到——而且,更好的是,这些新的信息是针对我们项目目标的。

其他统计方法

我当然没有涵盖如何对每种统计方法进行粗略近似的细节,但希望前面的例子给你提供了思路。与数据科学中的几乎所有事情一样,没有一种解决方案;也没有 10 种或 100 种。有无数种方法可以处理过程的每一步。

你在设计和应用快速简便的方法时必须富有创意,因为每个项目都是不同的,并且有不同的目标。主要观点是,在确保它们合理适用于你的项目目标和数据,并且你正确使用它们之前,你不应该应用复杂的统计方法。首先应用统计分析的简单版本,这会让你对方法如何与你的数据互动以及它是否合适有一个感觉。第七章详细讨论了几种统计方法,从中你应该能够为你的分析收集更多想法。

5.5.2. 取数据的一个子集

通常你拥有太多的数据,以至于无法及时进行简单的分析。在这个阶段进行许多粗略的初步分析时,使用数据子集来测试简单统计方法的适用性可能是可以的。

如果你确实将粗略的统计方法应用于整个数据集的子集,请注意以下一些陷阱:

  • 确保你有足够的数据和实体,以便统计方法可以给出有意义的成果。方法越复杂,你需要的数据就越多。

  • 如果子集不能代表整个数据集,你的结果可能会大相径庭。对这个子集计算描述性统计,并将它们与整个数据集的相关描述性统计进行比较。如果它们在重要的方面相似——记住你的项目目标——那么你处于良好的状态。

  • 如果你只尝试一个子集,你可能会无意中选择了高度专业或具有偏见的子集,即使你运行了一些描述性统计。但如果你取三个不同的子集,对它们进行快速分析,并得到相似的结果,你可以合理地确信这些结果可以推广到整个数据集。

  • 如果你尝试不同的子集并得到不同的结果,这不一定是个坏事情。试着找出原因。数据本质上具有变异性,不同的数据可能会给出不同的结果,无论是轻微还是显著。使用描述性统计和这些简单统计方法的诊断来确保你理解正在发生的事情。

5.5.3. 提高复杂性:它是否改善了结果?

如果你无法从一种简单的统计方法中获得至少中等或令人期待的结果,那么继续使用更复杂的方法是危险的。提高你方法的专业性应该能改善结果,但前提是你必须走在正确的道路上。如果该方法简单版本不适合你的数据或项目,或者算法配置不当,那么提高复杂性可能不会有所帮助。此外,更复杂方法的配置更难修复,所以如果你从一个简单方法的错误配置开始,那么更复杂版本的配置可能同样不正确,甚至更难修复。

我喜欢确保我有一个稳固、简单的方法,我理解它,并且它清楚地给出了一些有用的结果(即使不是理想的结果),然后我在提高方法复杂性的过程中检查结果。如果每一步的结果都在改善,我知道我在做正确的事情。如果结果没有改善,我知道我可能做错了什么,或者我已经达到了数据或项目目标可以处理的复杂性的极限。

应用过于复杂的数据或设定无法处理的目标通常被称为过度拟合。具体来说,这意味着方法有太多的可变部分,并且所有这些可变部分在你的数据上工作得完美无缺,但当你给方法提供新数据时,结果的准确性却远不如之前。我在第七章中更详细地讨论了过度拟合,但就目前而言,可以说,专业性应该导致更好的结果——直到某个程度——如果你没有体验到这一点,那么可能某个地方存在问题。

练习

继续使用在第二章中首先描述的 Filthy Money Forecasting 个人理财应用场景,并关联到之前章节的练习,尝试以下练习:

1.

考虑到该应用的主要目标是提供准确的预测,描述三种你希望在数据上执行以更好地理解数据的描述性统计类型。

2.

假设你强烈考虑尝试使用统计模型来分类重复和一次性金融交易。关于这两种类别中的交易,你可能会有哪些假设?

摘要

  • 在数据科学的探索阶段,不要直接跳入复杂的统计分析,要故意谨慎,因为大多数问题可以通过知识和意识避免或快速解决。

  • 在分析数据之前,明确你对数据的先验假设,并检查它们是否合适。

  • 在假设你的麦秆堆里有针之前,手动筛选数据,找到一些好的例子,这些例子是你希望在项目期间找到更多类似事物的类型。

  • 对数据的子集进行粗略的统计分析,以确保在投入大量时间进行完整软件实现之前,你处于正确的轨道上。

  • 将任何探索性结果记录在方便的地方;它们可能在后续决策时有用。

第二部分. 使用软件和统计学构建产品

任何数据科学项目的核心目标是产生有助于解决问题和实现目标的东西。这可能以软件产品、报告或一组见解或对重要问题的答案的形式出现。生产这些任何一种的关键工具集是软件和统计学。

第二部分 的本书从一篇关于基于在第一部分(part 1 中涵盖的探索和评估所学的制定目标计划的章节开始。然后第七章 转向统计学领域,介绍了一系列重要的概念、工具和方法,重点关注它们的主体能力和如何帮助实现项目目标。第八章 以同样的方式介绍了统计软件;本章旨在为你提供足够的知识,以便为你的项目做出明智的软件选择。第九章 然后对一些流行的非统计软件工具进行了高级概述,这些工具可能使构建和使用你的产品更容易或更有效率。最后,第十章 通过考虑在统计学和软件方面的先前偏离所获得的知识以及处理数据、统计和软件的许多陷阱,将所有这些章节汇集在一起。

第六章. 制定计划

本章涵盖

  • 评估在项目初步阶段学到的内容

  • 根据新信息修订项目目标

  • 认识到何时应该重做先前工作

  • 向客户传达新信息并获得反馈

  • 制定执行阶段的计划

图 6.1 展示了我们在数据科学流程中的位置:通过正式规划开始构建阶段。在本书中,我一直强调不确定性是数据科学工作的主要特征之一。如果没有任何不确定性,数据科学家就不需要探索、假设、评估、发现或以其他方式应用科学方法来解决问题。同样,数据科学家也不需要在只有绝对确定性构成的项目中应用统计学——一个建立在不确定性基础上的领域。正因为如此,每个数据科学项目都包含一系列开放性问题,这些问题随后通过科学过程部分或全部得到解答。

图 6.1. 数据科学流程构建阶段的第一步:规划

在这些新答案出现时不考虑它们将是一个错误。这就像在你被告知前方道路关闭,最佳绕行路线需要你转弯后,仍然继续沿着这条特定的道路行驶。在过去的十年里,互联网连接的导航设备,通常是智能手机,已经变得无处不在。这些设备不断更新新的信息,特别是交通和事故信息,并使用这些信息来尝试优化提供给驾驶员的路线。这也是数据科学家在不确定性消失或减少时需要做的事情。

我喜欢在项目过程中定期停下来,总结我所学到的所有内容,并在更大的目标背景下考虑它们,就像我在第二章中描述的如何制定好的问题和目标一样。但每次我这样做时,都会出现新的信息,这些信息可能会影响决策。可能没有必要故意暂停项目并正式考虑所有新的信息,但通常是有用的,因为许多人倾向于继续按照之前可能不再是最佳计划的计划前进。这与意识的概念有关,我强调每个数据科学家都应该保持这种意识,正式的定期审查新知识可以帮助在团队中分享这种意识。即使你独自工作,稍微正式的审查对于组织以及向客户或其他项目利益相关者传达进度也是有帮助的。

本章中描述的进度评估和规划类型也可以持续进行或定期进行,而不是一次性完成,在某些情况下这是首选。但绝对应该在项目的前期阶段(我在前几章中已经介绍过)和我所称的执行阶段之间进行,我将在下一章开始讨论这个阶段,它包括典型项目的大部分正式统计建模和软件开发时间和精力。因为执行阶段劳动密集,你想要确保计划是好的,并且将解决项目的重大目标。你不想不得不重新执行执行阶段。

6.1. 你学到了什么?

在第二章中提出一些问题和设定一些目标后,你在第三章中调查了数据的世界,在第四章中整理了一些具体的数据,并在第五章中了解了这些数据。在每一步中,你都学到了一些东西,现在你可能已经能够回答你在项目开始时提出的一些问题。

例如,在搜索数据时,你是否找到了你需要的一切,或者是否还有关键数据块缺失?在评估数据后,它是否包含了你预期的所有内容?你计算出的描述性统计结果是否如预期,或者有任何意外?

6.1.1. 例子

由于每个问题的答案都将极大地取决于项目的具体细节,因此对我来说很难将提问和回答的过程形式化。相反,我将考虑在这本书中已经展示的例子,并描述我在这些项目这一阶段的某些经验。在本章的后面部分,我将回到这些例子,并讨论我是如何利用新信息来对项目做出后续决定的。

啤酒推荐算法

在第二章(kindle_split_011_split_000.xhtml#ch02)中,我简要描述了一个假设的项目,其目标是根据用户为其他啤酒提供的评分来向啤酒网站的用户推荐啤酒。因为这个项目是一个我从未承担过的假设项目(尽管很多人都有),我可以从项目的初步阶段回答“我学到了什么?”这个问题,但我认为它仍然具有说明性。

尤其是提到,我们最初开始的数据集,一个包含用户啤酒评分的 CSV 文件,并没有将啤酒的具体类型作为其中一列。这是一个问题,因为啤酒的类型或风格通常在确定一个人是否会喜欢它时很有信息量。知道类型缺失后,我的初步计划是要么找到一个包含数据类型的数据集,要么从啤酒的名称中推断类型。如果这两者都不可能,我就不得不在没有类型的情况下完成项目。因此,有三种不同的路径来分析和推荐啤酒,确定最佳路径需要一些初步的数据科学。

在审视数据世界时,就像在第二章(kindle_split_011_split_000.xhtml#ch02)中描述的那样,我可能找到了一个与啤酒类型匹配的啤酒列表。如果找到了,那么这个特定的不确定性就会变成确定性(如果我相信它),我会在我的推荐算法中使用给定的啤酒类型。如果没有找到那个啤酒类型的列表,那么,正如我在第四章(kindle_split_013_split_000.xhtml#ch04)中描述的那样评估我的数据时,我可能编写了一个脚本,希望它能快速地解析每个啤酒的名称并确定它的风格。如果它看起来工作得很好,我会制定一个计划来尽可能改进它,并且那条特定的路径就会实现。

如果我找不到包含啤酒类型的数据库,而且我也无法编写一个成功推断啤酒类型的脚本,那么只剩下一条可能的路径:我不得不放弃。不知道啤酒类型,统计挑战将大大增加,但并非不可能,因此我必须制定一个考虑到这一点的计划。

在描述的所有三种情况下,我从搜索数据、处理数据和评估数据的过程中获得了新的知识。这种新知识影响了继续进行项目的最佳计划。

生物信息学和基因表达

在第三章中,我介绍了我作为博士研究的一部分所参与的一个项目,该项目涉及分析特定 miRNA 的表达与单个基因之间的关系。

在寻找更多可能有助于公共生物信息学数据仓库和出版物中的数据和信息的搜索过程中,我发现了一些包含预期调节某些基因表达的 miRNA(miRs)的算法预测的数据库。我还发现了一些其他分析工具,它们以与我追求的不同方式实现了我追求的一些相同目标。

对于每个基于 RNA 序列的算法预测数据集,我必须决定它们是否对我的项目有帮助,以及这种帮助是否足以证明利用这些数据所需的工作。每个新的数据集都需要一定程度的努力来获取、解析并将数据转换成有用的形式。在两种情况下,我决定投入工作来利用这些预测,以指导我尚未构建的统计模型。

但是,在处理和评估数据的过程中,我意识到 miR 和基因的名称与我的主要数据集(微阵列表达数据)中的名称不完全匹配。不同的科学组织根据特定需求为基因开发了不同的命名方案,并且尚未统一,但存在名称转换工具。如果我希望利用这些预测数据集,我必须在计划中包括将一个方案中的名称转换为另一个方案的内容,以便我可以将预测与微阵列数据中的特定值相匹配,这些值在某种程度上是预测结果的实现。

除了关于命名方案的新知识外,在评估数据时,我还了解了一些关于微阵列表达数据分布的情况。因为我使用了大多数生物样本的重复微阵列——我把相同的生物 RNA 样本放在多个微阵列上——我可以计算出所谓的“技术方差”。技术方差是由技术(微阵列)引起的方差,与生物过程无关。我发现,在 10,000 个左右的基因中,技术方差大于生物方差。这意味着在这些情况下,基因的测量表达水平与测量微观化学物质的随机性比与生物效应更多。我必须决定是否要在我的分析中包括这些高技术方差(或低生物方差)的基因,或者忽略它们。一些统计方法处理方差不好,而另一些则可以,所以我必须明智地选择。

田径和田径的顶级表现

在第四章中,我讨论了我如何分析田径历史上最佳表现列表,以评分和比较男女各项目的成绩。特别是,我想比较所有项目的世界纪录,找出哪一个是最好的。

在这个项目中,我必须做出的第一个选择是,我是否想要使用可用的最佳免费数据集,在www.alltime-athletics.com,还是为另一个网站上声称的更完整的数据集付费。我是一个开放获取数据的粉丝,所以我选择了免费数据集,但如果我后来意识到我没有足够的数据来告知我的统计模型,我就不得不重新考虑我的选择。

在检查我的数据集时,我检查了每个表现列表的长度,发现有些项目,如女子一英里赛跑,只包含几百次表现,而其他项目,如女子障碍赛,长度达到 10,000 条记录。这种表现列表长度的巨大差异是否会导致问题并不立即明朗,但这是我在规划时必须考虑的新信息。

另一个我没有预料到的情况是,一些事件,如跳高、撑杆跳和 100 米和 200 米短跑,产生的性能值是离散值而不是连续值。在跳高和撑杆跳中,横杆逐渐升高,运动员要么跳过这个高度,要么跳不过,这意味着在每一天,许多运动员都有相同的成绩。在最佳跳高成绩列表中,超过 10 人的成绩是 2.40 米,超过 30 人的成绩是 2.37 米,在更低的高度上,数字更大。观察成绩分布,如直方图或类似的东西,可以很好地论证,连续分布,如对数正态分布,可能不是最佳选择。在规划时,我必须决定是否以及如何将这些事件与其他事件进行比较,例如长跑项目,这些项目的成绩显然是连续的,因为几乎没有人能在最佳历史记录名单上与其他人并列。

安然电子邮件分析

在第五章中,我谈到了已经公开的安然电子邮件集合,以及我以及当时在同事是如何使用社交网络分析技术来尝试检测可疑行为的。

这是一个开放性的项目。我们知道在安然公司发生了一些不好的、犯罪的事情,但我们不知道这些事情会在数据中如何体现,如果确实存在的话。我们知道我们希望将电子邮件数据视为一个社交网络中的通信集合,因此分析的第一步就是从数据中构建一个社交网络。

第一个令人惊讶和失望的发现之一是,在包含电子邮件的 100 多个 PST 文件中,同一个电子邮件发送者(或接收者)可以用许多不同的方式表示。例如,数据集中最活跃的电子邮件发送者之一,Jeff Dasovich,可能在电子邮件的“发送者”字段中显示为 Jeff Dasovich、Jeffrey Dasovich、jeff.dasovich@enron.com,甚至 DASOVICH,而这些只是众多可能性中的一小部分。乍一看,这似乎不是一个大问题,但实际上却是。Dasovich 本人并不难识别,但还有多个名叫 Davis、Thomas 等的人并不容易区分。

作为数据处理过程的一部分,我最终编写了一个脚本,该脚本会尝试解析出现的任何名称,并确定该名称是否已经被遇到,优先考虑格式良好的名称和电子邮件地址,但这个脚本远非完美。在某些情况下,我们在构建的社会网络中有两个人或更多人实际上是同一个人,但我们未能将他们合并,因为脚本没有识别出这些名称彼此匹配。我们手动检查了最活跃的发送者和接收者,但在前 100 或 200 人之后,我们无法保证脚本正确地匹配了名称。在规划中,我们必须考虑到电子邮件与命名发送者和接收者匹配的不确定性。

不言而喻,在项目开始时,我们并不知道将名称相互匹配将是我们面临的最大挑战之一。如果我记得正确的话,编写一个相当成功的脚本的任务消耗了我比项目其他任何方面更多的精力。任务的不可确定性和对问题复杂性的认识对于项目的执行阶段的评估和规划至关重要。

在我们找到了解决名称匹配问题的合理方案之后,我们对安然数据集进行了一些描述性统计分析。出人意料还是不出所料,我们很快意识到某些人,比如 Jeff Dasovich,在几年时间里发送了几千封电子邮件,而像 Ken Lay 这样的关键高管几乎一封都没有发送。显然,个人行为,至少就电子邮件而言,必须在我们的统计模型中予以考虑。我们隐含地假设安然的所有重要员工都写了足够的电子邮件,以便我们能够模拟他们的行为,但显然这并不正确。我们后来进行的任何分析都必须允许存在极大的行为差异,同时仍然能够检测到某些在某种犯罪意义上的异常行为。这并非小问题,而且在规划过程中还要考虑的另一件事。

6.1.2. 评估你所学的

我已经提供了例子,但关于你在特定项目中可能学到的具体指导很少。具体的指导很难找到,因为每个项目都是不同的,而且似乎也不可能将项目分组为将产生相同类型教训和新信息的类型。这个评估阶段体现了数据科学项目的不可确定性,并强调了数据科学家为什么需要首先始终保持警觉,成为技术驱动的解决问题者。对于数据科学中的实际问题,没有现成的解决方案。唯一的解决方案是在应用工具(无论是现成的还是定制的)时保持警觉和创造力,以及智能地解释结果。在数据科学项目的初步阶段,你会学到一些东西,你应该像对待任何其他信息一样认真对待这些信息,尤其是因为它们在项目的主要执行阶段之前,并且因为它们是你拥有的最具体的项目信息。由于这一新信息的可能重要性,执行本章中描述的评估阶段可能会有所帮助。

这个评估阶段是项目的一个回顾阶段。如果你是一个喜欢做大量笔记的人,记住你所做的工作和结果不会是问题,但如果你像我一样,很少对自己的工作做笔记,那么记住的任务可能会更困难。我开始倾向于使用那些能自动记录我工作技术的工具。Git,在它未被明确设计用于的另一个用途中,提供了一些可以帮助我记住项目近期历史的功能。如果我的提交信息是有信息的,有时我可以从它们中重建项目历史,但这显然不是理想的。电子邮件记录也可以作为记忆辅助。因此,我现在也更倾向于使用更冗长的电子邮件和 Git 提交信息;我永远不知道我什么时候需要记住某件事,以及哪些具体细节会有帮助。

无论如何,如果你能收集和总结自项目开始以来所学到的内容,你就处于良好的状态。人们常常带着不切实际的期望开始项目,并根据自己的期望来设定目标,但随后他们等待太久,没有根据新的信息调整自己的期望,而这些新信息证明他们的期望是不合理的。这就是本节的意义所在:每个数据科学家都应该时不时地停下来,考虑是否有任何新的信息改变了在项目具体信息较少时制定的目标和假设的基本期望和假设。根据这一新信息重新考虑目标和期望是下一节的主题。

6.2. 重新考虑期望和目标

根据上一节收集的新信息总结可能很大、无关紧要、不出所料、具有变革性、不好、好,或者任何你可能会想到的描述。但直到你拥有这些信息,你才知道如何描述它们。然而,主要的是,新信息具有某种特质,这种特质会改变你对项目、其进展、其有用性等方面的看法,即使这种特质是确认你之前认为正确的事情。正如我在第五章中讨论的那样,假设的确认很重要,对于项目的期望也是如此。如果你做了大量工作,但期望没有改变,可能会觉得进展很小,但实际上,在消除不确定性方面已经取得了进展。消除不确定性是加强项目基础的一种形式,其他更深思熟虑的进步都是在此基础上建立的。

另一方面,如果你的期望没有通过项目的新信息得到满足,那么你完全处于不同的境地。有些人因为他们的期望得到确认而变得自信,而有些人则相反——他们喜欢挑战自己的期望,因为这意味着他们在学习和发现新事物。在你项目的期望受到挑战,甚至完全被推翻的情况下,这是一个同时运用各种数据科学技能的机会。

6.2.1. 意外的新的信息

在数据科学项目的初步阶段发现新事物并不罕见,你可能会想,“当然!有了新信息,我显然会从容应对,并从那时起充分利用它。”但事情往往并不那么简单。

有时候,人们如此渴望某件事是真的,以至于即使它已经被证明是假的,他们仍然继续相信它。我发现生物信息学领域有许多这样的乐观者(这并不意味着整个领域有问题)。这一定与两种不同类型的尖端技术的交汇有关——能够在细胞水平上提取和测量分子生物活性的机器,以及现存的最复杂的统计方法——这使得一些人相信,一种可以拯救另一种。

在我的工作中,不止一次遇到了由实验室实验产生的数据集,这些实验的准备可能需要几天甚至几周的时间。在分析过程中,我意识到数据质量很低。在生物信息学中,这种情况有时会发生;即使是微小的污染或手部失误也可能破坏整个实验基础的溶解 RNA 滴。然而,考虑到创建某些数据集所涉及的实验室工作量大,生物学家们往往不愿意丢弃这些数据。在一个案例中,我发现一个微阵列数据集的技术差异远大于生物差异,这意味着测量结果更接近随机数生成器,而不是有意义的基因表达值。在另一个案例中,我们使用了一种耗时且被认为是测量单个基因表达的金标准的过程,但得到的数据集自相矛盾。在这两个案例中,据我所知,数据几乎毫无价值,实验需要重新进行(在生物信息学中这种情况经常发生,这就是为什么检查数据质量很重要的原因)。但在两种情况下,与项目有关的人员都无法承认这一事实,他们花费了一周或更长时间试图找出如何挽救数据或以巧妙的方式分析数据,以便缺点不会产生影响。对我来说,这是不理性的。这在科学上等同于财务会计术语中的沉没成本:实验已经完成,最终成为了一个坏的投资。研究人员不应该担心浪费的钱、时间和精力,而应该找到一种方法,最大限度地提高以后获得好结果的机会。这并不是说在失败中没有可以吸取的教训——弄清楚为什么会发生通常是有所帮助的——但主要目标是优化未来,而不是为过去辩护。

在新信息与预期相矛盾的情况下,尽管不确定性有所减少,但它并不是以预期的那种方式减少的,因此可能会感觉不确定性增加了,因为相关人员可能还没有充分考虑所有的影响。考虑一下选举中一场激烈的战斗,其中一位巨大的落后者最近才进入广泛关注。如果落后者获胜,一旦结果公布,所有的不确定性都会消除。但由于这个结果并不被预期,它感觉比预期候选人获胜的情况更不确定,因为大多数人已经为这位热门候选人的预期胜利及其许多影响做好了心理准备。

这可能看起来不像数据科学,但这种思维方式确实在我的工作中发挥了作用。当我处理期望——以及它们被证明是正确、错误或介于两者之间时——情感往往会介入。情感不是数据科学,但如果情感影响我对新信息的反应,它们就变得相关。尽管处理不确定性是数据科学家的主要技能之一,但人类在不确定性以及正确或错误面前产生情感反应是正常的。毕竟,数据科学家也是人。

如果有解决方案的话,解决所有这些问题的方法就是尽可能地将所有情感从决策过程中排除出去。这比说起来容易做起来难,对我来说也是如此,以及我几乎与所有人合作过。每个人都喜欢正确,而很少有数据科学家或软件工程师喜欢在中途改变计划,尤其是对于大型项目。以下是一些消除数据科学决策中情感的方法:

  • 正式性— 制作列表、创建流程图或编写逻辑的 if-then 语句,直接展示新信息对未来结果的影响。最重要的任务是把这些写下来,以便你有一个永久记录,说明你基于这些新信息选择特定新路径的原因。

  • 咨询同事— 一个对项目没有投入的同事可能最好,但肯定也要咨询项目中的其他人。讨论新信息通常有帮助,但有一个善于倾听的外部人士思考你的新信息和处理它的计划可能非常有价值。

  • 搜索互联网— 在某些情况下,这可能不适用,但世界各地的数据科学家和统计学家已经看到了很多意外结果。如果你擅长使用搜索引擎,并且能够将你的结果和待决决策简化为某些通用术语,互联网可以提供很大帮助。

一旦你将问题简化为其核心事实,包括你拥有的所有新信息和迄今为止所做的一切工作,基于新信息制定任何新目标或调整任何旧目标是一个理性的过程,这个过程没有因放弃最终无用的前期工作而产生的情感惩罚。

6.2.2. 调整目标

无论你的期望是否得到满足,或者你是否完全被初步结果所震惊,评估并可能调整你的项目目标通常都是值得的。第二章描述了一个收集初始信息、提出好问题以及规划一些回答这些问题的方法,这些方法有助于实现项目目标。你现在需要回顾这个过程,结合项目早期探索阶段获得的新信息,再次回答以下问题:

  • 什么是可能的?

  • 什么是有价值的?

  • 什么是高效的?

“可能实现什么?”的实践限制是面向商业的空想家的问题“什么是有价值的?”的平衡,两者都是界定实用探究“什么是有效的?”的极端,最终可能成为前两个的重要混合体。

什么是可能的?

在问自己在这个项目阶段可能实现什么时,你应该考虑你在项目开始时考虑过的同样的事情:数据、软件、障碍等等。但是因为你现在比当时知道得更多,一些之前看似不可能的事情现在可能看起来可行,而一些你认为可能实现的事情现在可能看起来正好相反。

通常在这个时候,是数据集使得事情看起来不太可能。在深入挖掘和探索之前,人们往往对数据和内容的能力和内容过于乐观。现在你已经知道得更多,可以得出更多关于可能性的更明智的结论。

什么是有价值的?

很可能,项目的目标价值并没有太大变化,但再次考虑它们作为参考通常是值得的。另一方面,在一些快速发展的行业中,各种目标的价值估计确实可能发生了变化。在这些情况下,最好与客户一起审查目标列表,看看有什么变化以及如何变化。

什么是有效的?

尤其是数据和软件的细节可能使得某些路径和目标看起来更容易或更难,资源消耗更多或更少,或者与之前有所不同。再次从概念上运行这些数字可以帮助你重新优化计划,以从你拥有的资源中获得最大利益。

6.2.3. 考虑更多探索性工作

有时在这个评估阶段,很明显,你没有从早期的探索阶段学到你希望学到的那么多。但你可能对哪些具体的探索会带来更多和更好的知识有更明智的想法。

例如,考虑一下我之前讨论过的啤酒推荐算法项目。也许在探索阶段,你编写了一个尝试从啤酒名称中推断啤酒类型的脚本,这是我在本章前面讨论过的一种可能的策略。这个脚本看起来工作得相当不错,但有人问你,“它有多好?”而你意识到你没有明确评估脚本的性能。在这种情况下,以客观的方式衡量性能会很好,这样你就可以有理由相信它工作得很好。衡量性能的一个不错的策略是抽查脚本推断出的几种啤酒类型。随机挑选 20、50 甚至 100 种啤酒,并将推断出的类型与,比如说,酿酒商的网页进行对比,这将是获得统计数据的可靠方法——可能是一个正确推断类型的百分比,这会告诉你你的脚本表现如何。一个好的统计数据可以给你的工作增添很多可信度,而一个差的统计数据意味着你可能需要改进脚本。

类似地,在这个评估和规划阶段出现的问题和担忧往往可以表明你尚未进行的其他探索性工作的有用性。如果觉得这样做会有益,回头去做更多的探索是完全可以的——有时甚至是有益的。

6.3. 规划

正如第二章中所述,评估和设定目标紧接着创建计划。有些人可能喜欢同时进行这两项工作,但我喜欢将它们分开,至少在概念上是如此,因为规划涉及许多细节——例如时间、资源、人员、日程安排、货币成本的具体细节——这些通常与设定目标没有直接关系。哪个团队成员将在何时负责项目的哪个方面不应该在设定项目主要目标时扮演重要角色。但在规划中,这确实有作用。

正如早期规划阶段一样,不确定性和灵活的路径应该放在你的脑海中。你现在对你的项目了解更多,所以之前存在的一些不确定性已经不再存在,但某些新的不确定性已经出现。

将你的计划想象成穿过一个城市,街道一直在施工的试探性路线。你知道你想去哪里,以及一些到达那里的方法,但在每个路口,可能会有道路关闭、交通拥堵或路面坑洼破碎。你将在到达这些障碍时做出决定,但现在有一个备用计划或两个就足够了。

6.3.1. 示例

之前,我讨论了四个项目,以及我在它们之前的探索阶段学到的一些具体事情。现在,我想在设定目标和规划的大背景下再次讨论它们。与学到的经验教训一样,设定目标和规划的过程是项目特定的,并且不适合具体的如果-那么陈述,因此例子可以说明问题,并且非常有价值。

啤酒推荐算法

啤酒推荐算法的目标可能很单一:提供好的啤酒推荐。但你可能希望更加具体。你是想为每个用户制作一个啤酒的前十名列表,还是希望在推荐类似用户可能会喜欢的啤酒之前,让用户先选择一种啤酒或风格?这是一个数据科学家的工作可能会涉及到产品设计师典型职责的案例。回想一下第二章中关于倾听和理解客户的讨论:客户将如何使用你项目的产品?让我们假设——经过深思熟虑,并咨询了项目团队和一些潜在用户——目标应该是为每个用户制作一个他们应该尝试的啤酒前十名列表。

让我们回到我提出的设定目标过滤器:可能、有价值、有效。制作这样一个前十名列表是可能的;根据所采用的统计方法的质量,列表可能从好到坏不等,但无论如何,制作列表肯定是可能的。从某种意义上说,这个列表也是有价值的;项目基于这样一个事实,即某个地方有人想要发现一些他们可能会喜欢的啤酒,所以让我们假设它是有价值的;否则,项目本身就没有价值。目标看起来很有效;它似乎 100%可能,而且很难想到一个相关目标,它以更少的努力提供更多的价值。在可能性、价值和效率方面,这个目标很简单:可以考虑替代方案,但很少有反对前十名列表的论点。

那么,主要目标就是:一个用户尝试后可能会喜欢的啤酒列表。关于计划呢?我之前提到过,计划是一系列与一系列不确定性交织在一起的应急措施。构建啤酒推荐算法的不确定性主要在于统计方法以及数据集支持这些方法的能力。即使是最优秀的统计方法,当数据集太小或不可靠时,也不会给出好的结果。因此,项目中的最大不确定性就是算法输出的质量。你不知道算法用户喜欢推荐啤酒的概率。

一个好的计划应该考虑到算法可能不会像你希望的那样好。如果你有一个完美的算法,所有用户都会喜欢推荐的啤酒,但如果算法推荐错了其中一些,你应该怎么办?作为一个统计学家,我首先建议你诊断一些不良推荐并修改算法以考虑这些因素。这样几次迭代可能解决问题,也可能不会。

另一个替代方案是开发一个对错误友好的产品。根据用户如何与算法互动,他们可能期望得到完美的啤酒推荐,或者他们可能完全理解错误是可能发生的。评分系统是一个可能的解决方案,它更多地来自产品设计角度而不是分析角度。如果算法出错,但它给出了一个推荐分数,表明推荐的可靠性(并且这个分数本身是可靠的),那么用户可能会容忍一些错误的推荐,以寻找好啤酒的风险。

有这两个替代方案,一个好的计划正在形成。如果算法产生可靠的结果,那么你可以信任前 10 名列表,并且可以向用户展示列表时感到自信。但如果列表并不那么可靠,你应该要么修订统计方法使列表更可靠,要么生成一个推荐可靠性分数,向用户传达并非每个推荐都能保证被喜欢。因此,未来有两种可能的路径和选择。但你怎么做出选择?做出选择的过程也应该包含在计划中,只要可能的话。

在这种情况下,未来选择这两条路径取决于算法生成的推荐的可靠性,因此你需要一种方法来评估推荐的可靠性,并且你需要将这一点纳入计划。我将在后面的章节中更详细地讨论这些统计方法,但检查统计算法预测准确性的一个好方法是,在算法训练或学习时保留部分数据集,然后通过检查它是否正确预测这些数据来测试算法。在这个项目中,你可能会在训练统计方法时为每个用户保留一些啤酒推荐,然后检查在训练期间被保留但用户高度评价的啤酒是否确实被算法推荐。同样,你可以检查低评价的啤酒是否被排除在推荐列表之外。无论如何,算法的推荐应该与被保留的用户评分相印证;如果不一致,那么算法并不好,你需要选择前面提到的补救措施之一。

总体来说,这是一个好的基本计划。图 6.2 以流程图的形式展示了该计划。请注意,这个计划包括一系列需要遵循的步骤,但同时也存在一个关于算法性能的主要不确定性。根据性能评估的结果,可以采取不同的路径。在规划阶段提前承认这种不确定性及其在决定下一步发生什么方面发挥的主要作用,对于你作为数据科学家以及依赖你的客户来说,都是非常有信息的。我将在下一节讨论如何向客户传达修订后的目标和计划,在我介绍完其他示例之后。

图 6.2. 一个展示开发啤酒推荐应用可能计划的流程图

图片

生物信息学和基因表达

这个项目的目的,总的来说,是弄清楚 miR 是否影响了基因的表达水平,具体是在小鼠干细胞发育的背景下。可能的目标包括以下内容:

  1. 发现最有可能相互作用的 miR-基因对

  2. 发现调控干细胞发育的 miR

  3. 发现影响干细胞发育和调控的 miR 和基因之间的途径(原因和效果序列)

目标 1 包括对 miR 和基因在时间序列数据中的特定相关性进行统计分析。无法保证结果有多可靠,但分析肯定是可能的。同样,目标 2 需要分析 miR 在整个干细胞发育阶段的表达水平。这同样是完全可能的,尽管在功能生物学方面,统计方法的结果是否正确并不确定。目标 3 要复杂一些。途径涉及不止两个 miR 或基因,因此发现单一途径需要证明三个以上组成部分之间的多个相关性,以及证明这些多个相关性不仅仅是通过巧合连接起来的额外工作。生物科学期刊在发表我关于发现途径的文章之前,会期望有大量关于各个部分之间联系的证据。尽管目标 3 是可能的,但它比其他两个可能的目标要复杂得多,也难以证明。此外,分析途径所需的统计工作比简单的 miR-基因相互作用要多。

这些目标中每一个的价值都有些不确定。毫无疑问,发现新的 miR-基因相互作用、调控干细胞发育的 miR 或干细胞通路都是很有价值的,但每个目标的价值将取决于涉及的基因以及我能够多么有力地证明结果。如果我发现了一个可以令人信服地与流行生物学现象(如遗传疾病或性别分化)相关的新相互作用或通路,那么我在高影响力期刊上发表这项研究的机会就会大得多。但所有三个目标实现这样有价值结果的可能性大致相同,因此我认为每个目标的价值是相等的。

在评估目标效率时,很明显,目标 3 由于更加复杂,将需要比其他目标多得多的工作。目标 1 和 2 需要大致相同的工作量,并且实现有意义结果的机会也大致相等。这两个目标之间的唯一决定性因素是,确定与特定基因相互作用的特定 miR 似乎比发现只能与干细胞发育一般相关联的干细胞对科学界来说更有趣。

因此,由于目标 1 比目标 2 稍微更有价值和效率,我选择了目标 1 作为我的主要目标,目标 2 紧随其后,目标 3 则排在第三位,只有在完成前两个目标后有大量额外时间时才会进行。

考虑到这些目标,计划必然包括开发旨在发现目标 1 的 miR-基因对的统计方法,同时可能考虑到目标 2 中的调控 miR。在尝试实现这两个目标的过程中发现的关联可以组装起来,以构建类似于目标 3 的合理通路。

描述我的计划的流程图出现在图 6.3 中。正如您所看到的,这个计划相当直接。科学研究往往趋向于这种结构:你致力于改进方法和结果,直到你有一个科学界会欣赏的结果,然后你写一篇关于你的研究的文章,然后提交给期刊发表。这与典型的商业项目形成对比,在商业项目中,通常对时间有更严格的限制,这在某种程度上要求数据科学家为了满足截止日期而妥协结果的质量。另一方面,尽管学术研究人员通常没有强烈的时间限制,除了会议申请截止日期外,他们通常被要求比工业界更高的统计严谨性和发现的重要性标准。一个学术数据科学家的计划可能看起来与一个在可比私营行业中的数据科学家的计划不同,这也是确定特定项目具体计划的因素之一。

图 6.3. 显示我的基因相互作用项目基本计划的流程图

图片

田径和田径的顶尖表现

在第四章中,我讨论了我的一个项目,该项目分析了田径各种项目的最佳表现列表。正如我在本章前面提到的,该项目中剩余的两个主要不确定性涉及数据的质和完整性,以及我应用于它们的统计方法的有效性。

第一个不确定性可以这样表述:是否有足够的数据?因为我只有精英运动员的最佳表现列表,而没有所有表现的完整列表,所以我只能查看统计分布尾部的数据,这使得估计参数变得更加困难。为了确定是否有足够的数据——这一点我将在后面的章节中更深入地讨论——我会检查估计参数的估计方差是否足够小,同时也会检查模型生成的预测的估计方差,以确保它不会因为输入的微小变化而做出剧烈变化的预测。最重要的是,直到我编写将统计模型应用于数据并生成估计值的代码,我才知道是否有足够的数据。如果没有足够的数据,我可能需要考虑从另一个网站购买更多数据(尽管我不确定它会有多少数据,如果有)或者,可能的话,简化模型以便它需要更少的数据。

第二个主要不确定性涉及我将使用的统计分布。我想在统计模型中使用对数正态分布,但在编写统计代码之前,我无法确定这种分布是否合适。在第五章中描述的探索阶段,我确实生成了一些数据的直方图,这些直方图似乎遵循类似于正态曲线的钟形曲线的尾部,但直到我后来编写了估计此类曲线最佳参数的代码并将其与数据比较,这个不确定性才得到解决。因此,在本节的目的上,这个不确定性仍然存在。

最后,还有统计挑战中始终存在的确定性:结果是否足够好?几乎从未有保证你会得到好的结果,这个项目也不例外。出于学术目的——这是一个我打算在科学期刊上发表的学术项目——“足够好”通常意味着“比其他人更好”。我的主要竞争对手是国际田联的田径评分表。我选择比较他们评分表所隐含的预测和我的评分表,看看哪个预测得更准确。如果我未能达到预期,那么我几乎没有什么可以发表的。我必须改进模型和预测,或者——记住,这是阴暗的、无耻的、邪恶的统计学家做法——我改变比较两个评分系统的方式,使其对我有利。我提到第二种非常、非常糟糕的改进结果的方法,并不是因为我建议这样做,而是因为人们确实会这样做,任何阅读统计方法比较的人都应该记住这一点!

考虑到这三个不确定性,我制定了图 6.4 中出现的计划。有两个检查点——数据是否符合分布以及预测的质量——如果我没有遇到问题就到达了检查点,我针对每个检查点有一到两个可能的解决方案。在分布检查点,如果看起来有问题,我会找到一个更好的分布,而在预测质量检查点,我可以选择获取更多数据或改进模型,这取决于当时我认为哪个更合理。如果我想写一个更详尽的计划,我可以包括一些具体的标准,这些标准将使我决定选择数据路线而不是模型质量路线,或者反之亦然。

图 6.4. 展示我的项目基本计划的流程图,该项目涉及对田径表现的分析

图片

到目前为止,你可能已经注意到,不确定性的主要来源与数据和统计学密切相关。这通常是真的,对我而言也是如此,因为我更倾向于统计学家而非软件开发者,只要有统计模型存在,不确定性就不可避免。

安然电子邮件分析

在第五章中,我介绍了一个旨在寻找和分析公共恩隆电子邮件集中犯罪、可疑或疏忽行为的计划,并应用来自社会网络分析领域的概念来描述它们。关于这个项目的几乎所有事情都充满了不确定性。我们不仅面临我在本章开头提到的数据挑战,而且我们也不知道数据集中有任何具体的犯罪或可疑行为例子。我们大多数情况下假设它们确实存在——在恩隆发生了很多坏事——但是面对超过 10 万封电子邮件,找到它们将非常困难。

由于我们有很多关于恩隆丑闻的新闻报道和研究,并且我们有几位在商业欺诈领域的专家,我们认为开发一些粗略的统计分析来过滤数据可能是一个合理的想法,之后我们可以阅读得分最高的 100 封左右的电子邮件,寻找可疑行为的良好例子。如果我们这样做几次,可能比试图阅读所有电子邮件直到找到可疑的邮件更有效率。

另一个建议是构建一个完整的社会网络行为统计模型,并尝试用它来寻找可疑行为。对我来说,这似乎是本末倒置。或者至少,如果我们偶然发现了一些有趣的电子邮件,这会让我们容易受到一些严重的确认偏差的影响——方法是有效的,还是我们偶然找到了我们寻找的结果?

由于这个项目几乎没有什么具体或保证的事情,我们的计划将更多地关注时间和资源管理,而不是具体的步骤。鉴于我们有截止日期,即使它是一个模糊的截止日期,我们必须记住,我们需要在项目结束时产生一些结果。我们不能使用学术策略,即一直工作直到有好的结果然后提交,所以我们使用了一些人称之为时间盒的策略。它并不像听起来那么令人兴奋——抱歉。这是对一个开放性任务设定一个相对随意的截止时间,以提醒自己你必须继续前进。

对于 10 天的项目时间表,我们松散的计划看起来就像图 6.5 中出现的流程图。这确实是一个模糊的计划,但至少它是一个起点,并确保整个团队知道你期望时间表如何运作,即使它不会完全按照这种方式运作。即使计划本身也可能随着新信息或其他对目标、优先级或情况其他方面的变化而改变。与其他例子相比,这个例子最显著的特点是它展示了即使在几乎绝对的未知、开放的目标和截止日期的情况下,如何创建一个时间表。

图 6.5. 展示 Enron 项目基本计划的流程图

图片

6.4. 传达新的目标

计划和目标可能会在任何时候因新的信息、新的限制或其他任何原因而改变。你必须向所有参与项目的人传达重大变化,包括客户。显然,项目的客户对项目的最终产品有直接利益——否则项目就不会存在——因此客户应该了解任何目标的变化。由于大多数客户喜欢保持知情,因此通常建议你通知他们你如何实现这些目标的计划,无论是新的还是旧的。客户也可能对进度报告感兴趣,包括迄今为止的初步结果以及你是如何得到它们的,但这些是最低优先级的。

我在这里提到优先级,因为对数据科学家来说有趣或重要的事情,对非数据科学家的客户来说可能并不有趣或不重要。例如,我参加过许多生物会议上的演讲,演讲者似乎更感兴趣于讲述他们如何得到结果的故事,而不是展示结果本身及其影响。对数据科学家来说,项目的整个故事——通常包括每一个转折、曲折和障碍——代表了已完成工作的吸引力、难度和最终胜利。但非数据科学家最感兴趣的是你是否得到了好的结果,以及他们是否可以信任你的好结果不是偶然的。例如,解释数据整理花了整整一周时间,这仅仅是为了说,“我的工作有时可能很艰难”,并且可能为错过截止日期提供理由,但除此之外几乎没有其他作用。

专注于客户关心的问题:已经取得的进展,以及当前预期可实现的 X、Y 和 Z 目标。他们可能会有问题,这很好,他们可能对了解你项目的各个方面感兴趣,但根据我的经验,大多数人并不感兴趣。在这个阶段与客户会面时,我唯一必须得出的结论是,我清楚地传达了新的目标,并且他们批准了这些目标。其他一切都是可选的。

你可以考虑向客户传达你的基本计划,尤其是如果你在完成项目时使用了他们的任何资源。他们可能会有建议、建议或其他领域知识,这些你还没有经历过。如果他们的资源涉及其中,例如数据库、计算机、其他员工,那么他们当然会对了解你将如何以及将如何充分利用它们感兴趣。

最后,正如我提到的,初步结果以及你是如何得到它们的故事是最低优先级的。根据我的经验,分享这些信息可能会有所帮助,但只有出于以下原因:

  • 为了增强客户对有希望初步结果的信心

  • 通过展示你的方法是可靠的来赢得客户的信任

  • 在一个好的故事中,让客户感觉像团队中的数据科学家,这个故事客户能够理解

所有这些在某种情况下可能是可取的,但在与客户的沟通中,最好确保你不失去你项目角色和他们的主要交叉点:目标。

练习

继续使用在第二章中首次描述的 Filthy Money Forecasting 个人理财应用场景,并关联到前几章的练习,尝试以下内容:

1.

假设你从上一章的初步分析和描述性统计中得出结论,认为你可能为拥有大量交易的财务账户的活跃用户生成一些可靠的预测,但你认为对于交易相对较少的用户和账户则不能做到同样的事情。将这一发现转化为“可能做什么?什么是宝贵的?什么是有效的?”的调整目标框架。

2.

根据你对上一个问题的回答,描述一个在应用内生成预测的一般计划。

摘要

  • 如我建议的明确和正式的评估阶段可以帮助你组织你的进度、目标、计划和项目知识。

  • 在继续之前,问自己,“我学到了什么?”

  • 根据初步发现调整期望和目标。

  • 根据任何新的信息和新的目标制定计划,同时考虑到不可避免的不确定性。

  • 向客户传达新的目标、计划和进度。

第七章. 统计与建模:概念与基础

本章涵盖

  • 统计建模作为数据科学的核心概念

  • 数学作为统计的基础

  • 其他有用的统计方法,如聚类和机器学习

图 7.1 展示了我们在数据科学过程中的位置:数据统计分析。统计方法通常被认为几乎占了一半,或者至少占三分之一的数据科学所需技能和知识。另一大部分是软件开发和/或应用,剩下的小部分是主题或领域专业知识。统计理论和方法是数据科学中极其重要的,但我在本书中至今对此说的相对较少。在本章中,我试图提供一个全面的概述。

图 7.1. 数据科学过程构建阶段的一个重要方面:统计数据分析

统计学是一个庞大的领域。我不敢奢望在一本书中涵盖所有的统计学,更不用说在一章中。关于这个主题已经写了几百本教科书,几千篇期刊文章,甚至更多的网页。所以如果你有具体问题,你会找到很多参考资料。然而,我尚未在其他书面作品中看到对统计学及其最重要的概念的概述,为那些没有正式统计学培训或教育背景的数据科学爱好者提供了一个坚实的理论基础。在本章中,我将介绍统计学领域作为一个相关工具的集合,每个工具都有其优缺点,以实现数据科学的目标。这种介绍的目的是让你开始考虑在项目中可能应用的统计方法的范围,直到你能够感到舒适地从更详细、更技术性的参考资料中寻求更具体的信息。

7.1. 我对统计学的思考

你可能已经对描述如何应用其复杂技术的统计学和技术参考感到很自在,在这种情况下,这一章可能看起来是不必要的。但除非你接受过大量的正式统计学教育,否则你很可能还有许多尚未见过的领域。或者你可能不熟悉各个统计学领域之间是如何相互关联的。我确实认为,即使是经验丰富的数据科学家,从整体上思考统计学领域,了解其组成部分之间的关系,以及统计学方法与执行它们的软件以及它们所使用的数据之间的区别,也能从中受益。我不打算提供任何这些概念的最终描述,但我确实打算开始对这些概念、它们之间的关系以及每个概念的重要性进行讨论。

在本章中,我将继续强调方法、软件和数据之间的独特性。使用机器学习库并不等同于将一种机器学习类型应用于你的数据集。一个是工具;另一个是行动。同样,数据库并不等同于其中包含的数据,无论它们可能多么交织在一起。因此,因为我想要在本章中专注于统计学方法,所以我通常会抽象地提及软件和数据,尽管在适当的时候我会引用具体的例子。

最后,在我们深入探讨之前,我想说的是,我认为并从概念上思考统计方法的世界。我设想了一些场景,其中我用手抓取数据,将其塞入某种方式能够学习这些数据的机器的管道中,我的工作是调整管道和机器的旋钮,以便从另一端输出好的、有用的信息。或者,在分类数据点的情况下,我想象自己用粉笔画一条线,最好地将红色点与蓝色点分开,然后考虑如何画另一条线来纠正一些落在蓝色一侧的红色点,反之亦然。我就是这样思考的,我在这里提醒你,以防你期待的是一本充满微分方程和相关性系数的章节。相反,这将是一个宏观的章节,充满了许多概念性和富有想象力的段落。我喜欢思考这些事情,所以我希望我能以一种对你来说也很有趣的方式呈现它们。

7.2. 统计学:与数据科学相关的领域

《牛津统计学词汇词典》(OUP,2006 年)将统计学描述为“对数据的收集、分析、解释、展示和组织的研究。”在本章的目的上,我们将跳过收集、展示和组织,而专注于分析和解释。我假设你已经根据前几章的描述收集并组织好了你的数据,我将在稍后的章节中讨论展示。

从我的角度来看,分析和解释是统计学的科学方面。它们关注从数据中提取知识,并识别是否有足够的证据来支持一个给定假设或推论。面对许多不确定性——这在数据科学中高度统计的项目中总是如此——良好的分析和解释非常重要,因此我愿意在本章的大部分内容中讨论一些统计学帮助实现这些目标的方法。

7.2.1. 统计学是什么

统计学位于数学理论领域和可观察数据现实之间。出人意料的是,对大多数人来说,数学与数据几乎没有关系。尽管如此,它与数据科学有着密切的联系。数据科学家需要数学来进行有意义的统计分析,因此,如果我不开始讨论数据科学中的统计学,我将感到疏忽。在下一节中,我将撰写关于数学的内容,它所依赖的主要概念,以及它在现实世界中的应用中的有用性。

统计学的一边是数学,另一边是数据。数学——尤其是应用数学——为统计学提供了一套工具,这些工具使得分析和解释成为本章的主要焦点。除了数学之外,统计学还拥有自己的一套以数据为中心的技术。

描述性统计,如第五章第五章中所述,是一种通常直观或简单的统计方法,可以在不过度复杂或难以理解的情况下提供数据的良好概述。在某种意义上,描述性统计通常紧贴数据。

推断统计本质上与数据有一或多个步骤的距离。推断是根据可测量的、相关的数量估计未知数量的过程。通常,推断统计涉及一个统计模型,该模型定义了数量,包括可测量和不可测量的,以及它们之间的关系。推断统计的方法可以从非常简单到极其复杂,其精确度、抽象性和可解释性也各不相同。

统计建模是使用统计结构描述系统的一般实践,然后使用该模型来帮助分析和解释与系统相关的数据。描述性和推断性统计都依赖于统计模型,但在某些情况下,对模型本身的明确构建和解释起着次要作用。在统计建模中,主要关注的是理解模型及其所描述的底层系统。数学建模是一个相关的概念,它更强调模型构建和解释,而不是它与数据的关系。统计建模侧重于模型与数据的关系。

最远离原始数据的是一套通常被称为(无论好坏)黑盒方法的统计技术。黑盒这一术语指的是某些统计方法具有许多相互之间关系复杂的移动部件,以至于在特定数据、特定背景下应用该方法时,几乎不可能剖析该方法本身。许多来自机器学习和人工智能的方法都符合这一描述。如果你试图将数据集中出现的个体分类到几个类别之一,并且应用了随机森林或神经网络等机器学习技术,那么在事后往往很难解释为什么某个个体被以某种方式分类。数据进入黑盒,输出一个分类,而你通常不确定中间到底发生了什么。我将在本章后面更详细地讨论这一概念。

在接下来的几节中,我将更详细地介绍统计学中的各种概念。我通常会倾向于使用高级描述而不是具体应用,以便在许多情况下具有广泛的适用性,但当我认为有帮助时,我会使用说明性示例。有许多优秀的资源可以提供关于每个特定主题的更多细节,我会尽量提供足够的细节,包括关键词和常见方法名称,以便您能够快速找到互联网或其他地方的相关资源。

7.2.2. 统计学不是什么

关于我作为数据科学家所做的工作最常见的误解通常出现在我与其他公司或机构的招聘人员或招聘代理交谈时。有时,这种误解在我已经接受工作并正在进行项目中途出现。这种误解是,作为数据科学家,我可以以各种方式为大量人群设置、加载和管理多个数据存储。我已多次向许多人解释,数据科学不是数据管理,也绝对不是数据库管理。这两个角色没有任何问题——事实上,当我有机会与一个高度胜任的数据库管理员(DBA)合作时,我永远都会感到感激——但这些角色与科学正好相反。

科学是探索未知的事业。移动数据并提高可靠性和查询速度是一项极其重要的工作,但这与发现未知无关。我不确定为什么,确切地说,有些人会混淆这两个以数据为导向的工作,但这已经发生在我身上不止一次了。当有人要求我为一个大组织设置数据库时,这尤其让我觉得好笑,因为,在所有最常见的数据科学任务中,数据库管理可能是我最不擅长的一项。我可以设置一个对我很有用的数据库,但我肯定不会依赖自己为一个大组织构建数据管理解决方案。

也许是因为我是一个数学家,但我认为数据管理是我有用的技能之一,但它是次要任务。我想进行数据分析。任何能够促进良好数据分析的东西无疑是好的,但在我感到需要为了最佳性能而控制它及其所有管理难题之前,我会忍受可接受的数据库性能很长时间。然而,我专注于统计学,无论它们需要多长时间。

数据管理对于统计学,就像食品供应商对于厨师一样:统计学是一门深深依赖可靠数据管理的艺术,正如以熏肉包裹的鲑鱼闻名的餐厅,严重依赖于来自当地猪场和鲑鱼养殖场的及时、高质量的原料。(向我的素食读者和野生鲑鱼爱好者致歉。)对我来说,统计学就是我的工作;其他的一切只是辅助。餐厅顾客首先想要的是用优质原料做的美食;其次,他们可能还想知道他们食物的来源是可靠且快速的。统计分析的消费者——数据科学项目的客户——希望知道他们以某种方式获得了可靠的信息。然后,只有那时,他们才会关心使用这些数据的存储、软件和工作流程是否可靠且快速。统计分析是产品,数据管理是这个过程的一个必要部分。

统计学在数据科学中的作用不是处理数据的次要、外围功能。统计学是提供洞察力的数据科学的一部分。数据科学家所做的所有软件开发和数据库管理都促进了他们进行统计分析的能力。网站开发和用户界面设计——这两项可能要求数据科学家完成的任务——有助于将统计分析交付给客户。作为一个数学家和统计学家,我可能有所偏见,但我认为统计学是数据科学家工作中最具智力挑战性的部分。

另一方面,我在数据科学中处理的一些最大挑战涉及让各种软件组件相互友好地协作,所以我可能低估了软件工程。这都取决于你站在哪里,我想。下一章将涵盖软件的基础知识,所以我将推迟对它的进一步讨论,直到那时。

7.3. 数学

尽管数学的确切边界存在争议,但这个领域完全基于逻辑。具体来说,每个数学概念都可以分解成一系列的“如果-那么”陈述加上一组假设。是的,即使是长除法和求圆周长也可以简化为纯粹逻辑步骤,这些步骤遵循假设。碰巧的是,人们已经做了这么长时间的数学,有无数逻辑步骤和假设已经长期普遍使用,以至于我们常常认为其中一些是理所当然的。

7.3.1. 示例:长除法

长除法——或者说是你小学时学到的普通除法——是一种两个数之间的运算,它伴随着许多假设。对于阅读这本书的每个人来说,很可能都是以一系列步骤学习如何进行长除法,这是一种算法,它接受两个数作为输入,被除数和除数,并给出一个称为商的结果。长除法在日常生活中的确很有用(在没有计算器或电脑的情况下更有用),例如,当你想要将餐厅账单平均分给几个人或者与朋友们分享几十个纸杯蛋糕时。

许多人认为数学领域由许多这样的适度有用的计算算法组成,这些人并不完全错误。但比数学算法更重要的是那些可以组装成证明某事物为真或为假的假设和逻辑步骤。事实上,每个数学算法都是由一系列逻辑陈述构成的,这些陈述最终可以证明算法在给定必要的假设下确实做了它应该做的事情。

以三个逻辑陈述 X、Y 和 Z 为例,每个陈述在各种情况下都可能为真或为假,以及以下陈述:

  • 如果 X 是真的,那么 Y 必须是假的。

  • 如果 Y 是假的,那么 Z 就是真的。

这显然是一组任意的陈述,可能直接来自逻辑学教材,但这样的陈述是数学的核心。

基于这些陈述,假设你发现 X 是真的。那么 Y 就是假的,同时 Z 也是真的。这就是逻辑,看起来并不激动人心。但如果我们给 X、Y 和 Z 赋予现实生活中的意义,在一个包括零售网站访客或潜在顾客的例子中:

  • 陈述 X— 潜在的顾客将超过两个商品放入他们的在线购物车中。

  • 陈述 Y— 顾客只是在浏览。

  • 陈述 Z— 潜在的顾客将会购买某物。

这些陈述对在线零售商来说都是有意义的。你知道,对于任何试图赚钱的零售商来说,“Z 是真的”这个陈述都是令人兴奋的,所以前面展示的逻辑陈述意味着“X 是真的”这个陈述也应该让零售商感到兴奋。更实际地说,这可能意味着如果零售商能够让潜在顾客将超过两个商品放入购物车,那么他们就会做成一笔交易。这可能是在网站上实施的一种可行的营销策略,如果其他做成交易的方法更困难的话。显然,现实生活中很少如此纯粹地逻辑化,但如果你将所有陈述都变得模糊一些,使得“是真的”变成“可能是真的”,同样地,“是假的”也是如此,那么这种场景确实可能是一个数据科学家可以帮助零售商增加销售额的现实场景。这种模糊的陈述通常最好使用概率陈述来处理,我将在本章后面介绍。

回到长除法的例子:即使是基本算术的算法(你可能在学校学过)也是基于假设和逻辑陈述的。在深入探讨这些之前,为了避免用我如何进行长除法的描述让你感到无聊,让我们假设你有一种你喜欢的正确使用铅笔和纸进行长除法的方式——我们将这种方式称为“算法”在此例中。算法必须是给出小数结果的那种,而不是给出一个几乎正确答案加上余数的长除法。(你将在几分钟内理解原因。)此外,让我们假设这个算法最初是由一位数学家开发的,并且这位数学家已经证明,在适当的条件下,算法会得到正确答案。现在让我们探讨一下数学家要求你正确使用算法的一些条件。

首先,你必须假设被除数、除数和商是称为实数集的集合的元素。实数集包括你习惯的所有小数和整数,但不包括其他数字,例如,如果你尝试对负数开平方可能会得到的虚数。还有各种其他非实数集以及不包含数字的集合,但我将把那留给数学教科书去讨论。

除了假设你处理的是实数集之外,你还假设这个特定的实数集也是一个称为域的特殊类型的集合。域是抽象代数中的一个中心概念,它是由一系列性质定义的集合,其中包含两种常见的运算,即加法和乘法,在抽象意义上并不保证以它们在普通算术中工作的方式工作。你知道,在域中,这两种运算总是以某种特定方式作用于域的两个元素,但加法和乘法以这种方式工作的事实,是你进行长除法时必须做出的另一个假设。有关域的更多信息,请参考抽象代数的参考资料。

你假设你有一个由实数组成的域,并且你拥有加法和乘法这两种运算,它们以你在学校学到的方式工作。作为一个域的定义的一部分,这两种运算都必须有逆运算。你可能已经猜到了,你通常把加法的逆运算称为减法,乘法的逆运算称为除法。任何运算的逆运算都必须撤销该运算所做的操作。一个数 A 乘以 B 得到一个结果 C,使得 C 除以 B 再次得到数 A。除法定义为已知乘法的逆运算。这不仅仅是一个假设;它是由其他假设和域的定义推导出来的。

总结一下,以下是关于长除法的假设:

  • 假设:

    1. 你拥有实数集。

    2. 你有一个在实数集上的域。

    3. 场运算加法和乘法与算术中的运算方式相同。

  • 陈述:

    1. 如果你有一个域,那么加法和乘法有逆元:分别是减法和除法。

    2. 如果域运算加法和乘法与算术中的运算方式相同,那么减法和除法也以算术中的方式工作。

    3. 如果除法与算术中的运算方式相同,那么该算法将给出正确答案。

将这些假设与这些陈述结合起来,得到以下结论:

  • 假设 1 和 2 与陈述 1 一起意味着减法和除法运算存在。

  • 假设 3 和陈述 2 意味着减法和除法与算术中的运算方式相同。

  • 前两个陈述与陈述 3 一起意味着该算法将给出正确答案。

那个例子可能看起来很 trivial,从某些方面来看确实如此,但我认为它说明了我们对世界的认识,特别是在定量问题上,是基于数学结构的特定实例构建的。如果由于某种原因实数系统不适用,那么带有小数结果的长期除法将无法工作。如果你使用的是整数集或整数,那么可能需要不同的长除法算法,可能是一个结果为某种商加上余数的算法。长除法不能在整数或整数上以与实数相同的方式工作,原因在于这两个集合——整数或整数——都不构成域。对基础数学的了解,例如当你有一个域或没有域时,是确定算法何时适用以及何时不适用的唯一确定方法。扩展这个想法,数学知识在为数据科学项目选择分析方法以及在诊断这些方法及其结果可能存在的问题时非常有用。

7.3.2. 数学模型

一个 模型 是对系统及其工作方式的描述。一个数学模型使用方程、变量、常数以及来自数学的其他概念来描述系统。如果你试图描述存在于现实世界中的系统,那么你就是在进入 应用数学 的领域,这个短语通常意味着所做的工作可以应用于数学之外的事物,例如物理学、语言学或数据科学。应用数学当然通常与统计学紧密相关,我不会尝试在这两者之间做出明确的区分。但是,一般来说,应用数学侧重于改进模型和技术,可能根本不需要任何数据,而统计学则侧重于使用数学模型和技术从数据中学习。数学建模和应用数学这两个领域同样没有明确的界限;前者侧重于模型,而后者侧重于某种现实世界的应用,但两者都不是专门如此。数学模型的概念和使用并不对每个人都直观,所以我会在这里简要地讨论它们。

最简单且最常用的数学模型之一是线性模型。一个 线性模型 只是一个直线,由线性方程描述,旨在表示两个或更多变量之间的关系。当这种关系是线性的,它等同于说变量是直接成比例的,这种术语在某些领域中更为常用。描述二维(两个变量)线性模型的线性方程可以写成斜截式(记得从学校学过的!):

y = Mx + B

其中 M 是斜率,B 是 y 轴截距。

线性模型在许多应用中被使用,因为它们易于处理,也因为许多自然量可以合理地预期彼此之间大约呈线性关系。汽车行驶的距离与使用的汽油量之间的关系是一个很好的例子。你行驶得越远,消耗的汽油就越多。使用的确切汽油量还取决于其他因素,例如汽车类型、你的驾驶速度以及交通和天气条件。因此,尽管你可以合理地假设距离和汽油消耗量大约呈线性关系,但其他一些随机变量会导致每次旅行中汽油消耗量的变化。

模型通常用于做出预测。如果你有一个基于行驶距离的汽油消耗的线性模型,你可以通过将旅行的距离放入描述你模型的线性方程中来预测你下一次旅行将使用的汽油量。如果你使用了线性模型

y = 0.032x + 0.0027

其中 y 是需要汽油的量(以升为单位),x 是行驶的距离(以公里为单位),那么线的斜率 0.032 意味着你的数据集中的行程平均每公里需要 0.032 升汽油。除此之外,似乎每次行程都会额外使用 0.0027 升汽油,无论行驶的距离如何。这可能会解释启动汽车和开始行程前几分钟怠速所需的能量。无论如何,使用这个模型,你可以通过设置 x = 100 并计算 y 来预测即将到来的 100 公里行程的汽油使用量。根据模型的预测,y 将是 3.2027 升。这是一个线性模型如何用于预测的基本例子。

图 7.2 展示了一个没有轴标签或其他上下文的线性模型的图形表示。我包含了这个图表而没有上下文,因为我希望专注于模型的纯粹概念方面和数学,而上下文有时可能会分散对这些方面的注意力。在图中,线代表模型,点代表模型试图模拟的数据。y 轴截距似乎约为 5.0,斜率约为 0.25,线似乎合理地遵循数据。但请注意数据点围绕线的分散情况。例如,如果你想要从一个给定的 x 值预测一个 y 值,模型可能不会给出完美的预测,并且会有一些误差。根据点,y 值的预测似乎在线性模型的三到四个单位之内,这可能是好是坏,取决于项目的目标。我将在统计建模部分讨论如何将模型拟合到数据。这里的主要思想是模型与数据之间的概念关系。在建模数据时,在脑海中保留这样的图像——以及它带来的概念理解——可以增加意识并提高分析过程中的决策能力。

图 7.2。一个线性模型(线)和模型试图描述的一些数据(点)的表示。线是一个数学模型,其最佳参数——斜率和截距——可以使用统计建模技术找到。^([1)]

¹

来自 en.wikipedia.org/wiki/Linear_regression,公有领域。

强调数学模型是人为构建的事物,它们与现实生活中的系统没有固有的联系是很重要的。模型是对你认为那些系统中发生的事情的描述,并且不能保证它们有效。找到适合项目目的并且正确应用的模型是数据科学家(或数学家或统计学家)的责任。

数学模型的例子

爱因斯坦的引力模型,正如他在广义相对论中所描述的那样,著名地取代了牛顿的引力模型。牛顿的模型是一个简单的方程,它能够相当准确地描述在正常质量和距离下的引力作用力,但爱因斯坦的模型,它使用基于度规张量(一种描述线性关系的更高阶对象)的数学模型,在极端尺度上要准确得多。

现今的粒子物理标准模型,在 20 世纪 70 年代最终确定,是一个基于量子场论的数学模型,该模型理论化了物理力和亚原子粒子的行为。它已经经受了几次对其适用性作为模型的实验测试,最近是在确认希格斯玻色子存在的过程中。希格斯玻色子是标准模型预测的一种基本粒子,在 2012 年之前,关于其存在几乎没有实验证据。从那时起,在 CERN 的大型强子对撞机上的实验已经确认了一个与希格斯玻色子一致的粒子的存在。像任何优秀的科学家一样,CERN 的研究人员不会肯定地说他们只发现了希格斯玻色子,因为该粒子的某些性质仍然未知,但他们确实肯定地说,标准模型中的任何内容至今尚未被实验证据所反驳。

从我自己的经验来看,数学模型的一些更有趣的应用发生在社交网络分析中。社交网络由个体及其之间的连接组成,这使得图论成为寻找适用模型的一个极好的数学领域。连通性、中心性和介于两者之间的理论,以及其他理论,可以几乎直接应用于现实生活中人们以各种方式互动的实际情况。图论还有许多其他应用,而且坦白地说,它还有一些相当有趣的纯粹数学(非应用)问题,但互联网上社交网络的最近出现为建模提供了丰富的新的现象以及支持它的数据。

线性模型的几何等价物是欧几里得几何的概念,这是三维空间正常运作的概念——长度、高度、宽度,所有线条都可以无限延伸而不相交——扩展到任意多个维度。但存在其他几何,这些几何在建模某些系统时可能是有用的。球面几何是存在于球面上的几何。如果你站在地球(一个球体,大约)上,沿着直线行走,忽略水体,你会在一段时间后回到起点。在欧几里得几何中不会发生这种情况——你会走进无限——这是一个在建模某些过程时可能很有用的属性。当然,任何飞机交通模型都可以从球面几何中受益,我相信还有更多用途,例如在制造工程中,精确铣削球节或其他曲面可能需要一个球面几何模型来得到完全正确的形状。

数学模型在每一个定量领域都被使用,无论是明确还是隐含地。就像我们在日常算术中必须做出的某些逻辑陈述和假设一样,一些数学模型在特定目的上被频繁使用,以至于它们被视为理所当然。例如,调查、民主选举民意调查和医学测试都利用变量之间的相关性来得出有信息量的结论。常见的相关性概念——特别是皮尔逊相关系数——假设线性模型,但这一事实往往被视为理所当然,或者至少不是众所周知。下次你阅读即将到来的选举的预测时,要知道预测以及误差范围都是基于线性模型的。

7.3.3. 数学与统计学

真正的数学由假设和逻辑陈述组成,并且仅在特定情况下才涉及数值量。因此,在美国高中数学课程中教授的所有主题中,几何——包括三角形全等和直线平行的证明——最接近数学的核心。但日常生活显然经常涉及数值量,所以我们往往关注处理数量的数学分支。数据科学经常这样做,但它也已被知会渗透到不那么数量化的,或纯,的数学分支,如群论、非欧几里得几何和拓扑学,如果它们看起来有用的话。因此,对某些纯数学主题的了解可能对数据科学家是有用的。

无论如何,数学通常并不涉及现实世界。完全基于逻辑,并且始终——始终——从一个假设集合开始,数学必须首先假设一个可以描述的世界,然后才开始描述它。每个数学陈述都可以用“如果”(如果假设是真实的)来表述,而这个“如果”将陈述及其结论提升到抽象层面。这并不是说数学在现实世界中没有用处;恰恰相反。数学与其说是一门科学,不如说是一种我们可以用其描述事物的词汇。其中一些事物可能存在于现实世界中。就像词汇和它们包含的单词一样,很少有一个描述是完全正确的。目标是尽可能地接近正确。数学家和统计学家乔治·博克斯(George Box)曾著名地写道:“本质上,所有模型都是错误的,但有些是有用的。”确实,如果一个模型与正确性相当接近,那么它是有用的。

统计学领域对模型的正确性也有这些担忧,但统计学并非是一种词汇和逻辑体系,而是一扇观察世界的窗口。统计学从数据开始,尽管统计模型在大多数情况下与数学模型难以区分,但其意图却大相径庭。统计模型不是从内部描述系统,而是通过汇总和操作相关可观察数据,从外部观察系统。

然而,数学确实为统计学提供了大量的重型机械。统计分布通常由具有实际、科学意义的根的复杂方程描述。拟合统计模型通常使用数学优化技术。即使是一个项目数据假设所在的空间也必须用数学来描述,即使这个描述仅仅是“N 维欧几里得空间”。尽管边界有些模糊,但我喜欢说数学结束和统计学开始的地方是真实数据进入方程的地方。

7.4. 统计建模与推断

在第五章中,我提到了在数据评估建议的粗略统计分析背景下统计推断。推断是估计无法直接测量的量值的任务。因为你没有直接测量这个量值,所以有必要构建一个模型,至少描述你想要的量和已有的测量之间的关系。由于推断中存在模型,我在本节中将统计建模和推断合并在一起。

统计模型与数学模型并没有太大的不同,我在本章中已经介绍过。正如我写的,区别主要在于焦点:数学建模侧重于模型及其固有属性,而统计建模侧重于模型与数据的关系。在两种情况下,模型都是一组变量,其关系由方程和其他数学关系描述。我已经介绍过的 x 和 y 之间的线性模型可能看起来像

y = Mx + B

而指数模型可能看起来像

y = Ae^x

其中 e 是指数常数,也称为欧拉数。模型参数值 M、B 和 A 可能直到通过某种统计推断技术估计之前都是未知的。

这两个模型中的每一个都是描述 x 和 y 之间可能存在的关系。在第一种情况下,线性模型,假设当 x 增加一定量时,y 也会增加(或减少,取决于 M 的值)相同的量,无论 x 有多大。在第二种情况下,指数模型,如果 x 增加一定量,那么 y 的增加量将取决于 x 的大小;如果 x 更大,那么 x 的增加将导致 y 增加的量比 x 更小时要大。简而言之,如果 x 变大,那么第二次移动将导致 y 比第一次移动增加得更多。

指数增长的常见例子是不受限制的人口增长。如果资源不稀缺,那么人口——细菌、植物、动物,甚至人类——有时会呈指数增长。增长率可能是 5%,20%或更大,但“指数”这个词意味着百分比(或比例),而不是标量数字,描述了增长。例如,如果一个人口有 100 个个体,并且每年以 20%的速度增长,那么一年后人口将包含 120 个个体。两年后,你预计比 120 多 20%,这将增加 24 个个体,总数达到 144。正如你所看到的,增长速度随着人口的增加而增长。这是指数增长模型的一个特征。

这两个模型,线性模型和指数模型,都可以用一个单一方程来描述。如果你在一个项目中使用这些模型,挑战就是找到代表数据的参数 M、B 和/或 A 的估计值,这些估计值可以提供对所建模系统的洞察。但模型可以远远超出一个单一方程。

现在你已经看到了几个简单的例子,让我们来看看统计模型在一般情况下是什么。

7.4.1. 定义统计模型

一个 统计模型 是对系统中涉及的量或变量的描述,也是对这些量之间的数学关系的描述。到目前为止,你已经看到了一个线性模型以及一个指数模型,这两个模型都只涉及两个变量 x 和 y,无论这些量是什么。模型可以更加复杂,包括许多维度的变量以及需要许多不同类型的方程。

除了线性方程和指数方程之外,在统计建模中使用了各种函数类型:多项式、分段多项式(样条)、微分方程、各种类型的非线性方程等等。某些方程或函数类型比其他类型有更多的变量(运动部分),这影响了模型描述的复杂性和估计所有模型参数的难度。

除了这些模型的数学描述之外,统计模型应该与被建模系统的相关数据有某种明确的关系。通常这意味着,存在数据的值被明确地包括在模型中的变量。例如,如果你考虑上一节中的人口增长示例,并且你的数据集包括随时间变化的人口规模的几个测量值,那么你将希望在你的模型中包括人口规模以及一个时间变量。在这种情况下,这可以很简单,例如使用模型方程

P = P[0]e^(rt)

其中 P 表示时间 t 的人口数量,P[0] 表示初始时刻的人口数量——你可以选择时间零的具体时刻,然后所有其他时间都是相对于这个 t = 0 点的——r 是增长率参数(e 仍然是指数常数)。

假设,建模这些数据的一个目标是你希望能够预测未来某个时间点的人口数量。你所拥有的数据集,即随时间变化的人口规模集合,是 P 和 t 的值对的集合。那么任务就是使用这些过去的值来找到好的模型参数,帮助你对未来的人口做出良好的预测。在这个模型中,P[0] 被定义为 t = 0 时刻的人口数量,因此唯一剩下的未知模型参数是 r。在这个假设项目中,估计 r 的一个良好值是统计建模的主要任务之一。

一旦你对模型参数 r 有了一个良好的估计,并且由于 P[0] 的值是由数据以及选择的时间变量 t 的定义所确定的,那么你将拥有一个可用的模型来描述你所研究的人口增长。然后你可以用它来对过去、现在和未来的人口状态做出结论和预测。这就是统计模型的目的:基于该系统的模型和某些数据,对该系统做出有意义的结论。

为了通过统计模型对系统得出有意义的结论,模型必须良好,数据必须良好,它们之间的关系也必须良好。这说起来容易做起来难。复杂系统——大多数现实生活中的系统都非常复杂——需要特别注意,以确保模型及其与数据的关系足够好,以便得出你所寻求的有意义的结论。你通常必须考虑系统中的许多未知因素和变动部分。一些未知因素可以明确地包含在模型中,例如指数人口模型中的增长率。这些被称为潜在变量,将在下一节中描述。

7.4.2. 潜在变量

当你创建一个系统的模型时,有些量是可以测量的,有些则不能。即使在可测量的量中,也有一些你在数据中已经有了测量值,而另一些则没有。在指数增长模型中,很明显,增长率是一个存在的量,无论你是否能测量它。即使你想使用不同的模型,比如线性模型,可能仍然至少有一个变量或参数代表人口的增长率。在任何情况下,这个增长参数可能不是可测量的。可能会有一些罕见的情况,你可以追踪人口的新成员,在这种情况下,你可能能够直接测量人口增长率,但这似乎不太可能。让我们假设增长率不是直接可测量的,或者至少你在数据中没有直接的测量值。每次当你知道一个变量或参数存在但你没有测量值时,你就可以称之为潜在变量。

潜在变量,例如指数人口增长模型中的增长率参数,通常基于对系统如何工作的直观概念。如果你知道人口在增长,你就知道必须有一个增长率,因此为那个增长率创建一个变量是一种直观的做法,这有助于解释你的系统以及其他变量之间的关系。此外,如果你可以对该增长率得出结论,那么这可能对你的项目目标有所帮助。这是在统计模型中包含潜在变量的两个最常见原因:

  • 该变量在系统如何工作方面起着直观的作用。

  • 你希望对这个特定变量得出统计结论。

在任何情况下,潜在变量代表的是你希望知道其值但无法测量或由于某种原因尚未测量的变量或数量。为了使用它们,你必须从你对其他相关变量的了解中推断它们。

在指数人口增长模型中,如果你知道在多个时间点的种群大小 P,那么了解该种群的变化率相对容易。一种方法就是计算连续时间点之间种群的变化/差异,这几乎等同于直接测量,但并不完全准确。那么问题是,这些绝对差异是否随时间保持恒定,这意味着线性增长,或者它们随着种群的增长而增长,这意味着指数增长或类似情况。如果种群似乎在每个时间段内以恒定的数量增长(例如,每年、每月、每天),那么线性模型似乎更合适,但如果种群似乎在每个时间段内以一定的百分比增长,那么指数模型可能更适合该系统。

我将在本章后面讨论模型比较——找到一个好的统计模型很重要——但现在我将专注于这样一个事实:一个看似直观的数量的性质——例如,我们例子中的种群增长率——在很大程度上取决于模型的选择。人们可能会认为种群增长率是可以直接测量的,但事实上,即使可以测量,你仍然至少需要决定种群是每次以绝对数量增长还是以百分比增长。此外,还有许多其他模型也是可能的;线性(绝对)和指数(百分比)只是用于人口增长的两种最常用的模型。模型的选择及其潜在变量的性质密切相关。两者都高度受到系统如何工作的直观影响,以及系统与数据的关系。

7.4.3. 量化不确定性:随机性、方差和误差项

如果无法直接测量潜在变量,那么对潜在变量的估计值总是存在不确定性,即使可以测量也是如此。获取潜在变量和模型参数的近似精确值是困难的,因此通常在模型中明确包含一些方差和误差项是有用的,这些项通常由值的概率分布的概念来表示。

在统计模型中使用概率分布

如果你模型描述的量有一个期望值——通过某种统计方法估计——那么该量的方差描述了该量的单个实例可能偏离期望值的程度。例如,如果你正在建模人类的身高,你可能会发现男性的平均身高为 179.0 厘米。但每个个体都会以某种程度偏离这个期望值;每个男人可能比这个高度高几厘米或矮几厘米,有些男人几乎正好是 179.0 厘米高,然后是那些极高和极矮的人,他们可能比这个高度高 20 或 30 厘米,或者矮 20 或 30 厘米。围绕期望值的价值分散的概念自然地唤起了人们熟悉的钟形曲线或正态分布的想法。

概率分布通常精确地描述了从随机过程中获取的随机值的分散情况,这些随机值跨越了一组可能值。如果你观察到了由随机过程生成的值,该过程的概率分布会告诉你期望看到某些值的频率。大多数人知道正态分布的形状,他们可能能够说出由正态分布的随机过程生成的值中有多少百分比会高于或低于某些标记。尽管正态分布是最受欢迎的概率分布,但存在各种形状的分布,包括连续和离散的分布,每个分布都附带一组假设。特别是,正态分布处理异常值的能力不强,因此在存在异常值的情况下,更稳健的分布或方法可能更好。每个特定的分布都有其自身的优点和注意事项,选择一个合适的分布对你的结果可能具有重大影响。所有随机性并不相等,因此在确定特定的分布之前进行一些调查是最好的。为此目的,存在大量的统计学文献。

正态分布是一种具有两个参数的概率分布:均值和方差。在建模单个量,例如人类身高的情况下,正态分布可以描述整个模型。在这种情况下,你正在建模的系统在某种意义上是一个产生各种身高的人的系统。均值参数代表系统在制造每个人类时追求的身高,而方差参数代表系统通常偏离该平均身高的程度。

试图达到一个值,但以某种数量错过它,是统计模型中误差项的核心思想。在这个意义上,“误差”是指数量(或测量)的实例(或测量)偏离目标有多远。从概念上讲,这意味着每个男人都应该身高 179.0 厘米,但由于系统中的一些误差,一些男人更高,一些更矮。在统计模型中,这些误差通常被视为不可解释的噪声,主要关注的是确保所有测量中的误差都是正态分布的。

建立具有不确定性的统计模型

涉及误差项的人类身高模型的一个可能版本是以下方程

h[i] = h[p] + ε[i]

其中 h[p] 是人类生产系统的期望身高,h[i] 是标签为 i 的个体的身高。误差项由变量 ε[i] 表示,这些变量被假定为具有均值为零且相互独立的高斯分布。希腊字母 epsilon 是表示误差和其他任意小量的首选符号。请注意,下标 i 表示每个个体都有一个不同的误差。如果你对 h[p] 有一个好的估计,并且对 ε[i] 的方差有了解,那么你就有了男性人类身高的可靠模型。这并不是建模男性人类身高的唯一方法。

因为根据经验,你了解到人类男性的身高在个体之间存在差异,你可能会考虑人类群体中期望身高与个体身高之间的概念差异。个体可以被视为相同数量或数量集的不同实例。存在两个概念上的原因,使得相同数量的不同实例可能会发生变化:

  • 系统或测量过程是嘈杂的。

  • 数量本身可能从一个实例变化到另一个实例。

注意这两个原因之间的微妙差异。第一个原因体现在误差项的概念中。第二个对应于随机变量的概念。

随机变量具有它们自己的固有随机性,这通常不会被考虑为噪声。在人类身高示例中,你与其称系统是嘈杂的,不如假设系统本身随机选择一个身高,然后生产一个几乎正好是这个身高的个体。这种概念上的区分确实有好处,尤其是在更复杂的模型中。这个模型版本可能被描述为

h[i] ~ N( h[p] , σ² )

m[i] = h[i] + ε[i]

其中第一个陈述表明,个体 i 的身高 h[i] 是通过使用均值为 h[p] 和方差 σ² 的正态分布的随机过程生成的。第二个陈述表明,h[i] 的测量是一个嘈杂的过程,导致测量值为 m[i]。在这种情况下,误差项 ε[i] 仅对应于现实生活中的测量过程,因此可能只有几厘米。

在同一模型描述中将概率分布符号与误差项符号混合可能是不恰当的,因为它们实际上描述的是同一个随机过程,但我认为这可以说明模型中内在随机变量(对模型很重要)和假设未解释的误差项之间的概念差异。

如果你要将你的关于男性人类身高的模型推广到也包括女性,那么一个内在随机变量不是误差项的好例子就会出现。全世界男性普遍比当地女性高,所以在人类身高的模型中将男性和女性合并可能是错误的。假设建模任务的一个目标是有能力生成关于随机选择的人类身高的预测,无论他们是男性、女性还是其他性别。你可以构建一个与之前相同的男性模型,以及相应的女性模型。但如果你要生成一个关于随机选择的人类身高的预测,你将无法确定哪个模型更适用,因此你应该包括一个表示个体性别的随机变量。这可能是一个二进制变量,你假设在从适合该性别的正态分布身高模型中进行预测之前首先选择。这个模型可能可以用以下方程描述

s[i] ~ B( 1, p )

h[i] ~ N( hp, σ²(s[i]) )

其中 s[i] 表示个体的性别,根据第一条陈述,性别是从伯努利分布(一个具有两种可能结果的常见分布)中随机选择的,假设选择女性的概率为 p(对于两种性别,男性的概率假设为 1–p)。给定个体的性别 s[i],术语 hp 的目的是表示与性别 s[i] 匹配的人群的平均身高,σ²(s[i]) 是与性别 s[i] 匹配的人的方差。第二条陈述概括地描述了如何从由个体的性别(该性别是从伯努利分布中随机选择的)确定的参数的正态分布中生成预测身高。

从涉及不确定性的模型中得出结论

假设你已经找到了这个模型中参数的一些良好估计,你可以预测随机选择个体的身高。这种预测在分析小样本量时很有用;例如,如果你随机选择了一组 10 个人,并且他们平均身高为 161 厘米,你可能想知道你是否没有获得一个代表整个群体的样本。通过从你的模型中生成身高预测,以 10 人为一组,你可以看到你得到如此小的平均身高的频率。如果这种情况很少发生,那么这将是你的样本不代表整个群体的证据,你可能需要采取某种措施来改善你的样本。

随机变量在统计建模中有许多用途,其中最不重要的是许多现实生活中的系统包含随机性。在用模型描述这样一个系统时,重要的是永远不要混淆模型的期望与模型所依赖的分布。例如,尽管人类男性身高的模型期望个体身高为 179.0 厘米,但这并不意味着每个男性都是 179.0 厘米。这看起来可能很明显,但我见过许多学术论文混淆了这两者,并采取统计捷径,因为假设每个人都具有平均身高会方便得多。有时这可能无关紧要,但有时可能很重要,弄清楚你处于哪种情况是有益的。如果你是建筑师或建筑工人,你当然不希望建造 180 厘米高的门廊;可能 40%或更多的民众需要低头才能通过,尽管平均身高的人不需要。如果你要根据你项目的结论做出重要决策,通常最好在各个阶段承认不确定性,包括在统计模型中。

我希望关于随机变量、方差和误差项的讨论能够说明不确定性——这在整个数据科学中无处不在——是如何在统计模型中体现的。这可能是一种低估;事实上,我认为减少不确定性是统计学的首要任务。但有时为了以你想要的方式减少不确定性,你必须承认模型各个部分中存在不确定性。将不确定性——随机性、方差或误差——视为确定性可能会导致你得出过于自信的结果,甚至得出错误的结论。这两种情况本身也是不确定性,但是不良的不确定性——那种你不能以严谨和有用的方式解释的不确定性。因此,我倾向于首先将每个量视为一个随机变量,只有在严格说服自己这样做是适当的情况下,才将其替换为确定的、固定的值。

7.4.4. 拟合模型

到目前为止,我主要是在抽象的意义上讨论模型,没有过多地谈论模型与数据之间的关系。这是故意的,因为我相信在尝试将模型应用于数据之前,考虑我打算建模的系统并决定我认为模型应该如何工作是有益的。拟合模型到数据集是这样一个过程:你设计了一个模型,然后找到描述数据的最佳参数值。短语“拟合模型”与估计模型参数的值同义。

模型拟合是在所有可能的参数值组合中,对拟合优度函数进行优化。拟合优度可以以多种方式定义。如果你的模型旨在进行预测,那么它的预测应该接近最终结果,因此你可以定义一个预测接近度函数。如果模型旨在代表一个总体,例如在本章前面讨论的人类身高模型,那么你可能希望从模型中抽取的随机样本与你要模拟的总体相似。想象你的模型接近代表你所拥有的数据的方式可能有多种。

由于定义拟合优度的可能方式有很多,决定哪种最适合你的目的可能会令人困惑。但有一些常见的函数适用于大量应用。其中最常见的一种被称为似然性,实际上这种类型的函数非常常见且研究得很好,我建议将其作为你的拟合优度函数,除非你有充分的理由不这样做。这样的充分理由之一是,似然函数仅适用于由概率分布指定的模型,所以如果你的模型不是基于概率的,你就不能使用似然性。在这种情况下,最好查阅一些关于模型拟合的统计文献,以找到更适合你模型的拟合优度函数。

似然函数

在英语中,“似然性”这个词被广泛使用,但在统计学中它有特殊的意义。它类似于概率,但方向相反。

当你有一个具有已知参数值的模型时,你可以任意选择一个可能的输出结果并计算该结果的概率(或概率密度)。这就是评估概率密度函数。如果你有数据和模型,但不知道模型的参数值,你可以以相反的方式做同样的事情:使用相同的概率函数并计算数据集中所有点的联合概率(或概率密度),但这样做是在模型参数值的一定范围内。似然函数的输入是一组参数值,输出是一个单一数值,即似然性,这也可以(不完全正确地)称为数据的联合概率。当你移动输入参数值时,你会得到数据似然性的不同值。

概率是基于已知参数的输出结果的函数,而似然性是参数值函数,它基于数据集中已知的输出结果。

最大似然估计

对于数据集而言,模型的最大似然性解正是其名称所暗示的:产生似然函数最高值的模型参数集。最大似然估计(MLE)的任务是找到这个最优参数集。

对于具有正态分布误差项的线性模型,MLE 有一个快速且简单的数学解决方案。但并非所有模型都如此。对于大型和复杂的参数空间,优化是出了名的困难。MLE 和其他依赖于优化的方法正在寻找复杂、多维表面上最高点。我总是把它想象成一次登山探险,寻找一个大型区域中最高峰,而这个区域没有人探索过。如果没有人去过这个区域,也没有空中照片可用,那么找到最高峰就很困难。从地面上看,你可能只能看到最近的峰值,但看不到更远的地方,如果你走向看起来最高的那个,你通常会看到从你所在位置看起来更高的一个。更糟糕的是,优化通常更像是无视野的登山。在试图优化的数学表面上,通常无法看到超出直接周围的环境。通常你知道自己有多高,也许知道地面倾斜的方向,但你无法看得足够远以找到更高的点。

许多优化算法可以帮助改善这种情况。最简单的策略就是一直向上走;这被称为贪婪算法,除非你能保证该区域只有一个峰值,否则它通常效果不佳。其他策略则结合了一些随机性,并使用一些智能策略,在尝试一个方向后,如果结果不如预期,就会重新调整方向。

在任何情况下,MLE 试图在所有可能的参数值空间中找到最高的峰值。如果你知道最高的峰值是你想要的,那就太好了。但在某些情况下,找到有几个非常高的峰值的最高高原可能更好,或者在你做出决定之前,你可能想对整个区域有一个大致的了解。你可以使用其他模型拟合方法来完成这个任务。

最大后验估计

最大似然估计在所有可能的模型参数值表面上寻找最高的峰值。这可能并不适合你项目的目的。有时你可能更感兴趣的是找到最高的峰值集合,而不是寻找绝对最高的一个。

以前几章详细讨论的安然电子邮件数据为例,以及基于社会网络分析建模安然员工行为的项目。由于社会网络分析基于一系列人类行为,这些行为至多是对人们以某种方式与其他人互动的趋势的模糊描述,因此我在做出对项目有意义的结论时,往往不会过于依赖任何单一的行为。我更愿意基于行为集合得出结论,其中任何一个都可以解释我所观察到的任何现象。正因为如此,我对那些看似是社交网络中发生事件的最佳解释的奇特行为解释持怀疑态度,实际上,如果解释中的任何方面哪怕只有一点不真实,那么整个解释和结论就会崩溃。找到一系列相当不错的解释,比找到一个看似非常好但可能脆弱的解释要好。

如果你想要找到一系列高峰值,而不仅仅是最高整体峰值,那么最大后验概率(MAP)方法可以帮助你。MAP 方法与 MLE 方法相关,但通过利用贝叶斯统计(本章后面将讨论)的一些值,特别是对感兴趣变量的先验分布的概念,MAP 方法可以帮助找到模型参数空间中的一个位置,该位置被拟合数据的点包围,尽管不如单个最高峰值拟合得那么好。选择取决于你的目标和假设。

预期最大化与变分贝叶斯

与 MLE 和 MAP 方法都导致参数值的点估计不同,预期最大化(EM)和变分贝叶斯(VB)方法找到这些参数值的最佳分布。我相当倾向于贝叶斯统计,而不是频率统计(本章后面将讨论,如果你不熟悉的话),因此 EM 和 VB 这样的方法对我有吸引力。类似于我喜欢将每个量都视为随机变量,直到我确信否则,我也喜欢在建模的所有步骤中携带方差和不确定性,包括估计的参数和最终结果,如果可能的话。

两者 EM 和 VB 都是基于分布的,因为它们试图为模型中的每个随机变量找到最佳概率分布,以描述数据。如果 MLE 找到最高的峰值,而 MAP 找到一个被许多高区域包围的点,那么 EM 和 VB 各自找到一个你可以从每个方向探索的区域,并且始终处于一个相对较高的海拔区域。除此之外,EM 和 VB 还可以告诉你,在你进入一个低得多区域之前,你可以走多远。从某种意义上说,它们是 MAP 的随机变量版本——但它们并非免费提供。它们可能计算密集,且在数学上难以表达。

EM 和 VB 之间的具体区别主要在于优化模型中潜在变量分布所使用的算法。当优化一个变量的分布时,EM 对模型中其他变量的假设进行了更多的简化,因此,在涉及到的数学和计算需求方面,EM 有时可能比 VB 更简单。VB 始终考虑所有随机变量的完整估计分布,在这个领域没有走捷径,但它也做出了 EM 也做出的某些假设,例如大多数变量之间的独立性。

与 MLE 和 MAP 类似,EM 和 VB 都专注于在参数空间中寻找具有高似然性的区域。它们的主要区别在于对变化的敏感性。MLE 可能会走到悬崖边,而 MAP 可能不会,但它不能对单个位置之外的很多情况做出保证。EM 理解周围区域,而且除此之外,VB 还会稍微更多地关注向一个方向行进如何影响其他方向的地形。这就是一些常见参数优化方法的层次结构的精髓。

马尔可夫链蒙特卡洛

虽然 MLE、MAP、EM 和 VB 都是优化方法,它们都专注于在参数空间中找到一个或一个区域来很好地解释数据,但马尔可夫链蒙特卡洛(MCMC)方法旨在以巧妙的方式探索和记录所有可能的参数值的空间,这样你就可以得到整个空间的地形图,并可以根据这张地图得出结论或进一步探索。

不深入细节——你可以找到大量关于其行为和特性的文献——单个 MCMC 采样器从参数空间的一个特定点开始。然后它随机选择一个方向和距离来移动。通常,步长应该足够小,以至于采样器不会跨越重要的轮廓,例如整个峰值,并且步长应该足够大,以至于采样器理论上可以穿越(通常最多几百万步)包含合理参数值的整个参数空间区域。MCMC 采样器很聪明,它们倾向于进入似然性更高的区域,但并不总是这样做。在选择一个暂定的步点后,它们根据当前位置和新的暂定位置的似然性做出随机决策。由于它们很聪明,如果参数空间的一个特定区域比另一个区域的似然性高约两倍,那么 MCMC 采样器在继续穿越空间时,位于该区域的频率大约是另一个区域的两倍。因此,调整良好的 MCMC 采样器发现自己位于参数空间的每个区域中的频率大约与似然函数预测的频率相同。这意味着步长位置(样本)集合是对模型参数最优概率分布的良好经验表示。

为了确保样本集能够很好地代表分布,通常最好在参数空间的不同位置启动几个 MCMC 采样器——理想情况下是很多个——然后观察它们在经过一定步骤后是否都会给出基于它们步骤位置的相同景观图。如果所有采样器都倾向于反复在相同区域以相同比例混合,那么这些 MCMC 采样器就被说成是收敛了。已经开发了一些启发式收敛诊断方法,专门用于判断是否在有意义的意义上发生了收敛。

一方面,MCMC 通常比其他方法需要的软件开发工作要少,因为你只需要一个拟合优度函数和一个实现了 MCMC 的统计软件包(这样的软件包有很多)。我提到的其他模型拟合算法通常需要操作拟合优度函数和各种其他特定于模型的优化函数,以便找到它们的解,但 MCMC 不需要。只要指定了数学模型并有一个数据集可以操作,MCMC 就可以立即开始运行。

在缺点方面,MCMC 通常需要比其他方法多得多的计算能力,因为它随机地探索模型参数空间——尽管是巧妙地——并在它落下的每一个点上评估高度。它倾向于停留在更高的山峰周围,但并不总是停留在这里,并且通常会足够远地漫游以找到另一个好的山峰。MCMC 的另一个缺点是,它是否巧妙地探索空间通常是由算法本身的调整参数集决定的。如果调整参数设置不正确,你可能会得到较差的结果,因此 MCMC 需要一些监护。然而,从另一个角度来看,一些好的评估启发式方法已经集成到常见的软件包中,这些方法可以快速让你了解你的调整参数是否设置得当。

通常,MCMC 是一种非常适合拟合没有其他明显更好的选择来拟合的模型的优秀技术。一方面,MCMC 应该能够拟合几乎任何模型,代价是增加的计算时间以及监护和评估启发式方法的检查。公平地说,其他模型拟合方法也要求一些监护和评估启发式方法,但可能没有 MCMC 那么多。

过度拟合

过度拟合不是拟合模型的方法,但它与之相关;这是在拟合模型时可能意外发生的一些不好的事情。“过度拟合”是最常用来指代这样一个观点:一个模型可能看起来很好地拟合数据,但如果你得到一些应该与旧数据一致的新数据,模型根本不适合那些数据。

例如,在股票市场建模中,这是一个常见的现象。似乎就在有人发现股票价格模式的时候,那个模式就消失了。股票市场是一个复杂的环境,产生了大量的数据,所以如果你检查一千或一百万个特定的价格模式,看看它们是否与数据相符,至少有一个看起来是相符的。这尤其适用于你调整模式参数(例如,“这种股票价格通常上涨四天,然后下跌两天”)以最好地解释过去的数据。这是过度拟合。模式和模型可能适合你有的数据,但它们可能不适合接下来进入的数据。

当然,模型应该很好地拟合数据,但这不是最重要的。最重要的是,模型应该服务于项目的目的和目标。为此,我在开始将模型应用于数据之前,喜欢对模型的外观有一个大致的了解,以及它哪些方面对我的项目是不可或缺的。使用先前的探索性数据评估来指导模型设计是一个好主意,但让数据为你设计模型可能不是。

过拟合可能由几个原因引起。如果你的模型中有太多参数,那么在模型参数值已经解释了你的数据中的真实现象之后,它们将开始解释不存在于数据集中的虚假现象,例如数据集中的异常。当你的数据有一些严重的异常,而这些异常在你可以预期在未来收到的数据中大部分都不存在时,也可能发生过拟合。如果你正在模拟书面语言,那么一个充满安然电子邮件或儿童书籍的语料库将导致模型很好地拟合你的数据集,但并不很好地拟合整个书面英语语言。

在检查模型过拟合方面,两种有价值的技术是训练-测试分离和交叉验证。在训练-测试分离中,你根据你的一些数据,即训练数据,来训练(拟合)你的模型,然后你在剩下的数据,即测试数据上测试你的模型。如果你过度拟合了训练数据,那么当你对测试数据进行预测时,你应该很明显地发现你偏离得很远。

同样,交叉验证指的是基于数据的不同(通常是随机)分区进行重复的训练-测试分离评估的过程。如果基于训练数据做出的预测在交叉验证的几个重复中与测试数据的输出相匹配,你可以合理地确信你的模型将泛化到类似的数据。另一方面,新数据可能不同于旧数据的原因有很多,如果你只在旧数据上进行了交叉验证,你就无法保证新数据也会适合模型。这就是股市等系统中的诅咒,只能通过仔细应用和测试模型以及理解数据来规避。

7.4.5. 贝叶斯统计与频率统计

虽然两者至少自 18 世纪以来就存在,但在 20 世纪的大部分时间里,频率统计比贝叶斯统计更受欢迎。在过去的几十年里,关于两者优缺点的争论一直存在——我甚至不敢说成是纷争——但我不会煽动这场争论的火焰。我不想加剧争论,但这两派统计学在对话和文献中经常被提及,因此了解它们的内容是有帮助的。

两者之间的主要区别是一个理论上的解释性区别,这确实对某些统计模型的工作方式有影响。在频率统计中,结果置信度的概念是衡量你重复实验和分析多次时预期得到相同结果的频率。95%的置信度表示在实验的 95%重复中,你会得出相同的结论。频率统计这个术语来源于这样一个观点,即统计结论是基于许多重复中某个事件发生的预期频率得出的。

贝叶斯统计学更紧密地遵循概率的概念。贝叶斯统计推断的结果,而不是具有频率主义的置信度,通常使用概率分布来描述。此外,贝叶斯概率可以直观地描述为对随机事件将要发生的信念程度。这与频率主义概率形成对比,频率主义概率描述概率为在无限系列的事件中某些随机事件发生的相对频率。

老实说,对于许多统计任务来说,你使用频率主义还是贝叶斯方法并没有太大区别。常见的线性回归就是其中之一。如果你以最常见的方式应用它们,两种方法都会给出相同的结果。但两种方法之间有一些差异,这些差异导致了实际上的差异,我将在下面讨论。

声明:我主要是一个贝叶斯主义者,但我不至于偏颇到说频率主义方法不好或低劣。主要我觉得决定采用哪种方法最重要的因素是理解它们各自隐含的假设。只要你能理解这些假设并觉得它们是合适的,两种方法都可以是有用的。

先验分布

贝叶斯统计学和推断要求你对模型参数的值持有先验信念。从技术上讲,你应该在开始分析主要数据集之前制定这种先验信念。但基于你的数据来建立先验信念是称为经验贝叶斯的技术的一部分,这可能是有用的,但在某些圈子中不受欢迎。

先验信念可能很简单,比如“我认为这个参数接近于零,上下浮动一两个单位”,这可以正式地转化为正态分布或其他适当的分布。在大多数情况下,可以创建非信息性(或平坦)的先验,这些先验被设计用来告诉你的统计模型“我不知道”,在严谨的意义上。无论如何,先验信念必须被编码成一个概率分布,成为统计模型的一部分。在本章前面提到的微阵列协议比较示例中,我描述的超参数是某些模型参数的先验分布的参数。

一些频率统计学家反对制定这样的先验分布的必要性。显然他们认为,如果你在看到数据之前对模型的参数值一无所知,你不需要制定先验信念。我倾向于同意他们的观点,但在大多数情况下,非信息先验分布的存在使得贝叶斯统计学家可以通过使其无关紧要来规避先验分布的要求。此外,如果你试图形式化频率统计学的无先验信念概念,它将非常类似于贝叶斯统计学中的非信息先验。你可能会得出结论,频率统计方法通常有一个隐含的先验分布,而这个先验分布并没有明确表示。我并不是说频率统计学家是错误的,贝叶斯方法是更好的;相反,我旨在说明这两种方法可以非常相似,并驳斥先验信念要求的必要性是一种劣势的观点。

使用新数据更新

我已经解释了贝叶斯统计中先验分布的存在并不是一个缺点,因为大多数情况下你可以使用非信息先验。现在我将解释先验不仅不是坏事,而且是好事。

最常引用的频率统计和贝叶斯统计之间的差异之一,除了“你必须有一个先验”之外,还有“你可以用新数据更新你的模型,而无需包括旧数据。”在贝叶斯框架中实现这一点非常简单。

假设你之前有一个统计模型,并且收到了第一批数据。你使用非信息先验进行了贝叶斯统计分析,并拟合了你的模型。拟合贝叶斯模型的结果是一组参数分布,称为后验分布,因为它们是在数据被纳入模型之后形成的。先验分布代表你在让模型查看数据之前所相信的,而后验分布是基于你的先验信念以及模型所看到的数据形成的新信念。

现在你正在获取更多数据。你不需要挖掘旧数据,一次性将模型拟合到所有数据上,使用旧的非信息先验,你可以根据第一批数据取后验分布,并将其作为拟合第二批数据的先验分布。如果你的数据集大小或计算能力是一个问题,那么这种贝叶斯更新技术可以节省相当多的时间和精力。

现在,随着许多实时分析服务的开发,贝叶斯更新提供了一种方法,可以在不重新检查所有过去数据的情况下,即时分析大量数据。

传播不确定性

在频率统计和贝叶斯统计之间的所有差异中,我最喜欢这个,尽管我很少听到有人提到它。简而言之,因为贝叶斯统计紧紧抓住概率这一概念——它从先验概率分布开始,以后验概率分布结束——它允许不确定性在模型中的数量中传播,从旧数据集传播到新数据集,并从数据集一直传播到结论。

在这本书中,我多次提到我是一个承认存在不确定性并跟踪它的忠实粉丝。通过将概率分布提升为第一类公民,正如贝叶斯统计所做的那样,模型的每一部分都可以携带自己的不确定性,如果你继续正确地使用它,你就不会发现自己对结果过于自信,从而得出错误的结论。

我在生物信息学领域发表的几篇学术论文中,我最喜欢的就是强调这个确切的概念。那篇论文的主要发现,称为“通过综合贝叶斯聚类和时间序列表达数据的动态建模改进基因调控网络推断”(PloS ONE,2013)——标题听起来怎么样?——展示了基因表达测量中的高技术差异是如何从数据传播到贝叶斯模型,再传播到结果中,从而更准确地描述了哪些基因与哪些其他基因相互作用。在相同主题的先前工作中,大多数研究完全忽略了技术差异,并假设每个基因的表达水平仅仅是技术重复值平均的结果。坦白说,我觉得这很荒谬,所以我着手纠正它。正如论文引用次数如此之少所暗示的,我可能并没有完全达到那个目标,但我认为这是一个完美的、现实生活中的例子,说明了在统计分析中承认和传播不确定性如何导致更好的结果。此外,我在论文中提出的算法命名为BAyesian Clustering Over Networks,也称为 BACON,所以我有这个优势。

7.4.6. 从模型中得出结论

在所有关于设计模型、构建模型和拟合模型的讨论中,我感觉我几乎失去了统计建模的真正目的:了解你正在研究的系统。

一个好的统计模型应包含你感兴趣的系统中所有变量和数量。如果你对全球女性的平均身高感兴趣,那么这应该成为你模型中的一个变量。如果你对雄性和雌性果蝇的基因表达差异感兴趣,那么这也应该包含在你的模型中。如果你和你项目的重要部分是了解恩隆员工对收到的电子邮件的反应性,那么你应该有一个代表反应性的变量在你的模型中。然后,使用你选择的方法拟合模型的过程,将产生这些变量的估计值。在潜在模型参数的情况下,拟合模型直接产生参数估计。在预测建模的情况下,其中预测是未来的一个潜在变量(从未来?),拟合的模型可以生成估计值或预测。

基于你的拟合模型得出结论的形式有很多种。首先,你必须弄清楚你想向它提出哪些问题。参考你在项目规划阶段生成的列表,这些列表在本书的前几章中讨论过。模型能帮助回答哪些问题?为了你的项目目的,一个设计良好的模型应该能够回答关于所讨论系统的许多问题,如果不是所有问题。如果它做不到,你可能需要创建一个新的模型。重建模型虽然令人遗憾,但比使用一个糟糕或无用的模型要好。你可以通过始终保持对项目目标的意识来避免这种情况,特别是这个统计模型旨在解决的项目方面。

假设项目规划阶段提出的问题中涉及许多变量和数量,这些变量和数量在你的模型中都有所表示。对于这些变量中的每一个,模型拟合过程都产生了价值估计或概率分布。你可以对这些估计提出两种主要类型的问题:

  • 这个变量的值大约是多少?

  • 这个变量是否大于/小于 X?

我将在各自的子节中介绍解决这些问题的技术。

值是多少?估计值、标准误差和置信区间

我之前描述的所有模型拟合方法都产生了模型中变量和参数的最佳猜测,称为点估计。大多数但并非所有这些方法也给出了一些关于该值不确定性的度量。根据你使用的特定算法,最大似然估计(MLE)可能不会自动产生这种不确定性的度量,所以如果你需要它,你可能需要找到一个产生这种度量的算法。所有其他模型拟合方法都将不确定性作为模型拟合过程的固有输出。

如果你只需要一个点估计值,那么你就可以继续了。但如果你通常想要某种保证,即该值大约是你所认为的那样,那么你需要一个概率分布或标准误差。参数估计的标准误差是频率派标准差(方差的平方根)的概率分布的等价物。简而言之,你可以有 95%的信心认为参数在其点估计值的两倍标准误差内,或者有 99.7%的信心认为它在三倍标准误差内。这些都是置信区间,如果标准误差相对较小,那么置信区间将较窄,你可以合理地(无论你选择多少百分比)确信真实值落在区间内。

标准误差和置信区间的贝叶斯等价物分别是方差可信区间。它们几乎以完全相同的方式工作,但正如通常那样,它们在哲学基础上有所不同。鉴于贝叶斯参数估计是一个概率分布,你可以自然地提取方差并创建可信区间,对于正态分布,真实值在均值两倍标准差内的概率是 95%,在均值三倍标准差内的概率是 99.7%。

有时报告一个值或一个区间可以解决你项目的一个目标。但有时你可能想了解更多——例如,变量是否具有特定的属性,比如是否大于或小于某个特定数量。为此,你需要假设检验。

这个变量 _______? 假设检验

通常,你需要了解一个变量比仅仅是一个点估计值或可能包含真实值的值域更多。有时,了解变量是否具有某些属性很重要,比如这些:

  • 变量 X 是否大于 10?

  • 变量 X 是否小于 5?

  • 变量 X 是否为零?

  • 变量 X 是否与另一个变量 Y 有显著差异?

这些问题中的每一个都可以通过假设检验来回答。一个假设检验包括一个零假设,一个备择假设,然后是一个适合这两个假设的统计检验。

零假设就像是现状。如果这个假设是真的,那就会显得有些无聊。备择假设是一种假设,如果它是真的,那就会令人兴奋。例如,假设你认为变量 X 大于 10,如果这是真的,那么它将对项目产生一些有趣的影响(比如 X 可能是下周预期下载的百万级歌曲数量)。“X 大于 10”是一个好的备择假设。零假设则是其对立面:“X 不大于 10。”现在,因为你希望备择假设是真的,所以你需要证明零假设不是真的,并且要有合理的怀疑。在统计学中,你通常不是证明某事是真的,而是证明另一种可能性,即零假设,几乎肯定不是真的。在语言上这是一个细微的差别,但在数学上却有着相当大的影响。

为了测试零假设和备择假设,并拒绝零假设,正如他们所说,你需要证明 X 的值几乎肯定不会低于 10。假设基于你的模型,X 的后验概率分布是正态分布,均值为 16,标准差为 1.5。重要的是要注意,这是一个单侧假设检验,因为你只关心 X 是否太低(低于 10),而不关心它是否太高。选择正确的检验版本(单侧或双侧)可能会影响你结果的显著性。

你需要检查 10 是否超出了 X 可能有多低的合理阈值。让我们选择 99%的显著性水平,因为你想要确保 X 不会低于 10。参考单侧检验的参考数据,99%的阈值是 2.33 个标准差。你会注意到 16 的估计值比 10 高 4.0 个标准差,这意味着你可以几乎肯定,并且肯定超过 99%的把握,X 的值是高于 10 的。

我几乎不能不提到 p 值就谈论假设检验。你可以在其他地方找到更详尽的解释,但p 值是置信度的倒数(1 减去置信度,或 99%的置信度对应于 p<0.01),它代表了频率主义中假设检验最终给出错误答案的概率。在频率主义统计学中,不要将 p 值视为贝叶斯统计学中的概率。p 值应该用于假设检验结果的阈值,而不应该用于其他目的。

另一个概念和潜在陷阱发生在你对相同的数据或相同的模型运行许多假设检验时。运行几个可能没问题,但如果你运行了数百或更多的假设检验,那么你肯定能找到至少一个通过的检验。例如,如果你做了 100 个假设检验,所有都在 99%的显著性水平上,即使没有一个应该通过(零假设被拒绝),你仍然期望至少有一个检验通过。如果你必须执行许多假设检验,最好是进行多重检验校正,其中结果的显著性被调整以补偿这样的事实:否则,一些真正的零假设将被随机机会拒绝。有多重检验校正的几种不同方法,它们之间的差异太微妙,无法在此详细讨论,所以我会跳过它们。再次提醒,如果你对此感兴趣,请查阅良好的统计参考书籍!

7.5. 杂项统计方法

统计建模是一个明确的尝试,使用数学和统计概念来描述一个系统,目的是理解系统内部和外部是如何工作的。这是一个整体过程,我认为无论你是否最终在严格意义上实现统计模型,理解系统整体——以及项目的数据到目标过程——都是重要的。许多其他统计技术至少部分地超出了我对统计模型的定义,这些可以帮助你了解系统,甚至可能被用来代替正式模型。

我已经讨论了描述性统计、推断以及其他可能被称为原子统计的技术——通过这个,我的意思是它们构成了统计学的一些核心概念和构建块。如果你沿着复杂性的阶梯向上移动,你会发现一些统计方法和算法不能说是原子的——它们有太多的移动部件,但它们如此受欢迎,并且通常非常有用,因此在任何统计分析技术概述中都应提及。在接下来的小节中,我将简要描述一些这样的高复杂度技术,它们何时可能有效,以及使用它们时应注意什么。

7.5.1. 聚类

有时候你有一堆数据点,你知道其中有一些模式,但你不确定它们的确切位置,因此你可以将数据点分组为一般相似的数据点集群,以获得数据中发生的事情的大致轮廓。这样,聚类,如果它通常没有那么多难以分解和诊断的移动部件,可以成为描述性统计的一个很好的技术。

聚类也可以是统计模型的一个组成部分。我之前提到的基因相互作用模型(我将其命名为 BACON)的主要方面就是聚类。在 BACON 中,“C”代表聚类。全称是 BAyesian Clustering Over Networks。在这个模型中,我假设某些基因的表达是一起移动的,因为它们参与了某些相同的高级过程。大量的科学文献支持这个概念。我没有事先指定哪些基因的表达会一起移动(文献通常在这个问题上并不具有结论性),而是将一个聚类算法纳入我的模型中,允许具有相似表达移动的基因在模型拟合过程中自行聚集。鉴于有数千个基因,聚类有助于减少移动部件的数量(称为降维),这本身可以是一个目标,但在这种特定情况下,学术文献为这种做法提供了一些依据,更重要的是,我发现聚类模型比未聚类模型有更好的预测能力。

它是如何工作的

有许多不同的聚类算法——k-means、高斯混合模型、层次聚类等——但它们都遍历数据点的空间(所有连续的数值),并将以某种方式彼此接近的数据点分组到同一个聚类中。

k-means 和高斯混合模型都是基于质心的聚类算法,这意味着每个聚类都有一个类似于中心的点,通常代表该聚类的成员。在这些算法中的每一个,数据点距离哪个聚类质心更近,粗略地说,这个聚类就可以说包含了该数据点。聚类可以是模糊的或概率性的,这意味着一个数据点可以部分属于一个聚类,部分属于其他聚类。通常在运行算法之前,你必须定义一个固定的聚类数量,但也有一些替代方案。

层次聚类略有不同。它关注单个数据点及其彼此之间的接近程度。简单来说,层次聚类寻找最接近的两个数据点并将它们合并成一个聚类。然后它寻找包括新聚类在内的两个最接近的尚未合并的数据点,并将这两个点合并。这个过程一直持续到所有数据点都合并成一个单一的大聚类。结果是树状图(大多数统计软件包都会乐意为你绘制这样的图),在树的分支结构上,接近的数据点彼此靠近。这有助于看到哪些数据点接近哪些其他数据点。如果你想要多个聚类而不是一个大(树状)聚类,你可以在树的一定深度处切割,以得到你想要的(分支)聚类数量——在这个意义上切割树意味着在某个高度上,将树干和最大的树枝与较小的树枝分开,每个较小的树枝都会保持为一个统一的聚类。

何时使用它

如果你想要将你的数据点(或其他变量)出于任何原因分组,考虑聚类。如果你想能够描述每个组的特性以及典型的聚类成员看起来像什么,尝试使用基于质心的算法,如 k-means 或高斯混合模型。如果你想要了解你的数据点中哪一个最接近哪一个,而你并不太关心这个接近程度——如果接近在绝对意义上比接近更重要——那么层次聚类可能是一个不错的选择。

注意事项

在聚类算法中,通常有很多参数可以调整。你通常不能保证每个聚类中指定的所有成员都很好地由每个聚类代表,或者重要的聚类甚至存在。如果你的数据点的维度(方面、领域)高度相关,那可能会成问题。为了帮助解决所有这些问题,大多数软件工具都有许多诊断工具来检查聚类算法的性能如何。请谨慎使用它们,以及聚类算法。

7.5.2. 组件分析

理解具有许多维度的数据可能很困难。聚类将数据点组合成相似性组,以减少需要审查或分析的对象数量。成分分析的方法——其中主成分分析(PCA)和独立成分分析(ICA)是最受欢迎的——做的是类似的事情,但它们将数据的维度而不是数据点组合在一起,并且按照它们解释数据方差的大小对分组进行排序。从某种意义上说,成分分析直接减少了数据的维度性,通过排序和评估由旧维度构建的新维度,你可能能够仅用少数几个维度来解释每个数据点。

例如,假设你正在分析汽车旅行中的汽油使用情况。在每一个数据点中,字段包括行驶距离、行程时长、车型、车龄以及其他一些信息,还包括旅行中使用的汽油量。如果你试图用所有其他变量来解释汽油使用情况,那么行驶距离和行程时长很可能有助于解释使用了多少汽油。它们都不是完美的预测因子,但它们都可以在很大程度上做出贡献,实际上它们高度相关。在面临高度相关的情况下,构建一个同时使用这两个变量的汽油使用预测模型时,你必须非常小心;许多模型会将这些高度相关的变量相互混淆。成分分析通过操纵维度——混合、组合和排序它们——来最小化相关性,从广义上讲。一个基于成分分析生成的维度来预测汽油使用的模型通常不会混淆任何维度。

行驶距离和行程时长高度相关的观点可能很明显,但想象一下你正在处理一个不太熟悉的研究系统,并且你有数十、数百甚至数千个数据维度。你知道一些维度可能是相关的,但你不知道具体是哪些,你也知道以巧妙的方式减少维度总数通常是有益的。这正是成分分析擅长的地方。

它是如何工作的

成分分析通常将数据集视为一个在多个维度上的数据云,然后找到数据云长度最长的成分或角度。这里的“成分”或“角度”是指维度的组合,因此所选的维度与原始维度相比可能是对角线。选择第一个成分后,这个维度以一种巧妙的方式被折叠或忽略,然后选择第二个成分,目标是找到与第一个成分没有任何共同点的最长或最宽的成分。这个过程会继续进行,按照重要性顺序找到你想要的那么多成分。

何时使用它

如果你数据中有许多维度,而你又想减少维度数量,成分分析可能是减少维度数量的最佳方法,而且结果维度通常具有一些良好的特性。但如果你需要维度具有可解释性,你必须小心行事。

需要留意的事项

主成分分析(PCA),这种最流行的成分分析方法,对数据集中各个维度的值相对尺度很敏感。如果你对数据中的某个特定字段进行缩放——比如说,你从千米换算成英里——那么这将对 PCA 生成的成分产生重大影响。问题不仅在于缩放可能成为问题,原始(或任何)变量的尺度也可能成为问题。每个维度都应该进行缩放,使得其中任何一个维度的相同大小变化在某种程度上都是同样显著的。在尝试任何成分分析的特技之前,最好先咨询一下好的参考资料。

7.5.3. 机器学习和黑盒方法

在分析软件开发的世界里,机器学习如今正风靡一时。并不是说它之前没有流行过,但最近几年,我看到了一些最初的产品进入市场,声称要“将机器学习带给大众”,或者类似的说法。这在某种程度上听起来很棒,但在另一个层面上,它听起来像是在自找麻烦。我认为大多数人并不了解机器学习是如何工作的,或者如何察觉它是否出了问题。如果你是机器学习的新手,我想强调的是,在大多数形式中,机器学习是一个棘手的工具,不应该被视为任何问题的魔法解决方案。为什么需要花费数年或数十年的学术研究来开发一种全新的机器学习技术,原因就在于此,同样也是为什么大多数人还不懂得如何操作它:机器学习极其复杂。

“机器学习”这个术语在许多语境中使用,并且具有某种流动的含义。有些人用它来指代任何可以从数据中得出结论的统计方法,但那并不是我使用的含义。我使用“机器学习”这个术语来指代那些可以从数据中得出结论但模型(如果你愿意这样称呼它们)难以剖析和理解的算法类别。从这个意义上说,只有机器本身才能以某种方式理解其模型。当然,对于大多数机器学习方法,你可以深入了解机器生成的模型内部,了解哪些变量最重要以及它们是如何相互关联的,但以这种方式,机器的模型开始感觉像是一个数据集——如果没有相当复杂的统计分析,很难把握机器模型的工作方式。这就是为什么许多机器学习工具被称为黑盒方法。

使用一个接收数据并产生正确答案的黑盒并没有什么不妥。但是,制作这样一个盒子并确认其答案持续正确可能会很具挑战性,而且在调试完成后几乎不可能查看盒子的内部。机器学习很棒,但可能比其他任何统计方法类别都需要更加小心地使用才能成功。

我不会详细解释机器学习概念,因为互联网和印刷品上都有无数优秀的参考资料。然而,我会简要解释一些关键概念,以便将其置于适当的背景中。

特征提取是一个将你的数据点转换为更信息丰富的版本的过程。为了获得最佳结果,每次进行机器学习时提取良好的特征至关重要——除非是进行深度学习。数据点的每个特征都应该向机器学习算法展示其最佳的一面,如果它希望在将来被正确分类或预测。例如,在信用卡欺诈检测中,可以添加到信用卡交易中的一个可能特征是交易金额超出卡正常交易金额的程度;或者,该特征可以是交易规模在所有最近交易中的百分位数。同样,好的特征是那些常识会告诉你可能有助于区分好与坏或任何两个类别之间的特征。还有一些不常见的特征可能没有常识,但你总是必须小心判断这些特征是否真正有价值,或者它们是否是训练数据集的副产品。

这里有一些最流行的机器学习算法,你将把它们应用到从数据点中提取的特征值上:

  • Random forest— 这是一个有趣的名字,对应一个有用的方法。决策树是一系列以决策结束的 yes/no 问题。随机森林是一系列随机生成的决策树,它倾向于那些能够正确分类数据点的树和分支。当我知道我想要机器学习,但没有充分的理由选择其他方法时,这是我的首选机器学习方法。它非常灵活,而且不容易诊断问题。

  • Support vector machine (SVM)— 几年前这相当流行,现在它已经进入了一个特别有用的领域,随着下一个机器学习潮流的到来,它已经稳定下来。支持向量机(SVM)被设计用来将数据点分类到两个类别之一。它们操作数据空间,将其扭曲和变形,以便在两个已知属于不同分类的数据点之间打入楔子。SVM 专注于两个类别之间的边界,所以如果你有两个数据点类别,每个类别在数据空间中倾向于聚集在一起,而你正在寻找一种方法来以最大分离度(如果可能的话)划分这两个类别,那么 SVM 就是为你准备的。

  • Boosting— 这一点解释起来有些棘手,我的有限经验也无法提供所有可能需要的洞察。但我知道,提升(Boosting)在特定类型的机器学习中是一个重大的进步。如果你有一堆平庸的机器学习模型(弱学习器),提升可能能够智能地组合它们,从而得到一个优秀的机器学习模型(强学习器)。因为提升结合了其他机器学习方法的输出,所以它通常被称为元算法。这可不是一件容易的事情。

  • Neural network— 神经网络的全盛时期似乎是在 20 世纪最后几十年,直到深度学习的出现。在其早期的流行版本中,人工神经网络(更正式的名字)可能是最黑的黑盒子。它们似乎是为了不被理解而设计的。但它们在某些情况下工作得很好。神经网络由一层又一层的单向阀门(或神经元)组成,每个神经元都以某种任意的方式转换输入。这些神经元在一个大网络中相互连接,从输入数据到输出预测或分类,所有的计算工作都涉及以巧妙的方式对每个神经元进行加权和重新加权,以优化结果。

  • 深度学习—— 这是本世纪的一项新进展。粗略地说,深度学习指的是你可能不必太担心特征提取的想法,因为有了足够的计算能力,算法可能能够找到它自己的良好特征,然后使用它们来学习。更具体地说,深度学习技术是分层机器学习方法,在低层次上执行与其他方法相同的学习类型,但在高层次上,它们生成可以普遍应用于识别许多形式中重要模式的抽象。今天,最流行的深度学习方法基于神经网络,导致后者的一种复兴。

  • 人工智能—— 我包括这个术语,因为它经常与机器学习混淆,这是有道理的。机器学习和人工智能之间没有根本的区别,但人工智能带来了这样的含义,即机器正在接近人类的智力水平。在某些情况下,计算机已经在特定任务上超越了人类——例如著名的国际象棋或危险边缘!——但他们远远达不到普通人类在广泛日常任务中的普遍智力水平。

它是如何工作的

每个特定的机器学习算法都是不同的。数据输入,答案输出;你必须做很多工作来确认你没有犯任何错误,你没有过度拟合,数据已经被正确地分为训练集和测试集,以及当全新的数据到来时,你的预测、分类或其他结论仍然有效。

何时使用

机器学习通常可以做到其他统计方法做不到的事情。我更喜欢首先尝试统计模型,以便了解系统和其与数据的关系,但如果模型在结果方面不足,那么我开始考虑应用机器学习技术的方法,同时不放弃太多与统计模型相关的意识。我不会说我最后的选择是机器学习,但通常我更喜欢一个良好形成的统计模型的直观性和洞察力,直到我发现它不足。如果你知道自己在做什么,并且有一个复杂的问题,它远远不是线性的、二次的或统计模型中其他常见的变量关系,那么直接转向机器学习。

注意事项

在你用测试-训练分离以及一些你和你机器之前从未见过的完全新的数据完全独立地验证它们之前,不要相信机器的模型或其结果——与机器学习实现完全无关。数据挖掘——在正式分析数据之前查看数据,并使用你所看到的内容来偏袒你的分析——如果你在挖掘之前没有进行测试-训练分离,可能会成为一个问题。

练习

继续使用在第二章中首先描述的“脏钱预测”个人财务应用场景,并关联到之前章节的练习,尝试以下内容:

1.

描述两种你可能用来预测个人财务账户的统计模型。对于每种模型,至少给出一个潜在的弱点或劣势。

2.

假设你已经成功创建了一个可以将交易准确分类到“常规”、“一次性”和其他你想到的任何合理分类的分类器。描述一个利用这些分类来提高预测准确性的统计模型。

摘要

  • 在开始软件构建或全面分析阶段之前,从理论上考虑一个项目和问题是有价值的。通过停下来思考问题一段时间,可以在数据科学和其他领域学到很多东西。

  • 数学是一种描述系统如何工作的词汇和框架。

  • 统计建模是一个描述系统并将其与数据连接的过程。

  • 可用的分析方法和实现它们的软件范围很广;从中选择可能会令人感到畏惧,但不应感到压倒。

  • 机器学习和其他复杂的统计方法可以作为实现其他情况下不可能实现的目标的好工具,但只有在你小心使用它们的情况下。

第八章. 软件:统计学的实践

本章涵盖

  • 一些统计软件应用的基本知识

  • 介绍一些有用的编程语言

  • 选择合适的软件使用或构建

  • 如何考虑将统计学融入你的软件中

图 8.1 显示了我们在数据科学过程中的位置:构建统计软件。在上一个章节中,我将统计学作为数据科学两个核心概念之一进行了介绍。软件开发和应用知识是另一个核心。在少数情况下,数据科学家在项目过程中可能不需要软件,但我想,当数据集非常小的时候,这是可能的。

图 8.1. 数据科学过程构建阶段的一个重要方面:统计软件和工程

图片

除了不使用之外,数据科学家必须为任何项目做出许多软件选择。如果你有一个喜欢的程序,那通常是一个不错的选择,至少是因为你对它的熟悉。但选择其他东西也可能有很好的理由。或者如果你是数据科学或统计软件的新手,可能很难找到一个起点。因此,在本章中,我在提供一些选择指南之前,给出了数据科学中可能使用的不同类型软件的广泛概述。正如第七章中所述,我打算只提供相关概念的高级描述和一些示例。

经验丰富的软件开发者可能不会太喜欢这一章,但如果你是统计学家或软件新手,我认为这些高级描述是一个不错的开始方式。关于任何特定主题的更多信息,互联网和印刷品中都有许多深入的参考资料。

8.1. 电子表格和基于 GUI 的应用程序

对于那些在 Microsoft Excel 或其他电子表格应用程序上花费了大量时间的人来说,这通常是进行任何类型数据分析的首选。特别是如果数据以表格形式存在,如 CSV,并且数据量不是很大,那么在电子表格中开始分析可以很容易。此外,如果你需要的计算不是太复杂,电子表格甚至可能覆盖项目所需的全部软件需求。

8.1.1. 电子表格

对于少数可能不太了解的人来说,电子表格是一种软件,它以行和列的表格格式表示数据。它通常允许通过一系列函数(如平均值、中位数、总和)来分析这些数据,这些函数可以对数据进行操作并回答一些问题。Microsoft Excel、OpenOffice 和 LibreOffice Calc 以及 Google Sheets 是流行的电子表格应用程序的例子。

当电子表格包含多个工作表、交叉引用、表格查找和函数/公式时,它们可以相当复杂。我制作的最复杂的电子表格是为一个关于房地产的大学财务课程。作为课程的一部分,我们参与了一个模拟房地产市场的活动,其中每个学生都拥有一座公寓楼,这需要关于融资和保险等方面的决策。在模拟过程中,会有随机事件,如灾害,可能会对建筑物造成损害,以及运营成本、空置和利率波动。模拟的目标是在五年后出售建筑物后,从为公寓楼支付的初始现金中获得最高的回报率。

到目前为止,最重要的决定是具体选择哪种抵押贷款来为购买建筑提供资金。我们被提供了八种不同的抵押贷款结构选择,这些结构在期限/持续时间、固定或浮动利率以及它们的利差、购买时支付的点数和临时促销利率方面有所不同。公寓楼有一个明确的购买价格,但就像现实生活中一样,我们有权选择我们想要为购买借多少钱。我们没有通过抵押贷款借到的金额是我们将用来计算回报率的现金支出,这个回报率衡量了我们的成功。

在模拟中包含了所有变量,无论是随机选择还是人为选择,很明显,在做出决策时进行一些计算将非常有帮助,尤其是在融资方面。当时,我只在我的图形计算器上编写程序,而且没有一个程序分析数据。除了学习另一种工具之外,使用微软 Excel 成了我唯一的选择。虽然我和许多人一样对 Excel 有些轻微的厌恶,但它完成了工作。

对于 8 种抵押贷款结构和 12 种不同的借款可能金额,我计算了模拟的 5 年期间每年的现金流量,并从这些现金流量中计算了预期的回报率。我使用了 Excel 的几个公式,包括标准的SUM公式用于加法,PMT公式用于计算抵押贷款还款,以及IRR公式用于计算现金流量的内部回报率。到那时,我可以看到在预期条件下所有可能性的回报率。然而,正如我提到的,模拟的每年都会有随机事件发生,因此预期的结果几乎肯定不会是观察到的结果。为了解决这个问题,我在电子表格中添加了一些代表模拟灾难、利率波动和空置率等值的数值。然后,我能够输入一些可能的随机结果,并查看它们如何影响回报率和最佳选择。

最终,我的电子表格包含了两张工作表,一张用于进行繁重的计算,另一张用于总结结果(如图 8.2 所示 figure 8.2),随机变量及其波动,以及需要做出的决策。进行繁重计算的工作表包含了 96 种不同的现金流陈述,每种陈述对应于 12 种不同借款金额的 8 种不同抵押类型。每个现金流陈述都使用总结工作表中的随机变量值来计算基于特定抵押参数的几种收入和支出的五年现金流。每个现金流计算的结果产生了一个回报率数字,总结工作表随后引用这个数字。当我查看所有 96 个回报率——使用 Excel 的条件格式选项突出显示其中最高的一个——以及如果随机变量结果不同,每个回报率如何变化时,我对选择一个不仅预期回报率最高的抵押贷款,而且在发生灾难时风险也不会太大的抵押贷款感到自信。

图 8.2. 我在大学金融课程中用来模拟管理公寓楼的工作表的第一页

图片

我认为我的同学们并没有花像我一样多的时间分析选择。我不相信他们中的任何一个人创建了电子表格。我最终以超过一个百分点的优势赢得了比赛,这在有金融倾向的人看来是一个相当大的胜利。如果我记得正确的话,我的初始投资回报率超过了 9%,而其他人则不到 8%——如果这笔钱是真的,这个数字将相当于几千美元。我还从比赛中赢得了真正的钱:作为一等奖的 200 美元现金,尽管这比我在虚构的公寓楼中赚到的几千美元要少,但对于我这样一个大学生来说,这笔钱也不是小数目。

我可能在那次单独的金融项目中学到了比整个一生中更多的关于电子表格的知识。电子表格的一个优点,尤其是 Excel,变得很明显:内置公式的数量是天文数字的。对于你可能在电子表格中应用到的几乎任何静态计算,都有一个公式或其组合可以为你完成这项任务。在这里,我用“静态”一词来指代一步完成的计算,没有迭代或值之间的复杂相互依赖。

电子表格的一个主要缺点是,即使是中等复杂的公式也可能看起来像这样:

=-((-PMT((F15)/12,$B20*12,F$14,1))*12-(F$14-((1-((1+(F15)/12)¹²-1)/((1+(F15)/12)^($B20*12)-1))*F$14)))

或者更糟。我从我的房地产模拟电子表格中直接取了那个例子,尽管即使对我来说也不太清楚这个公式最初是如何打算用来计算现金流量表中某项抵押贷款一年内利息支出的。不用说,这个公式难以阅读和理解——而且它远非我所见或编写过的最糟糕的公式——主要是因为整个公式都在一行上,所有的变量都通过无用的字母和数字组合来引用。更别提括号了。这种计算的易读性可能是一个严重的问题。坦白说,我并不惊讶 Excel 公式错误被卷入许多大型银行机构的失误中,比如 2013 年摩根大通“伦敦鲸”案件,其中显然一个 Excel 公式错误导致了对特定投资组合风险的严重低估,最终导致 60 亿美元的损失。这是一个代价高昂的错误,也是使用具有可读计算说明的统计工具的充分理由。

电子表格不仅限于行、列和公式。Excel 中另一个值得注意的功能是求解器,这是一个可以帮助你找到复杂方程的优化解或优化参数以实现某些目标的功能的工具。要使用它,你必须告诉求解器哪些值可以改变,你还得告诉它哪个值是要最大化或最小化的目标值;然后求解器使用优化技术来找到最佳解。例如,我在另一个大学项目中使用了求解器,当时我试图在繁忙的一天内最大化一组电梯将乘客运送至他们希望到达的楼层的人数。我找到的优化解涉及限制每部电梯访问的楼层;例如,一部电梯负责 1-10 层,另一部负责 10-15 层。通过让 Excel 的求解器改变每部电梯可能访问的楼层,它找到了一个最大化总运输人数的解决方案。

大多数电子表格应用的一个显著特点是宏。,在像 Excel 这样的应用中,是用户可以自己编写的迷你程序。在 Excel 中,宏是用 Visual Basic for Applications (VBA)编写的,通常可以完成 Excel 中可以完成的所有事情。你不必通过点击和指向来执行某个操作,理论上你可以编写一个执行相同操作的宏。如果你经常做同样的事情,这可能会很有帮助。运行一个宏可能比手动执行一系列步骤要快得多,也容易得多,这取决于这些步骤的复杂性。我为我的房地产电子表格创建了一个宏,这个宏将我已更改的第一个现金流量表中的更改复制到所有其他 95 个现金流量表中。值得注意的是,我没有用 VBA 编写宏,而是 Excel 能够记录我执行复制和粘贴的手动步骤,然后将这个记录转换成我可以使用的宏。如果你经常做特定的事情,你想为它创建一个宏,但又不想在 VBA 中编写代码,这是一个 Excel 的便捷功能。

8.1.2. 其他基于 GUI 的统计应用

我认为像 Excel 这样的电子表格应用是数据分析的第一级软件;即使是初学者也不害怕打开电子表格随意探索一下。第十级是从零开始编写自己完美无缺的软件;这对胆小的人来说不是一件容易的事情。介于两者之间的级别被各种软件应用所占据,这些应用具有不同的易用性和多功能性,如电子表格和编程。如果你觉得你最喜欢的电子表格应用在复杂性方面有所欠缺,但你又不想一头扎进编程语言,那么一些中间解决方案可能适合你。

最近我参加了一个大型统计学会议,并对提供中级统计应用软件的供应商数量感到惊讶。我看到了 SPSS、Stata、SAS(及其 JMP 产品)和 Minitab 等展位。这些应用软件在某个圈子里都很受欢迎,我在生命中的某个阶段使用过其中的三个。我决定玩一个小游戏,在展位不忙的时候去参观,并请每个公司的代表向我解释为什么他们的软件产品比其他产品更好。这是一个诚实的问题,更多的是一种幽默,因为我觉得直接问这些代表,他们大多是统计学家,贬低竞争对手很有趣。一位代表很友好地立刻承认,这些公司都在生产大致相当的产品。这并不是说它们是相等的,只是说没有一个比其他产品明显或绝对更好。对某一产品或另一产品的偏好是一个具体用途和个人喜好的问题。

各家公司核心产品的共同之处在于进行统计分析时具有相似的用户体验。在我看来,每个应用的图形用户界面(GUI)都是基于电子表格的。如果你将典型的电子表格应用屏幕分成几个面板,其中一个面板显示数据,一个显示某些图表或数据的可视化,另一个描述回归分析或成分分析的结果,这就是这些中级统计应用的基本体验。我知道我的描述听起来有些轻视,所以请不要误解,我认为这些应用没有价值——恰恰相反。事实上,我所知道的一切都告诉我,它们非常有用。但软件公司,作为公司,有时会使用夸张的言辞让你认为他们的产品比竞争对手的产品高出一头,并且能迅速解决你的数据问题。尽管我承认我们在当前十年比过去几十年做得好得多,但这几乎从未成真。有一点是真实的:要成功使用统计应用,你必须至少对统计学有所了解。这些应用中没有一个会教你统计学,所以用冷静的头脑和敏锐的眼光去接近它们。通常,如果你想要选择一个,最好的选择就是你的朋友使用的那个,因为如果你遇到问题,他们可以帮你。跟随大众在这里可以是一件好事。

这些中级统计应用的功能远不止于看起来像带有面板的电子表格。它们执行多种不同统计分析的能力通常远超过 Excel 或其他任何电子表格程序。我的印象是,这些软件工具是专门为了实现具有高级复杂度的点选数据分析而构建的,这是电子表格所无法比拟的。如果你想要使用在 Excel 菜单中找不到的统计技术,那么你可能需要升级到上述提到的某个应用。回归分析、优化、成分分析、因子分析、方差分析(ANOVA)以及其他众多统计方法,在中级工具中通常比在电子表格中做得更好,如果后者甚至能实现的话。

除了点击式统计方法之外,这些中级工具通过其关联的编程语言提供了更大的多功能性。我提到的每个专有工具都有自己的语言来进行统计分析,这可以在灵活性和可重复性方面优于点击式操作。有了编程语言,你可以做更多的事情,你还可以保存代码以供将来使用,这样你就可以确切地知道你做了什么,并且可以根据需要重复或修改它。一个包含可运行命令序列的文件通常被称为脚本,这是编程中的一个常见概念。并非所有语言都能做到这一点。脚本语言在数据科学中特别有用,因为数据科学是一个由步骤组成的过程,是对数据进行操作和执行的动作。

学习这些中级工具之一的编程语言,如果这是你的目标,那么这可以是一个很好的步骤,朝着学习真正的编程语言迈进。这些语言本身就可以非常实用。特别是 SAS,在统计行业中拥有广泛的追随者,学习其语言本身就是一个合理的目标。

8.1.3. 适用于大众的数据科学

近年来,关于将数据科学带到分析师那里的话题引起了很大的关注,分析师是能够直接使用从数据科学本身获得智能的人。我完全支持让每个人都能从数据中获得洞察力,但我仍然不认为没有受过统计学训练的人应该应用它们。或许这是统计学家自我偏见的体现,但在面对不确定性的情况下——正如数据科学中始终如此——认识到分析中存在问题需要培训和经验。

话虽如此,在这个信息、数据和软件创业公司盛行的时代,似乎总有人声称他们使数据科学和分析对每个人来说都变得简单,甚至对初学者也是如此,因此你可能不需要雇佣数据科学家。对我来说,这就像说一个包含医学信息的网络汇编可以取代所有医疗专业人员一样。当然,在许多情况下,网络汇编或新的数据科学产品可以提供与专业人员相当甚至更好的结果,但只有专业人员才有知识和经验来检查必要条件,并在特定策略出错时识别出来。我在这本书中强调,面对不确定性时的可能性意识是数据科学的关键方面,我认为这一点永远不会改变。当风险很大时,经验至关重要。

很有诱惑力,想要列举一些新增加的统计应用来展示我的意思。但是,我不确定其中大部分在几年后是否仍然存在,而且除此之外,名字并不重要。在过去十年中,数据科学已经成为一个大生意,并且是一项非常受欢迎的技能。每当一项技能成为新闻,并且有人花钱去获取它时,总会有公司声称能够使它变得容易,成功率各不相同。我挑战你,作为一个数据科学家,去挑战那些声称简化我们工作的软件产品行业的新手。我在写作时试图超越自己的自我:他们是否认为他们可以用软件完成我们可能只能通过仔细考虑手头项目的需求和愿望,再加上软件和统计才能完成的事情?

我虽然跑题了,但确实想分享我对分析软件行业的(我认为是健康的)怀疑态度。如果有人声称有一个针对严重挑战任务的神奇药丸,请保持怀疑态度。现在有许多好的统计软件应用;一个新应用获得巨大优势的可能性极低,但嘿,我之前已经被证明是错的。新产品几乎总是旧主题或目的性很强的工具的变体,它们在某一件事上非常出色。两者都可以是有用的,但确定新事物的真正适用范围可能需要一些努力。

8.2. 编程

当现成的软件工具无法或不能胜任时,你需要自己动手制作。我在上一节提到,最受欢迎的中级统计应用都有自己的编程语言,可以用来任意扩展功能。除了 SAS 及其工具集之外,这些编程语言通常不被视为与其母应用程序无关的语言——这种语言的存在是因为并且与该应用程序并行。

同时也存在相反的情况——为某些独立编程语言构建了基于 GUI 的统计应用。Python 的 iPython 和 R 的 RStudio 都是流行的例子。统计应用的市场可能会有些混乱;我经常看到互联网论坛帖子询问,例如,SPSS 或 R 哪个更好学习。我可以理解,也许 RStudio 的 GUI 和 SPSS 的 GUI 看起来大致相同,但这两个工具的实际功能却大相径庭。两者之间的选择——或者任何中级统计工具和编程语言之间的选择——归结为你是否想要学习和使用编程语言。如果你不想编写任何代码,不要基于 R 或其他编程语言选择任何工具。

编程语言比中级统计应用要灵活得多。任何流行语言的代码都有可能做任何事情。这些语言可以在任何机器上执行任何数量的指令,可以通过 API 与其他软件服务交互,并且可以包含在脚本和其他软件组件中。与父应用程序绑定的语言在这些能力上受到严重限制。

如果你被代码吓到了,编程并没有你想象的那么困难。除了我在高中图形计算器上写的几个简短程序外,我在大学期间的暑期实习之前没有写过任何代码,直到研究生阶段才开始认真编程。我有时希望我早点开始,但事实是,如果你勤奋,掌握一些编程技能并不难。

8.2.1. 编程入门

我在高中的时候就在图形计算器上编写了我的第一个程序,但它们并不复杂。我在大学里唯一上的编程课程叫做面向对象编程;我在这门课程中表现不错,但不知何故,直到在国防部的一个暑期实习期间,我使用 MATLAB 分析了一些图像数据之前,我几乎没有编程。在那之后到研究生期间,我在生物信息学应用中使用 R 和 MATLAB,我逐渐了解了这些语言和编程。我的生活中并没有一个决定学习编程的时刻,而是随着各种项目需要,我学习了编程的各个方面。

我并不一定推荐这种方法来学习。事实上,有很多时候,我缺乏正规训练导致我不得不重新发明轮子,正如人们所说。在开始之前了解可用的语言、工具、规范和资源当然有帮助。但可供选择的东西太多了,如果你对软件工程知之甚少,可能会很难知道从哪里开始。

直到研究生毕业后,我开始在一家软件公司工作,才获得了宝贵的 Java、面向对象编程、几种不同类型的数据库、REST API 以及大多数软件开发者可能已经知道且我发现有用的各种有用的编码规范和良好实践的经验。所有这些都是所谓的现实生活中的数据科学,因此对我学习来说非常有帮助。对于那些几年前像我一样,没有太多纯软件开发经验的人,我会分享我在起步时希望知道的事情。希望这一部分能帮助初学者了解编程的某些方面如何与其他方面相关联,并使他们在使用、讨论和搜索更多相关信息时感到更加自在。

脚本

一个程序可以简单到只是一系列按顺序执行的命令。这样的一组命令通常被称为脚本,编写它的行为脚本编写。在 MATLAB(或开源克隆 GNU Octave)中,一个脚本可以像这样简单:

filename = "timeseries.tsv";
dataMatrix = dlmread(filename);
dataMatrix(2,3)

此脚本中的行将一个名为filename的变量设置为"timeseries.tsv"的值,将包含在filename中的文件加载到名为data-Matrix的变量中,然后在屏幕上打印dataMatrix的第二行和第三列的值。安装 MATLAB 或 Octave 后,初学者编写这样一个简单的脚本几乎没有障碍。timeseries.tsv 文件需要以特定的格式存在——在这种情况下,为制表符分隔值(TSV),以便内置函数dlmread能够正确读取它。您还需要了解dlmread以及语言语法的一些知识,但很容易在网上和其他地方找到好的例子。

我展示这个极其简单的例子的主要观点是,开始编程并不那么困难。如果您有一个电子表格,您可以将其数值表导出为 TSV 或 CSV 格式,并像我展示的那样加载数据,然后您可以通过这样的脚本立即与数据交互,添加或删除命令以完成您想要的工作。

需要注意的一个重要事项是,此类脚本的命令既可以在交互式语言壳中运行,也可以在操作系统的壳中运行。这意味着您可以选择编写一个脚本,然后告诉您的操作系统(具体方法取决于您的操作系统)运行整个脚本,或者打开交互式语言壳,并在该壳中直接输入命令。运行脚本的操作方法通常更便携,而交互式壳的优势在于允许在执行其他命令、编辑和检查时逐行执行,这可能对您有所帮助。例如,假设您忘记了文件 timeseries.tsv 的内容。在交互式壳中(要进入交互式壳,请打开 MATLAB,壳提示立即出现在屏幕上),您可以运行前两条命令来加载文件,然后您可以在提示符下输入dataMatrix并按 Enter 键显示该变量的内容,这些内容是从 timeseries.tsv 加载的。对于小文件,这可能会很方便。另一种选择是在 Excel 中打开文件并查看那里的值,这对某些人来说至少同样吸引人。但假设您想查看包含在 CSV 中的表格的第二百万行。Excel 可能很难加载该文件;截至本文撰写时,没有任何版本的 Excel 可以处理包含两百万行的文件。但 MATLAB 不会有任何问题。要检查第二百万行,请按照前面所示加载文件,然后在交互式壳提示符下输入dataMatrix(2000000,:)

我的大部分早期编程岁月都花在了 MATLAB 和 R 的交互式壳上。我的典型工作流程是这样的:

  1. 将一些数据加载到交互式环境中的一个变量中。

  2. 在数据上玩弄,检查并计算一些结果。

  3. 将步骤 2 中有用的命令复制到文件(脚本)中,以便以后重用。

随着我的命令变得越来越复杂,脚本文件也变得越来越长和复杂。大多数日子里,当我分析数据,尤其是在探索阶段,我最终会得到一个脚本,其中包含恰好能够让我回到当时状态的命令,这样我就可以在以后的日期继续我的工作了。这个脚本会加载到交互式环境中我需要的任何数据集,进行数据转换或计算,并生成图表和结果。

多年来,这就是我编写程序的方式:几乎作为在交互式壳环境中摆弄数据的副作用而创建的脚本。这种随意的编写软件方式并不理想——我将在本节的后面部分回到一些好的编码约定。但我仍然鼓励初学者使用它,至少一开始是这样,因为这种方式很容易开始,而且你可以通过尝试交互式壳中的各种命令来学习很多关于一种语言的知识。

我仍然经常使用脚本,但它们有其局限性和缺点。如果你发现自己无法阅读和理解自己的脚本,因为它们太长或太复杂,那么可能需要使用其他风格或约定。同样,如果你正在将脚本的一部分复制粘贴到另一个脚本中,你可能应该寻找替代方案——当你改变一个脚本中复制部分的某个部分时,但忘记改变同一部分的其他脚本会发生什么?如果一个脚本或一组脚本看起来很复杂或难以管理,那么可能是时候考虑在你的代码中使用函数或对象了;我将在本节的后面部分讨论这些。

从电子表格切换到脚本

Excel 和其他电子表格最常见的功能可以通过使用内置命令如sumsort(这些命令的版本在每个语言中都有)以及使用涉及ifelse的逻辑结构来检查数据是否符合某些条件来在脚本语言中复制。除此之外,每种语言都有数千个函数(就像在 Excel 中一样),你可以在你的命令中使用这些函数。这是一个找出哪个函数能做什么以及它是如何工作的过程。

编程语言处理得很好,而电子表格处理得不好的基本命令类型之一是迭代。迭代是一组步骤的重复,每次可能使用不同的值。假设 timeseries.tsv 是一个数值表,其中每一行代表商店购买的一些物品。第一列给出了项目编号,范围从 1 到某个数字 n,它唯一地标识了购买的是哪个项目。第二列给出了购买物品的数量,第三列给出了支付的总金额。文件可能看起来像这样:

3    1    8.00
12   3    15.00
7    2    12.50

但如果商店经营成功,可能更长。

假设您想获取项目 12 的总销售数量,而文件有数千行。在 Excel 中,您可以按项目编号排序,滚动到项目 12 的行,然后使用 SUM 公式将数量相加。这可能是最快的方法,但需要相当多的手动工作(排序、滚动和求和),尤其是如果您还预计要对项目 12 之后的其它项目也这样做。在 Excel 中还有另一种选择,即创建一个新的第四列,并在该列的每个单元格中使用 IF 公式来测试第一列是否等于 12,结果为 1 或 0(对于真或假),然后逐行乘以第二列和第四列,最后将所有行乘积相加。这也是一个相当快速的选择,但如果您还想查看其他项目的总和,您必须巧妙地使用公式,并确保将项目编号放在一个单独的单元格中——并在第四列的每个公式中引用它——这样您就只需在一个地方更改项目编号,以获取其总数量。

如果您只想获取一个或几个项目的数量,那么上述任何一种 Excel 解决方案都可以工作,但如果您需要所有项目编号的总数量呢?排序策略会变得相当繁琐,创建额外列也是如此,无论您是创建了一个额外的列然后更改项目编号,还是为每个项目编号创建了一个新列。在 Excel 中,可能有一个更好的解决方案,涉及条件查找或搜索值,但我一时想不出来。如果您对 Excel 非常熟悉,您可能比我更清楚如何解决这个问题。我试图说明的是,这种类型的问题在大多数编程语言中都很容易解决。例如,在 MATLAB 中,在将文件读取到前面显示的变量 dataMatrix 之后,您只需编写以下代码:

[ nrows, ncolumns ] = size(dataMatrix);
totalQuantities = zeros(1,1000)
for i in 1:nrows
  totalQuantities( dataMatrix(i,1) ) += dataMatrix(i,2);
end

此代码首先找到dataMatrix中的行数nrows,然后初始化一个包含 1000 个值的向量totalQuantities,从零开始,将数量添加到这个向量中。通过命令total-Quantities(k)访问totalQuantities中的第 k 个条目,将给出第 k 个商品的总销售量。如果商品编号超过 1000,你需要使这个向量更长。for循环,通常称为forend之间的代码结构类型,遍历dataMatrix的行(在每次迭代中,当前行由变量i给出),将dataMatrix中第 i 行第 2 列的值添加到totalQuantities中适当的位置,该位置由dataMatrix中第 i 行第 1 列的值给出。

对于那些之前没有编写过代码的人来说,这个例子可能比使用 Excel 或其他他们所知道的工具要复杂。但我认为应该很清楚,几乎任何人都可以通过脚本语言和交互式 shell 开始,并且只需要一点知识就能产生结果。

函数

我之前提到过,脚本有时可能会变得很长、复杂,难以管理。你也可能拥有多个包含一些共同步骤或命令的脚本——例如,如果不同的脚本需要以相同的方式加载数据,或者不同的脚本加载数据但以相同的方式处理数据集。如果你发现自己正在从一个文件复制粘贴代码到另一个文件,并且打算继续使用这两个文件,那么是时候考虑创建一个函数了。

假设你需要将之前的数据文件 timeseries.tsv 用于多个脚本。你可以在 MATLAB 中通过创建一个名为 loadData.m 的文件并包含以下内容来创建一个函数:

function [data] = loadData()
  filename = "timeseries.tsv";
  data = dlmread(filename);
end

然后你可以在脚本中包含以下行

dataMatrix = loadData()

为了将数据从 timeseries.tsv 加载到变量dataMatrix中,就像之前一样。在我的脚本中,我只是将两行替换为一行,但如果你更改文件名或其位置等其他事情,现在你只需要在函数中做,而不是在多个脚本中做。当你有超过两行代码需要在脚本之间共享时,这也变得更有用。

函数,按照设计,就像数学函数一样,可以接受输入并给出输出。在前面的例子中,没有输入,但有一个输出,变量data;它在函数文件的第一行指定为输出。在脚本内部,函数也不会改变任何变量,除了函数输出分配到的变量之外——在这个例子中是dataMatrix。因此,使用函数是隔离代码以在脚本之间重用的一种好方法。

函数对于理解代码的功能也是很好的。在你的脚本中,当你大多数时间并不关心数据是如何被加载的,只要它能正确工作,阅读和理解函数调用loadData()就很容易。如果加载数据包含许多步骤,将这些步骤推入函数将大大提高脚本该部分的可读性。如果你脚本中有许多块代码协同工作以实现单一目的,比如加载数据,那么你可能希望将每个块推入它自己的函数,并将你的脚本转换为一串命名良好的函数调用。这样做通常对眼睛来说更容易。

为了参考,函数式编程是一种相对流行的编程范式——与面向对象编程相对——其中函数是一等公民;函数式程序强调函数的创建和操作,甚至包括匿名函数和将函数视为变量的处理。关于函数式编程,你需要知道的主要一点是,在严格的范式下,函数没有副作用。如果你调用一个函数,有输入和输出,并且不会影响调用环境的其他变量。就像函数的内部工作发生在某个完全独立的环境中一样,直到函数返回其输出。

无论你是否关心函数式范式的具体细节及其理论影响,函数本身对于封装通常连贯的代码块是有用的。

面向对象编程

函数和函数式编程可以说关注的是动作,而面向对象编程可以说关注的是事物,但这些事物也可以执行动作。在面向对象编程中,对象是可能包含数据和自身类似函数的指令,即方法的实体。一个对象通常有一个连贯的目的,就像函数一样,但它的行为却不同。

例如,你可以创建一个名为DataLoader的对象类,并使用它来加载数据。我在 MATLAB 中并没有使用很多面向对象的功能,所以在这里我将切换到 Python;本章后面会详细介绍 Python。在 Python 中,名为 dataLoader.py 的类文件可能看起来像这样:

import csv

class DataLoader:

  def __init__(self,filename):
    self.filename = filename

  def load(self):
    self.data = []
    with open(self.filename,'rb') as file:
      for row in csv.reader(file,delimiter='\t'):
        self.data.append(row)

  def getData(self):
    return self.data

第一行导入了用于从文件加载数据的csv包。之后,你看到单词class和类的名称DataLoader。在类内部,缩进处有三个方法定义。

每个方法的第一个输入是变量self,它代表DataLoader对象本身的这个实例。方法需要能够引用对象本身,因为方法能够设置和改变对象的属性,或状态,这是面向对象编程的一个重要方面,我将举例说明。

定义的第一个方法 __init__ 是在创建对象或实例化时被调用的方法。任何对于对象存在和功能至关重要的属性都应该在这里设置。在 DataLoader 的情况下,__init__ 方法接受一个名为 filename 的参数,这是对象将从其中加载数据的文件名。当在脚本中通过命令如

dl = DataLoader('timeseries.tsv')

单个输入参数被传递给 __init__ 方法供对象使用。__init__ 的定义显示 __init__ 将这个输入参数分配给对象属性 self.filename。在 Python 中,就是这样设置和访问对象属性的。通过 __init__ 方法实例化对象后,现在在脚本中被称为 dl 的对象(或在交互式外壳环境中),其 self.filename 属性被设置为 timeseries.tsv,但对象中不包含任何数据。

要从命名的文件中加载数据到对象中,你必须使用 load 方法通过命令

dl.load()

根据方法定义,这将为数据创建一个空列表属性 self.data,打开名为 self.filename 的文件,然后将数据逐行加载到 self.data 中。在此方法执行后,现在所有数据都已加载到名为 dlDataLoader 对象的 self.data 属性中。

如果你想访问并处理数据,可以使用定义的第三个方法 getData,它使用 return 语句将数据作为输出传递给调用脚本,就像一个函数一样。

根据对象类定义,一个加载并获取数据以供使用的脚本可能看起来如下:

from dataLoader import DataLoader

dl = DataLoader('timeseries.tsv')
dl.load()
dataList = dl.getData()

尽管这实现了与我在 MATLAB 或使用函数的原脚本相同的功能,但在这个脚本中,使用对象实现的方式在本质上是有区别的。在导入类定义之后发生的第一件事是创建一个名为 dlDataLoader 对象。在 dl 中有一个独立的环境,其中保存了对象的属性,从概念上讲,这些属性在主脚本之外。函数没有属性,因此是无状态的,而对象的属性——其状态——可以在方法调用期间被用来影响方法的结果或输出。对象及其方法可以有副作用。

副作用和保留状态属性可能具有很大的优势,但如果不小心,也可能很危险。严格来说,函数不应该改变输入变量的值,但对象及其方法可以这样做。在某些情况下,我构建了具有以某种方便计算方法输出的方式操作输入变量的方法的对象。在我学会更加小心之前,我多次没有意识到,在方法内部操作输入值时,我实际上也在影响方法或对象之外,在调用脚本或环境中的值。数学家和其他具有函数性思维的人并不总是首先考虑那种意外的副作用。

使用对象的一个重大优势是,对象提供了一种很好的方式,将大量紧密相关的数据和函数聚集到一个单一的自包含实体中。在加载数据的情况下,主脚本可能并不关心数据来自哪里或如何加载。在这种情况下,当某些变量或值组之间从未直接交互时,根据面向对象范式,它们可能应该以某种方式相互隔离,并将它们包含在自己的对象中可能是一个实现这一目标的良好选择。同样,任何几乎仅与这样一组变量打交道的函数可能都应该转换为包含这些变量的对象中的对象方法。

将程序数据、属性、函数和方法有意地分离成目的性统一的对象的主要优势主要在于可读性、可扩展性和可维护性。就像编写良好的函数一样,构建良好的对象使得代码更容易理解,更容易扩展或修改,也更容易调试。

在函数式编程和面向对象编程之间,没有一个绝对优于另一个。每个都有其优点和缺点,你通常可以从这两个范式中学到东西,以编写最适合你目的的代码。然而,重要的是要小心处理对象的状态,因为如果你混合了函数和对象,可能会很容易将对象方法当作函数来处理,而它具有副作用。

我想要指出的一点是:在本节中,我谈论调用脚本或主脚本时,好像总有一个主脚本在使用一组函数和对象。这并不总是情况。我这样写是为了初学者的清晰,但在实践中,函数可以调用函数并使用对象,对象可以调用函数并使用对象,甚至可能根本不存在脚本,这是下一节的主题。

应用

在这个背景下,我使用术语应用程序脚本相对。正如我之前讨论的,脚本是一系列要执行的命令。我使用“应用程序”一词来表示当启动或打开时,可以准备就绪并执行某些任务或动作以供用户使用的东西。从这种意义上说,应用程序在概念上与编程中的对象相似,因为对象可以被创建,然后由创建它的任何东西以各种方式使用。另一方面,脚本更类似于函数,因为它只是以直接的方式完成一系列动作。

电子表格是应用程序,网站和移动设备上的应用程序也是如此。它们都是我之前描述的那种应用程序,因为当它们启动时,它们会初始化,但可能不会做很多事情,直到用户与之交互。应用程序的主要用途在于与用户的这种交互,与脚本不同,脚本通常会产生一个有形的结果,提供一些实用性。

在这里提供足够的信息以帮助您开始应用程序开发可能会有点困难。它比脚本编写稍微复杂一些,所以我将指出一些可能对数据科学家有用的概念。更多信息,您可以在互联网上找到大量的参考资料和示例。

应用程序开发对数据科学家变得有用的主要原因有时向客户交付静态报告是不够的。我经常向客户交付我认为相当详尽的报告,但客户却要求在某些点上提供更多细节。当然,我可以向客户提供特定领域的更多细节,也许在额外的报告中,但必须在我自己回到数据中提取这些细节之后。我可能做得更好,提供一段软件,让客户能够深入探究他们感兴趣的每个报告方面。我可能通过设置数据库和 Web 应用程序来实现这一点,允许客户通过标准 Web 浏览器点击浏览结果和数据的良好表示。这正是大多数分析软件公司今天交付产品的方式。所有分析都在幕后进行,而 Web 应用程序以比报告更友好、更有用的方式交付结果。

数据科学家还创建应用程序,这些应用程序可以实时或以其他方式交互式地消耗和分析数据。Google、Twitter、Facebook 以及许多其他网站使用相当复杂的方法进行数据分析,这些方法通过应用程序持续地发送到网络上。热门话题、头条新闻和搜索结果都是数据科学密集型应用程序的产物。

但是,正如我所说的,创建一个应用程序并不像编写脚本那样简单,所以我会节省空间,在这里跳过细节,并建议你查阅比我更有信息的人写的参考资料。但是,如果你对此感兴趣并且已经有一些编程语言的经验,可以查看这些在数据友好型语言中构建 Web 应用程序的开源框架:

  • Flask for Python

  • Shiny for R

  • Node.js for JavaScript,加上 D3.js 用于惊人的数据驱动图形

8.2.2. 语言

现在我将介绍三种我用于数据科学和相关编程任务的脚本语言,以便比较和对比它们,而不是详细描述它们。这些语言是 GNU Octave(MATLAB 的开源克隆)、R 和 Python。对于这些语言,我给出示例代码,每个案例都使用 8.2.1 节 中的商品销售数量示例以及我描述的相同格式的 timeseries.tsv 数据文件。我定义了一个用于加载数据的函数,然后脚本统计所有商品(最多 1000 件)的销售数量,并将商品 12 的总销售数量打印到屏幕上。

在讨论了三种脚本语言之后,我简要地谈到了一种不是脚本语言的语言(因此不可能提供一个示例脚本),但它在软件开发和数据分析方面都足够重要,我认为我应该提到它。这种语言是 Java。

MATLAB 和 Octave

MATLAB 是一种专有软件环境和编程语言,擅长处理矩阵运算。根据本文写作时的价格,MATLAB 的成本相当高——单软件许可证就超过 2000 美元——但对于学生和其他大学相关人士有显著的折扣。有些人决定在一个名为 Octave 的开源项目中复制它。随着 Octave 的成熟,它在可用功能和能力上越来越接近 MATLAB。除了使用附加包(即工具箱)的代码外,大多数用 MATLAB 编写的代码在 Octave 中也能运行,反之亦然,这对于那些拥有 MATLAB 代码但没有许可证的人来说是个好消息。根据我的经验,你可能需要更改一些函数调用以实现兼容性,但不必很多。我曾经在我的 MATLAB 代码中找到大约 10 行存在不兼容性的代码后,让几百行代码在 Octave 中运行。

当不使用附加包时,几乎完全兼容性非常好,但 Octave 在性能方面有所不足。从我在互联网上收集的信息来看,MATLAB 在数值运算方面可能比 Octave 快两到三倍,这从我自己的经验来看似乎是合理的,尽管我没有进行过直接比较。因为 MATLAB 和 Octave 都是为了矩阵和向量运算而设计的,所以如果你想充分利用语言的效率,你必须编写向量化的代码。显然,如果你有应该向量化但未向量化的代码,例如使用for循环来乘矩阵,MATLAB 在识别正在发生的事情并编译代码方面做得更好,这样代码的执行速度几乎与显式向量化一样快。Octave 可能还做不到这一点。无论如何,利用向量化代码的效率来处理向量和矩阵始终会使你的代码更快,有时甚至非常显著。这在 MATLAB、Octave、R 和 Python 等其他语言中都是如此。

在 MATLAB 和 Octave 中编写向量化的代码对于熟悉矩阵运算的人来说非常容易,因为代码看起来与等价的数学表达式完全一样。这在其他我讨论的语言中并不成立。例如,如果你有两个兼容维度的矩阵AB,按照标准方式(非逐元素)相乘可以通过编写以下代码来完成

A * B

而对于维度相同的矩阵AB的逐元素乘积是通过以下方式实现的

A .* B

这两个操作都是向量化的。请注意,如果数学表达式需要转置矩阵或向量,你同样需要在代码中转置它,否则可能会得到错误或错误的结果。转置运算符是矩阵后面的单个引号,如A'

作为参考,这些非向量化的等价操作,矩阵乘法可能至少涉及两个嵌套的for循环,遍历矩阵的行和列。向量化的版本既容易编写,执行速度也更快,因此尽可能进行向量化是最佳选择。R 和 Python 也可以使用向量化,但那些语言中矩阵乘法的默认方式是逐元素版本,尽管提供了标准矩阵乘法的替代方案。

作为对 MATLAB 和 Octave 语法的稍作介绍,你可以通过创建一个名为 loadData.m 的文件并编写以下行来实现项目销售数量的示例:

function [data] = loadData()
  data = dlmread("timeseries.tsv");
end

在同一目录下,创建一个名为 itemSalesScript.m 的脚本文件,包含以下行:

dataMatrix = loadData();
[ nrows, ncolumns ] = size(dataMatrix);
totalQuantities = zeros(1,1000);
for i = 1:nrows
  totalQuantities( dataMatrix(i,1) ) += dataMatrix(i,2);
end

totalQuantities(12)

你可以通过以下命令在 Unix/Linux/Mac OS 命令行中从 Octave 运行此脚本

user$ octave itemSalesScript.m

输出到命令行的结果应该是你数据文件中出现的项目 12 的总销售量。要在 MATLAB 或 Octave 提示符下运行相同的代码,请将 itemSalesScript.m 文件的内容复制并粘贴到提示符中,然后按 Enter 键。

在以下情况下,我会使用 MATLAB 或 Octave:

  • 如果我在处理大型矩阵或大量矩阵。

  • 如果我知道某个特定的附加包,特别是在 MATLAB 中,将会非常有用。

  • 如果我有 MATLAB 许可证并且喜欢矩阵友好的语法。

在以下情况下,我不会使用 MATLAB 或 Octave:

  • 如果我的数据不适合用表格或矩阵表示。

  • 如果我想让我的代码与其他软件集成;由于 MATLAB 的应用范围相对较窄,这可能会很困难且复杂,尽管许多类型的集成都是可能的。

  • 如果我想将我的代码包含在要出售的软件产品中。特别是 MATLAB 的许可证可能会在法律上使这变得困难。

总体来说,MATLAB 和 Octave 对于工程师(尤其是电气工程师)来说非常出色,他们需要在信号处理、通信、图像处理和优化等领域处理大型矩阵。

要查看我几年前的一些 Octave 代码(曾经从 MATLAB 转移过来),请参阅我在 GitHub 上的生物信息学项目,关于基因互作的项目github.com/briangodsey/bacon-for-gene-networks。代码相当杂乱,但请不要因为过去的错误而责怪我。从某种意义上说,那段代码以及我的其他生物信息学项目代表了我当时混合脚本和函数编码风格的一种快照。

R

我对 R 的第一个笔记:如果你想在网上搜索帮助,在搜索框中输入字母 R 可能会带你到一些有趣的地方,尽管搜索引擎每天都在变得更聪明。如果你难以获得良好的搜索结果,试着在框中也输入缩写 CRAN;CRAN 是综合 R 存档网络,可以帮助 Google(和其他搜索引擎)将你引导到适当的网站。

R 基于在贝尔实验室创建的 S 编程语言。它是开源的,但其许可证比 Python 和 Java 等一些其他流行语言更为严格,尤其是如果你正在构建商业软件产品的话。

R 与我介绍的其它语言有一些独特之处和差异。它通常使用符号 <- 来给变量赋值,尽管后来添加了等号 = 作为替代,以方便那些更喜欢它的用户。与 MATLAB 相比,R 使用方括号而不是圆括号来索引列表或矩阵,但 MATLAB 在这里是个例外;大多数语言使用方括号进行索引。而 MATLAB 和 Python 都允许创建以方括号开始的列表、向量或矩阵等对象,但 R 不允许这样做。例如,在 MATLAB 和 Python 中,你可以使用赋值 A = [ 2 3 ] 来创建包含数字 2 和 3 的向量/列表,但在 R 中,你需要使用 A <- c(2,3) 来完成类似的事情。这不是一个很大的差异,但如果我有一段时间没有使用 R,我可能会忘记这一点。

与 MATLAB 相比,在 R 中加载和处理不同类型的数据更容易。MATLAB 擅长处理表格数据,但一般来说,R 在处理带有标题的表格、混合列类型(整数、小数、字符串等)、JSON 和数据库查询方面表现更佳。我并不是说 MATLAB 不能处理这些,但它在实现上通常更为有限或困难。此外,在读取表格数据时,R 通常会默认返回一个数据框对象。数据框是包含列数据的灵活对象,其中每一列可以具有不同的数据类型——例如,数值、字符串,甚至是矩阵——但每一列中的所有条目必须是相同的。一开始使用数据框可能会感到困惑,但它们的灵活性和强大功能在一段时间后就会变得明显。

R 作为开源软件的一个优点是,开发者可以在他们认为合适的地方更容易地贡献于语言和包的开发。这些开源贡献极大地帮助了 R 的成长并扩展了它与其它软件工具的兼容性。CRAN 网站上提供了数千个 R 包。我认为这是 R 语言最伟大的单一优势;你很可能找到一个包来帮助你执行你想要进行的分析,这样一些工作就已经为你完成了。MATLAB 也有包,但数量远不及 R,尽管它们通常非常好。R 有好的也有不好的,以及介于两者之间的。你还会在公共仓库中找到大量的 R 代码,这些代码可能尚未成为官方包,但它们是免费可用的。

在我多年的生物信息学研究生涯中,R 是我同事以及我们所在其他机构的同行最常用的语言。大多数开发新生物信息学统计方法的科研团队都会创建一个 R 包,或者至少将他们的代码放在某个地方,就像我在我的一个项目 PEACOAT 中做的那样,该项目在 GitHub 上github.com/briangodsey/peacoat

你可以通过创建包含以下代码的文件 itemSalesScript.R 在 R 中实现商品销售数量示例:

loadData <- function() {
  data <- read.delim('timeseries.tsv',header=FALSE)
  return(data)
}

data <- loadData()
nrows <- nrow(data)
totalQuantities <- rep(0,1000)
for( i in 1:nrows ) {
  totalQuantities[data[i,1]] <- totalQuantities[data[i,1]] + data[i,2]
}

totalQuantities[12]

你可以通过以下命令在 Unix/Linux/Mac OS 命令行中运行此脚本:

user$ Rscript itemSalesScript.R

或者,从 R 环境的 shell 提示符中,将 itemSalesScript.R 的内容复制并粘贴到 shell 中,然后按 Enter 键。

除了 MATLAB 和 R 之间的语法和函数名变化之外,你可能会注意到它们的基本结构是相同的。R 使用花括号{}来定义函数和for循环,而 MATLAB 则使用end命令来表示代码块的结束。函数还使用显式的return语句,这在 MATLAB 中是不存在的。

我会在以下情况下使用 R:

  • 如果我在一个有很多 R 包的领域工作。

  • 如果我在学术界工作,尤其是生物信息学或社会科学领域。

  • 如果我想快速加载、解析和处理各种数据集。

在这些情况下,我不会使用 R:

  • 如果我在创建生产软件。

  • 如果我在创建要出售的软件。GPL 许可证有影响。

  • 如果我想将我的代码集成到其他语言的软件中。

  • 如果我想使用面向对象架构。在 R 中这并不出色。

总体而言,R 是统计学家和其他那些更倾向于进行数据密集型、探索性工作而不是构建生产软件(例如,在分析软件行业中)的人的好选择。

Python

首先也是最重要的,Python 是这三种脚本语言中唯一一个不是旨在主要作为统计语言的。在这方面,它更自然地适用于非统计任务,如与其他软件服务的集成、创建 API 和网络服务以及构建应用程序。Python 也是这三种语言中唯一一个我会认真考虑用于创建生产软件的语言,尽管在这方面 Python 仍然不及 Java,我将在下一部分讨论这一点。

Python,像任何语言一样,有其独特之处。最明显的一点是它没有大括号来表示代码块,甚至没有像 MATLAB 中的 end 命令来表示 for 循环或函数定义何时结束。Python 使用缩进来表示这样的代码块,这对许多程序员来说是一种永恒的痛苦。通常,这样的代码块会缩进,但 Python 是唯一一个强制你这样做,并且在其中它是最受欢迎的。简而言之,如果你想结束这样的代码块,而不是像在 MATLAB 中那样输入 end,或者像 R、Java 和许多其他语言那样使用闭合大括号 },你只需停止缩进你的代码。同样,你必须立即在 for 命令或包含 def 的函数定义行之后缩进你的代码。否则,在执行过程中你会得到错误。

很可能是因为 Python 最初是一种通用编程语言,它拥有一个强大的面向对象设计的框架。相比之下,R 和 MATLAB 的面向对象特性似乎是一种事后补充。我已经开始喜欢面向对象设计,即使是对于简单的任务,因此我经常使用这个特性,因为 Python 在过去几年中已经成为我的主要编程语言。

虽然 Python 最初并不是一个旨在高度统计的语言,但已经为 Python 开发了几个包,使其能够与 R 和 MATLAB 竞争。在处理向量、数组和矩阵时,numpy 包对于数值方法来说是必不可少的。scipyscikit-learn 包在优化、积分、聚类、回归、分类和机器学习等其他技术中增加了功能。有了这三个包,Python 在核心功能上与 R 和 MATLAB 相当,在某些领域,如机器学习,Python 似乎在数据科学家中更为流行。

对于数据处理,pandas 包已经变得极其流行。它在某种程度上受到了 R 中的数据框概念的影响,但在功能上已经超越了它。诚然,我在第一次尝试 pandas 时有些困难,但经过一些练习,它变得非常方便。我的印象是,pandas 数据框作为内存中、Python 优化的数据存储。如果你的数据集足够大以至于会减慢计算速度,但足够小以至于可以适应你的计算机内存,那么 pandas 可能适合你。

然而,在数据科学中最著名的 Python 包之一是自然语言工具包(NLTK)。它无疑是自然语言处理(NLP)中最受欢迎和最健壮的工具。如今,如果有人从 Twitter、新闻源、Enron 邮件语料库或其他地方解析和分析文本,他们很可能会使用 NLTK 来这样做。它利用其他 NLP 工具,如 WordNet 和各种标记化和词干提取方法,提供了一站式解决方案,集成了最全面的 NLP 功能。

对于核心功能,一个名为 itemSalesScript.py 的文件中用 Python 编写的商品销售数量示例可能看起来像这样:

import csv

def loadData():
  data = []
  with open('timeseries.tsv','rb') as file:
    for row in csv.reader(file,delimiter='\t'):
      data.append(row)
  return data

dataList = loadData()
nrows = len(dataList)
totalQuantities = [0] * 1000
for i in range(nrows):
  totalQuantities[ int(dataList[i][0]) ] += int(dataList[i][1])

print totalQuantities[12]

您可以通过 Unix/Linux/Mac OS 命令行中的命令运行此脚本

user$ python itemSalesScript.py

或者将文件内容复制粘贴到 Python 提示符中并按 Enter 键。

注意缩进是如何用来表示函数定义和 for 循环的结束。还要注意,像 R 一样,Python 使用方括号从列表/向量中选择项目,但 Python 使用基于零的索引系统。要在 Python 中获取名为 dataList 的列表中的第一个项目,您将使用 dataList[0] 而不是在 R 或 MATLAB 中使用的 dataList[1]dataList(1)。这在我学习 Python 时让我困惑了好几次,所以请注意这一点。然而,大多数软件开发人员已经习惯了 Java 和 C 等语言中的基于零的索引,所以他们更有可能被 R 和 MATLAB 而不是 Python 搅乱。

关于代码示例的最后一句话:在两个地方我不得不使用 int 函数将给定的字符串值强制转换为整数。这是因为 csv 包默认将所有值视为字符串,除非告知否则。当然,肯定有比我在这里使用的方法更好的处理方式,其中之一就是使用 numpy 包将数据转换为数组,如果我在更深入地处理数据时,我通常会这样做,但为了示例的清晰性,我将其省略了。

我会在以下情况下使用 Python:

  • 如果我在创建分析软件应用、原型或可能的生产软件。

  • 如果我在做机器学习或自然语言处理(NLP)。

  • 如果我要与其他软件服务或应用集成。

  • 如果我在做大量的非统计编程。

在这些情况下,我不会使用 Python:

  • 如果我在一个大多数人使用其他语言并共享其代码的领域工作。

  • 如果我的领域中的 Python 包不如其他语言,如 R。

  • 如果我想快速轻松地生成图表和绘图。R 的绘图包要好得多。

我提到过,Python 现在我的首选语言,这是在我几年前从 R 转换过来之后。我做出这个转换是因为我一直在编写生产专有软件,这涉及到大量的非统计代码,对于这些代码,我发现 Python 非常出色。Python 的许可证允许自由销售软件,无需提供源代码。总的来说,我推荐 Python 给那些想要进行一些数据科学以及一些其他纯非统计软件开发的人。它是唯一我知道的既流行又健壮的语言,可以很好地完成这两项任务。

Java

虽然 Java 不是脚本语言,因此不适合探索性数据分析,但它是最突出的软件开发语言之一,因此它经常用于分析应用程序开发。使 Java 对探索性数据分析不利的许多原因也使其适用于应用程序开发。

首先,Java 具有强大的静态变量类型,这意味着在创建变量时你必须声明该变量的类型,并且它永远不能改变。Java 对象也有许多不同类型的方法——公共的、私有的、静态的、最终的等等——选择适当类型可以确保方法被正确且仅在合适的时间使用。变量作用域和对象继承规则也非常严格,至少与 Python 和 R 相比是这样。所有这些严格的规则使得编写代码的速度变慢,但结果的应用程序通常更加健壮,并且远少出错。我有时希望我能对我的 Python 代码施加一些这些限制,因为每隔一段时间,一个特别棘手的错误就可以追溯到我所做的一个愚蠢的事情,而这本可以通过 Java 的严格规则来预防。

Java 对于探索性数据分析来说并不出色,但它可以非常适合基于数据科学的大规模或生产代码。Java 拥有众多统计库,可以用于从优化到机器学习的各种操作。其中许多都是由 Apache 软件基金会提供和支持的。

在以下情况下,我会使用 Java:

  • 如果我正在创建一个需要非常健壮和可移植的应用程序。

  • 如果我已经熟悉 Java,并且知道它具有我需要的功能。

  • 如果我正在一个主要使用 Java 的团队中工作,使用其他语言会对整体开发工作造成困难。

在以下情况下,我不会使用 Java:

  • 如果我正在做大量的探索性数据分析。

  • 如果我对 Java 不太了解。

  • 如果我不需要一个真正健壮且可移植的应用程序。

虽然我没有提供很多关于 Java 的细节,但我确实想传达这种语言在数据科学相关应用中的流行度,并且可以说,对于我所知道的绝大多数经验丰富的开发者来说,它将是他们尝试构建一个坚不可摧的分析软件的首选。

表 8.1 总结了我在数据科学项目中使用每种编程语言的情况。

表 8.1. 总结了我在数据科学项目中使用每种编程语言的情况
语言 我会使用它的时候 我不会使用它的时候
MATLAB/Octave 如果我在处理大型矩阵或大量矩阵时。如果我知道某个特定的附加包,尤其是在 MATLAB 中,将非常有用。如果我有 MATLAB 许可证,并且我喜欢矩阵友好的语法。 如果我的数据不适合用表格或矩阵表示。如果我想让我的代码与其他软件集成;这可能很困难且复杂,尽管有各种选项。如果我想将我的代码包含在要出售的软件产品中。MATLAB 的许可证在法律上可能会使这变得困难。
R 如果我在一个有很多 R 包的领域工作。如果我在学术界工作,尤其是生物信息学或社会科学。如果我想快速加载、解析和操作各种数据集。 如果我在创建生产软件。如果我在创建要出售的软件。GPL 许可证有影响。如果我想将我的代码集成到其他语言的软件中。如果我想使用面向对象架构。在 R 中并不出色。
Python 如果我在创建一个分析软件应用程序、原型或可能的生产软件时。如果我在进行机器学习或 NLP。如果我要与其他软件服务或应用程序集成。如果我在进行大量的非统计编程。 如果我在一个大多数人使用另一种语言并共享他们的代码的领域工作。如果 Python 在我的领域中的包不如 R 等其他语言中的包。如果我想快速轻松地生成图表和绘图。R 的绘图包要好得多。
Java 如果我在创建一个需要非常健壮和可移植的应用程序时。如果我已经熟悉 Java,我知道它具有我需要的功能。如果我在一个主要使用 Java 的团队中工作,使用另一种语言会对整体开发工作造成困难。 如果我在进行大量的探索性数据科学。如果我对 Java 不太了解。如果我不需要一个真正健壮、可移植的应用程序。

8.3. 选择统计软件工具

到目前为止,在本章中,我已经讨论了一些统计应用和编程的基础知识,并希望我已经给你一个关于可用于实现上一章所讨论的统计方法的工具范围的清晰概念。如果那一章达到了其目的,你将你的项目和你的数据与一些适当的数学或统计方法或模型相关联。如果是这样,你可以将这些方法或模型与可用于实现它们的软件选项进行比较,从而得出一个或两个好的选择。在选择软件工具时,有许多事情需要考虑,以及一些一般规则需要遵循。我将在下面概述这些内容。

8.3.1. 工具是否有这些方法的实现?

当然,你可以自己编写这些方法,但如果你使用的是一个相当常见的方法,那么许多工具可能已经实现了这些方法,使用其中之一可能更好。已经被许多人使用过的代码通常比你在一天内编写的只使用一次或两次的代码相对较少错误。

根据你编程的能力以及你对各种统计工具的熟悉程度,你可能在你的一个喜欢的工具中已经有一个现成的实现,可以快速投入使用。如果 Excel 有,那么其他大多数工具也可能有。如果 Excel 没有,那么可能是中级工具有,如果没有,那么你可能需要编写一个程序。否则,唯一剩下的选择是选择不同的统计方法。

如果你决定使用编程语言,请记住,并非所有包或库都是平等的,所以请确保你打算使用的编程语言和包可以做到你想要的一切。阅读文档或一些与你想要进行的分析相对相似的示例可能会有所帮助。

8.3.2. 灵活性是好的。

除了能够执行你想要的主要统计分析之外,如果统计工具能够执行一些相关方法,这通常是有帮助的。通常你会发现,你选择的方法并不像你希望的那样有效,你在过程中学到的知识让你相信另一种方法可能效果更好。如果你的软件工具没有其他替代方案,那么你可能只能坚持第一个选择,或者你必须切换到另一个工具。

例如,如果你有一个统计模型并且想要找到最优的参数值,你将使用似然函数和优化技术。在第七章中,我概述了几种从似然函数中寻找最优参数的方法,包括最大似然(ML)、最大后验(MAP)、期望最大化(EM)和变分贝叶斯(VB)。尽管 Excel 有一些不同的特定优化算法,但它们都是 ML 方法,所以如果你认为你可以用 ML 解决问题但又不确定,你可能想要升级到一个更复杂的统计工具,它有更多的优化选项。

在回归、聚类、成分分析、机器学习等多种类型中,有些工具可能提供其中一种或多种方法。我倾向于偏好那些提供每个方法类别中几种统计工具的工具,以防我需要切换或尝试另一种。

8.3.3. 信息丰富是好的

我强调,面对不确定性时的意识是数据科学的一个主要方面;这一点也体现在统计软件工具的选择上。有些工具可能给出好的结果,但并不提供关于如何以及为什么得到这些结果的认识。一方面,能够分解方法和模型以便更好地理解模型和系统是很好的。另一方面,如果你的方法在某些方面犯了错误,而你发现自己面对一个奇怪、意外的结果,那么关于方法和其在你数据中的应用的更多信息可以帮助你诊断具体问题。

一些统计工具,尤其是像统计编程语言这样的高级工具,提供了查看几乎所有统计方法和结果的能力,甚至包括像机器学习这样的黑盒方法。这些内部信息并不总是用户友好的,但至少是可用的。我的经验是,像 Excel 这样的电子表格软件并不提供对其方法的太多洞察,因此很难分解或诊断比线性回归更复杂的统计模型的问题。

8.3.4. 常见是好的

在生活中,许多事物——如音乐、电视、电影、新闻文章——的流行并不总是意味着质量,实际上往往相反。在软件领域,使用软件的人数越多,意味着尝试过它的人越多,得到了结果,检查了结果,并且可能报告了他们遇到的问题(如果有的话)。以这种方式,软件,尤其是开源软件,有一个反馈循环,可以合理及时地修复错误和问题。参与这个反馈循环的人越多,软件相对无错误和稳健的可能性就越大。

这并不是说现在最受欢迎的东西就是最好的。软件就像其他一切一样,会经历趋势和时尚。我倾向于查看过去几年与我处于相似情况的人的使用流行度。在统计工具的一般流行度竞赛中,Excel 显然会获胜。但如果你只考虑数据科学家,也许只考虑特定领域的数据科学家——排除会计、金融专业人士和其他半统计用户——你可能会看到它的流行度在更严肃的统计工具面前减弱。

如果我要使用一个工具,它必须满足以下标准:

  • 工具至少要有几年的历史。

  • 工具必须由一个信誉良好的组织维护。

  • 论坛、博客和文献必须表明很多人已经使用这个工具很长时间,并且最近没有遇到很多重大问题。

8.3.5. 文档详尽是好事

除了普遍使用外,一个统计软件工具应该有全面且有帮助的文档。当我试图使用某个软件时,如果遇到一个我觉得应该有直接答案的问题,但找不到答案,这非常令人沮丧。

如果你找不到一些重要问题的答案,比如如何配置输入进行线性回归或如何格式化特征以进行机器学习,那么这可不是什么好兆头。如果重要问题的答案不在文档中,那么你以后遇到更具体的问题时找到答案将会更加困难。

文档通常是软件的年龄和流行度的函数。工具的官方文档应该放在维护组织的网页上,并且应该包含用通俗易懂的语言编写的说明和规范。对我来说,很多软件组织在文档中不使用通俗易懂的语言或使示例过于复杂,这很有趣。也许是我对不必要的术语的反感,但我避开使用那些文档我不容易理解的软件。

除了确定工具是否足够普遍外,我还检查论坛和博客帖子,以确定是否有足够的示例和有答案的问题来支持官方文档。不管文档有多好,它几乎肯定存在某些空白和模糊不清的地方,因此拥有非正式文档作为备份是很有帮助的。

8.3.6. 定制开发是好事

一些软件工具或它们的包是为了特定目的而构建的,后来又添加了其他功能。例如,MATLAB 和 R 中的矩阵代数例程在构建这些语言时是首要关注的问题,因此可以安全地假设它们是全面且稳健的。相比之下,矩阵代数在 Python 和 Java 的初始版本中并不是首要关注的问题,因此这些功能是以包和库的形式后来添加的。这并不一定不好;Python 和 Java 现在恰好拥有稳健的矩阵功能,但并非每个声称能够高效处理矩阵的语言都如此。

在我想要使用的统计方法是一个包、库或是我想要使用的软件工具的附加组件的情况下,我对这个包的审查与对工具本身的审查相同:它是否灵活、信息丰富、常用、文档齐全,以及在其他方面是否稳健?

8.3.7. 兼容性是好的

兼容性是针对特定目的构建的一种逆过程,但它们并不是相互排斥的。一些软件工具与其他工具配合良好,在这些工具中,你可以期待能够集成功能、导入数据以及以通常接受的格式导出结果。这在使用其他软件进行相关任务的项目中很有帮助。

如果你正在与数据库一起工作,使用一个可以直接与数据库交互的工具可能会有所帮助。如果你打算基于你的结果构建一个 Web 应用程序,你可能想要选择一个支持 Web 框架的工具——或者至少一个能够以 JSON 或其他 Web 友好格式导出数据的工具。或者,如果你将在各种类型的计算机上使用你的统计工具,那么你希望软件能够在各种操作系统上运行。将统计软件方法集成到完全不同的语言或工具中并不罕见。如果这种情况发生,那么检查你是否可以调用 Python 函数从 Java 中(经过一些努力,你可以做到)是一个好主意。

R 是为了统计而专门构建的,兼容性只是事后考虑的事情,尽管有一个庞大的生态系统支持与其他软件的集成。Python 被构建为一个通用编程语言,统计只是事后考虑的,但正如我所说,Python 的统计包是其中最好的。在它们和其他工具之间进行选择是一个审查你打算使用的所有语言、应用程序和包的问题。

8.3.8. 开放式许可协议是好的

大多数软件都有一个许可,无论是明确的还是隐含的,它声明了软件使用的限制或权限。专有软件许可通常很明显,但开源许可通常并不那么清晰。

如果你使用商业软件进行商业用途,使用学术或学生许可证这样做在法律上可能存在风险。如果没有确认许可证不禁止这样做,将修改后的商业软件或未修改的商业软件卖给他人也可能很危险。

当我使用开源工具进行数据科学时,我主要的问题是能否使用这个工具创建软件并将其卖给某人而不泄露源代码?一些开源许可证允许这样做,而一些则不允许。据我所知(尽管我不是律师),我不能在没有提供源代码的情况下将我编写的 R 应用程序卖给某人;在 Python 和 Java 中,这样做通常是允许的,这也是为什么生产应用程序通常不是在 R 和具有类似许可证的语言中构建的原因。通常有法律途径可以解决这个问题,例如自己托管 R 代码,并将其功能作为网络服务或类似的服务提供。无论如何,最好检查许可证,并在怀疑可能违反软件许可证的情况下咨询法律专家。

8.3.9. 知识和熟悉度是好的

尽管我怀疑包括我在内的大多数人首先考虑的是这一点,但我还是把这个一般规则放在最后。我必须承认:我倾向于使用我所知道的。只要它能够合理地与之前的规则一起工作,使用你最擅长的工具可能并没有什么问题。例如,Python 和 R 在数据科学中的几乎所有事情上都做得很好,如果你对其中一个比另一个更熟悉,那么当然可以使用它。

另一方面,许多工具并不是完成这项工作的正确工具。例如,尝试使用 Excel 进行机器学习通常并不是最好的主意,尽管我听说随着微软扩展其产品,这种情况正在改变。在这种情况下,如果你可能能够使用你熟悉的工具应付,那么考虑学习一个更适合你项目的工具绝对是值得的。

最后,这是一个权衡使用你熟悉的工具节省的时间与使用不合适的工具所损失的时间和结果质量的问题。你的项目的时间和需求通常是这里的决定因素。

8.4. 将统计学转化为软件

将数学融入你的代码并非易事。许多人似乎认为在数据上做数学运算就像导入一个统计库然后点击运行一样简单。也许如果他们运气好,它可能会工作,但直到不确定性悄悄地降临到他们身上,他们对统计方法和代码中发生的事情缺乏意识,未能防止某种类型的问题。我知道这是一个人为的场景,但我故意构建这样一个卑鄙的稻草人来强调理解你所选择的统计方法以及它们与你正在使用或创建的软件之间关系的重要性。

8.4.1. 使用内置方法

任何中级统计工具都应该有适当的说明,说明如何将它们的各种统计方法应用于你的数据。尽管我没有多少最近的经验,但我预计应用程序内的指导或在线可用的文档应该足以让任何人了解如何应用标准统计方法。

编程语言通常要复杂一些,我发现找到关于如何实现和执行甚至最简单的统计分析的基本指令和示例往往相当困难。我的印象是,大多数文档都假设对语言有一定的了解,这可能会让初学者感到困惑。因此,我将在这里提供两个示例,说明如何应用线性回归,一个在 R 中,一个在 Python 中。

R 中的线性回归

R 通常使用函数式风格,就像这个例子一样:

data = data.frame(X1 = c( 1.01, 1.99, 2.99, 4.01 ),
                  X2 = c( 0.0, -2.0, 2.0, -1.0 ),
                  y  = c( 3.0, 5.0, 7.0, 9.0 ))

linearModel <- lm(y ~ X1 + X2, data)
summary(linearModel)

predict(linearModel,data)

首先,这个脚本创建了一个包含三个变量X1X2y的数据框对象。数据框是一个对象,但在这里它是通过一个名为data.frame的函数构建的,该函数返回一个存储在变量data中的数据框对象。

在这里假设的任务是,你想要执行线性回归,使得X1X2是输入,而y是输出。你希望能够使用X1X2来预测y,并且你想要找到一个好的线性模型来完成这个任务。脚本中的第二个命令指定了你想通过lm函数创建一个线性模型,其参数首先是公式,y ~ X1 + X2,其次是包含数据的data数据框。公式是 R 中一种奇特但有用的结构,我在其他地方几乎没见过。它们旨在表示变量之间的一种数学关系。正如你可能猜到的,这个公式告诉lm函数,你想使用X1X2来预测y。截距(所有输入变量为零时的 y 值)也会自动添加,除非你明确地移除它。你可以在公式中添加更多的输入变量,或者添加变量的组合,例如两个变量的乘积、一个变量的平方等。在 R 中构建公式的可能性有很多,而且可能会变得相当复杂,所以我建议在编写自己的公式之前先查阅文档。

公式中命名的变量必须与传递给lm的数据框中包含的一些变量匹配。lm函数本身执行回归,并返回一个拟合的线性模型,该模型存储在变量linearModel中。重要的是要注意,我在这个例子中创建的数据是为了给出预期的回归结果。每个四个数据点都有X1X2y的值。数据框数据看起来像这样(>是 R 的提示符):

> data
  X1  X2  y
1 1.01  0  3
2 1.99  -2  5
3 2.99  2  7
4 4.01  -1  9

如果你仔细研究这些数字,可能会注意到,在每一行中,y 值都相当接近2*X1+1的对应结果,而X2的值对y的值的信息贡献不大。你说X1可以预测y,但X2不能,所以你预期你的回归结果几乎是这样。你预期线性回归的结果会表明这一点。

命令summary(linearModel)将输出打印到屏幕上,提供了有关拟合数据的线性模型的信息,如下所示:

> summary(linearModel)

Call:
lm(formula = y ~ X1 + X2, data = data)

Residuals:
       1        2        3        4
-0.02115  0.02384  0.01500 -0.01769

Coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.001542   0.048723  20.556  0.03095 *
X1          1.999614   0.017675 113.134  0.00563 **
X2          0.002307   0.013361   0.173  0.89114
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.03942 on 1 degrees of freedom
Multiple R-squared:  0.9999,       Adjusted R-squared:  0.9998

你可以从这个输出中看到,X1的系数估计值非常接近 2,X2的几乎为零,截距的值在 1 以上,所以结果符合你的预期。

其余的输出——标准误差、p 值、R 平方等——给出了拟合优度、系数的显著性以及其他关于模型的统计信息,这些信息表明它是一个好模型还是不好。

脚本的最后一行预测了输入数据框中提供的数据点的 y 值,在这种情况下,它与训练模型的数据相同。predict函数接受data中的变量X1X2,并根据linearModel中的模型输出 y 值。以下是打印的输出:

> predict(linearModel,data)
    1    2    3    4
3.021152 4.976159 6.985002 9.017687

这四个值中的每一个都是模型对data中每个数据点(行)的预测。正如你所看到的,这些值与模型训练时的 y 值非常接近。

Python 中的线性回归

Python 有多个提供线性回归方法的软件包,它提供了一个很好的例子,说明了在之前概述的选择软件的一般规则之间需要权衡。据我所知,sklearn包中的LinearRegression对象可能是最受欢迎的,但拟合模型的总结并不像 R 中使用的函数输出那样信息丰富。但statsmodels包中的linear_model对象可以轻松提供信息丰富的输出,所以我在这里使用它。

以下代码使用面向对象风格,创建了两个主要对象:线性模型,我在脚本中将其称为linearModel,以及一个结果对象,我将其称为results。调用这些对象的方法来创建模型、拟合模型、总结结果,以及做出与 R 脚本中相同的预测。如果你不熟悉面向对象编程,一开始可能会觉得有点奇怪:

import statsmodels.regression.linear_model as lm

X = [ [ 1.01,  0.0, 1 ],
      [ 1.99, -2.0, 1 ],
      [ 2.99,  2.0, 1 ],
      [ 4.01, -1.0, 1 ] ]
y = [ 3.0, 5.0, 7.0, 9.0 ]

linearModel = lm.OLS(y,X)
results = linearModel.fit()
results.summary()

results.predict(X)

注意我是如何创建变量Xy的。它们是值的列表,在拟合模型时,被强制转换为适当的数组/矩阵形式。pandas包的数据框也可以使用,但我选择在这里不使用它们。我还向X数据右侧添加了一个 1 列,因为模型不会自动添加截距值。还有其他更优雅的方法来添加截距,但我没有使用它们,以保持清晰。

创建数据对象后,脚本执行与 R 脚本相同的步骤:创建模型,使用statsmodels包中的OLS类;拟合模型,使用对象方法调用linearModel.fit();打印结果摘要;并从原始X值预测y值。

方法调用results.summary()的打印输出如下所示:

OLS Regression Results
===========================================================================
Dep. Variable:                      y   R-squared:                       1.000
Model:                            OLS   Adj. R-squared:                  1.000
Method:                 Least Squares   F-statistic:                     6436.
Date:                Sun, 03 Jan 2016   Prob (F-statistic):            0.00881
Time:                        13:45:54   Log-Likelihood:                 10.031
No. Observations:                   4   AIC:                            -14.06
Df Residuals:                       1   BIC:                            -15.90
Df Model:                           2
Covariance Type:            nonrobust
===========================================================================
                 coef    std err          t      P>|t|      [95.0% Conf. Int.]
---------------------------------------------------------------------------

它显示了与 R 中总结相同的许多统计数据,还有一些其他的。更重要的是,它给出了相同的结果。

8.4.2. 编写自己的方法

作为一名学术研究者,我主要开发了用于分析生物信息学领域各种系统和数据的算法。因为它们是新的,所以我无法享受在软件包中应用方法的奢侈,尽管在某些情况下,我使用了一些可用的方法来帮助进行优化和模型拟合。

创建新的统计方法可能耗时,除非你清楚自己在做什么或者这是你的工作职责,否则我不建议这么做。但如果你必须这么做,了解从哪里开始是很重要的。一般来说,我会从统计模型的数学规范开始,这可能看起来像我在上一章中描述的模型,其中包含了对模型参数概率分布的几个规范,例如:

x[n,g] ~ N( μ[g] , 1/λ )

如果模型及其所有参数和变量都像那样被指定,那么你需要将这些规范转换成一个似然函数,你可以使用它来找到最优的参数值或分布。

要得到似然函数,这是一个基于数据值的参数值的函数,你需要使用每个单独数据点的概率分布函数的数学规范并将它们相乘。或者因为概率密度通常很小,相乘只会得到一个非常小的数,所以通常更好的做法是取每个数据点的似然函数的对数并将它们相加。因为函数的对数在其本身的最大值处达到最大,最大化对数似然的总和等同于最大化似然率的乘积。实际上,大多数用于处理概率和似然率的软件和算法都利用了对数取值的优势。

一旦有了联合似然和联合对数似然的数学规范,你就可以直接使用该语言提供的任何数学库将它们转换为软件。编写一个软件函数应该很简单,该函数接受参数值作为输入,并根据数据给出对数似然作为输出。这个函数应该在软件中执行与数学中对数似然函数相同的功能。

现在你已经有了联合似然函数的软件版本,你需要使用第七章中讨论的算法之一来找到最优的参数值或分布:最大似然估计(MLE)、最大后验(MAP)、期望最大化(EM)、变分贝叶斯(VB)或马尔可夫链蒙特卡洛(MCMC)。大多数统计软件都有 MLE 的方法,这涉及到使用优化程序来最大化你的联合似然函数。对于简单模型来说这可能很容易,但对于复杂模型来说可能很棘手。

使用 MAP 方法意味着你将不得不回到你的模型方程,并从数学上计算参数似然的后验分布的指定。然后你可以像对 MLE 那样最大化这个后验分布。制定后验分布并不简单,所以在尝试之前你可能想查阅有关贝叶斯模型的参考资料。

EM、VB 和 MCMC 通常也依赖于与 MAP 相同的后验分布。许多软件工具都有 MCMC 的实现,所以你可能能够直接应用这些工具来估计参数后验分布,但对于 EM 和 VB,你通常必须自己编写模型拟合算法,尽管已经有人努力创建简化这一过程的软件。开发像 EM 和 VB 这样的算法的难度可能是 MCMC 如此受欢迎的主要原因之一。让 MCMC 工作可能很棘手,但一旦它工作起来,它就让原始的计算能力取代了其他两种算法所需的人类编程。

练习

在第二章中首先描述的 Filthy Money Forecasting 个人财务应用场景的基础上,并关联到前几章的练习,尝试以下内容:

1.

你在这个项目中执行预测计算时,最常选择的两种软件是什么?为什么?这些软件的缺点是什么?

2.

在问题 1 中提到的两个选择是否具有内置的线性回归或其他时间序列预测方法?它们是什么?

摘要

  • 统计软件是理论统计模型的实现;了解两者之间的关系对于了解你的项目很重要。

  • 可用于进行数据科学的软件范围很广,从电子表格到中级统计工具,再到统计编程语言和库。

  • 有时,即使是电子表格对数据科学家来说也可能很有用,用于简单的任务。

  • 市面上有几种很好的中级统计工具,每种都有其优势和局限性。

  • 编程并不那么难,但学习它需要一些时间,并且它为你进行数据统计提供了最大的灵活性。

第九章. 补充软件:更大、更快、更高效

本章涵盖

  • 可以帮助你更高效地进行统计的非统计软件

  • 与分析软件相关的流行且普遍的软件概念

  • 使用补充软件的基本指南

图 9.1 显示了我们在数据科学流程中的位置:使用补充软件优化产品。第八章中涵盖的软件工具非常灵活,但我在那里主要关注了每个软件的统计特性。软件可以做的不仅仅是统计。特别是,有许多工具被设计用来高效地存储、管理和移动数据。一些工具可以使几乎每个计算和分析的方面都更快、更容易管理。在本章中,我将介绍一些最受欢迎且最有益的软件,这些软件可以使数据科学家的生活和工作更加轻松。

图 9.1. 数据科学流程构建阶段的一个重要方面:使用补充软件优化产品

图片

9.1. 数据库

我在第三章中讨论了数据库的概念,将其视为一种数据源的形式。数据库很常见,你在项目中进行时遇到数据库的机会相当高,尤其是如果你经常使用他人使用的数据。但与其仅仅顺其自然地遇到一个数据库,不如自己建立一个数据库来帮助你完成项目。

9.1.1. 数据库类型

存在许多类型的数据库,每种数据库都设计用来以自己的方式存储数据并提供访问。但所有数据库都旨在比标准基于文件存储更高效,至少对于某些应用来说是这样。

存在更多类型(和子类型),但今天最常见的两种数据库类型是关系型文档型。尽管我当然不是数据库模型和理论的专家,但我将尝试从通常从概念上思考它们以及如何与之交互的角度来描述这两种类型。

关系型

关系型数据库全都是关于表格的。关系型数据库中的一个表格通常可以想象成类似于电子表格中的二维表格:该表格包含行和列,单元格中包含数据元素。

关系型数据库的强大之处在于它们可以存储许多表格,并且可以在幕后以巧妙的方式将表格相互关联。这样,即使是来自多个表格和数据类型的复杂查询也可以以最佳方式执行,与原始的扫描表格方法相比,通常可以节省大量时间。

在流行了几十年的关系型数据库中,一种主导语言已经出现,用于制定查询:结构化查询语言(SQL,通常发音为“sequel”)。SQL 实际上无处不在,尽管关系型数据库还有其他查询语言。简而言之,你可以使用 SQL 查询许多不同种类/品牌/子类型的数据库;所以如果你熟悉 SQL,你就可以开始使用一个不熟悉的数据库,而无需学习新的查询语法。另一方面,并非所有基于 SQL 的数据库都使用完全相同的语法,因此可能需要对特定查询进行一些相对较小的调整,以便它在新的数据库上工作。

文档型

在某种意义上,文档型数据库是关系型数据库的对立面。关系型数据库有表,而文档型数据库有,嗯,文档。这并不奇怪。

在这个例子中,“文档”可以是一组所谓的非结构化数据,如电子邮件的文本,以及一组结构化标识信息,例如电子邮件的发送者和发送时间。它与数据存储的键值概念密切相关,其中数据根据一组少量键进行存储和编目,以便于检索。通常选择为数据点的字段,通过这些字段在查询时可以找到它——例如,ID、姓名、地址、日期等等。数据点的可以被视为与键一起存储的某种有效载荷,但在查询中通常不用于查找数据点。如果你愿意,它可能是一堆混乱的数据,因为你通常不会使用值来查询数据(对于明显的例外,请继续阅读)。

原始文本、未知长度的列表、JSON 对象或其他似乎不适合放入表格中的数据通常可以轻松地适应文档型数据库。为了高效查询,每一条非结构化数据(一个可能的值)理想情况下都应与少量结构化标识信息(潜在的键)相匹配,因为几乎无一例外,数据库在处理结构化数据方面比非结构化数据要高效得多。

由于文档型数据库是关系型数据库和表格的对立面,因此术语NoSQL经常被应用于它们。你将发现其他类型的 NoSQL 数据库,但文档型是最大的子类。

除了通常比关系数据库更灵活且可能效率更低之外,面向文档的数据库还可以有自己的优势。这种优势的一个例子可以在流行的 Elasticsearch 数据存储中看到。Elasticsearch 是一个基于(也是开源的)Apache Lucene 文本搜索引擎的开源面向文档的数据库。Lucene 和 Elasticsearch 都擅长解析文本、查找特定的单词和单词组合,并生成这些单词出现次数的统计数据。因此,如果你正在处理大量文本文档,并且将研究单词和短语的出现情况,那么几乎没有(如果有的话)数据库(无论是关系型还是非关系型)会比 Elasticsearch 更有效率。

通过原始文本查询 Elasticsearch(或类似)数据库是通常规则的一个显著例外,即你应该通过键而不是值进行查询。由于 Lucene 在索引文本方面做得很好,因此通过文本中的术语进行查询的行为在大多数其他数据库中更像是通过键进行搜索。

其他数据库类型

如果你正在处理一种无法轻易表示为一系列表格或文档的数据类型,那么它可能不适合关系型或面向文档的数据库,在这种情况下,寻找适合该数据类型的数据库可能是有价值的。例如,我在社交网络分析项目中使用的图数据通常可以从图数据库的效率中受益。Neo4j 是一个流行的 图数据库,它以使存储、查询和分析图数据更简单的方式表示事物(如社交网络中的人)之间的连接。有许多其他数据库的例子,它们针对非常具体的数据类型,但在这里我不会尝试对这些数据库进行概述。快速在线搜索应该能引导你找到正确的方向。

9.1.2. 数据库的优点

与存储在计算机文件系统上的数据相比,数据库和其他相关类型的数据存储可以具有许多优势。主要的是,数据库可以通过查询提供对数据的任意访问,比文件系统更快,并且它们还可以以方便的方式扩展到大型规模,具有冗余性,这可以优于文件系统的扩展。在这里,我简要描述了数据库可以提供的一些主要优点。

索引

数据库 索引 是一系列软件技巧,它们生成所有数据的某种映射,以便任何内容都可以快速轻松地找到。索引 是构建此类映射的过程。通常,索引会有效地利用硬件(磁盘和内存)来提高整体效率。

拥有索引(与没有索引相比)的代价是占用一些磁盘和内存空间,因为索引本身需要占用空间。通常,你可以选择创建一个非常高效的索引,它占用更多空间,或者创建一个效率较低的索引,它占用较少空间。最佳选择取决于你试图实现的目标。

缓存

在广义上,缓存是指将某些经常访问的数据保留在一边,目的是为了提高整体效率,因为经常使用的数据在特殊位置可以快速获取。当某些数据被频繁访问时,你可以在某种程度上将经常使用的数据放在手边,从而减少整体平均访问时间(系统架构的各个方面使得这一点成为可能)。如果经常使用的数据访问时间非常短,偶尔很少访问的数据查找时间稍长一些并不重要。数据库通常会尝试识别最常用的数据并将其保留在附近,而不是将其放回其他数据中。与索引一样,缓存会占用空间,但你通常可以选择你希望分配给缓存的空间量,这反过来又决定了其有效性。

规模化

当今存在的许多类型的数据库可以分布到许多机器上。显然,这并不是将你的数据存储在磁盘上的文件上的直接优势,因为如果你可以访问许多机器,你就可以访问许多磁盘。那么,分布式数据库相对于分布式文件系统的优势在于协调。

如果你拥有许多机器上的多个磁盘上的数据,你必须跟踪你将数据保存在哪里。分布式数据库旨在自动执行此操作。分布式数据库通常由分片组成,即每个分片的数据都存在于单个位置。一个中央服务器(或多个服务器)管理分片之间的访问和传输。可以根据选择的数据库配置使用额外的分片来增加数据库的潜在大小或复制其他地方存在的数据。

并发

如果两个不同的计算机进程试图同时更改相同的数据点,这些更改被称为并发,而找到适当最终状态的问题通常被称为并发问题。数据库通常比文件系统更好地处理这个问题。具体来说,如果两个不同的进程试图同时创建或编辑相同的文件,可能会发生任何数量的错误,或者根本不会发生错误,这有时可能是一个更大的问题。一般来说,你希望在文件系统上不惜一切代价避免并发,但某些类型的数据库提供了方便的解决方案来解决任何冲突。

聚合

数据库的索引可以应用于除了查找与查询匹配的数据之外的任务。通常,数据库提供执行匹配查询或所有数据的聚合功能。数据库可能能够比你的代码更快地累加、乘法或汇总数据,因此将这种汇总推送到数据库并提高整体效率可能是有帮助的。

例如,Elasticsearch 使得在数据库中计算特定搜索词的频率变得容易。如果 Elasticsearch 没有提供这项功能,你就必须查询该词的所有出现,计算出现次数,然后除以总文档数。这看起来可能不是问题,但如果你这样做成千上万次,让数据库以优化、高效的方式计算频率可以节省大量时间。

抽象查询语言

查询数据库以获取特定数据涉及使用数据库理解的查询语言,例如 SQL。虽然学习一种新的查询语言以适应新的数据库可能会让人感到烦恼,但这些语言提供了对查询背后搜索算法的抽象。如果你的数据存储在文件系统上的文件中,并且你没有使用数据库,那么每次你想搜索满足某些标准的数据点时,你都必须编写一个算法,遍历所有文件——所有数据点——并检查它们是否满足你的标准。使用数据库时,你不必担心特定的搜索算法,因为数据库会处理它。查询语言提供了关于你正在寻找内容的简洁、通常可读的描述,并且数据库会为你找到它。

9.1.3. 如何使用数据库

大多数软件工具,包括 Excel,都可以与数据库接口,但有些做得比其他更好。所有最流行的编程语言都有访问所有最流行数据库的库或包。了解如何操作只需查看文档即可。一般来说,你需要知道以下内容:

  • 创建数据库。

  • 将你的数据加载到数据库中。

  • 配置和索引数据库。

  • 从你的统计软件工具查询数据。

每个数据库都有一些不同,但一旦你习惯了其中几个,你就会看到相似之处,并快速学会更多。似乎现在每类数据库都有相应的书籍出版,所以这是一个找到它并投入使用的问题。对于 NoSQL 数据库,提供的选项尤其广泛、多样且令人眼花缭乱,因此像《理解 NoSQL》(McCreary 和 Kelly,Manning,2013)这样的书籍可以帮助你梳理所有功能和选项。

9.1.4. 何时使用数据库

如果你从文件系统访问数据既慢又麻烦,那么可能是时候尝试使用数据库了。这也可能取决于你如何访问数据。

如果你的代码经常搜索特定数据——数千或数百万次——数据库可以大大加快访问时间和代码的整体执行时间。有时,代码在从文件系统存储切换到数据库后可以快几个数量级。我的一个项目在第一次切换时速度提高了 1000 倍。

如果你文件系统中的数据你通常是从上到下处理,或者如果你不经常搜索,那么数据库可能对你帮助不大。数据库最适合查找符合特定标准的需求数据,因此如果你不需要查询、搜索或跳转数据,文件系统可能是最佳选择。

我有时抵制使用数据库的一个原因是因为它给软件和项目增加了一些复杂性。拥有一个实时运行的数据库至少会给所有你需要关注的事物增加一个动态部分。如果你需要将数据传输到多台机器或多个位置,或者如果你担心你没有时间配置、管理和调试另一件软件,那么创建数据库可能不是最好的主意。这肯定需要至少一点维护工作。

9.2. 高性能计算

高性能计算(HPC)是一般术语,用于描述有很多计算要做,并且你希望尽可能快地完成的情况。在某些情况下,你需要一台快速的计算机,而在其他情况下,你可以将工作拆分,使用多台计算机来处理多个单独的任务。这两种方法之间也存在某种中间地带。

9.2.1. HPC 类型

无论是拥有一台还是多台计算机,你可能还会考虑使用擅长特定任务的计算机,或者配置和组织得特别有用的计算集群。我在这里描述了一些选项。

超级计算机

超级计算机是一台非常快的计算机。全球范围内存在一场关于最快超级计算机的竞争,这个头衔比其他任何东西都更有声望。但是,获得这个头衔的技术挑战并不小,结果也是如此。

新型超级计算机比标准个人计算机快数百万倍——它可能比你的 PC 快数百万倍来计算结果。如果你能访问到——而且不是很多人有这样的访问权限——那么考虑它可能是有价值的。

大多数大学和拥有 IT 部门的大型、数据导向的组织可能没有超级计算机,但它们某处有一台功能强大的计算机。如果你向正确的人请求访问权限,计算结果可能比标准个人计算机快 100 倍或 1000 倍。

计算机集群

计算机集群是一组相互连接的计算机,通常通过本地网络连接,配置得可以很好地协同完成计算任务。与超级计算机相比,计算任务可能需要显式并行化或以其他方式拆分成单独的任务,以便集群中的每台计算机都能完成部分工作。

根据集群的不同,各种计算机及其执行的任务可能能够高效地相互通信,也可能不能。一些类型的通用计算机集群(例如,HTCondor 是一个流行的软件框架,用于统一它们)更少关注优化单个机器,而是更多地关注最大化集群可以完成的总工作量。其他类型的集群高度优化性能,总体上类似于超级计算机。

与超级计算机相比,集群的一个缺点通常是可用的内存。在超级计算机中,通常有一个巨大的可用内存池,因此可以存储极其庞大和复杂的结构——这比尝试将结构存储在磁盘或数据库中要快得多。在集群中,每台计算机只有自己的可用内存,因此它可能一次只能加载复杂结构的一小部分。写入和读取磁盘可能会花费时间并影响整体性能,但这取决于正在进行的特定计算。高度并行的计算更适合集群。

GPU

图形处理单元(GPU)是设计用于在计算机屏幕上处理和操纵视频图像的电路。每个带有屏幕的计算设备上的显卡都有一个 GPU。

视频处理的特点导致了 GPU 设计非常适合执行高度可并行化的计算。事实上,一些 GPU 在特定类型的计算上非常出色,以至于它们被优先考虑于标准 CPU。几年前,研究人员购买并构建了由视频游戏系统(如索尼 PlayStation)组成的集群,因为这些系统 GPU 的计算能力超过了价格相似的其它计算机。

9.2.2. HPC 的好处

高性能计算(HPC)的唯一好处非常简单明了:速度。HPC 可以比标准计算(也称为低性能计算)更快地完成计算。如果你有访问权限——这是一个很大的“如果”——那么 HPC 是等待你的 PC 计算所有需要计算的事情的一个好选择。我将在本章后面讨论的云计算使 HPC 对每个人开放——但需要付费。在决定加入之前,必须权衡使用云 HPC 服务的好处——一些非常强大的机器可供选择——以及其货币成本。

9.2.3. 如何使用 HPC

使用超级计算机、计算机集群或 GPU 与使用你自己的个人计算机相当相似,前提是你知道如何利用你机器的多核。你使用的统计软件工具和语言通常有使用个人计算机多核的方法,并且这些方法通常很好地转移到高性能计算(HPC)上。

在 R 语言中,我过去使用multicore包来并行化我的代码并使用多个核心。在 Python 中,我使用multiprocessing包来达到同样的目的。在这些包中,我可以指定我想要使用的核心数量,每个包都有一些在各个核心上运行的过程之间共享对象和信息的方法。在进程之间共享对象和信息可能很棘手,所以,尤其是作为一个初学者,你应该避免这样做。如果你能够以这种方式实现你的算法,纯并行对代码和大脑来说都更容易一些。

在我的经验中,将我的代码提交到计算机集群与在我的机器上运行它类似。我向我在工作的大学里的同事们询问了提交作业到集群队列的基本命令,然后我调整了我的代码以符合要求。我可以指定我想要的计算机核心数量以及内存量,这两者都会影响我在队列中的状态。这个特定大学的集群,就像大多数集群一样,需求很高,排队既是必需的,也像是一种游戏。

有时,尤其是与 GPU 一起使用时,有必要修改你的代码以明确使用特殊硬件功能。通常最好是咨询专家或查阅文档。

9.2.4. 何时使用 HPC

由于 HPC 比替代方案更快,规则是:如果你有权限使用,就使用它。如果没有成本,而且你不需要对代码进行太多修改就能利用它,那么这个问题就一目了然。但事情并不总是那么简单。如果我有使用某些 HPC 解决方案的选择,我会首先考虑为了使用 HPC 我必须进行的代码更改和其他准备工作,然后我会将这个与我会节省的计算时间进行比较。有时,如果你不急于求成,HPC 可能不值得。其他时候,它可以在一小时内给你结果,而这在其他情况下可能需要一周或更长时间。

9.3. 云服务

云服务几年前非常流行。它们仍然非常受欢迎,但它们正在变得更加成熟,不再是新颖的技术。然而,可以肯定的是,它们会留下来。简而言之,云服务按小时出租,提供你通常只能通过购买和管理自己的服务器才能获得的性能。

云服务的主要提供商大多是大型科技公司,它们的主营业务是其他事情。像亚马逊、谷歌和微软这样的公司,在向公众开放之前,就已经拥有大量的计算和存储资源。但它们并不总是充分利用这些资源,因此它们决定既出租多余的容量,也扩大总容量,这最终证明是一系列有利可图的商业决策。

9.3.1. 云服务的类型

提供的服务通常大致相当于个人计算机、计算机集群或本地网络的功能。所有这些都在全球的地理区域内可用,通过在线连接和标准连接协议以及通常的网页浏览器界面访问。

存储

所有主要的云服务提供商都提供文件存储服务,通常按每月每千兆字节付费。通常还有各种存储层,如果你想要更快的读取或写入文件,你可能需要支付更多费用。

计算机

这可能是云服务中最直接的一种:你可以按小时支付访问具有特定规格的计算机的费用。你可以选择核心数量、机器内存量和硬盘大小。你可以租用一个大的,启动它,并将其作为你的超级计算机使用一天或一周。当然,更好的计算机价格更高,但价格每年都在下降。

数据库

作为云服务提供商提供的存储的扩展,还有云原生数据库服务。这意味着你可以创建和配置数据库,而无需知道数据库运行在哪些计算机或磁盘上。

这种机器无关性在维护数据库时可以节省一些麻烦,因为你不必担心配置和维护硬件。此外,数据库可以几乎无限扩展;云服务提供商需要担心的是涉及多少机器和多少分片。代价,字面上的代价,是你将经常为每次访问数据库——读取和写入——以及存储的数据量付费。

网络托管

网络托管就像租用一台计算机并在其上部署一个网络服务器,但它附带了一些额外的功能。如果你想部署一个网站或其他网络服务器,云服务可以帮助你这样做,而不必过多担心单个计算机和机器配置。它们通常提供平台,在这些平台上,如果你符合它们的要求和标准,你的网络服务器将运行并随着使用量扩展,而不会遇到太多麻烦。例如,亚马逊网络服务提供了使用 Python 的 Django 框架以及 Node.js 框架部署网络服务器的平台。

9.3.2. 云服务的好处

与使用自己的资源相比,使用云服务有两个主要好处,尤其是如果你需要购买本地资源。首先,云资源不需要任何承诺。你只需为使用的量付费,如果你还不确定你需要多少容量,这可以节省大量资金。其次,云服务的容量远远大于你可能会购买的东西,除非你是财富 500 强公司。如果你还不确定项目的规模,云服务可以在存储和计算能力等方面为你提供极大的灵活性,你可以在任何时候立即访问。

9.3.3. 如何使用云服务

随着云服务的种类繁多,你可以几乎无限地组合使用它们的方式。第一步始终是创建一个服务提供商的账户,然后尝试使用服务的基本级别,这通常免费提供。如果你觉得它有用,那么扩大规模就是更多地使用它并支付账单的问题。请注意,在深入之前,通常值得比较类似的服务。

9.3.4. 何时使用云服务

如果你没有足够的资源来充分满足你的数据科学需求,考虑使用云服务是值得的。如果你在与拥有自身资源的组织合作,在支付云服务费用之前耗尽本地选项可能更便宜。另一方面,即使你在本地拥有相当多的资源,云服务肯定有更多;如果你不断遇到本地资源限制,请记住云服务提供了几乎无限的容量。

9.4. 大数据技术

如果在过去 10 年中,在分析软件行业中,比云计算更常说的短语是大数据。遗憾的是,这个短语及其描述的技术被理解得远不如这个短语被说的那么频繁。

在谈论大数据时,我将采取一些自由度,因为我感觉它从未拥有过任何类似的具体定义。软件行业的每个人,从开发人员到销售人员,都使用这个短语来夸大他们正在构建或推销的软件的印象,而且并不是所有的用法都相互一致。在这里,我将描述的不是我认为每个人在说大数据时都意味着什么,而是我说这个词时我意味着什么。我认为我的个人意义很重要,因为我试图将这个概念提炼到核心思想和技术上,这些思想和技术在 2005 年中期上市时具有一定的革命性。

我不使用大数据来表示“大量数据”。这种用法注定会变得过时,而且会很快,因为我们争论“大量”意味着什么。根据我的个人经验,10 年前 100 吉字节是大量数据;现在 100 太字节是常规的。重点是,单词总是相对的,因此我为大数据制定的任何定义也必须是相对的。

因此,我对自己个人对大数据的定义是基于技术,而不是数据集的大小:大数据是一套软件工具和技术,旨在解决数据传输是计算任务限制因素的情况。每当数据集太大而无法移动时,在某种程度上,并使用特殊软件来避免这种数据移动的必要性,就可以适用大数据这个术语。

也许一个例子可以最好地说明这个概念。谷歌可以说是大数据技术背后的第一批力量之一,它定期处理大量的数据,以支持其主营业务:一个旨在找到互联网上任何东西的搜索引擎,这显然是一个广阔的地方,以及将广告智能地放置在网页上的系统。保持最佳搜索结果需要分析来自互联网上所有页面到所有其他页面的链接数量和强度。我不知道现在这个数据有多大,但我知道它不是小数据。当然,数据分布在许多服务器上,可能位于许多不同的地理位置。为了生成互联网搜索结果的基础,分析所有这些数据是一项涉及所有数据服务器和数据中心的复杂协调任务,涉及大量的数据传输。

Google,作为一个聪明的公司,意识到数据传输是一个严重的问题,它极大地减缓了其计算速度。它认为最小化这种传输可能是个好主意。然而,如何最小化它却是一个不同的问题。以下关于谷歌所做之事以及之前描述的大多数内容,是我从多年前谷歌发布的关于其 MapReduce 技术和它所启发的其他技术(如 Hadoop)的信息中推断出来的。我不知道谷歌发生了什么,也无法声称我已经阅读了所有关于这个主题发表的论文和文章,但我确实认为以下假设性的解释对于任何想知道大数据技术是如何工作的人来说都是启发性的。对于几年前的我来说,这肯定是有启发性的。

回顾起来,如果我在谷歌意识到数据传输正在扼杀分析效率的时候在那里工作,我会设计一个三阶段算法,目标是尽量减少数据传输,同时仍然执行我想执行的所有计算。

算法的第一步是在每个数据库本地服务器上的每个数据点上执行初步计算。这种本地计算产生了许多结果,其中包括一个属性,该属性指示这个特定的数据点属于哪个数据点组。在在线搜索术语中,这个属性对应于这个数据点(可能是一个网页)可能被找到的互联网角落。网页往往链接到同一角落内的其他页面,而不是互联网其他角落的页面。对于每个数据点,一旦确定了指定互联网角落的属性,谷歌的算法就会进入第二步。在 MapReduce 框架中,这是映射步骤。

第二步调查了数据点的新的属性,并最小化了从一个地理位置到另一个地理位置的数据传输。如果 Corner X 的大部分数据都在 Server Y 上,第二步将发送所有 Corner X 数据到 Server Y,因此只需要传输 Corner X 数据的一小部分;大部分数据已经在那里。这一步骤通常被称为洗牌步骤,如果做得巧妙,将提供使用最流行的大数据技术的最大优势之一。

第三个步骤,然后,就是将具有共同属性的所有数据点一次性分析,生成一些共同的结果和/或考虑其他具有相同属性的数据的个别结果。这一步骤分析了 Corner X 中的所有网页,不仅给出了关于 Corner X 的结果,还给出了 Corner X 中所有页面及其相互关系的结果。这被称为减少步骤。

三步的一般总结是这样的:在每个数据点上本地执行一些计算,并将数据映射到属性上;对于每个属性,收集所有数据点,同时尽量减少数据传输/洗牌;最后,将每个属性的所有数据点减少到一组有用的结果。从概念上讲,MapReduce 范式是许多其他大数据技术的基础,但当然并不包括所有这些技术。

9.4.1. 大数据技术类型

Hadoop 是 MapReduce 范式的开源实现。它曾经非常流行,但似乎在最近几年失去了势头。Hadoop 最初是一个批处理工具,自从成熟以来,其他声称是实时的大数据软件工具已经开始取代它。它们都有一个共同的观点,即过多的数据传输对过程有害,因此应尽可能优先考虑本地数据计算。

一些大数据概念导致了数据库的发展,这些数据库明确使用了 MapReduce 范式及其实现,如 Hadoop。Apache 软件基金会的开源项目 HBase 和 Hive 等,明确依赖于 Hadoop 来驱动设计用于在极大规模上良好运行的数据库,无论这对你在阅读此内容的任何一年意味着什么。

9.4.2. 大数据技术的益处

大数据技术的设计目的是尽量减少数据移动。当数据集规模非常大时,这可以节省时间和金钱。

9.4.3. 如何使用大数据技术

这在很大程度上取决于技术。但它们通常模仿非大数据版本,至少在小规模上是这样。你可以像使用标准数据库一样开始使用大数据数据库,但可能需要更多的配置。

其他技术,特别是包括 Hadoop 在内,需要付出更多的努力。Hadoop 和其他 MapReduce 的实现需要在第一步指定 mapper,在第二步指定 reducer。经验丰富的软件开发者不会在编写这些基本版本时遇到问题,但实现和配置中的一些棘手特性可能会引起问题,所以需要小心。

9.4.4. 何时使用大数据技术

当计算任务受数据传输限制时,大数据可以提高你的效率。但比本章中描述的其他技术,大数据软件在运行时需要更多的努力。只有当你有时间和资源去调整软件及其配置,并且你几乎可以确定你会从中获得相当大的好处时,你才应该采取这一步。

9.5. 任何作为服务的

这显然不是一件真实的事情,但我经常觉得它是。有时候很难阅读软件描述而不遇到“软件即服务”(SaaS)、“平台即服务”(PaaS)、“基础设施即服务”(IaaS)或其他任何“某物即服务”的短语。虽然我对此进行嘲笑,但许多事物以服务的形式提供对软件行业来说是非常有益的。雇佣服务的目的是替代我们自己会做的事情,我们希望提供的服务比我们自己做的更好或更有效率。

我非常喜欢让别人做我不愿意亲自做的任何标准任务,无论是在现实生活中还是在软件中。在当今的互联网经济中,越来越多的这类任务可以作为服务提供,我看不出有任何理由预期这种趋势会很快放缓。虽然我不会在本节讨论任何具体的技术,但我强调,通过外包一些更常见的方面,你可能会极大地简化你的软件开发和维护任务。需要注意的是,你应该信任你雇佣的人能够做好工作,而建立这种信任可能需要一些努力。简单的在线搜索可以提供一些值得考虑的候选人,以分担你的一些工作。

练习

继续使用在第二章中首先描述的“脏钱预测”个人理财应用场景,并关联到前几章的练习,尝试以下内容:

1.

在这个项目中,可能会使用哪些三个补充的(不是严格意义上的统计)软件产品,以及为什么?

2.

假设 FMI 的内部关系数据库托管在单个服务器上,每晚都会备份到异地的一个服务器。给出这可能是良好架构的一个原因以及它可能不好的一个原因。

摘要

  • 有些技术不属于统计软件类别,但它们在使统计软件更快、可扩展和更高效方面很有用。

  • 配置良好的数据库、高性能计算、云服务和大数据技术,在分析软件行业中都有其位置,并且每种技术都有其优势和劣势。

  • 在决定是否开始使用这些辅助技术中的任何一种时,通常最好问自己:我的当前软件技术中是否存在明显的低效或限制?

  • 迁移到新技术需要时间和努力,但如果你有充分的理由,这可能值得。

  • 云服务和大数据技术围绕了很多炒作;它们可以非常有用,但并非每个项目都需要。

第十章. 计划执行:整合一切

本章涵盖

  • 将你的统计(第七章)和软件(第八章–第九章)付诸实践的技巧

  • 何时修改计划(如第六章中所述)

  • 理解结果的重要性及其与实际用途的关系

图 10.1 展示了我们在数据科学流程中的位置:执行产品的构建计划。在前三章中,我介绍了统计学、统计软件和一些补充软件。这些章节提供了数据科学家在其项目过程中可用的技术选项的概述,但它们并没有从上一章继续沿着数据科学流程。正因为如此,在这一章中,我通过说明如何从计划的制定(第六章)到应用统计学(第七章)和软件(第八章和第九章)以实现良好结果的过程,将你带回到那个流程中。我还指出了一些有用的策略和一些潜在的陷阱,并讨论了良好结果可能意味着什么。最后,我给出了一份关于我职业生涯早期一个项目的详细案例研究,重点关注应用本章以及前几章中的想法。

图 10.1. 数据科学流程构建阶段的最后一步:高效且细致地执行计划

图 10.1 的替代文本

10.1. 执行计划的技巧

在第八章和第九章中,我讨论了与统计应用相关的各种软件,何时何地使用不同类型的软件最合适,以及如何思考软件与你要进行的统计之间的关联。但构建该软件的过程又是另一回事。即使你确切知道你想要构建什么以及你想要的结果是什么,创建它的过程可能会充满障碍和挫折,尤其是当你试图构建的工具越复杂时。

大多数软件工程师可能都熟悉构建复杂软件的艰辛和挑战,但他们可能不熟悉构建处理质量可疑数据的软件的困难。另一方面,统计学家知道脏数据是什么样的,但在构建更高品质的软件方面可能经验不足。同样,与项目相关的不同角色的人,每个人可能都有各种经验和培训,他们会期待并准备不同的事情。作为我在本书中强调的项目意识的一部分,我将简要考虑不同人可能遇到的经验和困难,以及一些可以预防问题的方法。我不敢断定其他人都在想什么,但根据我的经验,背景相似的人往往会犯类似的错误,我将在这里描述这些错误,希望对你们有所帮助。

10.1.1. 如果你是一名统计学家

如果你是一名统计学家,你就知道脏数据,也知道关于偏差和过度强调结果显著性的问题。这些对你来说都很熟悉,所以你天生就会警惕它们。另一方面,你可能没有太多为商业软件构建软件的经验,尤其是生产软件——我的意思是直接由客户使用来深入了解其数据的软件。生产软件可能会出现很多问题。

咨询一名软件工程师

统计学家是聪明人;他们可以在短时间内学习和应用大量知识。对于每一个聪明人来说,在学习新技术时按需学习并相信自己的能力能够正确使用它是很有吸引力的。如果你正在创建自己会使用或主要是原型的东西,这很好。但在错误和错误会对你的项目和团队产生重大负面影响的情况下,最好至少在构建分析软件工具之前、期间和之后咨询软件工程师。如果没有其他的话,软件工程师会给你点赞,并告诉你你的设计或软件很棒。更有可能的是——如果工程师在关注的话——他们能够指出一些你可以改进的地方,以使你的软件工具更加健壮,并减少因未知原因而失败的可能性。如果你不是软件工程师,自己构建一个生产软件就像你在没有木工或建筑培训的情况下为你的房子建造露台一样。理论上,你可以从书籍和其他参考资料中学习你需要知道的大部分知识,但将木材、钉子和接缝组合在一起可能会有些混乱。向有实际经验的人求助可能会有很大帮助。

让某人彻底测试你的软件

如果你打算将软件交给客户并让他们直接使用,你可以肯定他们会找到 dozen 种方法来破坏它。完全消除所有错误并优雅地处理所有可能的边缘情况是很困难的,但如果你将软件交给同事——理想情况下是背景与客户相似的人——并告诉他们使用工具的所有方面,尝试破坏它,那么你可以找到最明显的错误和问题。更好的做法是将软件分发给几个人,让他们都使用并尝试破坏它。这通常被称为虫子大战,但它可以扩展到用户体验领域以及工具的通用实用性。这里的反馈不应被轻视,因为如果你的同事几个小时就能找到错误,我几乎可以保证客户会更快地找到它,这可能会让你损失时间、金钱和声誉。

客户需要花费很多时间

如果你以前从未向客户交付过软件,可能会惊讶地发现,大量客户在没有显著提示的情况下不会使用你的软件——而那些确实使用你的软件的客户会向你提出大量问题、问题和暗示,认为你做错了所有事情。

假设你希望人们使用你的软件,花时间与客户交流,确保他们使用软件感到舒适,并且使用正确,这可能值得。这意味着你可能需要发送一些电子邮件,打电话,或者根据你的情况亲自出现。数据科学项目通常依赖于成功使用这一新软件,在常见的情况下,客户可能不完全理解你新的以数据为中心的解决方案对未来影响,你可能需要引导他们走上正确的道路。

客户不断向你施压是一个好兆头。这意味着他们已经参与其中,并且非常希望软件能够正常工作。但不利的一面是,要么软件存在很多问题,要么他们不知道如何正确使用它。这两者都可以假设通过你或你团队的其他人得到解决。提前意识到客户可能需要的维护至少和软件本身一样多。

10.1.2. 如果你是一名软件工程师

如果你是一名软件工程师,你知道软件开发的生命周期是什么样的,也知道如何在部署和交付之前测试软件。但你可能不知道数据以及它如何破坏你精心设计的编程机器。正如我之前提到的,不确定性是软件工程师的绝对敌人,但在数据科学中,不确定性是不可避免的。无论你在软件设计和开发方面多么出色,数据最终都会以你从未想到的方式破坏你的应用程序。这要求在构建软件时采用新的思维模式,并提高对错误和缺陷的容忍度,因为它们会频繁发生。

咨询统计学家

软件工程师是聪明的人;他们可以跟随逻辑和信息在复杂结构中的流动。然而,数据和统计学引入了一种不确定性,这是逻辑和刚性结构天生处理不好的。统计学家在预见和处理问题数据方面经验丰富,如异常值、缺失值和损坏值。与统计学家进行一次对话可能会有所帮助,关注数据的来源以及你打算如何使用它。统计学家可能能够提供一些见解,了解一旦你的软件上线可能会出现的各种问题和边缘情况。如果没有咨询统计学家或以统计为导向的数据科学家,你可能会忽视一个可能非常重要的特殊情况,这可能会破坏你的软件或给它带来其他问题。

数据可以破坏你的软件

软件工程师擅长连接不同的系统并使它们协同工作。使两个软件系统协同工作的关键部分是两个系统之间的协议或合同,它规定了它们如何相互通信。如果其中一个系统是统计系统,其输出或状态通常无法保证符合特定的合同指南。特殊和边缘的数据值可能会使统计系统做出奇怪的行为,当软件组件被要求做出奇怪的行为时,它们通常会崩溃。在处理数据和统计时,最好提前原谅它们。考虑最广泛的可能结果或状态集,并为此做好准备。如果你特别慷慨,你可能希望将统计声明放在 try-catch 块(或类似结构)中,这样在严格意义上就不会有任何东西崩溃,并且可以处理、记录、报告或引发异常,无论什么看起来都合适。

检查最终结果

对于你们大多数人来说,这可能是显而易见的,但在时间紧迫的情况下,这一步骤被跳过的频率令人难以置信。我建议统计学家让一些人尝试破坏他们的软件,并且我强烈建议软件工程师运行他们正在分析的数据的几个完整示例,并确保结果是 100%正确的。(真的,每个人都应该这样做,但我希望统计学家已经足够训练有素,能够默认这样做。)从少量原始数据开始,一直追踪到结果,可能是一个繁琐的过程,但如果不进行端到端正确性测试,就无法保证你的软件正在做它应该做的事情。即使进行一些这样的测试也不能保证软件完美,但至少你知道你得到了一些正确的答案。如果你想将测试提升到下一个水平,将你的端到端测试转换为正式的集成测试,这样如果你将来对软件进行更改,你将立即知道你是否犯了一个错误,因为集成测试将失败。

10.1.3. 如果你是一个初学者

如果你刚开始从事数据科学,在统计学或软件工程方面没有太多经验,首先,恭喜你!这是进入一个广阔领域的一大步,你需要有足够的勇气去迈出这一步。其次,要小心。如果你没有意识到我在这本书中强调的意识,你可能会犯很多错误。好消息是,周围有很多可以帮助你的人;如果他们不在你的公司,你可以在其他类似的公司、当地的技术组织或互联网上的任何地方找到他们。出于某种原因,软件行业的人喜欢帮助他人。如果你能向他们解释你的项目和目标,任何有经验的人都可以给你一些切实的建议。不过,更具体地说,最好遵循我在本章为统计学家和软件工程师提供的建议。作为一个初学者,在这个过程的这个阶段,你需要承担双重责任来弥补经验的不足。

10.1.4. 如果你是一个团队成员

如果你只是这个项目的一个团队成员,那么沟通和协调至关重要。你不需要了解团队内部的所有情况,但目标和期望必须明确,并且必须有人负责整个团队的管理。

确保有人负责管理

我见过一些奇怪的情况,其中团队没有经理或领导者。在特例团队中,这可能行得通。有时每个人都理解问题,处理自己的部分,并完成工作。这种情况很少见。但即使在这些罕见的情况下,如果团队中的每个人都跟踪每个人的工作,通常效率很低。通常最好有一个人跟踪所有发生的事情,这个人可以回答来自团队内部或外部(例如,客户)的任何关于项目状态的问题。这不是必需的,但通常建议指定一个团队成员负责跟踪与项目状态相关的所有事情。这个角色可能只是简单地做笔记,也可能是一个复杂的正式经理,他举行正式会议并设定最后期限。作为团队成员,你应该知道这个人是谁以及他们的管理职责范围。如果管理方面存在某些不足,你可能想向自己的老板或其他有权威的人提出。

确保有一个计划

大多数做过几个工作的人可能都遇到过一些工作做得不好的上司。有些上司可能很好但不够有效,有些则相反。在第六章(kindle_split_016_split_000.xhtml#ch06)中,我讨论了如何为你的项目制定计划;如果你在一个团队中工作,你可能没有自己制定计划,但你可能参与了关于应该做什么、什么时候做以及由谁做的讨论。这应该导致某种计划,你应该知道谁在跟踪这个计划。如果不是这样,可能存在问题。可能小组领导或经理有一个计划,这个人应该能够随时描述或概述它;如果这个计划不存在、不连贯或不好,你可能需要与团队领导进行严肃且可能困难的对话。管理计划可能不是你的个人责任,但确保有人以合理的方式处理它对整个团队都有好处。

明确期望

人员问题暂且不谈,但在团队工作中,没有明确的工作方向儿乎是最糟糕的事情之一。如果你不知道自己应该做什么,以及对你结果的期望是什么,那么做好工作会很困难。另一方面,只要每个人都清楚这一点,有一些开放式目标是可以接受的。无论如何,如果你的项目部分对你来说并不清晰,确保询问某人(或所有人),以便解决问题。

10.1.5. 如果你在领导一个团队

如果,像上一节中提到的,你是一个承担数据科学项目的团队的一员,那么这些建议仍然适用。但如果你除了这个角色之外还有领导职位,还有一些额外的建议需要补充。

确保你知道每个人在做什么

如果一个团队不知道自己在做什么,那么它就什么也不是,不能团结一致。并不是每个人都必须知道所有的事情,但至少应该有一个人几乎知道所有正在发生的事情,如果你是团队领导,那个人应该是你。我并不是建议你成为一个微观管理者,但我确实建议你对每个项目部分的状态保持积极关注。这种积极关注应该使你对团队和项目状态有足够的了解,以至于你可以在不咨询任何人的情况下回答关于项目状态的大部分一般性问题。如果你无法回答关于项目时间表和你是否认为会错过某些截止日期的问题,那么你对团队活动的兴趣可能还不够积极。对于更具体的问题,例如实施细节,询问相关团队成员可能是可以的。如果你是团队领导和经理,那么在非团队成员面前代表团队是你的工作的一部分,比如客户。

成为计划的守护者

在第六章中,我讨论了为你的项目制定计划的过程,包括针对不同中间结果的多种路径和替代方案。如果你有一个相当复杂的项目,你可能已经制定了一个需要花费一些时间才能理解的计划。如果团队中的每个人都必须做出决定时都花费时间去咨询和理解这个计划,那么这可能会效率低下。作为团队领导,负责计划并在整个项目过程中处理所有与计划相关的问题是一个好主意。这并不是说计划只属于你一个人;恰恰相反。计划应该是整个团队参与制定的,并且计划的某些方面可能仍然属于最合适的团队成员。但可能是一个好主意,即作为团队领导,你是唯一一个对整个计划以及团队在其中的地位都了如指掌的人。如果一个客户问:“你们在开发过程中处于什么位置?”你应该能够向他们解释计划摘要,然后说明团队在计划框架内的位置。

智慧地委派任务

除了拥有一个计划之外,承担数据科学项目的团队需要以一种方式合作,使得工作相对均匀地分配给最适合给定任务的人。软件工程师应该处理更多编程和架构方面的内容,数据科学家应该更多地关注数据和统计学,领域专家应该处理与项目领域直接相关的内容,而任何拥有一定技能的人应该处理与这些技能最相关的任务。我不建议任何人仅仅基于他们擅长什么就被归类,但每个团队成员的专业知识和局限性都与任务的分配相关。我在一些团队中工作过,其中少数数据科学家被当作许多软件工程师对待,结果并不理想。考虑团队成员与要完成的任务之间的关系应该足够了。

10.2. 在进行中修改计划

在第六章中,我讨论了制定完成数据科学项目计划的方法。该计划应包含多个路径和选项,所有这些都取决于项目的结果、目标和截止日期。不管计划有多好,在项目进展过程中,它总是有可能需要修改。即使你认为考虑到了所有不确定性并且对每个可能的后果都有所了解,计划范围之外的事情也可能发生变化。计划需要修改的最常见原因是新信息出现,这些信息来自项目外部,导致计划的一条或多条路径发生变化,或者目标本身发生变化。我将在下面简要讨论这些可能性。

10.2.1. 有时目标会发生变化

当项目目标发生变化时,它可能会对计划产生重大影响。通常目标变化是因为客户对某些事情改变了主意,或者出于某种原因,他们传达了之前未提及的信息。这是一个常见的现象——在第二章中讨论过——客户可能不知道哪些信息对你这个数据科学家来说很重要,因此信息收集和目标设定可能更像是一种启发式方法而不是商业行为。如果你在过程中已经很好地询问了客户正确的问题,那么你可能已经接近一个良好、有用的目标集合。但是,如果出现了新的信息,改变计划可能是必要的。

由于你已经在原始计划中走了一半的路,你可能已经有所成果:初步结果、一些软件组件等等。如果目标变化很大,这些成果可能不再像以前那样有用,而且可能很难说服自己放弃它们。但是,之前为构建某事物所付出的成本在做出未来决策时不应被视为内在因素;在金融行业中,这被称为沉没成本,这是一种无法收回的成本;它永远失去了,无法退款。因为金钱和时间已经花费,任何新的计划(针对未来)都不应考虑它们。但是,你已经生产出来的任何东西肯定是有用的,因此在制定新计划时,这肯定应该被考虑进去。例如,如果你已经构建了一个用于加载和格式化你打算使用的原始数据的系统,那么无论新的目标是什么,这个系统都可能是有用的。另一方面,如果你构建了一个针对原始目标但不是新目标的特定问题的统计模型,你可能想要丢弃该模型并重新开始。

当目标发生变化时,主要关注的是再次经历制定计划的过程,就像在第六章中那样,但这次你有一些额外的资源——原始计划已完成部分所产生的一切——你必须非常小心,不要让沉没成本和其他惯性阻止你做出正确的选择。再次正式运行规划过程通常是有价值的,并确保项目的每个持续方面都符合目标和制定的新计划的最佳利益。

10.2.2. 某些事情可能比你想象的更难

这对我来说经常发生。我记得 2008 年我计划在亚马逊网络服务(AWS)上使用 MapReduce 来计算我使用 R 编写的生物信息学算法的结果。当时,AWS 的文档、教程和简单工具相当稀少,R 中与 MapReduce 相关的包也是如此。我必须承认,我那时相当天真。简而言之,经过许多小时后,我既不知道如何在 AWS 上设置集群,也不知道如何使用 R 来操作它。不用说,我改变了我的计划。

当你计划中的一个你认为会相对简单的步骤变成了噩梦时,这是一个改变计划的好理由。这通常对整体计划的影响不如目标变化那么大,但它仍然可能很重要。有时你可能能够用一个更容易的事情来替换困难的事情。例如,如果你不知道如何使用 MapReduce,你可能会获得访问一个计算集群并在那里进行分析。或者如果某个分析软件过于复杂,你可能会用更简单的一个来替换它。

如果困难的事情不容易避免——例如,当没有可比较的软件工具来替换难以使用的工具时——你可能必须完全根据特定步骤必须被省略或改变的事实来改变计划。做出这个决定的关键是及早并且正确地认识到,弄清楚如何做困难的事情的成本要比做其他事情的成本高得多。

10.2.3. 有时你会意识到你做出了错误的选择

我也经常这样做。有许多原因说明,当你制定计划时,看起来不错的计划在你取得一些进展后可能会开始显得不那么好了。例如,你可能没有意识到某些软件工具或统计方法,后来你意识到那些是更好的选择。或者在你开始使用某个工具后,你意识到它有一个你之前没有意识到的限制。另一种可能性是,你有一些错误的假设,或者得到了关于使用哪些工具的糟糕建议。

在任何情况下,如果你开始意识到之前的某个选择以及将其纳入计划是一个糟糕的想法,那么重新评估情况并基于最新的信息重新制定计划永远不会太晚。建议考虑到目前为止的所有进展,忽略纯粹沉没成本。

10.3. 结果:知道何时足够好

随着项目的进展,你通常会看到越来越多的结果累积,这给了你一个机会来确保它们符合你的预期。一般来说,在涉及统计学的数据科学项目中,预期要么基于统计显著性的概念,要么基于这些结果的实际有用性或适用性的其他概念,或者两者兼而有之。统计显著性和实际有用性通常密切相关,并且肯定不是相互排斥的。我将简要讨论每个概念的优点以及它们之间的关系。

注意,在本节中,我使用“统计显著性”这一术语较为宽松,指的是准确度或精度的总体水平,从 p 值的概念到贝叶斯概率,再到机器学习方法的样本外准确性。

10.3.1. 统计显著性

我在第七章中提到了统计显著性,但提供了相对较少的关于选择特定显著性水平的指导。这是因为适当的显著性水平在很大程度上取决于项目的目的。例如,在社会学和生物研究中,95%或 99%的显著性水平很常见。然而,在粒子物理学中,研究人员通常在将结果视为显著之前需要 5 个标准差的显著性水平;为了参考,5 个标准差(即平均值的五个标准差)大约是 99.99997%的显著性。

根据你使用的统计模型和统计方法,存在不同的显著性正式概念,从置信度可信度再到概率。我不想在这里讨论每个概念的细微差别,但我要强调的是,显著性可以采取多种形式,尽管所有这些形式都表明,如果你重复分析或收集更多类似的数据,你将看到相同的结果,其确定性水平与显著性水平相匹配。如果你使用 95%的显著性水平,预计有 19 个 20 个可比较的分析会给出相同的结果。这种解释并不正式地符合每种类型的统计分析,但对于这里的讨论来说已经足够接近了。

假设你正在进行一个基因组学项目,并且你正在尝试找到与代谢相关的基因。给定你为这个项目开发的良好统计模型,并使用之前提到的 95%显著性水平的重复分析概念,你预计任何达到这个显著性水平的基因在 20 次重复实验中的 19 次也会达到这个显著性水平。这显然留下了一次实验,其中它不会达到显著性水平。假设这个基因确实与代谢有关,这个非显著的结果将被视为一个假阴性,这意味着结果是负面的(不显著),但它本不应该如此。如果你分析了成千上万的基因数据,你预计会看到许多假阴性。

另一方面,因为你只对每个基因进行了一次实验和随后的分析,所以肯定有一些基因不参与代谢,但达到了 95%的显著性水平。从理论上讲,这些基因大多数时候不应该给出显著结果,但你足够幸运,进行了一次罕见实验,其数据使它们变得显著。这些被称为假阳性

在实践中,选择显著性水平意味着在假阴性和假阳性之间选择正确的平衡。如果你绝对需要几乎所有的阳性结果都是真实的,那么你需要一个非常高的显著性水平。如果你更关心捕捉你正集中精力捕捉的所有真实事物(例如,所有真实的代谢相关基因),那么较低的显著性水平更为合适。这就是统计显著性的本质。

10.3.2. 实用性

我所说的实用性与我所描述的统计显著性非常相似,但更侧重于你打算如何使用结果,而不是纯粹的统计置信度。你计划如何使用结果应该在决定你需要它们有多重要中扮演重要角色。

在代谢相关基因的例子中,可能的下一步是选取一组显著基因,并对每个基因进行特定的实验,以在更高的精度水平上验证它们是否真正与代谢有关。如果这些实验在时间和/或金钱上成本高昂,那么你可能希望在分析中使用较高的显著性水平,这样你就可以相对较少地进行这些后续实验,只针对你确信的基因。

在某些情况下,甚至可能是这个例子,具体的显著性水平几乎无关紧要,因为你知道你想要取一些固定数量的最显著结果。例如,你可以选择 10 个最显著的基因,并对它们进行后续实验。如果你本来就不打算超过 10 个,那么你将 99%或 99.9%作为截止点可能并不重要。然而,显著性水平低于 95%可能不太可取,如果没有 10 个基因达到那个水平,可能最好只关注那些有统计证据支持、至少达到最低显著性水平的基因。

你可以通过首先问自己一个问题来开始决定显著性水平:我将如何使用这些显著结果?考虑到你打算对一组显著结果采取的具体行动,回答这些问题:

  • 你想要或需要多少个显著结果?

  • 你能处理多少个显著结果?

  • 你对假阴性的容忍度是多少?

  • 你对假阳性的容忍度是多少?

通过回答这些问题,并将答案与你的统计显著性知识相结合,你应该能够从你的分析中选择出一组显著结果,这些结果将服务于你的项目目的。

10.3.3. 重新评估你的原始准确性和显著性目标

作为你项目计划的一部分,你可能包括了一个目标,即在你的统计分析结果中实现一定的准确度或显著性。达到这些目标将被视为项目的成功。鉴于上一节关于统计显著性和实际有用性的讨论,在处理过程的这个阶段,重新考虑结果的期望显著性水平是值得的,以下是我概述的几个原因。

你现在拥有更多信息

当你开始项目时,你拥有的信息没有现在这么多。所需的准确度或显著性可能是由客户规定的,或者你可能自己选择了它。但在任何情况下,现在你到了项目的尾声,并且有一些实际的结果,你到达了一个更好的位置,可以更好地确定那个显著性水平是否是最有效的。

现在你已经有一些结果了,你可以问自己这些问题:

  • 如果我向客户展示一些结果样本,他们看起来是否满意或兴奋?

  • 这些结果是否回答了项目开始时提出的重要问题?

  • 我或者客户能否根据这些结果采取行动?

如果你对这些问题的回答都是肯定的,那么你处于良好状态;可能你不需要调整你的显著性水平。如果你不能肯定回答,那么重新考虑你的阈值和其他选择重要结果的方式将是有帮助的。

结果的数量可能不是你想要的

无论你之前如何选择显著性水平,你可能会得到比你能够处理更多的显著结果,或者结果太少以至于没有用处。对于结果过多的情况,解决方案是提高显著性阈值,而对于结果过少的情况,解决方案可能是降低阈值。但你应该注意以下几点。

提高或降低阈值,因为你希望得到更多或更少的结果,只要这不会违反你项目的任何假设或目标,这可能是一个好主意。例如,如果你正在从事一个涉及将文件分类为与法律案件相关或不相关的项目,那么你拥有很少的误判是重要的。将一份重要文件错误地分类为不相关可能会造成大问题。如果你偶然发现你的分类算法遗漏了一份或两份重要文件,降低算法的重要性阈值以包含这些文件确实会减少误判的数量。但这也可能增加误判的数量,这反过来又需要更多的时间来手动审查所有阳性结果。在这里,直接降低显著性水平直接增加了后来需要执行的手动工作数量,这在法律环境中可能是昂贵的。与其仅仅改变阈值,不如回到算法和模型,看看你是否可以针对当前任务使它们变得更好。

主要观点是,为了减少或增加任意数量的显著性结果,提高或降低显著性阈值可能是一个好主意,但前提是不影响项目的其他假设和目标。思考阈值变化的全部可能影响是好的,这样你可以避免问题。

结果可能并不完全符合你的预期

有时候,尽管你尽了一切努力,在考虑了所有不确定性之后,你得到的结果似乎并不是你所期待的。你可能有一组重要的结果,从一般意义上讲,似乎并不是你想要寻找的。这显然是一个问题。

一个潜在的解决方案是提高显著性阈值,并确保最显著的结果,即最顶尖的结果,确实符合你的预期。如果它们看起来不错,那么只要变化不会对项目中的其他方面产生不利影响,你就可以使用新的阈值。如果它们看起来不好,那么你可能面临的问题比显著性更大。你可能需要回到统计模型,并尝试诊断问题。

一般而言,随着显著性水平的阈值提高,你的结果集应该更符合你的预期。例如,在法律案例中,文件应该更有相关性;在基因组学案例中,基因应该更明显地与代谢相关,等等。如果情况并非如此,那么最好调查原因。

10.4. 案例研究:基因活动测量的协议

我将通过深入解释我职业生涯早期的一个项目来阐述本章节和前几章节的概念。在获得硕士学位和两年工作经验后,我决定回到学校攻读博士学位。我很快加入了一个在维也纳的研究小组,该小组专注于开发有效的统计方法,用于生物信息学的应用。

我之前没有在生物信息学领域工作过,但我对生物学的原始语言——DNA 序列——一直很感兴趣,所以我期待着这个挑战。我必须学习生物信息学以及相关的生物学知识,以及一些软件和编程工具,因为我的先前编程经验主要是由 MATLAB 和一点 C 组成。但我有两个导师和一个小团队的支持,他们在生物信息学、统计学和编程方面都有不同的经验。

在我的新办公桌安顿下来不久后,我的一个导师带着一个潜在的首个项目来找我,并让我看一下。大致的想法是以严格统计的方式比较微阵列的实验室方案。我的导师已经考虑了实验设置以及可以应用于结果数据的可能数学模型,所以第一步已经完成,这对作为初学者的我来说可能是个好事。作为项目的成果,我们想要知道哪个实验室方案是最好的,我们打算在科学期刊上发表结果,不仅因为实验室的意义,也因为统计学的意义。

在我着手这个项目的过程中,我学到了很多关于生物信息学、数学、统计学和软件的知识,所有这些知识综合起来,正好符合我们现在所知道的数据科学领域。在本节的剩余部分,我将使用本书前几章的概念来描述这个项目,希望这个案例研究能够阐明它们在实际中的应用。

10.4.1. 项目

该项目的目标是评估和比较几种实验室协议在测量基因表达方面的可靠性和准确性。每种协议都是一种化学过程,通过这个过程可以从生物样本中提取的 RNA 被制备用于微阵列的应用。微阵列技术,在过去十年中已被高通量基因测序大量取代,可以测量 RNA 样本中成千上万个基因的表达水平(或活性水平)。为微阵列制备 RNA 的协议在复杂性和每个微阵列所需的 RNA 输入量方面有所不同。根据协议的开发者,这些开发者通常是私营公司,他们可能有一些理由误导研究人员关于其协议的可靠性,以便销售更多的所需套件。尽管如此,能够使用每个微阵列更少的 RNA 将是有益的,因为维护和提取生物样本可能很昂贵。我们希望将协议在实验室中的使用进行面对面竞争,以查看是否任何承诺都得到了兑现,并为了从我们有限的实验室预算中获得最大效益。

我们总共有四种协议,其中一种非常可靠,因此可以说是我们拥有的最接近黄金标准的协议。对于每一种协议,我们会运行四个微阵列,其假定的实验目标是比较雄性和雌性果蝇的基因表达——黑腹果蝇,这是一种比大多数生物更易于理解的常见模式生物。在某些基因中,雄性和雌性果蝇的表达存在一些较大差异,尤其是那些已知与性发育和功能相关的基因,而在其他基因中则不应有太大差异。每组四个微阵列将以染色交换配置运行,这意味着雄性 RNA 在两个阵列上用绿色放射性染料染色,而在另外两个阵列上用红色放射性染料染色;雌性 RNA 在每个阵列上以相反的颜色染色。最终,每个微阵列,对于大约 10,000 个基因中的每一个,都会给出雄性基因表达与雌性基因表达比率的测量值。

因为我们使用其中一种协议作为某种黄金标准,所以我们使用它运行了两套四个微阵列。除了有两套可靠的协议可用于与其他协议进行比较之外,我们还可以比较这两套协议,以了解这种协议的可靠性。如果这两套协议给出了广泛不同的结果,那么这将证明即使是这种黄金标准协议也不是可靠的。

此外,在四个实验方案中,我们使用了比方案通常要求的更少的 RNA,这样我们就可以在一种公平竞争的场景下将这些方案与其他通常需要较少 RNA 的方案进行比较。一套微阵列,每个方案一套,四个方案,加上一套金标准微阵列和两套用于两个低 RNA 版本的微阵列,总共 28 套微阵列。这是我们将会使用的整个数据集。

10.4.2. 我所知道的内容

在开始这个项目时,我知道数学和统计学——至少是硕士水平——以及相当多的 MATLAB 知识。我知道 DNA 和 RNA 转录的基础知识以及基因如何在细胞内翻译和表达的一般原则。在相对较短的时间内,我还学习了关于项目描述的基础知识,包括微阵列的工作原理和实验配置的通用原则。

10.4.3. 我需要学习的内容

我在生物信息学方面有很多东西要学,但奇怪的是,这并不是我需要学习的主要内容。似乎我在相对较短的时间内就学会了关于基因和微阵列的相关知识,但有一些数学和统计学的特定方面是我之前没有见过的,而且我还不知道 R 语言,这是实验室首选的编程语言,因为它在生物信息学方面有特定的优势。

在数学方面,尽管我对概率和统计学非常熟悉,但我从未对数据进行过数学模型的构建和应用。我是一个倾向于贝叶斯理论的数学家,拥有一个完全贝叶斯理论的导师,因此我需要承诺学习构建和应用贝叶斯模型的所有相关含义。

在编程方面,我对 R 语言一无所知,但在导师的建议下,我将使用这种语言。用于加载微阵列数据的 R 库非常好,统计库也非常全面,因此我需要学习大量的 R 语言知识才能在这个项目中使用它。

10.4.4. 资源

除了我的两位导师外,我还拥有几位在生物信息学、数学和 R 语言编程方面经验丰富的同事。我确实处于一个很好的环境中学习 R。当我遇到问题或奇怪的错误时,我不得不大声问,“以前有人遇到过这个问题吗?”通常有人会给出有用的评论,甚至解决我的问题。我的同事们确实很有帮助。当我发现一些他们认为可能之前没见过的编程技巧时,我试图通过告诉小组的其他成员来偿还我的知识债务。

除了人力资源之外,我们还有一些技术资源。最重要的是,我的小组有一个能够从头到尾进行微阵列实验的实验室。尽管微阵列并不便宜,但如果我们认为这样做是谨慎的,我们可以为分析创建我们想要的任何数量的数据。

在计算方面,我有权访问两个大学拥有的服务器,每个服务器都有许多计算核心,因此可以比我在本地机器上快几倍地计算结果。我在编写代码时考虑到这一点,并确保我做的所有事情都可以在多个核心上并行运行。

10.4.5. 统计模型

在这个项目中,可能会有很多变量发挥作用。其中最重要的是真实的基因表达。项目的主要目标是评估每个协议的测量值与真实基因表达水平之间的匹配程度。我们希望在模型中有一个代表真实基因表达的变量。我们没有这个变量的完美测量值——我们最好的是金标准协议,我们知道它并不完美——因此,这个真实基因表达变量必须是一个潜在变量。除了真实基因表达之外,我们还需要代表协议产生的测量值的变量。这些显然是测量量,因为我们有它们的数据,并且可能存在一个相关的误差项,因为基因水平的测量通常很嘈杂。

除了真实基因表达值及其各种测量值之外,还涉及几种类型的方差。通常,我们会查看来自各种个体的 RNA 样本,并且个体之间会有基于他们自身遗传组成的方差。但在这个案例中,我们将雌性果蝇的所有样本混合在一起,同样对于雄性也是如此,因此个体之间不会有生物方差。微阵列臭名昭著的是,每次用相同的生物样本运行时都不会产生相同的结果。这就是为什么我们为每个协议运行四个微阵列的原因:为了估计每个协议产生的技术方差。较低的技术方差通常更好,因为它意味着对同一事物的多次测量将给出接近的结果。另一方面,较低的技术方差并不总是更好的;一个完全失败并总是为每个基因报告零测量值的协议将具有完美的零技术方差,但将完全无用。我们希望在模型中有一个关于技术方差的概念。

我们最终确定的模型假设,每个协议报告的微阵列测量值是基于真实基因表达值的正态分布随机变量。具体来说,对于每个基因 g,测量值 x[n,g] 表示微阵列 n 从金标准协议报告的基因表达值,而 y[m,g] 代表微阵列 m 从另一个协议报告的表达值。公式如下:

x[n,g] ~ N( μ[g] , 1/λ )

y[m,g] ~ N( μ[g] + β, 1/(αλ) )

在这里,μ[g]是基因 g 的真实基因表达值,λ是金标准协议的技术精度(逆方差)。变量β和α代表协议与金标准之间的固有差异。β允许对表达值进行可能的缩放;如果某个协议在所有基因中倾向于具有较低或较高的值,我们希望允许这一点(而不是惩罚),因为这并不直接意味着缩放后的数字是错误的。最后,α代表协议相对于金标准的技术方差。α值越高,表示该协议的技术方差越低。

我提到我喜欢将每一个变量都视为随机变量,直到我确信自己可以固定这些变量的值。因此,上述概率分布中的参数也需要指定它们的分布,如下所示:

μ[g] ~ N( 0, 1/(γλ) )

β ~ N( 0, 1/(νλ) )

λ ~ Gamma( φ, κ )

高斯分布与正态分布相关联,这种关联使得它在作为正态分布的方差参数时既有用又方便。方程中出现的其余尚未讨论的模型参数——γ、ν、φ和κ——我没有将它们视为随机变量,但我对此非常小心。

这些模型参数至少与数据有两步之遥——这里的含义是,它们中的任何一个都没有直接出现在描述观测数据 x[n,g]或 y[m,g]的方程中。由于这种移除,这些参数通常被称为超参数。此外,这些参数可以以非信息性方式使用——这意味着它们的值可以被选择,以便不会对模型的其他部分产生过多的影响。我试图使超参数几乎与模型的其他部分无关,但我检查以确保这一点。在找到最佳参数值(见本章关于模型拟合的部分)之后,检查以确保超参数的值几乎与模型和涉及的结果无关,进行了一种敏感性分析,其中我大幅改变了参数的值,并观察结果是否有所变化。在这种情况下,即使我将超参数乘以 10 或 1000,它们对结论的影响也不显著。

我已经用几段文字和方程式描述了一个相当复杂的模型,但我是一个视觉型的人,所以我喜欢制作模型的图表。数学或统计模型的良好视觉表示形式是有向无环图(DAG)。图 10.2 展示了多协议测量基因表达模型的 DAG。在 DAG 中,你可以看到我讨论的所有变量和参数,每个都在自己的圆圈中。灰色阴影的圆圈是观测变量,而未阴影的圆圈是潜在变量。从一个变量到另一个变量的箭头表示起源/第一个变量作为参数出现在目标/第二个变量的分布中。背景中的矩形或纸张表示存在多个基因g和微阵列重复nm,每个都拥有纸张中包含的每个变量的不同实例。例如,对于每个基因g,都有一个不同的真实基因表达值μ[g],以及一组金标准测量值 x[n,g]和另一组测量值 y[m,g]。这种视觉表示帮助我理清所有变量。

图 10.2. 表示基于不同实验室技术比较基因表达测量值的模型的有向无环图(DAG)

图片

10.4.6. 软件

我在完成这个项目的同时学习并使用 R 语言。R 有一系列优秀的生物信息学包,但我只使用了limma包,它非常适合加载和操作微阵列数据等。作为一个 R 语言的初学者,我决定只使用它来将数据转换成熟悉的格式:一个包含基因表达值的制表符分隔文件。

在用 R 处理和格式化原始数据后,我编写了在 MATLAB 中拟合统计模型的代码,MATLAB 是我更熟悉的一种语言,而且非常适合对大型矩阵进行操作,这是我的代码的一个重要计算方面。

我有一段 R 代码,用于处理和重新格式化微阵列数据,使其成为熟悉的格式,然后我还有相当多的 MATLAB 代码,用于加载处理后的数据并将统计模型应用于数据。在我职业生涯的这个阶段,这是我编写过的最复杂的软件。

10.4.7. 计划

学术时间线通常相当缓慢。这个项目没有真正的截止日期,除了我可能申请发表演讲的即将到来的会议。会议申请截止日期还有几个月,所以我有很多时间确保一切就绪后再提交。

这个项目的主要目标,就像大多数学术项目一样,是将论文发表在一家好的科学期刊上。为了使论文被接受,研究必须是原创的,这意味着它包含了一些以前没有人做过的事情,并且它必须是严谨的,这意味着论文表明作者没有犯任何错误或谬误。

因此,我的首要目标是确保主要科学结果是坚如磐石的。下一个目标是编制额外的统计数据和辅助证据,证明项目中使用的方法与生物信息学的常识和方法一致。最后,我会基于研究写一篇引人入胜的科学论文,并将其提交给会议和/或一家好的科学期刊。

因此,我的计划相对简单。考虑到我当时的知识水平,计划大致如下:

  1. 学习 R 语言,并使用它来将数据转换成熟悉的格式。

  2. 在 MATLAB 中编写统计方法,并使用大学的高性能计算服务器之一将其应用于数据。

  3. 编制一套衡量微阵列数据质量的已知统计数据,并将它们与主要统计模型的结果进行比较。如有必要,解决任何差异。

  4. 写一篇引人入胜的论文。

  5. 向我的导师展示论文,并反复编辑和改进它。某些迭代可能需要额外的分析。

  6. 一旦论文足够好,就提交给会议和/或期刊。

  7. 如果被拒绝,根据期刊审稿人的反馈编辑论文并再次提交。

这是一个相当直接的计划,没有太多相互竞争的利益或潜在的障碍。大部分时间都花在了开发复杂的统计模型、构建软件以及迭代检查和改进分析的各个方面。

在这个项目的进行过程中,我们确实遇到了一个问题,那就是其中一个微阵列协议的数据质量似乎非常差。经过几周的调查,我们的实验室研究人员发现,该协议中使用的其中一种化学试剂的保质期比预期的要短得多。它只在几个月后就变得无效了,而我们没有意识到这一点——可能是因为我们没有使用原始包装,并且与附近的实验室共享试剂。一旦我们弄清楚这一点,我们就订购了一些新的试剂,并重新进行了受影响的微阵列实验,结果更好。

除了试剂问题之外,没有出现重大问题,一切按计划进行。开始时最大的不确定性在于结果会好到什么程度。一旦我计算了它们,并将它们与已知的微阵列数据质量统计数据进行了比较,这个障碍就基本克服了。另一方面,我的导师和我之间就什么构成了好的结果以及是否需要做额外的工作进行了相当多的讨论。

10.4.8. 结果

本项目的目的是客观比较实验室中几个微阵列协议的忠实度,并决定需要哪些协议以及多少 RNA 才能产生可靠的结果。主要结果来自前面描述的统计模型,但正如大多数生物信息学分析一样,除非能够证明它不与已知的适用模型相矛盾,否则没有人会信任一个新颖的统计模型。我计算了四个其他统计量,这些统计量测量了我们打算用主要统计模型测量的忠实度的不同方面。如果这些其他统计量通常支持主要统计模型,其他研究人员可能会被说服这个模型是好的。

从科学论文草案中摘录的结果表可以在图 10.3 中看到。其他统计学的描述在原始的剪裁标题中给出,但具体细节在这里并不重要。重要的是,在四个其他统计学的组合中——技术变异性(TV)、相关系数(CC)、基因列表重叠(GLO)和显著基因数量(Sig. Genes)——有充分的证据表明,我们统计模型的对数边际似然(log ML)是协议忠实度的可靠度量。这些补充统计和分析就像描述性统计一样——它们与数据更接近——提供了易于解释的结果,难以质疑。而且因为它们通常支持统计模型的结果,所以我确信其他人也会看到同时考虑所有这些忠实度宝贵方面的统计模型的价值。

图 10.3. 从提交给科学期刊的草案中剪裁出的微阵列协议比较项目的主要结果表

图片 10fig03_alt

10.4.9. 提交出版和反馈

在科学研究,以及在工业中的数据科学中,你通过严格的研究证明为真的知识可能不会被整个社区接受。大多数人需要一些时间才能将新知识纳入他们的教条,因此,经历一些来自你认为应该知道得更好的人的阻力并不罕见。

在将我的研究论文版本提交给生物信息学会议几周后,我收到了一封电子邮件通知我,它没有被接受作为会议演讲的主题。我感到失望——但并不太失望——因为众所周知,这个特定会议的接受率远低于 50%,而且作为一名一年级博士生,可能处于明显的劣势。

拒绝信中只有少数几位阅读了我的论文并评判其价值的科学家的反馈。据我所知,没有人质疑论文的严谨性,但他们认为它很无聊。激动人心的科学确实会得到更多的关注和媒体报道,但有人确实需要做那些无聊的事情,我完全承认我就是在做这些事情。

在被拒绝后,我回到了计划中的后续步骤,专注于在再次将论文提交给科学期刊之前,如何使论文更具吸引力(如果可能的话,还要更激动人心)。

10.4.10. 它是如何结束的

并非每个数据科学项目都能有一个好的结局。会议最初的拒绝是我们重新定义将重新提交给科学期刊的论文确切目标的一个长期阶段的开始。从最初提交的前不久到项目结束(一年多以后),项目的一些目标和论文的目标一直在变动。因此,这种学术经历在很大程度上类似于我在后来的软件公司的工作经历。在这两种情况下,目标在整个项目过程中很少保持不变。在软件和数据科学中,商业领导和客户经常出于商业原因修改目标。在我的微阵列协议项目中,目标变动是因为我们对良好结果可能对潜在的论文审稿人意味着什么的印象。

由于项目的最终目标是让论文在科学期刊上发表,我们需要意识到期刊审稿人可能会说什么。在每一步中,我们都寻找我们自己论点的漏洞和我们所提供证据中的空白。除此之外,我们还需要考虑那些未参与我们项目的其他研究者的反馈,因为这些研究者是最终将成为我们审稿人的同侪。

目标不断变动可能会让人感到沮丧。幸运的是,没有出现大的目标变动,但确实有数十个小变动。由于目标变动,项目进展中充满了小的计划调整,我花了数个月的时间在可能对我们被好期刊接受的机会产生最大影响的变动之间权衡和优先排序。

最终,基于这项研究没有任何论文被发表。项目领导非常反复无常,无法就单一的目标集达成一致,无论他们或我做了多少修改,他们对研究和论文的状态始终不满意。我在与研究团队合作和发表学术论文方面也相当缺乏经验,无论我多么努力推动,如果没有所有作者的批准,论文通常根本无法发表。

这当然不是一个永远幸福的童话故事,但我认为整个项目提供了在数据科学项目中可能发生的好与坏的事例。事情在项目后期进行得相当顺利——我认为项目的分析和结果是好的——但是当我处于困境中时,被迫做出一些艰难的决定,计划在多次修改后被彻底放弃。数据科学并非像媒体有时似乎相信的那样总是阳光明媚,但它可以帮助解决许多问题。不要让失败的可能性阻止你做好工作,但要注意计划项目和项目可能偏离轨道的迹象;及早发现可以给你纠正问题的机会。

练习

在第二章中首先描述的 Filthy Money Forecasting 个人财务应用场景继续进行,并关联到前几章的练习,尝试以下操作:

1.

列出三个(按角色或专长)在 FMI 与你执行项目计划时最可能交谈的人,并简要说明你为什么可能这么频繁地与他们交谈。

2.

假设产品设计师已经与管理团队交谈过,他们一致认为你的统计应用必须为所有用户账户生成预测,包括那些数据极其稀疏的账户。优先级已经从确保预测是好的转变为确保每个预测都存在。你会如何解决这个问题?

摘要

  • 项目计划可以以多种方式展开;在结果发生时保持对结果的意识可以减轻风险和问题。

  • 如果你是一名软件工程师,小心使用统计学。

  • 如果你是一名统计学家,小心使用软件。

  • 如果你是一名团队成员,尽你的一份力来制定计划并跟踪其进展。

  • 在新的外部信息可用时,修改正在进行中的计划是一个选项,但应谨慎且故意地进行修改。

  • 优秀的项目结果之所以好,是因为它们在某种程度上是有用的,统计显著性可能就是其中一部分。

第三部分. 完成产品并总结

一旦产品构建完成,就像第二部分中提到的,你还有一些事情要做,以使项目更加成功,并使你的未来生活更加轻松。前面的章节更多地关注了我可能称之为原始结果,或者说是从统计意义上讲是好的结果,但可能不够精致,不适合向客户展示。

第三部分首先探讨了通过明确目的来精炼和整理产品形式和内容的优势,即简洁地向客户传达最有效地解决问题和实现项目目标的结果。这些内容以及产品交付的一些其他方面在第十一章第十一章中有详细讨论。第十二章讨论了产品交付后可能发生的一些事情,包括发现错误、客户对产品的低效使用以及需要改进或修改产品的需求。第十三章以一些建议结束本书,这些建议包括干净地存储项目并从项目中吸取教训,以提高你在未来项目中的成功机会。

第十一章. 产品交付

本章涵盖

  • 理解客户希望在结果中看到什么

  • 结果可以采取的各种形式,从简单的报告到分析应用

  • 为什么某些内容应该或不应该包含在结果产品中

图 11.1 展示了我们在数据科学过程中的位置:产品交付。本书的前几章讨论了设定项目目标、提出好问题以及通过严格的数据分析来回答这些问题。所有这些工作完成后,如果你是首席数据科学家,你很可能比任何人都更了解项目的各个方面,你处于一个可以回答关于项目各种问题的位置,从使用的方法和工具到结果的含义和影响。但通常不是一个好主意永远保持这个位置,让自己成为项目及其结果的唯一信息来源。这不仅会使你成为单一故障点(如果你因某种原因无法提供信息,会发生什么?),而且你还会为自己创造永无止境的工作,每当有问题出现时。由于这些可能性,通常最好创建一些总结或编目你的结果的东西,这样客户和其他人就可以在没有涉及你的情况下回答他们的问题——至少是最常见的问题。

图 11.1. 数据科学过程完成阶段的第一步:产品交付

为了创建一个可以交付给客户的有效产品,首先你必须理解客户的观点。其次,你需要为项目为客户选择最佳的媒体。最后,你必须选择在产品中包含哪些信息和结果,以及要排除哪些内容。在整个产品创建和交付过程中做出良好的选择可以大大提高项目成功的可能性。

11.1. 理解你的客户

在第二章中,我讨论了倾听客户并询问可以帮助你理解他们问题的提问,以及提供与他们问题相关的信息。希望我提出的某些策略在数据收集、探索、统计方法的设计和实施以及整体结果方面取得了良好的效果。我将再次探讨理解客户这一想法,重点是创建一个能够最有效地将良好结果提供给客户的产品。

11.1.1. 结果的完整受众是谁?

你现在可能非常了解客户,但可能还有其他人也对结果感兴趣。如果客户是一个团体或组织的领导者,该团体或组织的其他成员也可能成为你结果的目标受众。如果客户是一个组织,该组织内的某些部门或个人可能属于目标受众,但其他人可能不是。如果客户是个人或部门,结果可能会向上级传递,到老板或高管那里,以便做出决策。在任何情况下,最好不要假设你经常打交道的那位客户是你生成结果唯一的受众。考虑围绕客户的个人网络,并判断他们是否是受众的一部分。如果你不确定,可以向客户询问:“你认为谁会希望看到这些结果,为什么?”希望你能对受众有一个良好的了解。

11.1.2. 将如何使用这些结果?

一旦你知道了结果的目标受众,你就想弄清楚他们将如何使用这些结果。这通常比你想象的要困难。

在第二章中,我简要地介绍了你如何与客户讨论交付成果,因此你可能已经对客户希望在结果中看到哪些类型的事物以及他们可能如何使用这些事物有了很好的了解。例如,在生物信息学领域,客户可能打算从你的结果中选取前 10 个候选基因,并对它们进行广泛的实验。如果你构建了一个啤酒推荐算法,客户可能希望他们的朋友使用这个算法,然后品尝推荐的啤酒。可能性有很多。

在一个涉及组织行为的项目中,您使用了社会网络分析的一些技术,客户可能对探索每个个体的联系以及这些联系与个体相似或不同感兴趣。这个例子更多的是一种兴趣,而不是行动。如果客户以“我们想……”开始一句话,

  • “我们对此感兴趣……”

  • “我们想看到……”

  • “我们想知道……”

或者类似的情况,务必进一步追究这个问题,并找出他们打算如何具体采取行动来利用这一新知识。他们打算采取的行动比他们感兴趣的内容更重要。

例如,如果客户打算根据他们发现的内容做出商业决策,那么您可能需要弄清楚他们对错误的容忍度,并将其纳入您向他们展示的定制结果中。误解预期的行动及其后果可能会在以后造成更大的问题。本章末尾的例子给出了一种情况,即在交付产品时可能由于沟通不畅而造成问题。

客户可能会以无数种方式对您提供的结果做出反应,因此在最终确定结果及其格式之前,最好花些时间尝试明确这些反应。您可能尝试让客户通过一个涉及各种适合项目的结果的假设场景,或者甚至想访问他们的工作场所,观察他们的工作流程,并亲自见证他们如何做出决策。与多个人交谈也是一个好主意,尤其是如果您的受众由具有不同经验、知识和对结果感兴趣的个人组成。总的来说,您希望尽可能彻底地了解客户和受众的视角,以及他们对您将提供的结果的期望和意图。这种理解可以帮助您创建和提供帮助客户实现目标的产品。

11.2. 交付媒介

您为顾客创建和交付的产品——可以是多种形式。在数据科学中,产品最重要的一个方面是客户是否被动地从产品中获取信息,或者客户是否积极与产品互动,并能够使用产品来回答无数可能的问题中的任何一个。最常见的被动产品例子是报告或白皮书;客户只能在这份文档的文本、表格和图表中找到答案。最常见的主动产品例子是允许客户与数据和数据分析互动以回答自己问题的应用程序。各种类型的产品可以在被动和主动之间沿任何光谱位置。每种类型都有其优势和劣势,我将在以下章节中讨论。

11.2.1. 报告或白皮书

可能是将结果传达给客户的最简单选项之一,报告或白皮书包括文本、表格、图表和其他信息,这些信息回答了项目旨在回答的一些或所有问题。报告和白皮书可能打印在纸上或以 PDF 或其他电子格式提供。由于报告是一种被动产品,客户在交付时可以阅读它,并在需要时查阅报告,但报告永远无法提供在编写时未包含的新答案——这是报告与更活跃的产品类型之间的重要区别。另一方面,报告和白皮书是一些最简单且最容易消化的产品类型。

优点

报告或白皮书的一些优点包括:

  • 在纸上或电子形式下,报告和白皮书是便携的,并且使用它们不需要任何特殊的技术或知识,除了对报告主题的一般领域知识。

  • 报告和白皮书可以提供客户找到答案的最简单和最快的方式,如果所需的答案存在,且报告简洁且组织良好。对于大多数人来说,在页面上找到和阅读答案比例如打开应用程序或解释电子表格中的数据要容易。

  • 报告还提供了构建有助于有效传达答案、信息、警告和影响的叙述的能力。某些产品类型提供的数据和答案没有上下文,但叙述可以建立有助于报告读者更好地利用其中结果的上下文。例如,由机器学习算法生成的分类,如果客户理解该算法的准确性和适用范围的限制,将更有用。叙述可以在陈述结果之前提供上下文,以防止对结果的误解和误用。

局限性

报告或白皮书的一些局限性包括:

  • 报告和白皮书最大的局限性是它们完全被动。在撰写报告之前,你需要知道客户想要回答哪些问题,并且你需要以易于理解的方式回答这些问题。如果你没有成功地写出一份好的报告,客户会带着问题回来,或者更糟糕的是,将项目视为失败,对你和/或你的团队失去信心,即使结果本身相当不错。

  • 在涵盖所有主要观点并回答最重要的问题的同时,避免分散重要观点的细节,这可能是一项艰巨的任务。

  • 报告和论文只能在当前时间回答问题,可能不适用于未来的时间或当前数据集之外的其他数据集。如果客户将来可能希望重新审视项目的问题或使用另一个数据集,报告可能不是最佳选择。

  • 有些人不喜欢阅读报告。具有各种学习和领导风格的人可能更喜欢以不同的格式查看结果,如果这些人固执且处于权威地位,撰写报告将是浪费时间。

何时使用

  • 你的项目涉及一些关键问题,这些问题可以通过包含表格、图形或其他图表的书面报告完整且简洁地回答。

  • 你的项目的主要目标涉及一次性回答一些问题,而这些答案本身就有用,无需持续更新或扩展答案。

  • 客户希望得到一份书面报告,而你并不觉得这是一个不适当的要求。

11.2.2. 分析工具

在某些数据科学项目中,数据集的分析和结果也可以应用于项目原始范围之外的数据,这可能包括在原始数据之后生成的数据(未来),来自不同来源的类似数据,或由于某种原因尚未分析的其他数据。在这些情况下,如果你能为他们创建一个工具,可以对这些新数据集进行这些分析并生成结果,这可能对客户有所帮助。如果客户能够有效地使用这个分析工具,他们可能能够生成任何数量的结果,并继续在未来以及各种(但类似)的数据集上很好地回答他们的问题。

这样一个分析工具的一个简单例子是一个基于公司及其行业当前财务状况和预期的电子表格,它做出预测。理论上,客户可以将一系列值输入这样的电子表格,并查看如果公司的财务状况发生变化,预测会如何变化。如果电子表格包含复杂的公式和统计方法,客户可能无法自己创建它,但如果它们符合普遍接受的财务建模原则,他们可以理解意图和结果的意义。

你可能作为数据科学项目的成果交付的分析工具可能也是一个软件脚本,它接受一个数据集并对其进行分析,生成客户可以以特定且有用的方式使用的成果。它也可能是一个高度专业化的数据库查询,解决项目的一些问题。作为产品的分析工具可以采取多种形式,但它需要满足一些标准:

  • 分析工具需要在它旨在处理的数据集类型的范围内生成可靠的结果。

  • 应用的数据集集合必须明确指定。

  • 客户必须能够正确使用分析工具。

如果这三个标准都满足,那么您可能有一个良好的产品可以交付。工具的有用性还取决于它能够回答多少项目的问题以及这些问题对项目目标和客户的重要性。

优点

分析工具的一些优点包括:

  • 分析工具允许客户快速回答他们的一些问题,而无需涉及您。这为所有相关人员节省了时间和精力。

  • 在可回答问题的预期范围内,分析工具比报告更灵活。即使在狭窄的范围内,分析工具通常可以提供无限数量的结果,因为输入和数据集会变化。在报告中提供这样的无限结果是不可能的。

局限性

分析工具的一些局限性包括:

  • 建立一个能够可靠且简洁地回答客户重要问题的分析工具通常很困难。如果工具在某个时刻遇到边缘情况并给出错误或误导性的结果,客户可能不会意识到这一点。

  • 客户需要能够理解工具的基本工作原理,以便了解其局限性并正确解释结果。

  • 客户需要能够正确使用工具,否则他们可能会得到错误的结果。如果您无法协助他们,您需要有一定的保证,确保他们不会搞砸任何事情。

  • 如果工具存在错误或其他问题,客户可能需要您的支持。即使分析是好的,数据格式、计算机兼容性以及客户邀请的第三方共享工具等都可能导致意外问题,需要您的关注并减慢客户的进度。

  • 由于创建一个万无一失的分析工具非常困难,这样的工具通常只能复制项目分析中最清晰的部分。准确性、重要性和影响都必须很高,因此分析工具的范围必须缩小到仅限于满足这些严格标准的分析和结果。

何时使用

当以下条件满足时,分析工具可以是一个良好的交付产品:

  • 在您的项目中完成的分析有利于转化为这样的工具,特别是它可以相对容易地使用,并且其结果可以预期是可靠的。

  • 预计客户能够理解工具到一定程度,以便正确使用并正确解释结果。

  • 被动产品,如报告,不足以满足客户的需求,例如客户打算为新数据集复制项目的分析。

11.2.3. 交互式图形应用

如果你想要提供一个比分析工具更偏向于主动的产品,你可能需要构建某种类型的全功能应用。尽管可以争论像脚本和电子表格这样的分析工具也是应用,但在这里,我将在基于命令行的、数字输入数字输出的分析工具和基于图形用户界面(GUI)的点按式应用之间划一个模糊的界限。这些不是定义良好的类别,但我认为这里的概念描述足够了,因为你可以以任何数量的方式组合这两种类型,并将这里列出的优势局限视为适当。前者(命令行风格)我认为属于上一节中讨论的分析工具类别。在本节中,我主要考虑基于 GUI 的应用。

目前,基于 GUI 的应用通常建立在本书前面讨论过的 Web 框架之上。它们不必是 Web 应用,但目前这种类型最为常见。你可能提供给客户的这种交互式图形应用可能包括以下内容:

  • 图表、表格

  • 允许进行不同分析的下拉菜单

  • 交互式图形,例如可移动端点的时间线

  • 导入或选择不同数据集的能力

  • 一个搜索栏

  • 可以过滤和/或排序的结果

这些都不是必需的,但每个都能使用户(客户)在空闲时回答更多与项目相关的问题。

如果你考虑提供交互式图形应用,需要记住的最重要的事情是,你必须设计、构建和部署它。通常,这些都不是小任务。如果你想使应用具有许多功能和灵活性,设计和构建它将变得更加困难。软件设计、用户体验和软件工程在软件公司中都是全职工作,所以如果你在提供应用方面经验不足,最好咨询有经验的人,并在开始之前仔细考虑所需的时间、努力和知识。

交互式图形应用的优势和局限性包括上一节中讨论的分析工具的优势,但我将在此添加更多具体的内容。

优势

交互式图形应用的一些优势包括以下内容:

  • 如果设计得当,一个交互式图形应用可以成为你向客户提供的最强大的工具,因为它能够传达信息和答案。

  • 一个设计良好且部署良好的交互式图形应用易于访问和使用。在应用内部可以清楚地说明如何正确有效地使用应用。

  • 如果使用常见的框架构建和部署,交互式图形应用程序可以变得便携和可扩展。如果你预计用户数量会增加,或者你认为其他客户也想使用它,这可能很有用。

局限性

交互式图形应用程序的一些局限性包括:

  • 交互式图形应用程序的设计、构建和部署都很困难。不仅任务困难,而且可能需要很多时间。

  • 交互式图形应用程序通常需要持续的支持。随着软件和部署平台的复杂性增加,潜在的错误和问题也会增加,支持应用程序和修复错误可能需要相当多的时间和资源。

  • 客户可能不会正确使用应用程序。如果正确使用不明确,或者用户不够小心,可能会得出误导性的结论。

何时使用

当以下情况发生时,交互式图形应用程序可以是一个很好的交付产品:

  • 上一节中关于何时使用分析工具的指南同样适用。

  • 点击式图形用户界面(GUI)在分析工具中更受欢迎,无论是出于易用性还是结果交付的有效性。

  • 你有时间和资源来设计、构建、部署和支持这样的应用程序。

11.2.4. 重新进行分析的操作指南

无论你是否已经决定创建和交付我讨论过的产品之一,记录你为执行项目的最终分析所采取的步骤,并将其打包成客户使用或甚至你自己的操作手册,这可能是一个好主意。

如果你正在处理一个聪明且能干的客户,甚至可能是一位数据科学家,如果提供指令,他们可能能够复制你的分析。如果他们想要分析新的数据或其他类似数据,这可能对他们有所帮助。与构建分析工具一样,目标是让客户能够提出并回答他们自己的问题,而不需要花费你太多时间。提供详细的指令可以实现这一点,而不需要你创建和交付一个高质量、相对无错误的软件应用程序。如果你给他们任何代码,仍然有可能遇到错误,但在这个情况下,有合理的预期,即如果客户需要,他们可以自己阅读、编辑和修复代码。你可能仍然需要提供支持,但这种安排将大部分支持负担从你身上转移出去,前提是客户有能力这样做。

另一方面,如果客户不熟悉某些步骤或对您使用的工具没有太多经验,向他们提供指令集可能会产生各种问题。

优点

一套指令的优点包括:

  • 通常很容易写下你所做的一切,将其与你的代码或其他工具捆绑在一起,并交付给客户。

  • 如果您将来再次回到这个项目并需要以类似的方式分析数据,一套指令对您来说可能非常有用。

局限性

交付一系列指令的一些局限性包括:

  • 客户需要理解您交付的每一件事,并且他们需要能够复制它,可能是在数据集或其他方面有所变化,而不会遇到许多他们无法解决的问题。

  • 交付指令需要客户投入大量的时间和精力,因为他们将不得不阅读和理解一些复杂的分析和工具。

  • 不明确的指令或混乱的代码可能会使可靠地复制分析变得几乎不可能。您需要小心避免这种可能性。

何时使用它

当进行以下分析时,一套指令可以是一个很好的交付产品:

  • 探索性工作是具有挑战性的部分,而应用统计方法对客户来说相对容易。

  • 客户聪明且有能力,在使用您交付的指令、工具和/或代码时不应遇到太多问题。

  • 您认为将来有可能回到这个项目——保留这些指令以备自己使用。

11.2.5. 其他类型的产品

有许多其他产品也可能非常适合您的项目和客户。以下是一些:

  • 一个基于网络的 API,当以某种方式查询时,返回与查询相关的答案和信息—— 当客户希望能够将您的分析集成到现有的软件中时,这可能很有用。

  • 一个直接集成到客户软件中的软件组件—— 这比基于网络的 API 需要更多的协调,因为您必须了解现有软件的架构,但根据软件的使用和部署方式,这仍然是一个好主意。

  • 一个扩展的开发项目,您与客户的软件工程师合作构建一个他们自己将维护的软件组件—— 如果您现在有时间,让其他人根据您的分析构建和维护软件可以节省您以后的大量时间。

  • 一个填充了最有用数据以及/或某些分析结果的数据库—— 对于经常与数据库打交道的客户来说,如果您能想出一种高效且相对可靠的数据库结构方法,使其有用并得到正确使用,那么给他们一个数据库有时可能比 API 或其他组件更方便。

每个都有其自身的一套优势和局限性,其中许多与我在之前列出的其他产品的局限性相对应。如果您正在考虑一个不属于我详细描述的任何类别的产品,那么以与我相同的方式进行思考可能会引导您得出自己的结论。特别是,考虑一个特定产品是更被动的还是更主动的,以及您在近期和长期内(如果您需要提供支持)可能需要的时间要求是很重要的。除此之外,每个项目和每个具体潜在的产品都将有其独特的细微差别,如果您发现自己对后果甚至有一点点不确定,那么咨询有经验的人并征求他们的意见将非常有帮助。如果您能够区分好坏,互联网也可以是一个很好的指导来源。

11.3. 内容

除了决定交付结果的方式之外,您还必须决定它将包含哪些结果。一旦您选择了一个产品,您就必须弄清楚您将用来填充它的内容。

一些结果和内容可能是明显的包含选择,但其他信息的选择可能并不那么明显。通常,您希望包含尽可能多的有用信息和尽可能多的结果,但您希望避免任何客户可能会误解或误用您选择包含的结果的可能性。这在许多情况下可能是一个微妙的平衡,并且很大程度上取决于具体项目以及客户和结果的其他观众的知识和经验。在本节中,我提供了一些关于如何做出包含和排除决定的指导,我还讨论了用户体验如何使产品更有效或更无效。

11.3.1. 使重要的、明确的成果突出

如果您的项目旨在回答一些关键问题,并且您现在对这些问题的答案已经明确,那么这些答案应该突出显示在您的产品中。如果您提交的是一份报告,这些重要、明确的成果的摘要应出现在第一部分或第一页上,而方法及其影响的更深入讨论应在论文的后面进行。如果您提交的是一个交互式图形应用程序,这些结果应出现在分析结果的主页上,或者它们应该通过几次点击、搜索或查询非常容易地访问。

通常,在您交付的任何产品中,最好将最重要的、明确的、不容置疑的、直接和有用的结果放在最前面,以便客户和其余的观众能够轻松找到它们,而无需寻找,并立即理解这些结果及其影响。

11.3.2. 不要包含几乎无法得出结论的结果

向客户报告所有计划的分析中那些没有成功的,可能会很有诱惑力。在研究环境中,这可能是好主意,因为失败的实验有时可以揭示系统是如何工作的,以及为什么其他积极的结果以这种方式出现。但在非学术数据科学中,包括在报告中,那些尝试但未成功的事情可能会分散报告中的重要、可操作信息。

大多数人不需要工作上的干扰;他们可以自己找到。如果某条信息不支持可以直接用于做出商业决策或帮助实现项目声明的任何目标的宝贵情报,通常最好将其省略。然而,你可以自由地记录任何有趣的片段以供自己参考或作为补充报告,这不是客户的主要产品;有时这些信息在项目方向改变或开始新的相关项目时可能会很有用。

11.3.3. 包含对不那么显著结果明显的免责声明

你可能有一些结果介于结论性非结论性之间。决定如何处理这些结果可能很困难,包括和排除它们的原因可能有很多。然而,可以肯定的是,你不想让客户混淆那些绝对结论性的结果和那些只有部分结论性的结果。因此,为了保护自己的声誉,并确保客户理解如何最好地使用你提供的信息,我强烈建议在每个不到 99.9%统计显著或不是完全结论性的结果旁边包含免责声明和注意事项。

例如,假设你正在尝试检测欺诈性信用卡交易,并且客户会立即拒绝你的软件标记为欺诈的交易。如果你的软件准确率达到 99.9%,客户可能不会对那 0.1%被错误拒绝的情况提出投诉。但如果你软件的误报率为 10%,客户可能会非常强烈地投诉。在类似的情况下,如果你确实有 10%的误报率,在你向客户交付结果并让他们采取行动之前,最好将这个比率及其潜在影响告知客户。如果存在任何误解,并且他们在完全理解其影响和限制之前就采取了你的结果,那可能对客户、项目,甚至可能对你自己都是不利的。

如果你想要提供一些不是结论性的但可能仍然有帮助和信息的成果,确保你包含一个免责声明,明确说明这些结果的重要性,局限性,以及如果客户以某种方式使用这些结果可能产生的正面和负面影响。总的来说,客户需要完全理解你对这些结果并不完全确定,并且采取行动可能会有意想不到的后果。如果你能有效地传达这一点,客户将能够基于你的结果做出良好的决策,而你也将处于成功项目的位置。

11.3.4. 用户体验

大多数使用“用户体验”这个短语的人是指一个人如何与软件进行交互的方式。在软件行业,用户体验(UX)设计师已经变成了一种有利可图的职业,这是理所当然的。理解人们如何与软件进行交互并不是一件容易的事情,许多情境已经证明,人们与软件的交互方式对软件最终是否有效有着重大影响。用户体验是有效使用的优秀分析软件与大多数人无法理解如何使用的优秀分析软件之间的区别。

用户体验也可以指报告、分析工具或你可能提供给客户的任何其他产品。客户或受众与你的产品之间的体验就是用户体验,主要目标是确保这些用户能够正确地使用产品,从中得出正确的结论并做出良好的商业决策。如果客户没有正确使用产品,你可能需要重新考虑用户体验。目标是使客户和受众能够并且鼓励他们做正确的事情。

新闻学的倒金字塔

如 图 11.2 所示的流行的倒金字塔概念,展示了新闻故事如何被表示以对读者最为有效。隐含的假设是读者可能不会阅读整篇文章,或者至少读者可能不会全神贯注地阅读整篇文章。基于这个假设,最重要的信息,即 导语(或引言),应该位于文章的开头,接着是支持导语的主体,最后是补充其他内容但不是故事完整性的必要部分的结尾。

图 11.2. 新闻学的倒金字塔^([1])

¹

来自 zh.wikipedia.org/wiki/倒金字塔结构,公有领域图片

图片

对于数据科学项目的报告来说,这意味着可能最有效的方法是遵循相同的模式向客户交付结果:首先用清晰的语言提供最重要的、最有影响力的结果,然后包括直接支持这些结果的细节,最后包括其他有用但不必要的辅助结果。

对于分析工具和交互式图形应用程序(以及其他产品),在设计构建时考虑倒金字塔的概念可能会有所帮助。最重要的结果和信息应该在用户开始使用应用程序时就出现在他们面前。支持性细节可能需要一点额外的努力,但不要太多,然后用户可能需要四处看看,才能找到不那么重要但有用的额外信息。

虽然这当然不应该是数据科学项目的硬性规则,但遵循新闻中的倒金字塔结构在撰写报告或设计将最重要的信息首先提供给客户的应用程序时可能会有所帮助。

没有术语的平实语言

术语对于不从事该领域的人来说可能会令人困惑。你不应该在报告、申请或任何产品中使用术语。如果你确实使用了术语,你应该向客户、听众或产品使用者清楚地解释这些术语的定义。

术语“术语”很难定义。就我们的目的而言,“术语”是一套对特定培训、经验或知识背景的人熟悉的术语或表达方式,但对在实质上不同领域工作的人来说则不熟悉。由于你很少能保证你正在与之交谈的人有与你相似的背景,因此通常最好假设他们不了解你的术语。

我承认我完全反对使用术语,但我同时也看到了在高度专业化的对话和写作中使用术语的价值。术语使得人们能够在自己的领域内,或者至少在那些术语有效的子领域中高效地沟通。在这些情况下,我完全支持使用术语,但在任何可能使人们不理解专业术语的情况下,最好避免使用它们。

当涉及到展示你的工作时,如果你必须与人们交谈或在人们面前讲话,比你认为需要说得更加直白,并且说得更加缓慢也是有益的。使用你听众中很大一部分人都不理解的术语很少是有利的。

当你为应用程序编写报告或文本时,同样规则适用:文本应该被大多数受众理解,即使他们没有某些相关领域的经验。最重要的是,在语言和理解方面,使用术语并不能证明某人知道他们在说什么。根据我的经验,这通常相反。用普通语言解释复杂概念的能力是一种罕见的天赋,在我看来,这是一种比使用术语解释任何事物都更有价值的技能。

可视化

与用户体验设计领域一样,数据可视化可以极大地改善你的应用程序或其他产品,但它通常不是该产品的主要焦点。数据可视化也得到了很好的研究,通常最好遵循那些研究并深入思考数据可视化的人的警告和最佳实践。

爱德华·图菲的《定量信息的视觉展示》是那些想要将他们的数据可视化做到极致的人必读的一本书。关于这个主题还有其他一些很好的参考资料,但图菲通常是最佳起点。你不仅会了解到何时最好使用条形图或折线图,你还会发现一些适用于任何可视化——如地图、时间线、散点图等——的关键原则,例如“鼓励眼睛比较不同的数据点”和“与数据集的统计和文字描述紧密结合。”图菲的书中充满了这样的原则和大量示例,这些示例确切地展示了他的意思。

数据和结果的可视化在报告和应用中可能很有帮助,但如果它们设计得不好,可能会损害产品的意图。花些时间研究和考虑你试图创建的任何可视化的假设和含义通常是值得的。咨询一些数据可视化参考资料,如图菲的书,或具有经验的人,可以在可视化继续作为清晰、简洁的有用信息传达者时带来巨大的好处。

用户体验背后的科学

用户体验的研究是一门科学,尽管有些人并不把它当作科学来对待。直到几年前,当我亲眼目睹了用户体验研究和评估的实际操作时,我才意识到它可能比艺术更具科学性。有许多经过深入研究的原则,关于什么使得一个应用程序易于使用、强大或有效,如果你正在构建一个复杂的应用程序,运用这些原则可以在你项目的成功中产生巨大的差异。如果你正在构建一个应用程序,我鼓励你咨询一位经验丰富的用户体验设计师。有时候,甚至与一位用户体验设计师简短的交谈也能显著提高你应用程序的可用性。

11.4. 示例:分析视频游戏玩法

在与 Panopticon Laboratories 合作的过程中,这是一家以在多人在线游戏环境中描述和检测可疑游戏行为为目标的分析软件公司,我们向一位客户(游戏发行商)提交了一份初步报告,其中包括了对他们游戏社区状况的调查以及一些可疑玩家的名单。为此,就像我们对待所有客户一样,我们将专有的统计模型拟合到他们的数据上;这个模型为每位玩家分配了分数,表示玩家在各个类别中看起来有多可疑或欺诈。我们非常确信得分最高的玩家确实是欺诈玩家,但随着我们向下查看最可疑玩家名单,我们的信心就越来越小。我们合同上有义务向客户提供可疑玩家的名单,我们也知道我们必须与这份名单一起传达某种不确定性。

我们与的客户的安全部门没有雇佣任何数据科学家,因此我们必须小心不要在统计显著性或不确定性上含糊其辞。我们主要需要决定在名单上包含多少可疑玩家。如果我们提供了一个相对较短的名单,我们可能会遗漏一些非常可疑的玩家,这些玩家会继续给游戏发行商造成损失。如果我们把太多的玩家包含在报告中,我们可能会指责无辜的玩家(误报率并不小),并可能误导游戏发行商认为问题比实际情况更大,并可能还导致他们采取行动针对那些无辜的玩家,比如禁止他们在游戏中游玩。

为了解决这个决策问题,我们与客户合作确定,他们确实打算禁止那些高度可疑的玩家,并且他们愿意接受低于 5%的误报率。为了建立误报率,客户计划从我们最初的可疑玩家名单中随机抽样,并逐一手动检查。然后我们可以利用反馈来强化统计模型,并随后生成一份更准确的新报告。通常进行两到三轮这样的操作就足够获得高精度。

当我们使用第一轮反馈生成另一份报告,但在我们完全准备好宣布行为模型 100%完成之前,客户表示他们准备开始根据我们的名单禁止可疑玩家进入游戏。幸运的是,在他们采取行动之前,我们有机会与他们讨论最新的报告仍然不一定具有可操作性,并且他们对这份报告的反馈对于从下一阶段的报告(满足<5%误报率要求)和随后的软件部署中获得必要的可操作情报至关重要。这是一个典型的不知道客户会如何使用我们交付的产品的情况。我不确定他们是否会在事先承认他们的意图(也许他们甚至没有意识到这一点),因此任何询问都可能毫无意义,但尝试和保持警惕是值得的。最糟糕的事情莫过于交付了完全无效的东西,其次是交付了有效但随后被错误使用、造成很大损害的东西。

在下一轮反馈之后,我们提交了一份报告,我们预计其误报率将远低于 5%(以给自己留出余地),并且我们确保客户明白这份名单仍然不完美,并且他们可以预期名单上的一小部分玩家并非坏人。如果他们对所有这些玩家采取行动,他们应该预期会有一些不利影响。

在交付最终报告后,我们开始将他们的数据源连接到我们的实时分析引擎,该引擎为交互式图形网页应用提供动力,该应用提供与报告相同的信息,以及与玩家及其行为互动和学习更多信息的功能。该应用允许客户查看相同的可疑玩家精选名单,并允许他们点击玩家姓名以获取更多关于他们的信息——特别是更多关于他们为何被视为可疑的信息。在许多方面,由于可用信息的增加、结果可以查看的多种方式和格式,以及应用交互性让用户在需要时找到他们需要的答案,该应用在信息量、结果的可视化方式和应用的交互性方面优于报告。此外,该应用具有信息图表和精心设计的用户体验,这使得与数据和结果交互既容易又直观。支持客户部署实时应用是一项相当大的工作量,但该应用似乎比报告对客户更有用。

练习

在第二章中首先描述的 Filthy Money Forecasting 个人理财应用场景中继续进行,并与前几章的练习相关,尝试以下练习:

1.

假设你的老板或其他管理层人物要求你提交一份总结你预测工作结果的报告。你会在报告中包括什么内容?

2.

假设 FMF 网络应用的主要产品设计师要求你为应用用户写一段话,解释你的应用程序生成的预测,特别是关于可靠性和准确性。你会写什么?

摘要

  • 在某种意义上,产品是你整个项目期间一直努力实现的目标;确保格式和内容正确是非常重要的。

  • 尽可能地,产品的格式和媒介应满足客户现在的需求以及可预见的未来的需求。

  • 产品内容应侧重于重要的、结论性的结果,而不是用不确定的结果或其他琐事分散客户的注意力。

  • 最好花些时间正式思考用户体验(UX)设计,以便使产品尽可能有效。

  • 提前考虑产品是否需要持续支持,并据此制定计划。

第十二章。产品交付后:问题和修订

本章涵盖

  • 在产品交付给客户后诊断产品问题

  • 寻找产品问题的解决方案

  • 获取和使用客户反馈

  • 根据已知问题和反馈修订产品

图 12.1 展示了我们在数据科学过程中的位置:在初步反馈后修订产品。上一章涵盖了将产品交付给客户的过程。一旦客户开始使用产品,就可能出现一系列全新的问题和问题。在这一章中,我将讨论一些这些问题类型以及如何处理它们,并讨论客户反馈以及根据遇到的问题和收到的反馈修订产品。

图 12.1。数据科学过程完成阶段的第二步:在初步交付给客户后修订产品

12.1. 产品及其使用中的问题

尽管你尽了最大努力,你可能没有预料到客户使用(或尝试使用)你的产品的各个方面。即使产品做了它应该做的事情,客户和用户可能并没有做那些事情,也没有高效地做。在本节中,我将讨论一些导致产品效果不如预期的一些原因,并给出一些识别和解决这些问题的建议。

12.1.1. 客户未正确使用产品

客户倾向于以除预期方式之外的所有方式使用新产品。有时他们可能不理解在哪里输入哪种文本以及要按哪个按钮,这可能是因为他们没有投入很多精力去弄清楚正确的方法,或者因为很难弄清楚。有时客户试图使用产品来做它本不应该做的事情,通常是因为他们认为产品应该做那件事,或者因为他们试图聪明地扭曲产品的功能来做一些它不应该做的事情。

在任何情况下,以及在其他情况下,客户没有以预期方式使用产品都是问题,因为它可能导致错误或误导性的结果,或者完全没有结果。误导性的结果可能更糟,因为客户可能会对结果产生错误的信心并据此采取行动,这可能导致糟糕的商业决策。

如果客户根本无法获得任何结果,那么他们可能会回来向你寻求帮助,这比他们放弃并完全停止使用产品的替代方案要好得多。

如何识别它

如果你向客户交付了一个产品然后就把这件事忘了,你就永远不知道它对他们的效果如何。也许它很棒;也许它并不好。因此,识别你产品不当使用的第一步是与客户谈论这件事。你在产品交付时与客户交谈了,并且假设你提供了使用说明,但是,就像产品本身一样,说明可能被错误地、部分地或不正确地使用。至少应该有一次后续跟进。

在你用问题打扰他们之前,通常最好给客户一些时间来尝试产品,但在那之前,如果你需要的话,你可以再次确认你在这里帮助他们。当你进行后续跟进并提问时,你应该问一些像这样的问题:

  • 你是否从产品中获得了预期的结果?

  • 你是否能够使用产品来解决预期的商业需求?

  • 产品在哪些方面有所不足?

  • 产品被多少人以及多频繁地使用?

  • 你能描述一下产品的典型使用方式吗?

如果他们描述了任何问题、不良结果或意外情况,那么进一步探索这些问题并试图找出问题所在是很好的。

除了了解客户是否遇到问题外,注意如果客户无法回答你的任何问题,这也是很重要的。这可能表明你正在与之交谈的客户不是主要使用产品的人,或者他们没有从使用产品的人那里得到完整的信息,或者没有人使用产品。作为数据科学家,你的角色可能不是强迫客户使用你的产品,但通常你的工作是帮助他们正确使用它。如果他们因为无法理解而无法正确使用,一点鼓励可能是有益的。

有时候与客户交谈是不够的。如果你想了解他们如何使用产品,你可能需要在客户工作时花一些时间与他们在一起。当他们使用产品时,观察可能就足够了——你当然不希望干扰他们正常使用产品的方式——但在他们进行的过程中询问他们可能是有益的。尤其是如果他们做了意料之外的事情,你可能想知道他们为什么执行那个动作,但重要的是不要指导他们或以其他方式向他们展示如何做任何事情,直到你完全理解他们为什么执行了那个动作。有了理解,你可能能够修复误用的根本原因,而不是症状。

例如,如果客户在产品中点击了错误的按钮,而不是说,“我想你点击了错误的按钮”,更好的做法是问,“你为什么点击那个按钮?”并试图理解他们的推理。假设客户点击一个标记为 SUBMIT 的按钮,是为了在数据库中更改一个姓名-地址对,而你认为他们应该点击一个标记为 UPDATE 的按钮。如果你问他们为什么点击 SUBMIT 而不是 UPDATE,他们可能会说他们认为 UPDATE 是在他们只想更改现有条目的地址而保持姓名不变的特殊情况下使用的。

你设计了应用程序,使得 SUBMIT 添加一个新条目,而 UPDATE 则更改现有条目。因为客户想要更改现有条目,所以他们应该点击 UPDATE 而不是 SUBMIT。如果你指示他们点击另一个按钮,你将学到的很少,但现在你知道了他们行为的推理。你可能会解释说 SUBMIT 用于创建新的姓名-地址条目,而 UPDATE 用于修改现有条目,无论他们想更改条目的哪个部分。通过点击 SUBMIT,他们创建了一个新的、正确的条目,但旧的、错误的条目仍然存在。更重要的是,你了解到SUBMITUPDATE这两个词对客户来说不够清晰,因此可能值得更改它们或进一步教育客户了解这两个按钮之间的区别。

如何纠正

一旦你识别并诊断出产品不当使用的根本原因,有两种方法可以纠正它们:

  • 教育并鼓励客户正确使用产品。

  • 修改产品,使其更清晰地显示哪些操作是正确的。

进一步教育客户可能包括编写新的文档并将其交付给他们。也可能包括举办研讨会、工作坊或一对一会议,在这些会议中,你亲自引导他们了解产品使用的正确协议。哪种方式更有效将高度取决于具体情况。

修改产品以鼓励正确使用是用户体验(UX)的问题,我将在下一部分进行讨论。用户体验问题可能导致了产品的不当使用,但在“用户体验不佳”和“用户使用错误”之间总有一个灰色地带。最终的目标是使产品有效,因此能够实现这一目标的 UX 变化可能是一个好主意,即使它们在其他方面看起来可能不正确。如有疑问,请咨询 UX 专家。

12.1.2. UX 问题

除了客户以某种方式错误使用产品的情况之外,他们可能在使用产品时效率低下。他们可以使用产品来完成他们需要做的事情,并且可以使用它来解决他们的问题,但它需要更多的时间或精力。直接解决用户体验问题可以帮助提高效率。

产品可能以几种方式效率低下。其中一种方式是在工作流程中存在不必要的步骤。假设您的客户通常在产品中进行查询,然后总是以特定方式对结果进行排序。如果默认情况下结果不是以这种方式排序,那么客户除了查询之外还需要执行排序步骤。如果客户每天进行查询数十次或数百次,额外的时间和精力可能会累积起来。更改默认排序方法可以使产品更高效。

如果产品设计用于做很多事情,但客户经常只使用其中的一两件,那么在某种程度上,产品也可能效率低下。例如,也许您对果蝇从胚胎到成虫的几个阶段进行了复杂的遗传分析,并向客户提交了一份报告以及包含每个阶段结果的电子表格。然后您发现 95%的时间客户都在查找成年数据中的特定条目,并将其与其他发展阶段进行交叉引用。在设计电子表格之前,您并没有意识到客户会花这么多时间在成年阶段,但现在很明显,您可以通过添加一个包含客户通常进行的所有交叉引用的额外页面来为客户节省时间。添加更多页面进行更多交叉引用也可能是有意义的,但前提是客户可能会使用它们,并且数量足够少,以保持电子表格的大小可管理。

产品及其界面的外观或布局也可能导致效率低下。这些是用户体验和 UI 设计中一些最直接的问题:

  • 文本是否易于阅读且格式良好?

  • 按钮和文本框是否放置在便于使用的地方?

  • 常见操作是否有快捷键可用?

  • 是否一切都能轻松找到且易于使用?

用户体验和 UI 设计在范围和细节上都远远超出了这些问题,尽管最后一个问题在寻找 UX 问题时是一个万能的范畴。在最明显的情况之外,通常最好咨询 UX 专家。

如何识别它们

容易错过一些用户体验问题,尤其是如果你自己设计和构建了该产品。在你意识到界面存在重大缺陷之前,你可能需要将产品交给其他人。

一种策略是通过客户来诊断用户体验(UX)缺陷。你可能需要问他们以下问题:

  • 你觉得产品容易使用吗?

  • 产品中有什么是你觉得难以操作的?

  • 你发现自己最常做什么任务?

  • 你多久使用产品中的每个部分?

如果你收到的答案仍然让你怀疑可能存在一些用户体验问题,那么花些时间与客户一起观察他们是如何工作的,就像我之前描述的那样,可能是个好主意。

为了更彻底地处理用户体验和客户的操作流程,你可以聘请用户体验(UX)专业人士。他们的教育和经验位于技术、设计和心理学等领域,他们很可能会直接与客户合作,了解产品使用和工作流程的各个方面,然后再提出关于什么好、什么不好以及可能进行哪些改进的建议。

如何解决它们

就像大多数与产品相关的问题一样,你可以处理用户体验问题或更改产品。假设问题不会使产品无法使用,教育客户如何绕过问题是一个可行的选择,或者他们可能能够自己解决这个问题。另一种选择,更改产品,可能需要更多的时间进行重新设计和开发,而选择补救措施需要权衡更改产品的时间成本与客户通过绕过问题节省的时间成本。关于这个过程涉及的内容,请参阅本章后面的第 12.3 节。

12.1.3. 软件缺陷

缺陷是软件中存在的问题。这与用户体验问题形成对比,用户体验问题使产品不直观,但不一定是错误的。以下是一些例子:

  • 计算结果中的错误数值

  • 软件中的错误信息或崩溃

  • 显示不正确的文本、表格、图像等

  • 按钮或其他基于动作的对象没有任何作用

这显然不是一个详尽的列表,但对于那些处理软件错误经验不多的人来说,应该是有启发性的。

如何识别它们

许多软件开发团队在向客户交付软件之前,会进行一次“虫子大搜查”(bug bash),以寻找并修复软件中的错误。在“虫子大搜查”中,几名人员——可能是整个开发团队加上一些非团队中的产品导向人员——花上一小时(或一些固定的时间)来对软件进行测试,试图使其崩溃。虫子大搜查者可能会点击软件中的每一个按钮,在文本框中输入疯狂的数据(负数、荒谬的大数、应该输入数字的地方输入单词等),看看他们能否使软件崩溃、显示不想要的错误信息,或者做它不应该做的事情。每次他们成功找到错误时,他们会记录下详细信息,以便在软件发布给客户之前进行修复。

如果你和你的同事没有自己发现错误,你的客户可能会为你做这件事。他们很擅长这个。我的意思是,因为他们没有从设计到开发参与产品,所以他们比你拥有更少的偏见和符合规范的行为。因此,他们会尝试用软件做你未曾预见的事情,从而扩大可能发现错误的活动范围。

如何解决它们

由于错误,按照定义,是软件中的错误,你通常别无选择,只能修复它们。另一方面,如果一个错误难以修复,并且客户很少遇到它,你可能会证明不修复它是合理的。这比我想承认的更常见。如果你有一款广泛使用的软件,它有一个公开的错误报告系统(Ubuntu 和 Bitbucket 是很好的例子),你可以找到一些已经存在多年的错误示例,因为它们相对难以修复,并且影响很少的用户。尽管错误通常比用户体验缺陷更受重视,但是否修复错误的决策取决于比较修复错误所需的时间和精力与客户留下错误的成本。

12.1.4. 产品没有解决实际问题

这可能是产品最糟糕的结果之一:你花费时间设计和开发了一个产品,该产品满足了客户对功能、需求和问题的所有描述,但出于某种原因,你制作的产品并没有帮助客户实现他们的目标。这种情况发生并不令人愉快。假设产品与目标之间的不匹配不是由使用不当、用户体验问题或软件错误引起的,那么产品在更深层次上可能存在不足。

例如,在我为一家初创公司工作,分析受 SEC 监管的金融公司内部通信时,我的团队开发了检测风险行为的统计方法,并将它们纳入了软件产品中。我们的客户希望找到可能涉及非法交易机密信息的坏员工或可疑员工,无论是内部还是外部,例如可能用于证券内部交易的机密信息。我们为合规官员可能在潜在违法者身上寻找的可疑活动的组合建立了行为档案框架。这些档案可以转化为统计模型可以检测到的风险行为,并为每个行为档案生成了一份最可疑员工名单。

我们认为这可以回答金融监管合规部门的大部分问题,并且到目前为止所有潜在客户的反馈似乎都支持这一假设。但是当最初的合规官员接触到它时,他们对行为分析和风险行为的检测感到失望。他们中的许多人继续使用他们旧的合规工具,这些工具主要是由扫描内部通信的随机样本来寻找可疑的词语和短语组成。用户体验似乎没有明显的缺陷,也没有任何重大错误,但客户并没有感觉到产品回答了他们主要的问题。

回顾过去,我确信该产品已经接近了客户想要的答案,但在合规官员的心中却未能达到令人满意的程度。他们似乎想要以下两种东西之一:

  • 统计方法可以让他们毫无疑虑地知道谁是坏人

  • 一种更好的搜索和过滤通信和员工的方法

不幸的是,我们的统计方法还不够好,无法满足第一个要求,而拥有复杂的统计方法来更好地过滤,如第二个要求,似乎在信任这些统计方法和期望在搜索和过滤中的确定性行为之间产生了认知失调。换句话说,如果客户不能完全信任统计方法,他们宁愿使用他们完全理解的方法,例如简单的搜索和过滤。在产品构建和交付之后,这一点对项目中的任何人来说都不是显而易见的。

好消息——如果可以称之为好消息的话——是我们通过交付产品并从客户那里获得反馈中学到了很多。这些反馈,以及不止一位客户有相同的反应,让我们比之前与客户的所有互动都更多地了解了我们的客户和我们所销售的行业。

并非一切都已失去;软件产品的大部分功能即使没有行为分析也能使用,特别是那些管理、存储和查询数据的部分。但在产品界面上,开发团队必须进行一次著名的转型,这是初创公司如此著名的原因之一。围绕行为分析构建的 UI 必须重新设计成客户感兴趣使用的样子。幸运的是,我们比几个月前对客户了解得多得多,这主要归功于产品的交付和随后的拒绝。

每个这样的案例,即产品未能实现其主要目标,都可能与其他案例大不相同。许多变量都会发挥作用。有时,通过教育客户并说服他们——就像我的例子一样——统计模型对于过滤和排序是可信的,你可以弥合这个差距。有时,你必须对产品进行重大改变。但假设客户已经开始使用产品,认识到存在问题并不困难。

如何识别它

这一点很简单:当客户开始使用产品时,如果存在问题,他们要么会抱怨,要么会停止使用产品。一旦你与客户交谈足够长的时间,排除了不当使用、用户体验和重大错误,你很快就会意识到你的产品是否达到了目标。

如何纠正它

这一点很棘手。每个案例都不同,但你可以利用对客户需求的深入了解来做出最明智的产品决策。选项通常包括进一步教育客户、改变产品、开发新产品以及为现有产品寻找不同类型的客户。根据你的情况,这些选项中的一些可能比其他选项更有意义,但有一点是确定的:在做出决定之前,你应该充分考虑所有选项。

12.2. 反馈

获取反馈是困难的,这里的困难有两层含义。一方面,通常很难从客户、用户或其他人那里获得建设性的反馈。另一方面,如果不将其视为对你投入大量时间和精力构建的产品的一种攻击或误解,就很难听取反馈和批评。在本节中,我讨论了除了极少数情况外,任何反馈都是好事,并分享了一些关于如何充分利用你所获得的反馈的建议。

12.2.1. 反馈意味着有人在使用你的产品

如果你正在对产品获得反馈,这意味着有人在使用产品,这是好事。令人惊讶的是,在我的生活中,我见过多少次有人开发了一个没有人使用的产品。它没有被使用的理由多种多样,但包括以下内容:

  • 客户没有时间学习如何使用产品。

  • 提出产品需求的人不是一个用户,用户们抵制将产品整合到他们的工作流程中,或者对它不满意。

  • 该产品针对的是一个不断变化的目标,到它开发完成时,目标已经移动得太远,以至于产品变得不再有用。

无论顾客可能不使用产品的理由是什么,如果你从某人那里收到反馈,这些理由都不适用,这是好事。

12.2.2. 反馈不是反对

除非一个人是不建设性的或直接无礼的,否则他们的反馈不应被视为对你或你的产品的侮辱。地球上大多数人都喜欢提出建议,让其他人做得更好;根据我的经验,这一点尤其适用于工程师,无论是软件还是其他。他们没有恶意,他们的一些建议可能是有用的。

这可能更多地与商业礼仪有关,而不是数据科学,但如果你能不带个人感情地倾听反馈,你可以学到很多有用的信息。然而,花费数月时间构建某物,然后在使用了 10 分钟后听到有人将其拆解,这是困难的,我见过不止一个开发人员或数据科学家激烈地捍卫一个产品,反对最小的建议;这就是为什么我要在这里提到这一点。在这些情况下,我坚持“冷静的头脑占上风”的原则。

你不必把每个评论和每个建议都放在心上,但通常值得倾听。之后,你可能会考虑这个人的知识和专长,在决定是否认真对待他们的反馈之前。除非他们有统计学学位,否则我可能不会接受 CEO 关于统计方法的建议,同样,除非他们在商业事务上有一些相关经验,否则我也不会接受统计学家在商业事务上的建议。任何人和每个人都可能有一些可以改进产品的建议,而能够冷静倾听并考虑的人,比那些不能的人更有可能实现产品卓越。

12.2.3. 隐含之意

当你忙于倾听任何人和每个人的反馈时,你可以使用一种后处理步骤来获取更多信息。这并不完全精确,但它可以归结为几个应该一起考虑的概念:

  • 人们说的话并不总是精确地表达他们的意思。

  • 人们说的话反映了他们的身份和他们所经历的经验。

  • 人们对一个情况的看法是基于他们自己的感知,而不一定是基于现实。

我知道这些是模糊的陈述,并且它们并不严格适用于数据科学,但它们值得考虑。每次你考虑采纳某人的建议来改变产品时,在投入时间和精力进行改变之前,考虑他们的身份和来源可能是有价值的。我在以下小节中解释这三个观点。

他们所意味着的

如果有人使用你的网络应用程序,并且他们说,“你应该添加一个名为搜索的标签页,它应该搜索所有可用的数据”,直接采纳他们的建议可能不是最好的主意。他们可能真正想说的是,“我希望能够搜索所有数据”,但他们可能并不关心搜索功能是以标签页、页面或页面标题的形式出现。不考虑建议中哪些部分是重要的,就字面意思采纳某人的建议可能是错误的。他们可能没有在提出额外标签页的建议之前考虑所有潜在的 UX 解决方案,即使搜索功能是一个好的建议。

类似地,当我在这章前面提到的初创公司工作,开发分析金融机构内部组织沟通的产品时,我们经常听到这样的建议:用户不应该需要为要查找的可疑类型定义行为档案。用户定义这些档案的需求是产品使用的障碍,我们收到了要求一套无需用户输入即可使用的通用档案的请求。不久之后,我们意识到,尽管产品的典型用户对我们所寻找的可疑类型了解得比我们作为数据科学家和软件开发者要多得多,但构建档案对用户来说仍然是一项过于繁重的工作。我们开始将“你应该包含一套通用行为档案”理解为“我们不希望投入时间和精力去开发行为档案。”后者更为明确和直接,以这种方式理解它阻止了我们开发所请求的通用档案,而我们知道这些档案不会足够具体,无法提供可接受的结果。

这更像是一门艺术而不是科学,但能够区分客户说的是什么和他们的真正意图可以节省在产品修订上的大量时间和精力。

他们是谁

很明显,一个人的身份和经验会影响他们所说的话。在软件相关行业,我注意到某些角色的人思考方式存在一些模式。当然,并不是每个人都符合这些描述,但无论出于什么原因——共同的教育基础、经验、目标等等——以下角色的人通常倾向于共享一些观点和偏见:

  • 销售人员和企业开发者希望一切都能更简单、更清晰。他们的反馈可能看似将统计数据和软件工程简化,以便更容易理解。他们希望产品能够直接一步解决客户的问题(这很少可能实现)。

  • 软件工程师希望使一切更加高效,并希望添加每个功能。他们的反馈可能包括像“如果你能让它更快的话……”,“如果你只是 __________,它也可以做……”,“它做 __________ 吗?为什么不做?”这样的短语。他们希望产品能够处理每个潜在用户的边缘情况,并处理 EB 级的数据。

  • 其他数据科学家希望使分析更智能,使数据驱动的图形信息量达到不可能的程度。他们的反馈可能包括“你使用了哪些统计方法?哦。”和“你可能会通过……来可视化它。”他们希望产品为每个用例提供学术质量的结果,并具有足够的灵活性来进行强大的统计分析。

  • 领域专家主要希望他们的问题得到解决;他们对软件或数据科学经验不多,但愿意学习他们需要的知识,在一定范围内。他们的反馈通常谨慎且好奇,例如“我能做 __________ 吗?”和“我该如何 __________?”因为他们希望问题得到解决,所以他们几乎对任何策略或方法都持开放态度,除非他们意识到产品无法解决他们的问题。

我希望这些是你可以用来考虑来自各种商业角色的人的反馈的有用视角,尽管它们显然并不完美。所有反馈都可能很重要,但是否采取行动取决于你的目标是否与反馈者一致。

他们的看法

在他们给出反馈之前,考虑他们对产品及其可解决的问题的观察是很重要的。

在最近与一家初创公司合作时,一位顾问建议,对于我构建的复杂分析组件,我们应该停止使用文件系统进行中间存储,而是将中间和最终结果放入数据库。我理解他的想法:显然,数据库在除了最简单的读写之外的所有情况下都比文件系统快,所以转向数据库是一个明显的改进,无论哪种类型。此外,我们正在处理许多 TB 的数据,这在 2015 年是个很大的数字。这并不是我第一次收到这样的建议,所以我处理了双方论点的经验。鉴于我的经验,我没有被诱惑去遵循它。我当时不遵循这些建议的原因是:

  • 数据库是一个必须在每个环境中配置和管理的软件组件,无论是测试期间还是实际客户部署期间。这是一项相当大的工作和维护任务。

  • 我是这个分析软件组件的开发或维护的唯一一个人,我在数据库的多重部署方面没有太多经验。

  • 文件系统存储运行得相当高效。

  • 我进行了一些代码分析,发现大部分时间都花在计算上,而不是存储上。切换到数据库最多只能带来不到 2 倍的效率提升。

那位建议我们切换到数据库(任何数据库)的初创公司顾问并不知道这些事实。作为数据科学家和开发者,我了解这些事实,我知道他不知道,所以我能够根据他所知道的和不知道的来考虑他的评论。我知道如果我们切换到数据库,每次部署都会给我带来更多的工作,而我们没有钱雇佣其他人,所以不使用数据库更可持续。许多软件工程师可能不同意我的观点,但我专注于从软件中获得正确的结果,而不是关心它是否比预期多花 1.5 倍的时间。

要描述每个人的感知如何与你的感知不同是非常困难的,但我想要强调的是,每个人的感知至少在某种程度上与你的感知不同。他们的反馈应该通过他们对产品和情况的了解来审视。

12.2.4. 如果必须,请寻求反馈

一些数据科学家交付产品后就忘记了它们。一些数据科学家交付产品后等待客户给出反馈。一些数据科学家交付产品后不断骚扰客户。我不想暗示最后一类是最好的,但经常跟进客户以确保你交付的产品解决了它旨在解决的问题是个好主意。你可能会想这样做有两个主要原因。

声誉

你可能想要跟进客户的首要原因可能是,你的项目成功以及客户满意对你、你的团队和雇主(如果有)的声誉都是有益的。当然,花几周或几个月时间建造一些无用的东西并不有趣,这是件遗憾的事。但更重要的是,这个项目是你工作的关键部分,应该反映出你一般的工作质量以及客户未来可以期待的质量。跟进客户可以让你解决任何问题——特别是那些容易解决的问题——并大大提高客户满意度。

学习机会

在向客户寻求反馈——玩长线游戏——的过程中,你可以提升自己在数据科学方面的技能。在向客户交付产品时,没有多少方法可以让你在不与客户交谈的情况下了解你做错了什么以及你做对了什么。询问他们事情进展得如何——就像本章所描述的那样彻底——为你提供了唯一洞察你设计和构建的产品成功或失败的机会。

12.3. 产品修订

每个产品开发者都希望他们能够将产品交付给客户,产品能够解决客户的所有问题,然后大家都能幸福地生活下去。这种情况很少发生。就像数据科学过程中的每一步一样,产品使用的初始阶段也面临着不确定性。通常会有问题。有时,如本章前面所讨论的,这些问题可以通过与客户合作,帮助他们更有效地使用产品来解决或缓解。但在其他情况下,问题可能很难或无法通过培训客户来绕过它们来解决。这时,对产品进行修订或以某种方式改变它就变成了最好的——或者唯一的选择。

在本章前面,我讨论了产品可能出现的不同类型的问题以及如何解决这些问题。在某些情况下,我建议可以通过改变或修订产品本身来解决这些问题。进行产品修订可能会很棘手,找到合适的解决方案和实施策略取决于你所遇到的问题类型以及你需要改变什么来修复它。在本节中,我讨论了产品修订作为项目某些方面先前不确定性的直接结果,设计修订的过程,以及为了最大化项目成功的可能性,决定进行哪些产品修订。

12.3.1. 不确定性可能使修订成为必要

在整个项目过程中,如果你对不确定性以及每一步中可能出现的众多结果都保持着警觉,那么你现在发现自己面对的结局与之前预期的不同,这也许并不令人惊讶。但同样的警觉性几乎可以保证你至少接近一个可行的解决方案。从实际的角度来看,这意味着你从未期望第一次就能做到 100%正确,所以当然会有问题。但如果你一直很勤奋,问题就会很小,修复起来相对容易。

可能的情况是,你知道这些具体问题可能会出现,甚至可能已经为这种可能的结果做了计划。如果是这样,那么在整个项目过程中,你必须保持着难以置信的警觉性,现在你处于一个极佳的位置,可以找到并实施可行的解决方案来解决问题。如果你没有预见这些问题,那么你属于绝大多数人;知道存在不确定性并不等同于知道哪些结果可能会发生。但现在,你知道在产品交付后哪些问题出现了(以及哪些成功),你比以往任何时候都处于一个更好的、更了解的位置,可以达成项目的主要目标。然而,这将需要你进一步的努力来设计和构建必要的产品修订。

12.3.2. 设计修订

通过设计修订,我指的是确定哪些产品修订是必要的以及它们应采取何种形式的过程。设计过程从概念上识别并想象产品变化,而不实际进行这些变化。

设计修订在很大程度上与最初设计产品相同,只是已经存在一个产品,希望大部分内容你仍然可以使用。但尽管你可能能够继续使用现有产品的大部分,但可能最好不这样做。借用金融术语,你投入在已经建成的事物上的时间和精力是沉没成本,因此,在做出未来决策时,不应考虑构建现有产品的成本。在做出这些决策时,你可以将现有产品视为可利用的资产。

在本章前面,我谈到了识别和寻找解决产品各种类型问题的补救措施。此时,你应该大致了解问题是什么以及如何修复它,但我没有给出关于补救措施本身的很多具体信息。在这里,我将更多地讨论设计这些补救措施,即针对三种类型问题的产品修订。

缺陷

从设计角度来看,修复缺陷是微不足道的。在某个特定上下文中,软件在显然应该做其他事情时却做了某事。修订的设计是让软件停止做前者并做后者。然而,修订的工程可能并不那么简单。

UX 问题

UX 问题都与设计有关。棘手的问题可能需要 UX 专家的帮助。鉴于产品的第一个版本包含一个 UX 问题,除非问题及其具体解决方案现在非常明显(有时确实如此),否则再次尝试 UX 设计很可能也会出现问题。对于非平凡问题,可能值得在 UX 重新设计中投入比原始设计更多的努力,这通常需要具有 UX 经验的人的帮助。至少,花些时间与用户交流,了解他们如何使用产品以及问题是如何出现的,这是值得的。

功能性问题

关于分析软件应用,我使用功能一词来表示在幕后发生的任何操作——数据收集、数据流、计算、存储等,以及应用向用户交付信息和结果的一般能力。因此,功能问题可以是,例如,应用花费过多时间向 UI 交付结果,使用了不正确的统计模型,或者关键数据没有被正确使用。

对产品功能进行修订通常需要架构变更,这可能会对应用程序的许多其他方面产生后果。这样的修订不应被轻视。通常最好与参与产品设计和工程的所有人员进行讨论,以便运行考虑中的修订可能带来的影响。对于更复杂的分析应用,任何修订可能有多么剧烈和深远可能并不完全清楚,可能需要一些调查才能找到答案。

当设计功能上的重大修订时,我通常召集所有对产品及其架构有关键知识的人员,并遵循以下步骤:

  1. 明确陈述一个最佳修订,针对用户最终结果而言。

  2. 从所有在场人员那里获取关于应用程序中影响的反馈。

  3. 考虑是否可以实现最佳修订,或者修订的某些方面是否不可行。

  4. 获取所需时间和努力以实现所需变化的估计。

这些步骤可以重复进行,以处理任何数量的建议修订。然后可以根据所需的时间和努力、修订达到最佳效果的接近程度以及整体对产品和项目成功的影响来比较这些修订。

12.3.3. 工程修订

通过工程修订,我指的是将修订设计纳入产品的过程。对于分析应用来说,这意味着编写代码或整合新工具。

就像产品设计一样,产品架构可能具有相当大的惯性,但它应被视为既定的成本和资产。产品架构的许多部分可能仍然有用,但其中一些可能不是。决定何时丢弃大量代码可能是一个艰难的决定,但有时这是正确的事情。

另一方面,可能有一些巧妙的方法可以利用当前的架构来满足修订设计的需要。使用这些巧妙解决方案时请自行承担风险。它们可能现在能节省您的时间,但如果您需要进一步修订或添加产品,可能会花费您大量的时间和精力。如果您发现自己正在考虑一个巧妙解决方案——一个以非原始意图的方式组合现有软件组件的解决方案——那么向自己和团队提出以下问题可能是有益的:

  • 如果我重新构建整个产品,我会这样构建吗?

  • 为什么不呢?

  • 不适用的原因是否适用于当前情况,并指出我通过工程这个巧妙解决方案而承担了不必要的风险?

在这里有一点预见性可以走很长的路。一次巧妙的修订通常是可以的,但在巧妙的修订之上进行第二轮修订几乎可以保证会有更多、更巧妙的修订,修订的周期可以继续到整个代码库都变得如此巧妙,以至于没有人知道如何修复或修订任何事情,而不至于像一团线球一样解开应用程序。

尽管有巧妙的解决方案,但我还想根据几种类型的问题多说一点关于工程修订的内容。

缺陷

软件缺陷的生命周期是发现它、诊断它和修复它。发现它通常是容易的。诊断缺陷的根本原因可能容易也可能困难,这取决于缺陷与应用程序的深度和相互关联程度。修复缺陷也可能容易或困难,这种难度水平可能独立于缺陷是否难以诊断。有些缺陷容易诊断,但它们的修复却非常困难,反之亦然。

在大多数情况下,制作包含缺陷修复的产品修订版是直接的。你诊断并修复缺陷。涉及的设计很少,几乎不存在关于如何或是否修复缺陷的问题。但在一些不幸的情况下,缺陷的诊断或修复可能极其复杂,以至于无法在合理的时间内完成,甚至根本无法完成。在这些情况下,你必须就缺陷的影响以及你是否可以以某种有用的方式绕过它做出重要决定。容忍缺陷从来都不是一件有趣的事情,但有些缺陷固执到足以让你不得不让它们存在一段时间——或者永远存在——如果不是因为业务压力要求项目必须完成的话。在这种情况下,最小化缺陷的影响就成为修订的目标。

用户体验问题

旨在修复用户体验问题的修订通常设计密集。确定要做什么通常比实际进行修订本身要困难得多。当然,一些用户体验方面确实需要复杂的工程,但大多数不需要。存在许多 UI 框架,它们能够实现各种标准行为,而现有的大量软件应用程序使得很可能有人已经构建了一个与您类似的用户体验。在将修订版交付给客户之前,务必确保您的用户体验设计师彻底检查您的修订。

功能性问题

解决功能问题的修订可能对您的应用程序产生深远的影响。我在第 12.3.2 节中对这类修订的评论也适用于此处。最好召集团队,讨论各种期望修订的所有后果和影响。

除了已知或预见到的后果之外,还可能出现其他意外的影响。改变软件中的某个对象以使其具有新的功能可能会改变该对象在代码中使用的每个地方的应用程序行为。面向对象编程因其依赖于副作用来完成工作而臭名昭著(但也备受喜爱),其中一些副作用在首次阅读代码时可能并不明显。对于复杂的应用程序,软件开发者依赖于单元测试和集成测试(以及其他类型的测试)来帮助确保软件更改不会引入新的错误。如果你担心在修订过程中引入错误(即使你不担心),在代码中包含测试可能是一个好主意。

许多软件工程师认为(我也同意他们的观点)这是开始正式测试应用程序代码的较晚时间。理想情况下,你会在编写任何代码时就编写测试,并且始终为你的应用程序提供 100%的测试覆盖率。但作为一个数据科学家而不是软件开发者,我很少在第一次编写代码时就编写测试。我通常将实验或研究代码调整为应用程序代码,测试只是事后考虑。只有在意识到我将进行一系列修订,并担心在这个过程中会破坏某些内容时,我才开始编写测试。这可能不是理想的流程,但这是我倾向于做的事情。无论如何,编写测试比完全不写要好,如果在过程中还没有包含测试,这个阶段是一个很好的时机。你可以向经验丰富的软件工程师咨询或查阅任何流行的软件测试参考书籍,以获取更多关于如何使用测试来帮助防止在开发或修订产品过程中引入错误的信息。

12.3.4. 决定要修订哪些内容

一旦你认识到产品中的问题并想出如何修复它,剩下的决定就是是否修复它。有些人最初的倾向是每个问题都需要修复;这并不一定正确。你可能不想进行修订以修复产品中的问题,就像你可能想修复它们一样,这里有一些例子。

这里有一些支持进行修订的可能原因:

  • 你将拥有更好的产品,更满意的客户,以及更成功的项目。

  • 你有合同义务进行某些类型的修复或改进。

  • 未来的项目、产品或修订依赖于这个,现在进行修订将提高这些项目的质量。

  • 客户愿意为修订支付更多费用。

  • 客户要求进行修订,满足这一请求将有助于你维护或改善与客户的关系。

而以下是一些反对进行修订的可能原因:

  • 修订并没有显著提高产品。

  • 修订困难或耗时。

  • 你的合同义务已经履行,你感觉产品,正如人们所说,是“足够好,可以用于政府工作。”

  • 你怀疑客户不会注意到这个问题,或者它不会对客户产生重大影响。

这里有一些你应该一般考虑的变量:

  • 进行修订所需的时间和精力

  • 修订对产品及其有效性的实际影响

  • 合同义务

  • 其他可能的修订及其影响、时间和精力

  • 开发团队之间的冲突义务

最终的决定,就像许多决定一样,归结为权衡每一方的利弊,然后做出决定。在我的软件开发和数据科学经验中,重要的是要停下来考虑选项,而不是盲目修复发现的每个问题,这可能会花费大量时间和精力。

练习

在第二章中首先描述的 Filthy Money Forecasting 个人理财应用场景继续进行,并关联到前几章的练习,尝试以下操作:

1.

假设你已经完成了你的统计软件,将其与网络应用集成,并且应用中出现的多数预测似乎都是不正确的。列出三个好的地方来尝试诊断问题。

2.

在部署应用后,产品团队通知你,他们即将向选定的用户发送关于应用的调查。你可以在调查中提交三个开放式问题。你会提交什么?

摘要

  • 客户往往会找到完全新的方法来破坏你的产品。

  • 在产品中识别、诊断和解决问题的过程应该是故意和仔细进行的。

  • 获取反馈是有帮助的,但不应该被当作理所当然。

  • 产品修订应该以与设计并构建产品本身时相同(或更高)的谨慎程度进行设计和工程。

  • 并非每个问题都需要修复。

第十三章. 结束:将项目收尾

本章涵盖

  • 清理、记录和存储项目材料

  • 确保你或其他人可以在以后找到并重新启动项目

  • 项目总结反思和教训

  • 数据科学经验的项目及其作为工具的产品

图 13.1 显示了我们在数据科学过程中的位置:结束项目。当数据科学项目结束时,可能会感觉所有的工作都已经完成,剩下的只是修复任何剩余的错误或其他问题,然后你就可以完全停止考虑项目,并继续下一个(尽管继续产品支持和改进)。但在宣布项目完成之前,你可以做一些事情来增加未来成功的可能性,无论是扩展这个项目还是完全不同的项目。

图 13.1. 完成阶段的最后一步和整个数据科学流程的最后一步:整洁地结束项目

图片描述

现在采取行动可以增加你在未来成功的机会。一种方式是确保你可以在未来的任何时刻轻松地重新开始这个项目,重新做它,扩展它或修改它。通过这样做,你将增加后续项目成功的几率,与几个月或几年后挖掘出你的项目材料和代码,发现你完全不记得你做了什么或如何做的情形相比。增加未来项目成功机会的另一种方式是从这个项目中尽可能多地学习,并将这些知识带入每一个未来的项目。本章重点讨论这两件事:整洁且可重用地存放项目,以及尽可能多地从整个项目中学习。

13.1. 整洁地存放项目

在你的待办事项清单完成,没有更多任务在眼前时,可能会让人想要将项目抛诸脑后。你通常还有其他项目等待处理,你更愿意将时间用于推进这些项目,而不是纠结于已经完成的项目。但除了其他情况之外,以下三种未来情景,如果你现在花时间确保项目材料组织有序且安全存放,你会更加快乐:

  • 有人在项目、数据、方法或结果方面有问题。

  • 你可以基于这个项目开始一个扩展或新的项目。

  • 同事基于这个项目开始一个扩展或新的项目。

如果这些问题在相关项目完成后立即出现,这些情况并不令人畏惧。但如果时间过去了一段时间——几个月或几年——记住细节变得越来越困难。如果你无法记住它们,你需要查阅项目材料来找到答案。你需要花费多少时间和精力来做这件事,完全取决于你在结束项目时组织得有多好。

在本节中,我将讨论提前考虑这些未来情景,但首先我会介绍两个与整洁存放项目最相关的实用概念:文档和存储。为了在未来的情景中节省时间和精力,你应该确保你的项目材料易于找到(存储)和易于理解(文档)。

13.1.1. 文档

项目文档可以根据可能使用它的人以及理解它所需的技术专长分为三个级别。客户或用户看到的是顶级文档,而只有构建产品的软件工程师通常能看到最低级别的文档,中间是针对需要了解软件工作方式但不需要修改它的人的技术文档。与设计和构建产品类似,了解你的受众有助于创建文档。

用户文档

顶级文档,用户文档,是客户在产品或产品附带材料中看到的内容。这包括任何报告、结果摘要以及您构建和交付的任何应用程序中的文本和描述。用户文档应包含某人按您期望的方式使用产品所需的所有信息。它是驾驶汽车所需信息的概念等价物,没有任何关于汽车维护或如何建造汽车的参考。

用户文档可能包括以下内容:

  • 如何使用产品

  • 产品的主要功能和限制

  • 可能有助于用户的任何不明显的信息

  • 警告不要以已知会导致问题的方式使用产品

通常,用户文档出现在产品本身或产品附带材料中。以下是一些常见的用户文档形式:

  • 网络应用内的帮助页面

  • 一份书面文档,向所有用户解释如何使用产品

  • 为用户提供的一个维基或其他资源

通常,用户文档的范围不会超出用户界面或任何其他未明确设计供客户和用户查看的内容。关于产品幕后工作方式的信息应保留在较低级别的文档中。

开发者文档

中级水平的开发者文档包括软件开发者如果打算集成或以编程方式使用产品,或者如果他们打算自己构建类似的东西时,希望了解的信息。开发者文档应描述架构和接口,而不应过多深入到具体的软件实现。它是一个完整的表面级描述加上概念性的内部描述,其性质类似于进行汽车换油或更换刹车时所需的信息,而不太关注汽车是如何建造的。在更换机油时,了解汽车内部有发动机,发动机与油箱相连可能是有帮助的,但这些具体构造——包括正时皮带、活塞、阀门等——与这项任务在很大程度上是无关的。

开发者文档可能包括以下内容:

  • 对 API 和其他集成点的详细描述

  • 实施的具体统计方法

  • 软件架构或对象结构的概述描述

  • 数据输入和输出,包括内容和格式描述

开发者文档通常不会出现在产品本身中,除非产品的用户是软件开发者。以下是一些常见的开发者文档形式:

  • 一个维基、Javadoc、pydoc 生成的文档或其他描述 API 的文档

  • 代码仓库中的 README 文件

  • 软件架构或对象模型的图表或其他描述

  • 描述所使用统计方法的报告

  • 数据源和格式的书面或图形描述

  • 集成或程序化使用软件产品的代码示例

通常,我这里所说的开发者文档描述了软件产品的内部工作原理或方法,而不一定涉及产品代码和详细结构。

代码文档

最低级别的文档,代码文档,向产品开发或维护团队中的软件开发者说明代码在最低层面的工作原理,以便他们可以修复错误、进行改进或扩展产品的功能。在我的早期汽车比较中,代码文档的对应物解释了如何修复、拆解、重新组装和调整汽车的任何部分,无论是内部还是外部。这个比较的一个有趣但恰当的方面是,这种低级文档没有说明如何驾驶汽车。驾驶汽车和修复汽车需要几乎完全不同的知识体系,尽管必须承认它们相互辅助。对于代码文档和用户文档来说,也是如此。可能会有一些重叠,但一般来说,它们将是不同的。

代码文档可能包括以下内容:

  • 对象、方法、函数、继承、用法等的描述

  • 对象结构和架构的高度详细描述

  • 解释为何做出某些实现选择的说明

代码文档通常与代码本身一起或在其附近。一些常见的代码文档形式包括:

  • 与其描述的内容并排的代码注释

  • 代码仓库中的 README 或其他文档

  • 软件架构图

  • 使用 Javadoc、pydoc 或其他类似应用程序自动生成的文档

通常,代码文档应该提供足够的信息,使产品团队中的任何开发人员都能以合理高效的方式导航、理解和处理代码库。没有良好的代码文档,这样的开发人员将不得不通过艰难的方式了解代码的工作原理:通过阅读代码本身。对于足够复杂的软件产品或本身不易读的代码来说,这是一个没有人愿意陷入的兔子洞。

13.1.2. 存储

通过“存储”,我指的是你可能会存储所有材料——代码、文档、数据、结果等——的方式。更方便、更易于导航的位置使得找到后来出现的答案或必要时完全恢复项目变得更加容易。此外,更安全、更可靠的位置有助于确保材料在未来很长时间内继续存在。

在本节中,我考虑了材料存储的位置以及它们存储的格式。因为我正在混合这两个概念,所以列出的替代方案可能不是彼此的直接替代品,但通常很难将位置与格式分开——例如,远程 Git 仓库——所以我概述了每个方案的能力,以便清楚地了解哪些替代方案可以用于什么目的。

本地驱动器

通常,存储你的代码、数据和其它文件最简单的地方是你的本地电脑,对于不那么重要的项目来说,这是一个合理的选择。但是,当你只使用一台机器并且没有在其他地方备份文件时,会存在严重的风险和限制。参见表 13.1 以了解何时以及为什么使用本地驱动器。

表 13.1. 使用本地驱动器进行存储的利弊
优点 缺点 适合
易于方便,可以存储任何文件格式,你拥有完全的控制权。 磁盘空间有限,如果电脑损坏或丢失,可能会出现单点故障或数据丢失。没有你的允许,其他人无法访问它。 容易重做的个人项目,无论何时你都有其他地方的备份
网络驱动器

工作场所的共享网络驱动器通常比你的本地电脑驱动器大,IT 部门可能还会定期将其备份到另一个位置。通常,你可以通过登录系统或使用已明确连接到网络和驱动器的电脑来连接这样的驱动器。参见表 13.2 以了解何时以及为什么使用网络驱动器。

表 13.2. 使用网络驱动器进行存储的利弊
优点 缺点 适合
可以存储任何文件格式,由内部管理,例如 IT 部门管理,通常空间充足。如果你选择,其他人可以访问你的文件。 访问有限,如果没有在其他地方备份,可能会出现单点故障。如果许多人使用它,可能会变得杂乱无章。 人们在之间共享的项目,无需额外工作即可将所有文件存储在一个地方
代码仓库

我在这本书中之前提到过源代码存储库,与 Git 等版本控制工具一起使用。对于除了最简单的编码项目之外的所有项目,强烈建议将代码提交到存储库并将它推送到远程位置。你可以或你的组织可以管理自己的存储库服务器,或者你可以使用像 Bitbucket 或 GitHub 这样的流行基于网络的远程存储库提供商。这不仅为你的代码提供了一个重复的、管理的存储位置,而且还提供了一个查看和共享代码的中心位置。参见表 13.3 以了解何时以及为什么使用代码存储库。

表 13.3. 使用代码存储库的利弊
优点 缺点 适用于
适用于共享代码和共同工作的远程服务器既充当版本控制也充当备份副本。 不适用于除了代码和其他基于纯文本的文件格式之外的其他任何东西。Git 等存储库软件可能需要一些时间来学习。 所有代码 基于纯文本的文件 当变更历史可能很重要时
READMEs

虽然从技术上讲它不是一个位置或格式,但 README 是一个基于文本的文件,通常与一个应用程序或代码一起存在,通常位于同一文件夹中。其格式通常是某种标记语言,可以直接在任何文本编辑器中读取,或者由标记语言的解释器读取。可以有多个 README,每个文件夹中都有一个,每个都描述了该文件夹中的内容。它们也可以提交到代码存储库中,Bitbucket 和 GitHub 都提供了方便的设施,可以直接在网页浏览器中以特定格式(例如 Markdown 标记语言等)查看 README。我强烈建议在代码中提供 README,包括重要的开发人员和代码级别的文档。参见表 13.4 以了解何时以及为什么使用 README。

表 13.4. 使用 README 的利弊
优点 缺点 适用于
轻量级格式 与代码位于同一位置 存储在代码的所有相同位置 标记语言允许合理的格式化。 文本格式不如 Wiki 和其他更复杂的文档类型灵活。它是代码库的一部分,如果不包括代码,则不太容易共享。 不在代码本身内但应始终与代码一起伴随的文档
Wiki 系统

Wiki 是基于网络的系统,旨在设计用于多个、共享、相互关联的文档,这些文档可能被反复更新和扩展。维基百科是大型 Wiki 系统的典范。文档可以通过链接相互关联,好的 Wiki 系统会在文档位置更改时更新所有相关链接,如果你尝试使用不那么复杂的文档系统,你必须手动完成这项工作。参见表 13.5,了解何时以及为什么使用 Wiki 系统。

表 13.5. 使用 Wiki 系统进行文档和存储的利弊
优点 缺点 适用于
比其他媒体 Wiki 标记语言更好地处理文档间的链接和引用 允许相当复杂的格式化和页面结构 设置和管理可能很繁琐。很少有好用的 Wiki 产品是免费使用的。没有积极的维护,Wiki 可能会变得杂乱无章。
基于网络的文档托管

Google(Drive/Docs)和 Microsoft(Office 365)等公司提供在线文档和文件托管。这些可以帮助组织文件和文档,并提供一个基于浏览器的文档编辑器,可以从任何计算机访问。托管文档通常是几种熟悉的格式:文档处理风格的文档、电子表格、图表、图形等。你可能也能上传其他文件类型,但无法通过浏览器处理它们。这样,它们可以充当由托管公司管理的普通远程备份服务器。参见表 13.6,了解何时以及为什么使用基于网络的文档托管。

表 13.6. 使用基于网络的文档托管进行存储的利弊
优点 缺点 适用于
复杂的文档处理和电子表格处理 处理多种文件类型 协作编辑 主要公司提供近乎完美的可靠性。 不适合存储代码 没有组织努力可能会变得杂乱无章 这些系统有一些怪癖,例如,在文件到网页版本和相反方向的转换中。 你可能想要打印或转换为 PDF 的文档 电子表格、图表、图形和一些其他专业文件类型 可打印的用户文档

13.1.3. 预先考虑未来场景

现在我已经讨论了一些可用的文档和存储类型,我将回到本节开头提到的三个未来场景,并讨论每个场景对项目结束时可能做出的选择的含义。

有关项目的问题有人提出

如果你很久以前完成了一个项目,有人带着关于数据、方法或结果的问题来找你,而你又不知道答案,你将不得不翻找旧材料。你需要依次问自己这些问题:

  1. 你是否将相关材料保存在某个地方了?

  2. 如果可以,你现在能找到那些材料吗?

  3. 如果你找到了材料,你能在其中找到问题的答案吗?

对于第一个问题,让我们希望答案是肯定的。如果你没有保存材料,那么回答问题的希望就很小了。这里的教训是,无论将来是否有需要,都应该始终将项目材料保存在安全的地方。

第二个问题涉及到你找到回答问题的所有必要信息的能力。除了你确实保存了材料之外,希望你也保存了能让你记住(或能找到)并且相对方便访问的地方。如果不是这样,那下次完成项目时就要考虑这个问题了。考虑选择我之前提到的一种存储选项或另一种具有可靠性和便利性的选项,以便将你的项目材料安全保存多年。

你材料的组织和文档是第三个问题的关键。如果你在项目文件夹中只有一堆文件,而没有描述它们的文档,你可能会很难找到你想要的东西。下次可以考虑使用适合你拥有的材料类型的位置和格式。此外,良好的用户和开发者文档对于导航项目材料和找到关于项目细节的明确答案至关重要。没有这样的文档,项目的一些细节很可能会被遗忘。其中一些可能可以从代码和相关细节中推断出来,但也有一些不能。为了避免知识的重要损失,最好在项目结束时将你能捕获的所有知识都记录到文档中。这不仅可能节省你的时间和精力,还可能确保如果客户或其他人在以后提出问题时,不会对你失去信心。

你基于这个项目开始一个扩展或新的项目

就像之前的场景一样,当有人问你关于已完成项目的某个问题时,这个场景要求你能找到并整理你所有的旧材料。但在这里,你需要能够更深入地理解这些材料。你需要足够了解代码或其他统计软件的方面,以便对其进行修改或与之集成。这比弄清楚做了什么以及结果如何要困难得多。

具体来说,了解代码“做什么”和了解代码“如何做”是两回事。如果你将要与现有代码一起工作,你需要了解代码是如何工作的——至少是代码的一部分是如何工作的。用户和开发者文档帮助不大,但代码文档会很有帮助。如果没有良好的代码文档或可靠的代码工作记忆,你将不得不阅读代码并自行解读。

当完成一个项目时,设想一下一年或两年后你自己的困惑,问自己:“代码和软件功能中哪些部分我可能会觉得困惑?”在回答时要慷慨。现在看似明显的事情,在你离开项目一段时间后可能就不会那么明显了。

至少,你应该遍历代码的每个主要部分,并记录下那些可能在未来变得令人困惑的第一印象。关于如何使用产品的详细说明以及整体软件架构的描述也可能非常有用。

同事基于这个项目开始扩展或新的项目

如果你能够想象在不久的将来某个时刻恢复你的项目会是什么样子,那么想想别人在没有你的情况下做同样的事情会是什么样子——会发生什么?根据我的经验,他们会在你可能会失败的同一点上失败,还会更多,因为他们第一次没有参与这个项目。

设身处地为他们着想。他们需要了解哪些信息才能跟随你的脚步?你可以检查你的代码,找到任何可能令人困惑的代码示例,并写上注释说明你为什么这样做。这是 brute-force 方法,除非你的代码非常难以阅读或者你有大量时间,否则不建议使用。更有效且可能更有帮助的是,思考软件中哪些部分是新颖的,对于一个完全陌生的人来说可能会显得困惑。你是否做了任何非常规的事情,比如编码风格、复杂的统计方法或任何以非明显方式提高性能的算法技巧?如果是这样,你应该记录下来。

为他人记录工作比为自己记录要详尽得多。你必须超越自己的惯例和视角去思考。可能是一位被指派接管项目的新的同事。也许他们对项目的历史几乎一无所知,他们突然需要理解所有内容并从中获得一些用途。

就像在软件的用户体验上工作一样,编写文档是一项同理心的练习。如果你能想象到有人可能不理解项目或代码的哪些方面,通过帮助他们理解,你可以在所有层面上写出更好的文档。想象一下你当前雇主中的任何软件开发者——无论是资深还是初级,在你之后可能加入的数据科学家,或者坐在你旁边的人;这些人有无数的理由不理解你的项目、文档或代码。但如果你能站在他们的立场上,你就可以开始看到他们可能不理解你工作的哪些部分,然后你可以创建针对这些问题的文档。编写文档——用户、开发者和代码——就好像你永远不会回到这个项目一样,但其他人会。创建他们需要的文档,以便在没有你的输入的情况下继续进行。如果有人可以在你的帮助下接管项目,基于你的文档,你就成功了。

13.1.4. 最佳实践

在编写和存储项目材料时,我会采取以下步骤:

  1. 快速浏览代码,编辑以改善组织和可读性,并为未来的自己和其他可能需要阅读它的人添加注释。

  2. 为代码的每个主要组件编写高级 README,解释基本功能和用法;将这些内容与代码一起包含。

  3. 将代码提交到 Git 仓库,并将其推送到远程主机,如 Bitbucket。

  4. 收集所有项目结果、报告和其他非代码材料,并将它们放置在共享且可靠的存储位置;如果数据不是太大,也要包括原始数据。

  5. 留下线索以帮助再次找到所有这些部分;例如,主要的 README 可以包含指向所有材料位置的链接。或者我有时会给自己发送所有信息,这样所有链接都在一个地方。

  6. 如果我在一个团队中工作,我会将所有材料的位置记录在团队的主要文档系统中,例如共享的维基,这样每个人都可以轻松找到。

本章的大部分内容都是假设文档和存储是在项目结束时处理的。从项目开始的第一天就关注这两者更好、更容易(从长远来看)。特别是,如果你从一开始就记录文档,文档可能会好得多。以下是我贯穿每个项目所做的一些事情,以改善项目结束时的文档和存储状态:

  • 一旦代码变得非平凡,我就提交所有代码并将其推送到远程主机。

  • 我不会编写用户或开发者文档,直到我相当确信在不久的将来它们不需要完全重写(文档有一个令人讨厌的习惯,就是变得过时)。

  • 当我相当确信用户和开发者体验稳定时,我会为他们编写快速且粗糙的文档。

  • 每次我阅读我的代码时,如果我发现一个让我困惑的部分,即使只是一点点,我也会写一个注释来解释这个困惑的部分给自己看。

  • 我将用户和开发者文档放入 README 或维基百科中,这取决于它们的复杂性和其他具体情况。

遵循这些一般性指南,每次我不得不回到旧项目时,都节省了我很多时间和精力。

13.2. 从项目中学习

我介绍了一些方法,如果你在未来某个时刻需要重新审视那个项目,这些方法可以帮助你提高成功的几率。显然,从项目中获得的知识如果重新审视那个项目会有所帮助。但同样的知识也可以帮助你在其他数据科学项目中。为此,正式考虑从项目中学习到的所有东西,然后从中提取一些新的知识,这些知识不仅适用于这个项目。

这样的知识可能涉及你使用的技术。你使用的软件是否比预期的更好或更差?你是否使用了最终导致你问题的基础设施?新的知识也可能与你可能在数据中发现的意外有关。你的初步分析和描述性统计是否足够彻底?你的软件是否因为数据中的意外值而崩溃?无论如何,你学到了一些东西,也许如果你再次开始这个项目,你会做一些不同的事情。其中一些是特定于项目的,而另一些则可以被视为适用于未来项目的教训。通过进行项目复盘,你可以希望从中学到有用的教训。

13.2.1. 项目复盘

项目复盘是在项目结束后对项目过程中发生的一切进行正式考虑,目的是学习一些有助于未来项目的知识。

关于记忆的一个有趣的现象是,如果你现在知道某个特定的事实,那么你可能很难记住在知道这个事实之前的情况。弄清楚从项目开始以来你学到了什么并不总是容易的。如果你一直在做笔记,那么这项任务会容易得多。特别是,你很久以前在第六章中设定的目标和制定的计划,现在可以为你提供很多关于你当时知道什么以及你认为你知道什么的见解。第二章提出了一些问题,这些问题的答案将满足项目的一些基本目标;这些也是很有帮助的。

回顾旧目标

你在第二章或第六章中提出的目标是基于你当时所拥有的知识;你当时别无选择。如果你当时记不起你不知道的东西,那么你那时的目标可能会暴露你的无知。

让我们回顾一下第二章和第六章中的啤酒推荐算法项目章节 2 和章节 6。主要目标是能够推荐用户可能会喜欢的啤酒,为此,你必须预测应用用户对特定尝试的啤酒给出的评分。假设你设定了达到 90%推荐准确率和 10%评分预测标准误差的具体目标,但你都没有实现。

为什么你未能实现那些目标?一个可能的原因是数据集不够大或过于稀疏,无法支持如此高的准确性。也许有数百种啤酒和数百名用户,但典型用户平均只评价了 10-20 种啤酒。预测那些很少人评价的啤酒的评分将很困难。另一个可能的原因是人类的口味和偏好有很大的差异,并且本质上无法预测到所声称的程度。研究在一定程度上支持这一理论——未记录的因素,如人在尝试啤酒时的背景、他们事先吃了什么或喝了什么,都可能影响评分,超过你努力追求的 10%误差率。

在任何情况下,每个未能实现的目标都有一些主要嫌疑人导致其失败。锁定其中的一些可以显示出你之前不知道的东西,以及自从你设定目标以来你学到了什么。你所学到的知识能否应用于未来的数据科学项目?在数据过于稀疏的情况下,你未来可能会对数据集的承诺更加谨慎,并且在对结果的准确性持乐观态度之前,你可能会更加悲观。在你意识到人类偏好是反复无常的情况下,你了解到一些因素可能无法或不愿意被衡量,以至于你可以利用它们,这些因素可能会限制你的准确性和成功率。在未来的项目中,你可能不会那么雄心勃勃,或者至少承认内在的、无法克服的差异可能会阻止你实现目标。

回顾旧计划

就像旧目标一样,旧计划可以告诉你很多关于你在制定它时所知道的和所思考的事情。再次查看它可以帮助你过滤掉自那时以来你所学到的知识,再次考虑你所学到的知识是否构成了一项你可以带入未来项目的教训。

考虑出现在第六章的图 6.2 中的啤酒推荐算法项目的计划。这个计划有两个结果:要么达到了准确性目标,要么没有达到,用户界面将取决于这个结果。这个计划的一个现在看起来有点天真的方面是,它似乎暗示算法的准确性是应用程序中最重要的东西。整个产品设计都依赖于算法实现的准确性。这种观点本身并没有什么错误,但它严重依赖于数据和数学。好的算法并不能保证用户会出现在应用程序中,也许还有其他选项可以使应用程序成功,而无需高准确性的预测。

如果项目的核心目标是制作一个能够吸引用户并让他们不断回访的应用程序,那么算法几乎完美的准确性可能并非完全必要。也许还有其他方式可以吸引用户,这可能在发布应用程序后变得明显,但在发布前并不明显。一个更灵活的计划,其中包括重新设计应用程序的选项,可能是一个更好的选择。市场调研并未包含在计划中,但也许它应该被考虑;你可以将这个教训应用到未来的项目中。

审查你的技术选择

除了项目早期设定的目标和计划中的偏差之外,你可能对所选择使用的技术有了新的认识。你所使用的编程语言是否在某个时刻造成了问题?你是否选择了数据库,它带来的问题比其价值还要大?统计方法和所需的软件工具是否是正确的选择,或者现在是否已经清楚你应该选择其他的东西?

有时候软件工具能够实现你所期望的功能,但有时它们却未能达到预期。也许你尝试使用 Perl 语言进行一些文本解析,但后来发现将结果与用 Java 编写的其余应用程序集成非常困难。你放弃了 Perl,转而尝试 Python,发现 Python 在文本解析方面并不那么容易使用,但它与 Java 应用程序的集成更为容易。你对每种语言的体验完全取决于你对它们的了解和专业知识,但在这种情况下,你了解到了这两种语言的优缺点。

同样,你可能已经在原始实现中包含了像 Hadoop 或 Spark 这样的大数据软件,但你真的需要它们吗?对于专家来说,在应用程序中包含这些技术没有问题,但对于其他人来说,使用这些专业工具在应用程序中是一个额外的负担。在开发和维护上的额外努力是否值得?

每当你在一个项目中包含新的或有些不熟悉的技术时,你都会学到关于这些技术的一些东西。在未来项目中,无论你学到什么,都可以用来更好地决定使用哪些工具来完成哪些任务以及为什么。现在问自己,“我是否做出了好的技术选择,或者我现在意识到我可以做得更好?”可能会有所帮助。

下次做得不同

除非你完美无缺,否则在项目进行中你可能会意识到自己本应该以不同的方式行事。意识到另一个选择可能更好,这可以成为一种学习经验,但在许多情况下,这种认识是基于你在做决定时没有的知识。如果你能将这种认识推广到未来的项目中,可能会很有帮助。

将特定项目的教训推广到所有未来项目的集合中是一个困难的过程。如果教训涉及你使用的软件或其他工具,你当然可以将这些知识应用到未来的项目中;例如,如果一个软件工具最终证明不如你想象的那么好,你下次在类似情况下可能就不应该使用它。但如果教训涉及项目更具体的事情,比如数据或目标,那么在将来可能不容易应用它。

例如,可能无法预知人们的偏好太无常,无法支持 90%的啤酒推荐准确性,但未来项目中可以推广这一教训,认识到某些准确性基准是无法达到的。假设更好的统计分析将给出更好的结果,到一个任意精确的程度,这是一种谬误。最好是从这样的教训中尽早学习,并谨慎(但雄心勃勃)地进入未来的项目,仅仅是对可能发生相同类型结果的认识就可以帮助你规划和执行。

无论你能否将具体的教训应用到未来的项目中,或者是一个有助于你意识到可能出现的意外结果的普遍教训,在项目回顾中思考项目可以帮助你发现有用的知识,这将使你下次能够以不同的方式——并且希望是更好的方式——行事。

13.3. 展望未来

项目已经结束,所有材料都已记录并保存在安全的地方,项目回顾揭示了一些你可以带入即将到来的项目的教训。无论项目有多成功——让我们希望它是一个巨大的成功——有一点是肯定的:你在数据科学领域有了更多的经验。

这显然是一个不言而喻的真理:你做的数据科学越多,你的经验就越多。这并不能保证你过去或将来做过好的工作。但更多的经验确实直接意味着你有更多的项目可以从中学习。做好数据科学很大程度上取决于你已有的经验和从中学到的东西。这在所有科学领域都是正确的。你可以知道关于统计学和软件开发的所有知识,但如果没有经验,很难预测数据科学中影响每个项目的无数不确定性。如果你能利用你过去所有的经验来培养对某个项目中可能出错的所有事物的认识,那么你在该项目中成功的几率将大大增加。你可以制定一整套替代方案,并降低风险。

另一方面,无论你学了多少,你都无法预见到一切。数据是不可预测的,或者也许可以说,数据总是提供着出现意外情况的可能性。即使你能在 10 次中有 9 次或 100 次中有 99 次做出准确的预测——事情可能会改变。基础系统可能开始以不同的方式运行,或者数据源可能变得不那么可靠。在两种情况下,你的经验可能帮助你弄清楚问题是什么,尤其是如果你能将当前情况与你的任何过去项目联系起来,并据此制定策略。

在数据科学的种种不确定性中,有一个确定性是软件和其他工具将继续变化。每年,实际上每个月,市场上都会出现新的和改进的软件工具。其中一些很棒;有些则不然。我是一个喜欢使用经过验证的工具来完成手头任务的人;这样不确定性更少,当出现问题时有更多的人可以提供帮助。新工具可能能够做其他任何工具都无法做到的事情,但只有在特殊情况下,特殊工具才是必要的。然而,如果你能跟上新发展的步伐,并充分利用你的经验,你可能会认识到你需要完全新和专业的工具的时刻。这种情况并不常见,但它确实会发生,并且当它发生时,它可能导致分析技术的重大突破。但这只是例外,而不是规则。

在许多新软件工具的兴奋氛围中,更大的挑战往往是抵制使用它们,直到它们建立起可靠的记录。通常,使用最新、最令人兴奋的软件是酷的事情,但除非你能预见几年后的行业状况,否则你可能想在确信某个特定工具可靠且耐用之前,将其用于你的项目。与可以证明正确或错误的统计方法不同,软件的好坏只能通过使用来证明。最好根据你所知来选择软件,而不是根据你希望是真实的来选择。在统计和软件方面,你都必须小心选择,因为它们都对项目结果有重大影响。新进展可能会影响你的选择,但重要的是要记住数据科学两个主要方面之间的基本区别:软件一直在变化,但统计学是永恒的。

如果你从每个项目中只学到一个教训,那可能就是与你经历的最大惊喜相关。不确定性可能会渗透到你工作的各个方面,记住过去给你带来问题的所有不确定性,希望可以防止类似的问题再次发生。从数据到分析再到项目目标,几乎所有事情都可能随时改变。保持对所有可能性的警觉不仅是一个困难的挑战,而且几乎是不可实现的。优秀的数据科学家和杰出数据科学家的区别在于预见可能出错的事情并为此做好准备的能力。

练习

反思你的过去和当前项目,并尝试以下练习:

1.

想想你在过去(最好是超过一年前)参与过的项目。与该项目相关的材料和资源在哪里?如果有人要求你今天重复、重启或继续该项目,你能够做到吗?你当时能做些什么来使今天的工作更容易?

2.

考虑你的当前工作和就业地点。共享资源存放在哪里?你能轻松找到你想要的东西吗?如果让你制定一个详细的项目存档计划和政策,你会包括哪些内容?

摘要

  • 组织项目材料并将它们存放在可靠的地方,如果因任何原因需要重新启动项目,这可以让你避免后续的麻烦。

  • 记录软件和方法很重要,这样你和你的同事才能理解项目的各个方面,并在未来与之合作。

  • 文档化是一种同理心的练习;你必须想象你和你自己或他人未来可能不理解的内容,并据此编写解释。

  • 进行正式的项目总结可以揭示许多在其他情况下可能不明显的问题。

  • 每个项目都提供了许多可以学习的经验教训,其中许多可以概括并应用于几乎任何未来的数据科学项目。

  • 数据科学主要关于识别可能发生意外情况的时候,对这种不确定性的认识可以在未来的项目中区分成功与失败。

附录。练习:示例和答案

答案按章节列在下文。

第二章

1.

您会向产品设计师提出哪三个问题?

在开始之前,您可能想向产品设计师提出以下问题:

  • 您希望提供哪些预测示例给 FMI 的应用程序用户?
  • 您如何想象用户与预测的互动?
  • 预测如何与应用程序中的其他组件相匹配?

2.

您可能会对数据提出哪三个好问题?

一些示例问题包括:

  • 是否可以做出可靠的未来预测?
  • 如果可以,预测可以可靠地延伸到多远的未来?
  • 是否可以将诸如提款、购买等交易根据其对用户财务健康的影响分为“好”和“坏”的各个等级,并展示它们如何影响预测?

3.

该项目可能有哪些目标?

可能的目标可能包括:

  • 一种可以可靠地通知用户账户余额何时可能达到某个水平的预测——例如,银行账户空了、达到储蓄目标,或信用卡达到信用额度。
  • 为用户提供金融行为改变的建议,以改善他们的预测。
  • 一种清晰简洁地传达预测的可视化。

第三章

1.

列出三个您认为该项目值得检查的潜在数据来源。对于每个来源,也请列出您预计如何获取数据(例如,数据库、API 等)。

数据来源可能包括:

  • FMI 的内部数据库;我猜它们是关系型、基于 SQL 的数据库,但它们也可能是 NoSQL。
  • FMI 原始数据来源的银行和其他金融机构的 API;这些可能是基于 XML 或 JSON 的 API。
  • 用户可能会自愿提供一些有用的数据,例如交易、类别或其他属性;这些必须设计到产品中,最初是网络操作,可以放入 FMI 的内部数据库。

2.

考虑上一章练习问题 3 中列出的三个项目目标。为了实现这些目标,您需要哪些数据?

这些目标分别需要:

  • 制作可靠的预测需要每个相关账户几个月(至少)的完整交易数据,以及足够的交易属性来将它们分类到各种类别,如“重复”或“一次性”交易,以提高预测的准确性。
  • 向用户提供建议需要与预测(前一个项目符号)相同,还需要对交易属性有更严格的要求,以及能够对它们进行分类的能力。可能还需要更长的数据历史(一年?)。
  • 数据可视化可能没有特殊的数据要求,但可靠的交易属性无疑会帮助在例如,支出类别或交易类型是可视化的一部分的情况下。

第四章

1.

你即将开始从 FMI 的内部数据库中提取数据,这是一个关系型数据库。列出你在访问和使用数据时需要警惕的三个潜在问题。

例如,需要注意以下几点:

  • 数据库中存在缺失、无效或错误的数据值。
  • 名称和其他标识符类型的字符串在字段或表之间不一定匹配——例如,一个地方有“John Public”,另一个地方有“John Q Public”。这些是不相等的字符串,可能或可能不表示同一个人。
  • 如果你必须连接一些数据库表,最好确保你连接的字段与记录很好地匹配。我会检查每个表中有多少条记录与另一个表中的正确匹配记录,反之亦然。

2.

在内部数据库中,对于每个找到的金融交易,都有一个名为description的字段,其中包含一个字符串(纯文本),似乎提供了一些有用的信息。但这个字符串似乎没有从条目到条目的一致格式,而且你交谈过的人都不能告诉你该字段是如何生成的或更多关于它的信息。你将如何制定策略来尝试从该字段中提取信息?

我的策略可能如下:

  1. 从数据库中抽取一个可管理的交易集合(几百个?),检查描述字段,并尝试注意任何模式。
  2. 自问:字符串看起来主要是以逗号分隔、分号分隔、管道分隔、JSON 格式还是自由格式?从条目到条目中是否有重复的字符或术语?
  3. 如果我已经诊断出了一种格式,那么要确保它适用于我正在处理的整个集合。
  4. 如果仍然看不出有共同的格式,就关注字符串中最吸引我的方面。例如,如果我对交易的 ZIP 代码感兴趣(且它们在数据库的其他地方都没有出现),我会检查字符串中 ZIP 代码的表示方式。如果每个描述字符串都包含类似于"ZIP code: XXXXX"的子串,那么我可以编写一个脚本,搜索"ZIP code:"子串,捕获其后的字符,并将它们记录为 ZIP 代码。我会尝试使用上下文(所需数据周围的文本)来记录描述字符串中所有有趣的片段。
  5. 如果上述任何一种方法都不足以从描述字符串中提取某些特定信息,我就会开始扩展我用来检测和捕获字符串中数据的上下文和格式元素。如果没有明显的上下文,例如子字符串 "ZIP code:",我会尝试找出一种组合,包括标点符号、分隔符、空白和其他格式元素,这些元素可以区分数据与其相邻的数据。也许 ZIP 代码总是出现在两个逗号之间的前五个数字,例如",XXXXX,",或者它可能总是出现在两个字母州简称之后,例如"OH,XXXXX,"。一个脚本可以轻松地找到符合这些模式的子字符串,但如果我不小心,它们也可能捕获到不需要的数据。

第五章

1.

鉴于该应用的主要目标是提供准确的预测,描述三种你想要在数据上执行描述性统计以更好地理解数据的类型。

一些有用的描述性统计可能包括:

  • 你打算预测的财务账户集合每月典型、最小和最大交易数量。更多的交易对于统计预测方法来说将更有信息量。
  • 账户余额的方差或其他波动性度量。较小的方差可能使得预测变得更加容易。
  • 随机选择账户随时间变化的账户余额的一组图表。查看图表可以提供比均值和方差多得多的信息。它还可以让你对账户余额的行为有一个良好的感觉,并让你思考可能有助于预测的统计方法。

2.

假设你强烈考虑尝试使用统计模型来分类重复和一次性财务交易。关于这两个类别中的任何一个,你可能会对交易有哪些假设?

一些可能的假设是:

  • 重复交易每周、每月或以其他某种规律的时间间隔发生。
  • 重复交易在每个实例中都有相同的标签、名称或其他识别属性。
  • 一次性交易通常比平均交易额要大,例如在假期、大额购买、意外之财等情况。

第六章

1.

假设你从上一章的初步分析和描述性统计中得出结论,认为你可能会为拥有许多交易的财务账户的活跃用户生成一些可靠的预测,但你认为你无法为交易相对较少的用户和账户做同样的事情。将这一发现转化为“可能做什么?什么是宝贵的?什么是高效的?”的目标调整框架。

一种可能的翻译:

  • 什么是可能的? 对于具有最高质量数据的用户,可能可以做出可靠的预测,但对于具有最低质量数据的用户,几乎肯定不可能。
  • 什么是宝贵的? 最宝贵的是为最活跃的应用程序用户提供可靠的预测。不太活跃的用户可能不太关心,如果他们关心,他们可能会被吸引得更活跃,以便获得访问良好预测的机会。
  • 什么是高效的? 最有效的方法可能是从具有高质量数据的用户账户开始,生成可靠的预测,然后尝试将这些方法应用于低质量账户数据,尽可能调整/改进方法,直到达到某种拐点,即为了(可能)使方法适用于低质量数据所需的工作量过多,以至于无法证明其合理性。

2.

根据您对上一个问题的回答,描述在应用程序内生成预测的一般计划。

一种可能的基本计划是:

  1. 使用最高质量账户数据开发统计方法和软件,以生成可靠的预测。
  2. 如果你在(a)方面不成功,重新审视目标,并考虑是否值得在这个问题上花费更多时间。
  3. 如果你在(a)方面取得成功,将方法和软件应用于低质量数据,并找到一种可靠的预测的最低质量水平。
  4. 可选地,改进预测方法以更好地处理低质量数据,但权衡所花费的时间与边际收益。与产品设计师讨论,共同决定投入多少工作量。
  5. 当每个人都对预测的成功、范围和可靠性感到满意时,精炼并准备您的软件与网络应用程序集成。确保包括一些关于数据质量不足的概念;例如,当数据质量阈值未达到时,软件应告诉网络应用程序“数据不足;未生成预测”。

第七章

1.

描述您可能使用的两种不同的统计模型来预测个人财务账户。对于每种模型,至少给出一个潜在的弱点或劣势。

可能的统计模型包括:

  • 对过去六个月月末账户余额进行线性回归。这将捕捉账户的一般趋势,但弱点包括:它不能很好地捕捉余额的波动,如果支出变化很大;例如,如果一笔大额月度支票(收入)没有按时到达,它可能会落入下一个月,从而可能使月末余额——以及回归结果——大幅偏离。
  • 估计过去六个月账户的平均月收入和平均月支出;假设未来几个月的收入和支出水平将大致相同。这个模型可能避免了之前提到的问题,即支票没有按时到达,但它的弱点是六个月的数据点不多,该模型容易受到任何地方的一次性或异常交易的影响。

2.

假设你已经成功创建了一个可以将交易准确分类到“常规”、“一次性”和其他任何你想到的合理类别中的分类器。描述一个利用这些分类来提高预测准确性的统计模型。

我还试图创建一个代表日常支出(如咖啡、杂货、晚餐或鸡尾酒)的“正常支出”类别,但这些支出可能并不严格属于之前提到的其他两个类别,即“重复”和“一次性”。正常支出可能不像一次性交易那样庞大,但也不被认为是重复的。例如,如果我每周出去吃两三次饭,这并不是一次性的支出,因为我(可预测地)每周都会这样做,而且它也不是重复的,因为我去不同的地方,在不同的日子。

我会使用每个月落入这三个类别之一的金额来计算每个类别的基线。对于重复交易,我会使用六到九个月的数据,对最近几个月的数据赋予更高的权重。因此,我会有一个加权平均的月度金额,用于重复交易和正常支出,但我会排除一次性交易。我的预测将基于假设重复交易和正常支出的总和将继续在近期内持续,目的是以某种方式向用户展示一次性交易如何影响他们的预测和财务状况(但这更多是一个用户体验和产品问题,而不是一个统计问题)。

由于我非常重视承认存在的不确定性,我还会为我的预测计算一个方差的概念,对于更远的未来预测和收入和支出波动较大的账户,方差会更大。

第八章

1.

在这个项目中执行预测所需计算的两个顶级软件选择是什么?为什么?每个选择的缺点是什么?

我的前两个选择是:

  • Python— 我可以用 Python 轻松进行计算,但缺点是如果我的代码将要与实时“脏钱预测”应用一起部署,它必须相对无懈可击且无错误;我可能会尝试招募一位生产开发者来帮助解决这个问题。如果 Python 还没有与现有代码良好接口,我还需要找到一种方法。
  • 无论现有应用中已经使用什么语言/工具进行一些计算—— 我可能只需向代码中添加一个组件,但缺点可能是,根据语言的不同,它可能没有很好的内置统计库,而且我可能还不了解这种语言,所以我可能需要学习它。

2.

你在问题 1 中的两个选择是否具有内置的线性回归或其他时间序列预测方法的功能?它们是什么?

分别:

  • Python 有。在numpyscikit-learn和其他包之间,Python 在编程语言中拥有一些最好的统计功能。
  • 这取决于语言;在做出承诺之前,我会先咨询互联网。

第九章

1.

在项目期间可能会使用哪些三个补充(非严格统计)的软件产品,以及为什么?

一些可能包含的产品:

  • 一个关系数据库,因为 FMI 已经有一个,并且它包含了你需要的大部分数据。
  • 一台高性能计算服务器或集群,因为有很多数据需要处理,也有很多计算要做。
  • 云计算服务,因为 FMI 可能还没有足够的计算资源在合理的时间内完成预测计算。

2.

假设 FMI 的内部关系数据库托管在单个服务器上,每晚都会备份到异地的一个服务器上。请给出这种架构可能是一个好架构的理由以及可能是一个坏架构的理由。

优点:如果单个服务器足够强大,且数据集足够小,以至于服务器可以快速高效地处理所有内容,那么这种架构可能比一个更大的分布式系统更容易管理。

缺点:数据集会增长,在某个时刻,服务器可能会因为数据本身的巨大规模或管理查询的计算成本而超负荷;此外,单个服务器可能是一个单点故障。

第十章

1.

列出三位在 FMI 中你执行项目计划时可能最多交谈的人(按角色或专长),并简要说明你为什么可能这么频繁地与他们交谈。

我猜我会和这些人交谈最多:

  • 负责 FMF 应用的网络应用开发者*——将统计软件与应用集成可能需要大量的沟通、谈判和协议,以确保这两部分正确对齐。
  • 负责该应用的产品设计师*——他们可能对如何解读您统计应用输出的结果有很多疑问,而您也希望对他们的预期有很好的了解,以便确保您的统计结果适用。
  • 内部数据库专家——如果您还没有广泛使用数据库,那么在结构、访问和效率方面可能存在一些细微差别,这些差别可能不会立即显现,但对您可能非常有帮助。

2.

假设产品设计师已经与管理团队进行了沟通,并且他们一致认为您的统计应用必须为所有用户账户生成预测,包括那些数据非常稀疏的账户。优先级已经从确保预测结果良好转变为确保每个预测都存在。您将如何应对这种情况?

如果必须存在预测,无论发生什么情况,我可能会假设低数据质量的账户将保持其当前(或月末)的余额在近期内。没有数据来证明预测上升或下降的合理性,现状可能是最好的选择。然而,在实施之前,我会与产品设计师和其他人讨论这个问题。

第十一章

1.

假设您的老板或其他管理层人物要求一份总结您预测工作结果的报告。您会在报告中包含哪些内容?

我会首先包含高质量数据用户账户预期预测准确性的总结,然后是低质量数据的预测结果。这将把结果放在用户完全参与(这是 FMI 所有问题,而不仅仅是您的问题)主应用的情况下,并突出您项目的一个最大的成功。其次,我会讨论用户账户中高质量和低质量数据的分布,以说明数据质量问题在多大程度上影响了应用。最后,我会提供一些具体的例子(附带数据),说明预测如何影响用户行为,例如警告即将耗尽的银行账户或可能很快达到的信用额度。这将直接将预测项目与用户参与联系起来,这对于管理类型的工作者来说通常是至关重要的,尤其是在应用开发方面。

2.

假设 FMF 网络应用的主要产品设计师要求您为应用用户撰写一段话,解释您应用生成的预测,特别是关于可靠性和准确性的问题。您会写些什么?

本应用生成的预测基于您最近的消费和收入习惯,以及与 FMF 相连的任何账户中发生的其他交易或事件。您的财务习惯越一致,我们的预测就越准确——这又是控制您财务的另一个理由!不幸的是,在某些情况下,我们可能没有足够的数据来做出预测;在这种情况下,您可能需要将更多账户连接到 FMF,并更频繁地使用该应用,以便在指尖上拥有这些强大的预测,并最大化您的财务福祉。感谢您让 FMF 成为网络上最强大的个人财务网站!

第十二章

1.

假设您已经完成了统计软件,将其与 Web 应用集成,并且应用中显示的大多数预测似乎都不正确。列出三个检查点以尝试诊断问题。

我会首先检查以下内容:

  • 统计应用输出的结果。如果它输出正确的结果,那么问题可能在于使用这些结果的 app。如果不正确,那么问题可能在于统计部分。
  • 如果问题似乎出在 Web 应用上,我会检查该应用内部的数据库。如果数据存储不正确,那么在数据输入和存储之间可能存在问题。如果数据存储正确,那么在从应用内部存储中提取数据并在屏幕上显示数据的方式上可能存在问题。
  • 如果问题似乎出在统计应用上,我会考虑数据通过统计模型流动的方式,并检查直接从统计模型输出的结果,但在它们被转换为发送到 Web 应用格式的格式之前。
  • 额外答案 4—— 检查应用程序任何部分的初始数据。如果数据库查询有问题,那么之后的所有内容都不会正确。

2.

在部署应用后,产品团队通知您,他们即将向选定的用户发送关于应用的调查。您可以在调查中提交三个开放式问题。您会提交什么问题?

我可能会提出以下问题:

  • 应用中的财务预测对您有信息性吗?在哪些方面?
  • 您是否对财务预测中的信息采取了行动?如何?
  • 您觉得财务预测准确吗?为什么或为什么不?

第十三章

1.

回想一下您过去参与过的项目(数据科学或其他领域),最好是超过一年前。与该项目相关的材料和资源在哪里?如果有人要求您今天重复、重启或继续该项目,您能够做到吗?您当时能做些什么来使今天的工作更容易?

这主要是一个思想实验。它完全取决于你在哪里工作以及你在做什么工作。

2.

考虑你的当前工作和就业地点。共享资源存放在哪里?你能轻松找到你想要的东西吗?如果让你制定一个详细的项目归档计划和政策,你会包括哪些内容?

这又是一个思想实验,但有助于改善未来项目的成果。制定良好的项目文档和存储计划可以非常有帮助。

数据科学项目的生命周期

本书围绕数据科学项目的三个阶段组织:

  • 第一阶段是准备——在项目开始时投入的时间和精力用于收集信息,可以在后期避免大麻烦。

  • 第二阶段是构建产品,从规划到执行,使用在准备阶段学到的知识和所有统计和软件可以提供的工具。

  • 第三阶段也是最终阶段是完成——交付产品,获取反馈,进行修订,支持产品,并结束项目。

posted @ 2025-11-23 09:27  绝不原创的飞龙  阅读(11)  评论(0)    收藏  举报