DLAI-数据工程建模笔记-全-
DLAI 数据工程建模笔记(全)
001:数据工程(数据建模、转换和服务)第4课,完结|课程概述与欢迎 🎉
在本节课中,我们将要学习数据工程专项课程的第四门,也是最后一门课程。本课程的核心将聚焦于数据建模、转换和服务,旨在将原始数据转化为对最终用户有价值的资产。
通过前三门课程的学习,你已经掌握了数据摄取、存储、运维与编排等关键技能,为构建健壮的数据系统打下了坚实基础。本课程的目标,是教你如何运用这些技能,为利益相关者交付真正的商业价值。
从数据到价值:建模与服务的挑战 🤔
上一节我们介绍了本课程的核心目标,本节中我们来看看数据工程师在日常工作中面临的具体挑战。你的核心职责是获取原始数据,将其转化为有用的信息,并交付给最终用户。本课程的重点,正是“转化”与“交付”这两个环节。
数据旨在反映现实世界,但它通常只捕捉了现实的一个微小切片。例如,一个公司可能同时拥有网站点击流数据、用户购买记录、移动应用行为日志,以及调用各类API(如推荐系统、内容分类)产生的数据。
如何在一个数据库中建模,以捕捉世界上发生的所有事情?
此外,业务场景会不断变化:
- 如果公司决定开展用户调查,获得了用户人口统计和兴趣数据,如何将这些新数据整合进来?
- 如果公司收购了另一家业务相近的公司,对方拥有属性不同的用户数据库,且部分用户与你的数据库重叠,你该如何处理这两个数据库,并使它们协同工作?
现实世界的复杂性决定了,你设计数据表示方式的能力,将极大地影响你能从数据中高效提出的问题类型,以及数据反映现实世界的准确程度。
服务机器学习模型:应对数据漂移 🚀
除了传统的数据服务,一个日益常见的用例是向最终用户提供机器学习模型服务。这不仅仅是训练模型那么简单,更涉及让模型可供用户使用,以获取预测、分类或大语言模型的输出。
部署机器学习模型到生产环境,除了软件架构、数据存储等挑战外,还有一个美妙又恼人的问题需要处理:数据漂移和概念漂移。
当我们构建AI模型时,我们基于历史数据进行训练。但有时,未来会变得与过去不同,导致模型训练所用的数据不再能反映变化后的世界。
如何设置系统以监测世界何时发生了变化?
如果需要,如何在合适的时间尺度上收集新数据来更新模型?
因此,超越软件工程层面,真实世界的模型部署还需要应对这种额外的统计与机器学习复杂性。这也使得本课程非常令人兴奋,因为我们讨论的是如何建模和转换数据,并以一种能为用户持续创造价值的方式提供服务。
持续迭代的数据工程生命周期 🔄
正如我们在之前的课程中提到的,数据工程是一个生命周期。服务环节并非一劳永逸,而是一个持续的循环。我们常常需要回过头去重新审视生命周期中的其他部分,进行迭代和优化。
在很多时候,我们把最精彩、或者说最有意思的部分留到了最后。应对复杂性本身,有时就是一种乐趣。
本节课中我们一起学习了第四门课程的总体目标与核心挑战。我们明确了数据建模、转换和服务的重要性,并探讨了在整合多源数据、服务机器学习模型时可能遇到的典型问题,特别是数据漂移的挑战。接下来,让我们进入下一个视频,正式开始这最后一门课程的学习。
002:数据建模概览 🗺️

在本节课中,我们将要学习数据建模的核心概念、重要性以及它在数据工程生命周期中的位置。我们将了解什么是数据模型,为什么需要它,以及如何构建一个有效的模型来支持业务目标。
在第二和第三课中,你学习了如何将数据摄取到数据管道中,并探索了用于存储数据的各种解决方案。现在,在将数据提供给最终用户之前,你需要以一种支持其预期用例的形式来转换和建模数据。在本课程中,我们将从数据建模开始,然后在课程的后半部分更深入地探讨数据转换。

对你的数据进行建模,涉及有意识地选择一个与业务目标和逻辑相一致的、连贯的数据结构。那么,什么是数据模型呢?以下是我的定义:数据模型将数据组织和标准化为精确的结构化表示,以启用和指导人类与机器的行为、支持明智的决策并促进行动。
定义的第一部分意味着,当你对数据进行建模时,你需要定义数据的结构、关系和含义。例如,当你对表格数据进行建模时,你需要考虑构成模型的表、如何标记这些表、表之间如何关联以及为每个表选择哪些列。
你应该以一种与组织相关联的方式来构建数据,这是定义第二部分所暗示的。为了让数据服务于其目的,你需要让数据对人类而言是可理解和有价值的(如果你是为分析用例建模数据,例如创建报告或仪表板),或者让数据对计算机而言是有意义的(如果数据将用于机器学习用例)。
一个构建良好的数据模型应该反映业务目标和逻辑,同时纳入业务规则(例如,在处理订单前要求有效的支付方式),以确保符合运营标准和法律要求。一个好的数据模型还应概述业务流程之间的关系。例如,将销售数据与产品库存数据联系起来,以确保销售过程能直接了解当前库存水平,防止超卖。
除此之外,一个稳健的数据模型是一个强大的沟通工具,它在工程师、分析师和高管等利益相关者之间创建了一种共同语言。通过标准化业务词汇(例如,明确定义什么构成“活跃用户”——是过去30天内登录其账户的人,还是过去6个月内进行过购买的人,或是其他完全不同的标准),仔细定义业务术语可以对描述客户行为、预测客户流失等下游报告产生巨大影响。
因此,为了确保成功的数据建模,请回顾第一课中“像数据工程师一样思考”的框架,并始终从与利益相关者沟通开始。理解业务定义、规则和目标是建模数据并为业务提供高质量数据以获取可操作的见解和智能自动化的第一步。

另一方面,那些随意创建、不能反映业务运作方式的糟糕数据模型,可能会制造比解决的问题更多的问题。糟糕的数据模型非但不能促进沟通和共同理解,反而可能向利益相关者提供不准确的信息并造成混乱。我经常看到的另一个专业疏忽是,数据团队完全忽视数据建模,因为他们认为这是一个缓慢、乏味且无关紧要的过程,只适用于大公司。因此,他们直接开始构建数据系统,却没有计划如何组织数据以使其对业务有用。
这是一个巨大的错误。数据建模作为一种实践已有数十年历史,传统上用于构建存储在数据仓库和关系数据库中的数据。随着数据湖1.0、NoSQL和大数据系统的兴起,工程师们开始忽视传统的数据建模(有时是为了获得合理的性能提升)。然而,缺乏严格的数据建模导致了数据沼泽,以及大量冗余、不匹配或根本不准确的数据。
如今,数据管理(特别是数据治理和数据质量)日益普及,正在推动对连贯业务逻辑的需求。我认为数据建模是一项关键的实践,它能增强你作为数据工程师在整个数据生命周期中对数据的理解。数据建模有助于你提高数据质量和集成度,并鼓励在整个组织内采用数据。无论你所在的企业规模大小,你都应该采取有针对性的方法进行数据建模,专注于特定的业务领域。


例如,你可以创建一个数据模型来帮助营销团队更好地理解客户行为和活动效果。或者,你可以对公司的财务交易进行建模,以便财务团队能够分析支出模式并识别节省成本的机会。有针对性的数据建模工作可以提供有价值的见解,以推动更好的决策和有影响力的人工智能模型,即使在高度复杂的企业中也是如此。
在本课程的第一周,我将主要讨论批处理数据建模,因为大多数数据建模技术都源于此。我们将首先快速了解数据建模的三个传统层次:概念层、逻辑层和物理层,它们提供的细节程度各不相同。然后,我们将介绍两种基本模式:范式模式和星型模式,你将在本周的实验中实现它们。最后,我们将深入探讨一些用于分析用例的流行建模技术,例如Inmon和Kimball建模方法,以及其他技术,如数据仓库和数据保险库。
在本课程的第二周,我们将讨论用于机器学习用例的数据建模和转换技术。然后在第三周,我们将更深入地探讨转换,并讨论选择数据处理框架的各种技术考量。最后,我们将在本课程的第四周把你在这个项目中学到的所有知识整合起来,你将有机会构建一个端到端的数据管道,涵盖数据工程生命周期的所有阶段以及关键的底层支撑。

本节课中我们一起学习了数据建模的基本概念、其重要性以及它在数据工程中的核心地位。我们明确了数据模型的定义,理解了良好数据模型的特征,并概述了本课程后续将深入探讨的建模层次、模式和技术。下一节,我们将开始具体了解数据建模的不同层次。
003:概念性、逻辑性与物理性数据建模 📊

在本节课中,我们将学习数据建模的三个关键层次:概念性、逻辑性和物理性数据模型。理解这些层次将帮助你构建清晰、高效且易于维护的数据架构,从而促进团队沟通与协作。
概述
作为数据工程师,你可以通过构建和维护数据模型来为组织创造价值,这些模型能促进沟通并建立共识。那么,从哪里开始呢?建议从高层的概念性数据模型开始,它描述了业务实体。然后,你可以填充更多细节以创建逻辑性数据模型。最后,你将创建物理性数据模型,在其中决定用于存储和提供数据的数据库或其他存储系统,并概述实现细节,即你在数据管道中用于实现这些存储系统的具体工具。
概念性数据模型 🧠
上一节我们介绍了数据建模的整体流程,本节中我们来看看最抽象的层次——概念性数据模型。
概念性模型应聚焦于高层次的业务实体、实体之间的关系以及每个实体的属性。它还应反映业务逻辑和规则。例如,对于表格数据,描述可以包括表、表之间的关系以及列名。
创建概念性模型时,可以使用实体关系图(ER图)进行可视化,这是一种标准工具,用于可视化数据不同方面(如客户、产品或事件)之间的关系。
以下是我们在第一门课程中使用的经典模型数据集ER图的一部分:

如图所示,它包含了产品和每个订单的订单详情数据。它使用特定符号编码了产品数据与订单详情之间的连接。
|符号代表“有且仅有一个”,意味着每条订单详情记录只能与一个且仅一个产品关联。通常称这种从订单详情到产品的关系为一对一关系。O<符号代表“零个或多个”,意味着一个产品可以与零个订单详情关联(如果无人购买该产品),也可以与多个订单详情关联(如果被多次购买)。因此,这种从产品到订单详情的关系是零或一对多关系,更常简称为一对多关系,意味着一个产品可以与多个订单详情关联。
请注意,关系的性质取决于你观察关系的方向。此ER图还显示:
- 从订单详情到订单的关系是一对一(一个订单详情只能与一个订单关联)。
- 从订单到订单详情的关系是一对多(一个订单可以与多个订单详情关联)。
例如,如果客户在同一订单中购买了一批产品,那么就会有许多订单详情(每种购买的产品对应一个)。每个订单详情只与一个订单关联,但该订单会与多个订单详情关联。
逻辑性数据模型 📐
在概念性模型之后的下一个层次是逻辑性模型,你将在其中添加关于如何实现概念性模型的更多细节。
以下是逻辑性模型需要补充的关键信息:
- 为每个表的列添加数据类型信息。
- 规划出主键和外键。
物理性数据模型 ⚙️
最后,你将创建物理性模型,在其中选择特定的数据库管理系统(DBMS),并定义如何在该系统中实现逻辑性模型。
这个物理性模型应定义描述数据如何存储的配置细节(例如存储在磁盘、RAM或使用混合方法),以及如何实现分区和复制等过程。

因此,在对数据进行建模时,你将沿着这个连续体前进:从抽象的建模概念到具体的实现。
总结
本节课中,我们一起学习了数据建模的三个核心层次:
- 概念性模型:关注业务实体和关系,使用ER图可视化。
- 逻辑性模型:添加实现细节,如数据类型和键(主键、外键)。
- 物理性模型:决定具体的存储系统(DBMS)和配置细节(如分区、复制)。

接下来,让我们深入数据建模的细节,首先详细探讨规范化。
004:规范化 📊

在本节课中,我们将学习数据库设计中的一个核心概念——规范化。我们将回顾规范化的目的,并详细探讨其不同形式,从非规范化形式逐步演进到第三范式。通过理解这些概念,你将能够设计出更高效、更易于维护的数据库结构。
概述
在第二门课程中,我们讨论了关系型数据库作为源系统。我们学习了规范化如何减少数据冗余并提高数据完整性。在本视频中,我们将重新审视这一主题,并讨论规范化的各种形式。
规范化是一种数据建模实践,通常应用于关系型数据库,以消除数据库内的数据冗余,并确保数据库表之间的引用完整性。它最早由关系型数据库先驱埃德加·科德于1970年提出。
以下是科德当时概述的一些规范化目标:
- 使关系集合免受不良的插入、更新和删除依赖的影响。
- 减少在引入新数据类型时对关系集合进行重构的需求,从而延长应用程序的生命周期。
为了更好地理解科德的规范化目标,让我们看一个例子,其中销售订单数据以两种不同的方式表示。


第一个模型将数据表示在一个巨大的销售订单表中,而第二个模型将相同的数据分散在多个表中。
第一个模型的规范化程度较低,第二个模型的规范化程度较高。这意味着,与第二个模型中的较小表相比,巨大的销售订单表包含了更多的冗余数据。
例如,如果你想更新客户的地址(比如这里的乔·里斯),在第一个表中,你需要更新与乔·里斯对应的每一行。而在第二个模型中,客户数据存储在一个单独的表中。因此,每当你想更新客户的地址时,只需更改客户表中的一行即可。
现在,假设你想添加订单的发货信息。在规范化程度较低的模型中,你需要通过为发货信息添加新列来更改表的结构。但在规范化程度较高的模型中,你可以简单地创建一个新的发货数据表,然后使用订单ID作为外键将这个新表链接到现有的订单表。这样,你就不必更改任何其他表。
第一个规范化程度较低的模型称为第一范式,而这里规范化程度较高的模型称为第三范式。在规范化的谱系中,还有非规范化形式和第二范式。每种形式都包含不同级别的冗余,并包含了先前形式的条件。
从非规范化到第三范式
使用相同的销售订单示例,让我们从非规范化形式开始,逐步将其转换为第三范式。
以下是包含客户每个订单详细信息的非规范化表。它包含六列,以订单ID为主键。

非规范化形式不仅包含冗余数据,还包含嵌套数据。在这个例子中,“订单商品”列包含嵌套对象,每个对象包含诸如商品SKU编号、价格、数量和名称等信息。
转换为第一范式 (1NF)
为了将此非规范化表转换为第一范式(此处记为1NF),你需要确保每列都是唯一的且具有单一值(即没有嵌套数据),并且表必须具有唯一的主键。
因此,让我们展开“订单商品”列,并用四个新列替换它:item_sku、price、quantity 和 name。


现在,每一行代表给定订单中的一个商品。由于一个订单可以包含多个商品,订单ID不再是此表的唯一主键。为了创建唯一的主键,让我们通过添加一个名为 item_number 的列来为每个订单中的商品编号。
现在,由 order_id 和 item_number 组成的复合键共同构成了此表的唯一主键。
转换为第二范式 (2NF)
但这种形式仍然包含冗余数据,可以通过将其转换为第二范式(此处记为2NF)来进一步规范化。
对于第二范式,必须满足第一范式的要求,并且应移除任何部分依赖。部分依赖发生在非键列的子集依赖于复合键中的某些列时。
例如,customer_id、customer_name、customer_address 和 order_date 这些非键列都依赖于 order_id。这意味着,如果你知道 order_id,你就可以唯一地确定这最后四列中的信息。
因此,你可以将销售订单表拆分为两个表:一个订单商品表和一个订单表。


由 order_id 和 item_number 组成的复合键现在是订单商品表的唯一主键,而 order_id 是订单表的主键。
现在,这些表中不再有部分依赖,但它们有另一种形式的依赖,称为传递依赖。
转换为第三范式 (3NF)
传递依赖发生在非键列依赖于另一个非键列时。
例如,在订单商品表中,商品的价格和名称取决于其SKU。在订单表中,客户姓名和地址取决于客户ID。
虽然这种类型的依赖可以存在于第二范式的表中,但第三范式的表需要满足第二范式的所有要求,并且没有传递依赖。
因此,为了将这些表从第二范式转换为第三范式,你可以通过创建另一个名为 items 的表来移除订单商品表中的传递依赖,该表包含每个商品的名称、价格和SKU。sku 现在是 items 表的唯一主键。


同样,让我们通过创建一个包含每个客户的客户姓名和客户地址的新表来移除订单表中的传递依赖。customer_id 现在是 customers 表的唯一主键。
规范化的应用与权衡
数据库如果处于第三范式,通常被认为是规范化的,这也是我们在本课程中将使用的惯例。
作为数据工程师,你可能会从已规范化的源数据库(尤其是那些代表事务系统的数据库)中提取数据,或者你可能会处理包含规范化数据的数据仓库。
当你对数据进行建模时,应用于数据的规范化程度取决于你的使用场景。



没有一种放之四海而皆准的解决方案。你可能会遇到非规范化实际上具有性能优势的情况,因为它不需要你在表之间执行任何连接操作。在其他情况下,你可能更喜欢规范化的形式,以确保高效的读写操作和更好的数据完整性。
在本周的第一个实验中,你将有机会练习创建一个规范化的数据模型,类似于我们刚刚在本视频中一起做的练习。你将获得一个非规范化的表,并需要应用几个规范化步骤将其转换为第三范式。

总结

在本节课中,我们一起学习了数据库规范化的核心概念。我们从非规范化形式出发,逐步探讨了第一范式、第二范式和第三范式的定义与转换方法。我们了解到,规范化通过消除冗余和依赖,可以提高数据完整性并简化数据维护。同时,我们也认识到,在实际应用中,需要在规范化带来的结构优势与非规范化可能带来的性能优势之间做出权衡。掌握这些知识,将帮助你作为数据工程师,根据具体业务需求设计出更合理、更高效的数据模型。
005:维度建模与星型模式 ⭐

在本节课中,我们将要学习一种名为星型模式的数据建模方法。星型模式,也称为维度数据模型,其核心目标是通过特定的结构组织数据,以支持更快的分析查询,并使业务用户更容易理解数据。我们将详细探讨星型模式的构成、设计原则及其相较于规范化模型的优势。
概述:什么是星型模式?
规范化模型侧重于连接数据实体并建模关系以减少数据冗余。而星型模式则采用不同的思路。
星型模式将业务度量值收集在一个称为事实表的表中,并用存储在维度表中的必要上下文信息围绕该表,形成一个类似星型的结构,因此得名“星型模式”。

接下来,让我们深入了解事实表和维度表,看看它们如何更好地支持分析查询。
深入事实表与维度表
事实表:记录业务事件的核心
事实表包含由业务事件或流程产生的定量业务度量值。
例如,当你预订一次网约车时,该事件会产生诸如行程时长、行程价格、支付的小费、行程延误等度量值。这些业务度量值就是我们所说的事实。因此,事实表中的每一行都对应一个特定业务事件的事实。
在设计星型模式模型时,你还需要决定所谓的粒度,即你希望在事实表的每一行中显示的详细程度。
在网约车的例子中,事实表的每一行可以代表:一天内所有客户完成的所有行程、一天内单个客户的所有行程,或者单个客户完成的一次行程等。虽然存在多种粒度级别,但我建议你采用所谓的原子粒度,它指的是给定业务流程捕获数据的最详细级别。
因此,对于网约车示例,理想情况下,事实表的每一行应对应单个客户完成的一次行程。
由于事实与事件相关,而事件发生后无法更改,因此事实表中的数据是不可变的。换句话说,事实表不会更改,我们只追加新数据。因此,你会发现大多数事实表通常都是窄而长的,这意味着它们不会有太多列,但可以有很多行来代表事件。
维度表:提供事件的上下文
事实表总是伴随着维度表,维度表为存储在事实表中的事件提供参考数据、属性和关系上下文。
它们描述事实表中每个事件的内容、人物、地点和时间,并且通常有很多列。因此,与窄而长的事实表相比,维度表通常是宽而短的,这意味着它们会有很多描述性列,但行数较少。

在网约车示例中,你可以有包含客户、司机和行程位置信息的维度表。
因此,在星型模式中,中心是包含业务事件事实的事实表,周围是提供额外上下文的维度表。
在某些情况下,你甚至可以将一个维度表连接到来自不同星型模式的多个事实表。在多个星型模式中重复使用的维度称为一致性维度。
星型模式的结构与连接
事实表通过外键连接到维度表。每个维度由一个主键定义,事实表也有自己的主键,这个主键可以是来自生产表的自然主键,但最佳实践是创建自然主键的替代品,也称为代理键。


这样,你就可以组合来自不同源系统的数据,这些系统的自然主键可能以不同格式编写,并且你可以将星型模式的主键与可能发生变化的源数据库主键解耦。

以下是你在课程1的第一个实验中接触过的一个星型模式示例。
事实表中的每一行对应订单中放置的一个产品,并包含诸如订购数量、单价、总价等业务度量值。该事实表连接到三个维度表,这些表提供与客户、产品以及下单地点相关的进一步特征。
你可以看到事实表有一个由订单号和订单行号组成的复合主键,并且该表包含三个外键来连接三个维度表。
星型模式如何助力分析查询?
那么,星型模式究竟如何帮助分析查询呢?
你可以从事实表开始,通过应用聚合查询来查找事实表中特定事实度量值的总和、平均值或最大值。然后,你可以使用维度表来过滤或分组事实。
例如,假设你有兴趣找出美国境内每个产品线的总销售额。你需要对事实表中的订单金额列进行求和。


在SQL中,你可以从事实表中选择订单金额的总和,并将其命名为总销售额。然后,你需要使用产品维度表中的产品线列来对销售额进行分组,因此你可以基于产品代码将事实表与产品维度表连接起来。你还需要在SELECT子句中指定产品线,然后按产品线对结果进行分组。
接着,你必须使用位置维度表中的国家列来过滤结果,以便只计算发生在美国的销售额。因此,你可以基于邮政编码连接位置表,然后过滤国家等于“USA”的结果。
最终,你会得到一个如下所示的SQL查询:
SELECT
p.productLine,
SUM(f.orderAmount) AS total_sales
FROM
fact_table f
JOIN
product_dimension p ON f.productCode = p.productCode
JOIN
locations_dimension l ON f.postalCode = l.postalCode
WHERE
l.country = 'USA'
GROUP BY
p.productLine;
与规范化模型的查询对比
假设你想使用数据集的规范化版本查找相同的信息(美国境内每个产品线的总销售额)。那个查询会是什么样子呢?


首先,你需要定位你要查找的业务度量值,即总销售额。你可以通过将订单明细表中的单价列与订购数量列相乘来获得这个值。
然后,你必须将订单明细表与产品表连接,再将产品表连接到产品线表以按产品线分组。最后,你必须连接客户表、订单表和订单明细表,以按国家过滤结果。
虽然两种模型包含相同的信息,但星型模式以更易于业务用户理解和导航的方式组织数据。它还导致查询更简单,连接更少,从而加快了查询性能。
总结:两种模型的适用场景
在本节课中,我们一起学习了星型模式(维度数据模型)的核心概念。我们了解到,事实表存储不可变的业务事件度量值,维度表提供描述性上下文。它们通过外键连接,形成星型结构,旨在优化分析查询。

规范化形式和星型模式各有其用武之地。规范化形式确保数据完整性并避免数据冗余,而星型模式则有助于分析工作负载。

在下一个视频中,我们将讨论这两种形式如何在数据仓库中使用。我们下次见。
006:Inmon与Kimball数据仓库建模方法对比 🏗️

在本节课中,我们将要学习数据仓库领域的两种核心数据建模方法:Inmon方法和Kimball方法。我们将探讨它们各自的定义、核心思想、应用场景以及优缺点,帮助你理解如何在不同的业务需求下选择合适的建模策略。
概述
在之前的课程中,我们讨论了数据仓库作为一种存储系统,用于将事务处理系统与分析系统分离。但我们并未深入探讨数据在数据仓库内部是如何被建模以支持这一目标的。数据仓库的数据建模有多种方法,作为数据工程师,你在工作中很可能会遇到其中最主要的几种:Kimball方法、Inmon方法和数据仓库建模方法。本视频将重点讲解Inmon和Kimball方法。
Inmon数据建模方法

上一节我们回顾了数据仓库的基本概念,本节中我们来看看由“数据仓库之父”Bill Inmon提出的建模方法。Inmon在1989年创建了他的数据仓库建模方法,其核心目标是实现事务系统与分析系统的分离。
Inmon将数据仓库定义为一个面向主题的、集成的、非易失的、随时间变化的数据集合,用于支持管理决策。该定义的延伸指出,数据仓库包含细粒度的企业级数据,这些数据能够用于多种不同目的,甚至可以满足当前尚未明确的未来需求。
这里的“面向主题”和“细粒度”意味着,Inmon模型将数据按照业务的主要主题领域进行组织,并包含与这些主题相关的所有细节。例如,在一家电子商务公司,主题可能包括产品、订单、客户、货运等。对于每个主题,数据模型必须包含所有细节,如业务键、关系和属性。
因此,采用Inmon数据建模方法,你需要整合来自不同数据源的数据,并将其建模为高度规范化的形式,然后存储到数据仓库中。随后,你可以通过部门特定的数据集市为下游的报告和分析提供数据。
这种方法使数据仓库成为支持多种数据用例的单一事实来源,即使当前的分析需求尚未明确。数据仓库中严格的规范化要求减少了数据重复,从而降低了下游分析错误,并确保了更好的数据完整性和一致性。
以下是应用Inmon建模方法于电商数据的示例:
假设业务将订单、库存和营销信息存储在不同的源系统中。你可以从这些源摄取数据,并以高度规范化的第三范式将其存储在数据仓库中。为了满足部门特定的数据需求,你可以从数据仓库中取出这些数据,将其建模为各种星型模式或其他合适的模型,并放入下游的销售、营销和采购数据集市中。每个部门都有其独特且针对特定需求优化的数据结构,这样各部门的数据用户就能轻松查询数据以满足其用例。
Kimball数据建模方法
了解了以高度规范化的数据仓库为核心的Inmon方法后,本节我们来看看另一种思路。与Inmon方法不同,Kimball建模方法更侧重于直接在数据仓库中对部门特定的分析进行建模和服务,而无需先将数据规范化。
该方法由Ralph Kimball在20世纪90年代初提出,它允许你直接从数据仓库本身提供结构化为星型模式或类似变体的数据,从而将数据集市整合到整体的仓库架构中。
以下是Kimball数据仓库用于电商的示例:
从订单、库存和营销存储系统摄取数据后,你将数据建模为多个星型模式,以应对业务的不同事实,然后直接将其存储在数据仓库中。

Kimball方法支持更快的建模,从而实现比Inmon方法更快的迭代,但其代价是潜在的数据完整性问题。因为你将具有更多数据冗余和重复的星型模式直接存储在了数据仓库中。
方法对比与选择建议
我们已经分别介绍了Inmon和Kimball两种方法的核心流程,现在我们来对比一下,并探讨如何根据实际情况进行选择。
以下是两种方法的关键对比点:
- 核心理念:Inmon方法将数据仓库视为规范化的单一事实来源,在其上构建数据集市。Kimball方法则在数据仓库内直接构建面向部门的维度模型(如星型模式)。
- 数据结构:Inmon强调高度规范化(如第三范式)。Kimball强调维度建模(如星型模式)。
- 实施速度:Inmon方法通常较慢,因为需要先完成全面的规范化。Kimball方法通常更快,利于快速迭代。
- 数据一致性:Inmon方法通过单一事实来源确保更高的数据一致性和完整性。Kimball方法由于可能存在冗余,数据一致性维护的挑战更大。
- 灵活性:Inmon方法对未定义的分析需求更灵活。Kimball方法更针对已知的、具体的业务分析流程。
因此,如果你的组织优先考虑对特定业务流程的快速、实用洞察,并寻求数据仓库的快速实施和迭代,那么建议你采用Kimball方法。


另一方面,如果数据质量是你的最高优先级,或者分析需求尚未明确,那么建议你选择Inmon的数据建模方法。该方法将数据仓库视为单一事实来源,所有数据集市都建立在高度规范化的数据仓库之上,以确保数据的一致性和完整性。
根据你所在的组织,在为不同的数据仓库建模时,你可能需要同时应用Inmon和Kimball两种建模方法。因此,理解如何处理高度规范化的数据和星型模式中的数据非常重要。
总结与下节预告
本节课中,我们一起学习了数据仓库的两种基础建模方法:Inmon方法和Kimball方法。我们了解了Inmon方法如何通过高度规范化的数据仓库确保数据质量与一致性,以及Kimball方法如何通过维度建模实现快速的业务洞察交付。


在讨论另一种数据建模方法之前,请与我一起进入下一个视频,学习如何将第三范式中的规范化数据模型转换为星型模式。
007:从规范化模型到星型模式 🚀

在本节课中,我们将学习如何将存储在规范化模式中的数据转换为星型模式。这是数据工程师的一项常见任务,旨在使数据更易于查询和分析。
概述
作为数据工程师,您很可能需要将存储在规范化模式中的数据转换为星型模式。例如,您可能需要从关系数据库中提取规范化数据,并将其建模为星型模式,以便在加载到部门特定的数据市场之前更容易查询。本节我们将通过一个示例,演示如何将第三范式(3NF)的规范化数据转换为星型模式。
从规范化模型到星型模式的转换步骤
以下是我们在之前视频中通过应用规范化阶段获得的规范化数据的ER图。它由四个表组成:customers(客户)、orders(订单,由客户下达)、order_items(构成每个订单的商品)以及items(每个商品的特性)。我在这里添加另一个表stores,来表示每个订单下达的商店。在此图中,PK表示主键,FK表示外键。
假设您的任务是将这些数据建模为星型模式,以供公司内的数据分析师使用。我们将遵循Kimball提出的设计星型模式的四个关键步骤。
第一步:理解业务需求
这有助于您确定要在事实表中建模的业务事件或流程,并帮助您声明粒度,即您希望事实表中每一行代表的详细程度。
以下是确定业务需求的关键点:
- 与数据分析师沟通后,了解到他们有兴趣分析销售数据,以了解特定日期哪些产品在哪些商店销售。
- 分析不同商店之间的销售是否存在差异。
- 确定哪些产品品牌最受欢迎。
由此,您确定需要建模的业务流程是公司的销售交易。
第二步:确定事实表粒度
您可以选择在事实表的每一行中表示特定日期的总销售交易额、单笔销售交易,甚至是销售交易中的单个产品项目。在决定粒度时,建议选择原子粒度,以捕获销售交易的最低级别细节。这样可以使您的系统足够灵活,以应对未来不可预测的用户问题。
因此,我们将粒度声明为交易中的单个产品项目。
第三步:选择维度表
由于数据分析师有兴趣分析关于商店、日期和品牌的销售情况,您可以创建一个代表商店的维度表、一个代表商品特性的维度表以及一个日期维度表。
以下是创建维度表的SQL示例:
创建商店维度表
您需要从stores表中选择store_id、store_name、store_city和store_zip_code。您还需要为此表设置一个主键。通常,您希望生成代理键并将其用作维度和事实表的主键。这可以确保星型模式中的每一行都可以通过事实表和维度表的主键唯一标识,而不受商店系统可能发生的变化的影响。
要为商店维度表生成代理键,您可以创建一个从1开始的整数序列,并为每个商店分配一个整数;或者,您可以使用哈希函数,该函数接收自然主键并为每个商店输出唯一的代理键。流行的数据库管理系统(如PostgreSQL和MySQL)支持多种哈希函数。例如,MD5是一种将字符串编码为哈希输出的哈希函数。
假设生产数据库中的store_id是字符串,我可以对store_id应用MD5哈希函数来为每一行生成代理键,并将代理键标记为store_key。如果store_id实际上是整数值,则需要先将其转换为字符串。
SELECT
MD5(store_id) AS store_key,
store_id,
store_name,
store_city,
store_zip_code
FROM stores;
执行此SQL语句后,您将得到这个商店维度表。请注意,为了便于引用和解释,它同时包含代理键store_key和自然键store_id。
创建商品维度表
通过从items表中选择sku、name和brand来创建商品维度表。然后,对于主键,您可以对sku(假设sku是字符串)应用MD5哈希函数,然后将代理键标记为item_key。
SELECT
MD5(sku) AS item_key,
sku,
name,
brand
FROM items;
您将得到这个商品维度表。

创建日期维度表
日期维度背后的理念是,对于每个日期,您可以创建指定相应星期几、月份、季度和年份的列。这将帮助数据分析师回答诸如“2022年第一季度总销售额是多少?”或“周末哪些产品最受欢迎?”等问题。
要创建日期维度表,您可以生成一系列覆盖所需时间段的连续日期。以下是在PostgreSQL中生成一系列每日日期的一种方法:首先生成从2020年1月1日到2025年1月1日的一系列日期,然后从每个日期中提取星期几、月份、季度和年份。
SELECT
date_series AS date_key,
EXTRACT(DOW FROM date_series) AS day_of_week,
EXTRACT(MONTH FROM date_series) AS month,
EXTRACT(QUARTER FROM date_series) AS quarter,
EXTRACT(YEAR FROM date_series) AS year
FROM generate_series('2020-01-01'::date, '2025-01-01'::date, '1 day'::interval) AS date_series;
这样,三个维度表就创建好了。

第四步:创建事实表

事实表中的每一行必须代表销售交易中的一个产品。与每个产品相关的事实是销售数量(可以从order_items表获取)和每个商品的价格(可以从items表获取)。此外,事实表必须包含连接维度表的外键。在本例中,这些是store_key、item_key和date_key,它们是为每个维度表创建的代理键。当然,事实表必须包含一个主键来唯一标识每一行。您可以创建一个由order_id和item_line_number组成的复合键,但更好的做法是从这两个自然键的组合生成一个代理键。
因此,fact_order_item表应该类似这样。现在,让我们编写SQL查询来创建这个事实表。
对于主键,您将从order_items表中选择order_id和item_line_number,然后连接它们,以便对连接后的字符串应用MD5哈希函数来生成代理键。您将其标记为fact_order_key。为了便于引用,您还将在事实表中包含自然键order_id和item_line_number。然后,您将连接orders表和items表,以便创建其余属性。
对于外键,让我们对orders表中的store_id应用哈希函数,以创建连接到商店维度表的store_key。然后,对order_items表中的item_sku应用哈希函数,以创建连接到商品维度表的item_key。最后,从orders表中选择order_date,以创建连接到日期维度表的date_key。别忘了,我们需要添加两个事实:从order_items表中选择item_quantity,从items表中选择price。
SELECT
MD5(CONCAT(oi.order_id, oi.item_line_number)) AS fact_order_key,
oi.order_id,
oi.item_line_number,
MD5(o.store_id) AS store_key,
MD5(oi.item_sku) AS item_key,
o.order_date AS date_key,
oi.item_quantity,
i.price
FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
JOIN items i ON oi.item_sku = i.sku;
星型模式的关系分析
这是我们刚刚创建的星型模式的ER图。由于订单中的商品是在特定日期购买的,order_items事实表中的每一行只能与日期维度表中的一行关联。因此,从order_items事实表到日期维度表的关系是一对一的,并由此处的符号表示。
另一方面,一个特定日期可能不与任何订单关联(如果当天没有人购买任何东西),或者可能与多个订单关联(如果当天下了多个订单)。从日期维度表到order_items事实表的关系是零或一对多,或更常见地称为一对多。
类似地,订单中的商品在给定商店购买,并对应单个商品。因此,事实表中的每一行与商店维度表中的一行和商品维度表中的一行关联。从事实表到这些维度表的关系是一对一。但任何商店和任何商品都可能与多个订单关联,因此从这些维度表到事实表的关系是一对多。

工具选择:DBT 与 AWS Glue
如果您想进行更多练习,我已在视频后的阅读材料中包含了另一个建模示例。在本周最后一个实验中,您还将有机会将规范化数据建模为星型模式。虽然您可以像在前面的实验中使用AWS Glue那样编写自己的代码来转换数据,但在这个实验中,您将使用一个流行的数据转换工具DBT,它通过抽象掉大量编写纯SQL代码的繁重工作来帮助您建模数据。
DBT允许您连接到数据仓库,然后在数据仓库内部转换和验证数据。它将建模过程视为转换任务,并在后台生成SQL代码来转换数据。尽管DBT简化了转换步骤,但它只能连接到单个目标,这意味着您不能使用DBT连接来自不同源的数据,并且在数据转换后不能将其移动到另一个目标系统。要连接来自多个源的数据,您需要首先将数据引入同一个目标系统内。
另一方面,AWS Glue允许您连接到不同的源,应用转换,并将处理后的数据存储在其他地方。因此,如果您需要对将要移动的数据执行转换,应选择AWS Glue或类似的摄取工具,而不是DBT。
总结

在本节课中,我们一起学习了如何将规范化数据模型转换为星型模式。我们首先理解了业务需求并确定了事实表的粒度,然后创建了维度表(商店、商品、日期)和事实表,并分析了它们之间的关系。最后,我们简要比较了DBT和AWS Glue这两种数据转换工具的使用场景。通过掌握这些步骤,您将能够更有效地构建易于查询和分析的数据模型。
008:与Drew Banin讨论dbt 🛠️


在本节课中,我们将与dbt Labs的联合创始人Drew Banin进行对话,深入探讨dbt是什么、它解决了什么问题,以及数据从业者应如何有效地使用它。我们将了解dbt在现代数据平台中的角色,并学习一些使用dbt的最佳实践。
什么是dbt?🤔
上一节我们介绍了课程概述,本节中我们来看看dbt究竟是什么。Drew Banin首先为不了解他的观众做了自我介绍。
我的名字是Drew,是dbt Labs的联合创始人之一。我住在宾夕法尼亚州费城,我们从事dbt开发工作大约已有八年。
对于学习者可能首先会问的问题——“什么是dbt”,Drew给出了详细的解释。
这是一个很好的问题。我为所有新员工做一个入职培训,我们称之为“什么是dbt”。有趣的是,这个培训长达一小时,而我用前50分钟谈论dbt之外的一切。我喜欢先为现代数据平台搭建一个背景。
这涉及数据仓库或类似的东西,以及连接到数据平台的工具。所以,首先是数据仓库和数据摄取,将你所有的数据集中到一个地方。只有在介绍了所有这些之后,我们才谈论dbt。我将把这个60分钟的演讲浓缩到大约一分钟。
我认为dbt是为你的数据应用业务逻辑规则,从而将数据转化为有用信息的工具。
如果我们思考数据,它是数字、字符串、状态。但时间戳是UTC时间还是太平洋时间?出现的数字是美元、加元还是欧元的收入?如果我们把业务逻辑看作规则,那么使用dbt,你可以用SQL和Python将这些规则代码化,可以版本控制这些逻辑,并采用基于软件工程的工作流来管理逻辑随时间的变化。
然后,dbt会帮助你在数据平台内部将这些逻辑应用到你的数据上。我们不会把你的数据从数据仓库中取出、转换再放回去。我们所做的是指示数据仓库在原地转换你的数据。人们喜欢这一点,因为它更安全、治理更好,数据不会离开你的数据平台。
这是三万英尺的宏观视角,除此之外还有很多可以讨论的,但概括来说,dbt是关于业务逻辑的。
dbt出现之前的世界 🌍
上一节我们了解了dbt的核心概念,本节中我们来看看在dbt出现之前,数据分析师、数据工程师或分析工程师的工作流程是怎样的。
确实,“分析工程师”这个职位描述或头衔,可以说是从dbt以及我们通过dbt推广的模式中诞生的。在dbt出现之前,主要有两种情况。
第一种是“狂野西部”时期。那时人们将随机的SQL脚本存储在个人硬盘上。事实上,我以前工作过的一个地方,当一位数据分析师离职时,他给他的老板(CFO)发送了一个包含大约50个SQL脚本的压缩文件。这不是一个好的离职交接计划,这很混乱。
在dbt之前,人们只是临时处理这些事情,创建表格,运行随机的插入和删除语句来修复数据,并没有一个真正的审计追踪来记录所有应用于数据的转换,它们只是实时发生在数据仓库中。
在dbt之后,我们看到的情况是,人们会更好地版本控制他们的代码。我们围绕这项工作建立了一个实践社区,或者至少帮助促成了一个实践社区。这些人自称分析工程师,我们亲切地称他们为“紫色人群”。我们认为工程师是“红色人群”,业务人员是“蓝色人群”,而一个好的分析工程师正好处在中间。你理解业务背景,也理解技术,可以帮助这两个群体走到一起,在正确的时间创建正确的数据来解决正确的问题。
我认为学习者应该明白,在类似dbt的工具出现之前,世界是怎样的。我在那样的世界里工作过,非常繁琐。你版本控制SQL脚本,但有人改了,别人又改了,很快你就有数不清的SQL脚本,最终变成一团乱麻。
如何开始使用dbt 🚀
上一节我们对比了dbt出现前后的工作方式,本节中我们来看看对于dbt新手,Drew有哪些入门建议。
我想说,我们一直认为dbt类似于Ruby on Rails(如果你熟悉的话)。其理念是,dbt在很大程度上是一个应用业务逻辑的框架,有很多“约定”。其思想是,如果你遵循“dbt之道”,那么简单的事情会变得容易,困难的事情也将成为可能。
所以我认为,如果你一开始就发现自己在与dbt对抗,这可能是一个信号,提醒你应该查阅我们的文档或本课程,确保你使用了正确的思维模型来接近dbt。当然,不可避免地,你会需要突破框架的限制,因为数据很复杂,每个业务都不同。
我们确实有很多“逃生舱口”,你可以注入自己的逻辑,进行自定义操作。但显然,作为起点,开始使用dbt应该非常容易。你创建一个SQL文件,输入dbt build,就可以开始运行了。然后,你可以开始考虑测试数据、编写文档,最终在其核心模型和能力之上构建语义层等功能。但建议从模型开始,可能接着是测试,然后逐步深入。
dbt建模与维护的最佳实践 📋
上一节我们讨论了如何开始使用dbt,本节中我们来看看Drew推荐的一些关于构建和维护dbt模型的最佳实践。
如果人们在一个组织中大规模进行这项工作,我们总是建议使用SQL风格指南或类似的东西。其目标是确保每个人的代码,无论团队或个人是谁,看起来都非常相似。为如何命名列、如何将逻辑拆分为暂存表、中间转换表,以及更类似于集市或维度模型,制定约定,这对于大规模保持组织有序非常有用。
除此之外,我认为很多软件工程的最佳实践通常也适用。保持代码相对模块化,不要试图在一个文件中做太多事情。因为我们把SQL模型看作一个函数,所以不要试图在一个函数或文件中做太多事情。
确保在编码过程中进行测试,特别是我们刚刚真正支持了单元测试,实际上在dbt中有新的测试编写方式。
总之,模块化、养成创建拉取请求并在代码上线前进行代码审查的习惯、设置CI/CD等实现自动化部署,这些都是dbt Cloud可以帮助的事情,如果你需要,也可以用自己的方式实现。
软件工程知识的重要性 💻
上一节提到了软件工程实践,本节中我们来探讨学习者是否应该去学习一些软件工程原理。
这总是一个有趣的问题,因为我在大学学过计算机科学,但我不推荐它(主要是开玩笑)。
听我说,一些基础知识是超越时间和趋势而不变的。比如SQL是一项有几十年历史的技术,当然它进化了,但基础大致相同。在软件方面,Git相对较新,我想大概不到20年历史吧?我认为是的,Linus Torvalds有一天随机推出了它。
但在Git之前也有版本控制,同样的规则适用:保持更改较小、使其可测试、以可测试的方式编写代码。老实说,这实际上不是大学计算机科学课程教的内容,至少我上学的地方没有。有时他们把它放在另一类课程中,称之为“软件工程”,更多是关于测试、方法论和扩展。我自己在这方面没有大量的正式经验。
但我知道,如果你以前以任何身份编写过代码,你就能理解什么时候代码像意大利面一样混乱,或者架构良好。关键在于,事先认真思考你试图做什么,并确保你在解决正确的问题。我认为当人们一开始就写代码时就会遇到麻烦。有句话说得好:10小时的编码可以节省你1小时的规划,请记住这一点。
dbt社区与资源 👥
上一节我们讨论了技术实践,本节中我们来看看Drew希望学习者了解的关于dbt的其他方面,特别是其社区。
我们非常重视社区。dbt背后的很多能量来自社区。我们在全球各地都有聚会,我们有像社区Slack这样的地方,每年十月我们还有一个名为“Coalesce”的社区会议。所以我想说,除了课程本身,如果你正在寻找与其他从事这项工作的人建立联系的方式,一定要查看dbt社区,欢迎加入,我们很乐意有你。
总结 📝
本节课中,我们一起学习了dbt的核心概念及其在现代数据平台中的角色。我们了解了dbt出现之前数据工作的混乱状态,以及dbt如何通过引入版本控制、模块化和测试等软件工程最佳实践来改变这一局面。我们还探讨了如何开始使用dbt、一些建模和维护的最佳实践,以及软件工程原理的相关性。最后,我们认识到dbt拥有一个充满活力的社区,为学习者提供了额外的支持和联系机会。希望本课程能帮助你更好地理解dbt及其在数据工程领域的重要性。
009:数据仓库建模方法 🏗️

在本节课中,我们将要学习数据仓库的第三种核心建模方法:Data Vault。我们将了解它与Inmon和Kimball方法的不同之处,并详细解析其三层架构以及核心的Hub、Link和Satellite表结构。
概述
Data Vault建模方法侧重于将数据的结构(即业务实体及其关系)与数据的描述性属性分离开来。它通过使用独立的表来代表核心业务概念、概念间的关系以及这些概念的描述属性,旨在构建一个灵活、敏捷且可扩展的数据仓库结构,使其能够紧密跟随业务变化。
Data Vault 架构
上一节我们介绍了Inmon和Kimball方法,本节中我们来看看Data Vault的独特架构。与之前的方法不同,Data Vault架构通常包含三个层次。
以下是Data Vault的三层架构:
- 暂存区:从源系统加载原始数据,采用仅插入方式,不改变数据或强制执行业务逻辑,仅确保摄入预期的数据类型。
- 企业数据仓库层:使用Hub、Link和Satellite表对数据进行建模,将业务对象、关系与描述属性分离。这是Data Vault的核心层。
- 信息交付层:将数据加载到下游数据集市中,这些数据集市可以建模为星型模式或其他结构,以支持不同的业务领域。在此层进行聚合、分组等操作以满足用户需求。

企业数据仓库层模型详解
现在,让我们深入了解企业数据仓库层的具体模型。Data Vault模型主要由三种类型的表构成。
以下是三种核心表类型:
- Hub:存储业务键的唯一列表,用于表示核心业务概念,例如客户、产品、员工。其主键是业务键的哈希值。
- Link:连接两个或多个Hub,表示业务概念之间的关系、交易或事件。其主键是所连接Hub的业务键的复合哈希值。
- Satellite:包含为Hub或Link提供描述性上下文的属性。其主键是父级Hub或Link的哈希键与加载日期的组合。

在Data Vault中,没有“好”或“坏”数据的概念,只改变数据的存储结构。这种方式可以轻松追溯数据到其源头,并在业务需求变化时避免重构整个数据仓库。
构建Data Vault模型:一个电商示例
为了更直观地理解,我们通过一个电商示例来演示构建Data Vault模型的关键步骤。假设我们需要为客户、订单、商店和商品建模。

以下是构建模型的三步流程:
-
建模Hub:首先识别核心业务概念及其业务键。例如,
Customer Hub的业务键是Customer ID,Order Hub是Order ID。每个Hub还必须包含哈希键、加载日期和记录源这三个标准字段。- 公式/代码示例:
Hub_PK = HASH(Business_Key)
- 公式/代码示例:
-
建模Link:其次,使用Link表连接Hub以捕获关系。例如,创建一个
Order-Customer Link连接订单和客户Hub,表示“哪个客户下了哪个订单”。再创建Item-Order Link和Order-Store Link。Link表的主键由父Hub的业务键组合哈希而成。- 公式/代码示例:
Link_PK = HASH(Hub1_BK, Hub2_BK)
- 公式/代码示例:
-
建模Satellite:最后,创建Satellite表为Hub和Link提供描述性上下文。例如,为
Customer Hub创建Customer Satellite,包含客户姓名、邮编等属性。为Item-Order Link创建Satellite,包含商品订购数量。- 公式/代码示例:
Satellite_PK = HASH(Parent_Hub_Link_PK) + Load_Date
- 公式/代码示例:

通过这种设计,当业务规则变化时(例如,允许多个客户共同支付一个订单),只需通过Link表建立多对多关系,而无需重新设计整个模型,体现了其灵活性。
总结与延伸
本节课中我们一起学习了Data Vault数据建模方法。它通过分离业务键、关系和描述属性,提供了一种解耦于源系统的灵活设计,使数据仓库能够轻松适应业务演变。
然而,本章内容仅涵盖了Inmon、Kimball和Data Vault这三种最流行方法的基础,远未体现其各自的复杂性与细微差别。在资源部分,我列出了每种方法创始人的一些书籍,强烈推荐你阅读以进一步理解数据建模对于批处理分析数据为何及如何至关重要。

近年来,一种称为“单一大表”的方法已出现,用于为分析用例建模数据。请观看下一个视频,我们将“单一大表”作为本周要介绍的最后一种数据建模方法进行探讨。
010:大表模型 🗃️

在本节课中,我们将要学习一种在现代数据工程中日益流行的数据建模方法——大表模型。我们将探讨其核心概念、优缺点以及适用场景。
概述
本周我们已经学习了金博尔模型和英曼模型等传统数据建模方法。这些方法诞生于数据仓库成本高昂、资源受限且计算与存储紧密耦合的本地部署时代。虽然批处理数据建模传统上与这些严格的方法相关联,但一种更为宽松的方法,即“大表模型”,正变得越来越普遍。
什么是大表模型?📊
大表模型,简称OBT,其核心思想是将所有数据放入一个单一的宽表中。顾名思义,这是一个包含许多列的非常“宽”的表,通常在列式数据库中创建。
一个宽表可能拥有数千列,而关系数据库中的表通常只有十几列。列可以是单个值,也可以包含嵌套数据。因此,大表模型中的宽表是高度反规范化和灵活的。

以下是一个宽表的示例,它本质上是你在之前视频中见过的反规范化表。这只是一个简单的例子,实际的大表模型宽表可能拥有更多的列。
-- 示例:一个宽表可能包含客户、订单、产品等所有相关信息
SELECT
customer_id,
customer_name,
order_id,
order_date,
product_id,
product_name,
quantity,
unit_price,
-- ... 可能还有数百个其他字段
FROM one_big_table
如图所示,这个表结合了各种数据类型,每一行代表一个客户订单。你可以将大表模型视为金博尔方法的延伸,它将事实和维度数据都呈现在同一个表中。
大表模型的优势与原理 🚀
通过将事实和维度数据放在同一张表中,数据分析师可以免于执行任何复杂的连接操作,甚至完全不需要进行表连接。
此外,在宽表上运行相同的分析查询,通常比在高度规范化的数据上,甚至在星型模型(可能仍需连接维度表和事实表)上运行得更快。宽表简单地包含了所有原本需要通过连接才能获取的数据。对于任何需要扫描大量数据的查询性能而言,这都可能产生巨大影响。
大表模型之所以越来越普遍,主要得益于云存储的低成本。同时,许多组织选择在其源系统和分析系统中使用嵌套数据来设计灵活的架构。使用大表模型,你可以将这些嵌套数据全部存储在一个表中,而无需担心在存储中表示它们的最佳方式。
技术考量:列式存储的重要性 💾
宽表通常是稀疏的,这意味着任何给定字段中的绝大多数条目可能都是空值。在传统的面向行的关系数据库中,存储和读取包含大量空值的宽表成本极高,因为数据库需要为每个条目分配固定的空间,并且在读取时必须完整读取每一行的内容。
然而,随着列式数据库的出现,你可以只读取查询中选定的列,因此读取空值基本上没有成本。列式存储有助于优化宽表的存储和处理性能。
大表模型的缺点与权衡 ⚖️
对大表模型最主要的批评是,在混合数据的过程中,你可能会丢失分析中的业务逻辑。另一个缺点是,你需要使用数组等复杂数据结构来存储嵌套数据。这些结构在更新和聚合元素时性能较差。
因此,我建议在以下情况下使用宽表:当你拥有大量数据,并且需要比传统数据建模方法提供更大的灵活性时。

在数据建模领域,没有放之四海而皆准的解决方案。因此,你必须理解不同可能方法之间的权衡,包括灵活性、数据完整性以及对下游利益相关者的易用性,从而为你的具体用例选择最佳方法。
总结
本节课我们一起学习了大表模型。我们了解到,这是一种将数据高度反规范化、存储在一个宽表中的现代建模方法。它的优势在于查询简单、性能高效,特别适合云存储和列式数据库环境。然而,它也存在可能丢失业务逻辑、处理嵌套数据性能较低等缺点。关键在于根据数据量、灵活性需求和查询模式来权衡选择。


在接下来的实验中,你将练习将规范化数据建模为星型模式和大表模型,这是数据工程师的一项常见任务。但在进入实验之前,课程提供了一个关于DBT的演示和一个练习,让你在实验中使用的SQL查询中实践数据建模技巧。
011:使用dbt转换数据(第1部分)📊
在本节课中,我们将学习如何使用数据构建工具(dbt)将规范化的数据转换为星型模式。我们将从安装dbt和配置环境开始,为后续创建数据模型做好准备。
概述
我们将使用dbt对一个包含五个表(order_items、orders、customers、items、stores)的本地PostgreSQL数据库进行操作。这些表目前位于名为staging的模式下。我们的目标是在一个新的star_schema模式下,创建事实表fact_order_items以及维度表dim_stores、dim_items和dim_date。dbt可以帮助我们通过封装SQL语句来轻松创建这些表,并协助进行数据文档化和验证。
安装与设置dbt环境
首先,我们需要安装dbt并设置工作环境。dbt提供两种环境:dbt Core(开源命令行工具)和dbt Cloud(基于浏览器的托管环境)。本教程将使用dbt Core,这也是后续实验中将用到的工具。

以下是设置步骤:
- 创建一个新的虚拟环境并激活它。
python -m venv dbt_env source dbt_env/bin/activate # 在Windows上使用 `dbt_env\Scripts\activate` - 安装dbt Core及其PostgreSQL适配器。
dbt Core通过适配器与数据库通信,您可以根据所使用的数据库类型选择其他适配器。pip install dbt-core dbt-postgres
初始化dbt项目
安装完成后,我们需要创建一个dbt项目文件夹,该文件夹将包含定义新表所需的所有文件。


- 运行
dbt init命令来初始化项目。dbt init dbt_tutorial - 系统会提示选择数据库类型。由于我们只安装了PostgreSQL适配器,输入
1即可。 - 随后,dbt会要求输入数据库信息以自动创建连接配置文件。此时,我们可以按
Ctrl+C中断此过程,稍后手动创建该文件。
运行上述命令后,您的工作目录中会生成一个名为dbt_tutorial的文件夹。
认识dbt项目结构
让我们打开IDE,查看dbt_tutorial项目文件夹中的主要目录和文件:
models/:这是核心目录,您将在此处花费大部分时间。您需要为星型模式中的每个表创建一个.sql文件,其中包含定义该表的SQL语句。您还可以添加.yml文件来配置模型、定义测试和文档。analyses/:用于存放不属于核心模型、但可能用于探索性分析的SQL语句。macros/:用于存储希望在多个模型中复用的SQL代码片段。seeds/:用于存放希望加载到数据仓库的CSV文件。snapshots/:用于记录表随时间的变化。tests/:用于创建对数据执行特定测试的SQL语句。
在本视频和实验中,我们将主要使用models/子文件夹。
配置项目文件
除了子文件夹,项目根目录下还有一个重要的dbt_project.yml文件,它包含了项目的配置。
让我们查看这个文件的关键部分:
name: ‘dbt_tutorial’
version: ‘1.0.0’
profile: ‘dbt_tutorial’
model-paths: [“models”]
analysis-paths: [“analyses”]
...
models:
dbt_tutorial:
# 在此配置默认模型设置
+materialized: table
在配置中,您需要指定项目名称、版本和配置文件(profile)名称。profile包含了连接数据库的详细信息,需要在另一个名为profiles.yml的文件中定义。
您还可以在此处定义模型的默认配置,例如使用+materialized: table或+materialized: view来指定模型是物化为表还是视图。
创建数据库连接配置文件
现在,让我们创建profiles.yml文件来指定如何连接到本地PostgreSQL数据库。
在dbt_tutorial项目目录中(或将其移至~/.dbt/目录下以提高安全性),创建profiles.yml文件:
dbt_tutorial: # 此名称必须与 dbt_project.yml 中的 ‘profile‘ 一致
target: dev # 指定默认使用的目标配置
outputs:
dev: # 开发环境配置
type: postgres
host: localhost
user: your_username
pass: your_password
port: 5432
dbname: your_database_name
schema: star_schema # 默认模式
threads: 1
请务必将host、user、pass、port、dbname替换为您实际的数据库连接信息。
验证数据库连接
配置文件创建完成后,我们需要验证dbt是否能成功连接到数据库。
- 在终端中,确保位于
dbt_tutorial项目目录下。 - 运行连接测试命令:
dbt debug
如果所有凭证无误,您应该会看到连接成功的提示信息。
总结
在本节课中,我们一起完成了使用dbt进行数据转换的第一步准备工作。我们安装了dbt Core并设置了Python虚拟环境,初始化了一个新的dbt项目,并了解了其核心目录结构。接着,我们配置了dbt_project.yml和profiles.yml两个关键文件,成功建立了与本地PostgreSQL数据库的连接。

下一部分,我们将开始创建具体的SQL模型文件,来构建星型模式中的事实表和维度表。
012:第1周总结 📊

在本节课中,我们将回顾第一周学习的核心内容,重点总结为批处理分析用例进行数据建模的不同方法。我们将探讨每种方法的原理、优缺点,并理解它们在实际数据工程工作中的应用场景。
概述
第一周,我们深入探讨了为批处理分析用例设计数据模型的各种方法。在实验环节,你实践了多个规范化步骤,将理论数据转化为第三范式。同时,你也使用DBT工具将规范化数据建模为星型模式。我们讨论了四种主要的数据仓库建模方法:Inmon方法、Kimball方法、Data Vault模型以及“一张大表”方法。每种方法都有其独特的侧重点和适用场景。
四种数据建模方法详解
上一节我们概述了本周的学习内容,本节中我们将详细回顾这四种核心的数据建模方法。
1. Inmon 建模方法 🏛️
Inmon建模方法的核心是使用高度规范化的模型(即第三范式)在数据仓库中构建数据。
主要优势在于它使数据仓库成为组织的单一事实来源,并确保了数据的完整性。这是因为当你将数据建模为第三范式时,你避免了数据的重复和冗余。
公式表示规范化目标:消除数据中的部分函数依赖和传递函数依赖,以达到 3NF。
然而,对处于第三范式的数据执行分析查询时,通常需要依赖多次连接的复杂查询,这可能导致查询性能较慢。
2. Kimball 建模方法 ⭐
Kimball建模方法支持更快的迭代和建模,因为它直接使用星型模式在数据仓库中对数据进行建模。
在星型模式中,你将一个业务流程或事件的度量(事实)收集到一张事实表中。然后,为了提供关于这些事实的详细上下文信息,你用维度表包围事实表。
代码示例表示结构:
-- 事实表
FACT_SALES (sale_id, product_key, date_key, customer_key, amount)
-- 维度表
DIM_PRODUCT (product_key, product_name, category)
DIM_DATE (date_key, year, month, day)
DIM_CUSTOMER (customer_key, customer_name, region)
这种方式允许数据分析师聚合事实表的度量,并使用维度表对查询进行分组或过滤。但是,要使用这种方法,你需要对业务需求有很好的理解,而这些需求在某些情况下可能定义不清或非常不稳定。
3. Data Vault 模型 🔗
另一方面,Data Vault模型提供了一种更灵活的设计,适用于业务需求或源系统结构可能经常变化的敏捷环境。
Data Vault方法在中心表中对核心业务概念进行建模,并使用链接表表示它们之间的关系。这些表只包含标识核心概念的业务键。
为了提供更有意义的上下文,你可以将中心表和链接表连接到卫星表,卫星表包含父中心表或链接表的属性。

公式表示核心思想:数据仓库 = 中心表(Hubs) + 链接表(Links) + 卫星表(Satellites)。
然而,使用这种模型,你仍然需要在信息交付层进行一些下游工作,将数据建模为星型模式或其他易于查询的结构。
4. “一张大表” 方法 📑
最后,我们看了“一张大表”方法,这或许是建模数据最简单的方式。
顾名思义,这种方法就是将所有数据放入一张大表中。这样一来,数据分析师不需要执行任何复杂的连接操作,因此执行分析查询速度很快。
但是,使用这种方法,你会在分析中丢失业务逻辑,并且可能最终得到一个包含重复信息、占用大量空间的大表。
方法对比与总结
综上所述,每种方法都有其自身的优点和缺点。在你的数据工程师工作中,很可能会结合使用不止一种方法。
第一周,我们主要聚焦于为批处理分析进行数据建模。我们学习了如何通过规范化确保数据完整性,也了解了如何通过维度建模优化查询性能。
下周预告

下周,我们将继续讨论数据建模,但重点将转向如何为机器学习用例建模和转换数据。
我们下周见。
013:数据工程(数据建模、转换和服务)第4课,完结|第2周概览 🗺️


在本节课中,我们将要学习如何为机器学习用例准备和建模数据。我们将从机器学习项目生命周期框架入手,明确数据工程师在其中的角色,并概述本周将学习的结构化数据、图像数据和文本数据的处理方法。
上周,我们介绍了几种用于批处理分析的数据建模方法,这些方法以支持分析查询的方式构建数据。
本节中我们来看看如何为机器学习用例准备和建模数据。这类建模的目标是以一种能帮助数据科学家或机器学习工程师理解数据含义的方式来构建数据,以便他们能利用这些数据开发机器学习系统并发现数据中的隐藏模式。
机器学习项目生命周期 🔄
构建一个机器学习系统涉及多个阶段。这里我将参考Andrew Ng在《Machine Learning and Production》课程中提出的机器学习项目生命周期框架。你会发现这个框架对大多数机器学习项目都很有用。


以下是该框架包含的主要阶段:
- 项目范围界定:确定机器学习项目的目标和范围。
- 数据收集与准备:收集数据并进行预处理。
- 算法开发:开发机器学习模型(为避免与数据建模混淆,此处将原“模型”阶段更名为“算法开发”)。
- 系统部署:将机器学习系统部署到生产环境。
你的工作,作为一名数据工程师,是构建和维护为上述一个或多个阶段提供数据的数据管道。在这些管道中,你需要从多个来源收集数据,并将其组合成适合机器学习算法的格式。你的任务还可能包括清洗数据、转换数据,甚至创建一些额外的列或特征。最后,你需要存储数据并将其分享给机器学习或数据科学团队。


数据工程师的角色定位 🧑💻
你可能会好奇,数据工程师的角色与数据科学家或机器学习工程师有何不同。必须承认,机器学习工程、数据科学和数据工程之间的界限正变得越来越模糊,并且这些角色的职责在不同组织间差异巨大。
以下是几种常见的情况:
- 一些组织可能拥有完全独立的数据团队,负责处理所有机器学习项目的整个生命周期。
- 在其他情况下,你作为数据工程师,可能只负责向机器学习或数据科学团队提供原始数据,然后由机器学习工程师或数据科学家接管后续的数据处理流程。
- 你也可能被要求处理原始数据,以便下游利益相关者可以直接使用处理后的数据来训练机器学习算法。
- 如果你的组织规模较小或没有成熟的机器学习团队,你甚至可能需要处理一些与机器学习高度相关的任务,例如数据的特征工程。

无论如何,作为一名数据工程师,你在帮助组织采用以数据为中心的机器学习方法方面扮演着关键角色。这种方法侧重于通过收集高质量数据来增强机器学习系统。
你可能听过“垃圾进,垃圾出”的说法,它指的是任何系统的输出质量都取决于其输入质量。因此,通过仔细地为机器学习算法准备数据,你可以帮助数据科学家或机器学习工程师提取准确且有意义的见解,从而创建更有用的机器学习系统。
我建议你对机器学习的工作原理有一个基本的了解,因为这将极大地帮助你为组织创造价值。


本周学习内容预览 📚
我不会试图教你关于机器学习的一切,而是将重点放在与经典或高级机器学习算法配合工作时,如何构建表格数据、图像数据和文本数据。要了解更多关于这些机器学习技术的知识,你可以在本周结束时的资源部分找到一份可供查阅的书籍和课程列表。


以下是本周的具体学习安排:
第一课:表格数据与特征工程
我们将从一些关键的机器学习术语概述开始,并深入探讨机器学习项目生命周期的各个阶段。然后,我们将讨论如何为经典机器学习算法构建表格数据。特别是,你将在本周的第一个实验课中执行特征工程和数据处理的某些步骤。
第二课:非结构化数据处理
你将学习如何建模和处理非结构化数据。你将学习为经典机器学习以及更高级的机器学习算法(如卷积神经网络)准备图像数据。
第三课:文本数据处理
最后,你将学习处理文本数据。如今,大多数文本数据的预处理步骤都可以由大型语言模型处理。然而,你仍然应该了解这些步骤,以防你需要手动处理文本数据以满足机器学习项目的特定成本或系统要求。因此,我将介绍一些基本的预处理技术,并向你展示如何将文本转换为可用于训练机器学习算法的向量。你将在本周的第二个实验课中有机会练习处理文本数据。


本周有很多内容要学习。让我们在下一个视频中开始吧。


本节课中我们一起学习了:机器学习项目生命周期的框架,明确了数据工程师在其中收集、准备、转换和提供数据的关键职责。我们还预览了本周将深入学习的三大主题:为机器学习准备表格数据、图像数据和文本数据。理解这些内容将帮助你更好地支持组织构建有效的机器学习系统。
014:机器学习基础概念与项目生命周期 🧠

在本节课中,我们将学习机器学习的基本术语、核心概念以及一个完整的机器学习项目生命周期框架。作为数据工程师,理解这些内容对于与机器学习团队有效协作至关重要。
机器学习基本概念
上一节我们介绍了课程目标,本节中我们来看看机器学习的基础分类。

监督学习与无监督学习
机器学习主要分为监督学习和无监督学习。
以下是两者的核心区别:
- 监督学习:算法从带有标签的数据中学习。标签是我们要预测的目标值。例如,根据历史数据预测客户是否会流失(流失/不流失)。
- 无监督学习:算法从未标记的数据中发现模式或结构。数据只包含特征,没有预设的标签。例如,根据客户行为数据自动将客户分成不同的群组。
分类与回归
在监督学习中,根据预测目标(标签)的类型,可以进一步分为分类和回归。
以下是两者的具体说明:

- 分类:预测的标签是类别型的,属于有限个类别。例如,预测客户是否会流失(是/否),预测邮件是否为垃圾邮件(是/否)。其目标是找到一个决策边界。
- 回归:预测的标签是连续数值型的。例如,预测产品的销售额(具体金额),预测房价。其目标是拟合一个函数。
机器学习项目生命周期 🔄
理解了基本概念后,我们来看一个机器学习项目从构思到上线的完整流程。吴恩达教授提出的这个框架将帮助你理解数据在各个环节中的作用。

以下是项目生命周期的四个主要阶段:
- 范围界定:明确项目目标和要解决的业务问题。
- 数据处理:与机器学习团队协作,确定并收集所需的特征和标签,组织数据并建立基线。这是数据工程师参与最深的阶段。
- 模型开发与迭代:机器学习工程师使用你提供的数据来训练和评估模型。这个过程通常是迭代的。
- 部署与维护:将训练好的模型投入生产环境,并持续监控和维护。数据工程师需要为生产模型提供数据流,并可能提供新数据用于模型更新。
模型开发与迭代详解
在模型开发阶段,机器学习工程师会进行一系列操作。
以下是该阶段的关键步骤:
- 数据划分:将数据集分为训练集和测试集。
- 算法选择与训练:
- 经典算法:如线性回归、逻辑回归、决策树、随机森林。它们易于实现,通常要求数据为表格形式。公式示例:线性回归
y = w*x + b。 - 复杂算法:如深度神经网络。它们能处理更大规模的数据和更复杂的数据类型,如图像(卷积神经网络CNN)、时间序列(循环神经网络RNN)、文本(大语言模型LLM)。
- 经典算法:如线性回归、逻辑回归、决策树、随机森林。它们易于实现,通常要求数据为表格形式。公式示例:线性回归
- 模型评估与迭代:通过交叉验证选择最佳模型,并用测试集评估性能。如果效果不佳,团队可能会要求你提供更新或更丰富的数据集,重新开始训练,形成迭代循环。
数据工程师的角色

在整个生命周期中,数据工程师的核心职责是构建和维护数据管道,为各个阶段提供可靠、格式正确的数据支持。虽然不直接参与算法细节,但高质量的数据供给是项目成功的基石。

本节课中我们一起学习了机器学习的基础分类(监督/无监督学习,分类/回归),并深入了解了机器学习项目的完整生命周期框架。你明确了数据工程师在范围界定、数据处理、模型迭代和部署维护各阶段中的关键作用,即为机器学习系统提供坚实的数据基础设施支持。
015:传统机器学习算法的数据建模 📊

在本节课中,我们将学习如何为传统机器学习算法准备和建模数据。我们将重点介绍如何将原始数据处理成算法期望的数值表格形式,并详细讲解特征工程中的关键步骤,包括处理缺失值、特征缩放和类别特征编码。
概述
为传统机器学习算法提供训练数据时,数据通常需要是仅包含数值的表格形式。为这类用例建模数据时,你需要决定包含哪些特征以及使用哪个标签作为预测目标。这些决策通常由机器学习或数据科学团队做出。根据项目需求和迭代阶段,你可能直接向团队提供原始数据供其探索,也可能需要在提供数据前,将特征和标签处理并转换为数值表格形式。
接下来,我们将通过一些基本的预处理步骤,来学习如何为训练准备表格数据。
客户流失案例回顾
让我们回到上一视频中介绍的客户流失示例。原始数据集可能如下所示:
每一行对应一位客户,显示了他们的购买次数、最近购买日期、收入、在平台上的时长、账户类型以及是否流失。
根据你所在团队和项目的具体情况,你可能被要求提供这样的原始数据,也可能被要求处理数据并将其转换为数值表格形式,转换后的数据可能如下所示:
请注意,我已将“是否流失”列分离为一个向量,其中包含每位客户的标签:1表示客户已流失,0表示未流失。你可以看到数据中没有缺失值或重复行,且每列都由数值组成,这些数值处于相似的范围之内。
此外,请注意我通过将“购买商品数”列除以“平台使用时长(分钟)”列,创建了一个新的“每分钟购买数”特征。通过组合或修改现有列来创建新特征,这一决策通常由机器学习团队决定并传达给你。
这种数值表格数据是大多数传统机器学习算法期望接收的训练数据。在机器学习中,当你处理原始列或创建新特征时,这个过程被称为特征工程。该过程包括处理缺失值、特征缩放、将类别列转换为数值列,以及通过组合或修改现有列来创建新列等操作。
下面让我们更详细地了解这些常见的特征工程操作。


处理缺失值
处理数据时,你很可能会遇到缺失值。
你首先应该理解数值缺失的原因,然后确定处理此问题的最合适方法。
最简单的方法是删除包含缺失值的列或行,但这种方式可能会无意中丢失重要数据。因此,只有在没有丢失有价值数据的风险时,才删除行或列。
另一种方法是用该列的一些汇总统计量来填补缺失值,例如用该列的平均值或中位数填补,或用相似记录的数值填补。然而,当你填补缺失值时,可能会给数据引入噪声或偏差。
在我们的客户流失示例中,第三行大部分是空值。假设我与机器学习工程师沟通后,确定我们拥有的非空值并不特别有价值,因此我决定删除该行。对于缺失的客户收入值,我决定用相似记录的值来替换。
处理缺失值没有单一完美的方法,你通常需要与机器学习团队合作,选择不影响机器学习系统性能的最佳方法。
特征缩放
处理完任何缺失值后,你需要对数值特征进行缩放,使每个特征的值最终处于相似的范围。
不深入技术细节,机器学习算法本质上是优化算法,它们使用训练数据来计算一组参数,以产生最优的输出。如果特征值差异巨大,算法可能需要很长时间才能收敛。
此外,某些机器学习算法基于距离度量,因此它们的准确性可能会受到特征值不同范围的影响。
在我们的客户流失示例中,“购买商品数”特征的值范围将远小于“客户收入”特征的值范围。
要对每列的值进行缩放,你可以应用标准化或最小-最大缩放。
- 标准化:取列中的每个值,减去列的平均值,然后除以列的标准差。标准化后,列中的值将具有均值为0、方差为1的分布。
- 最小-最大缩放:取列中的每个值,减去列的最小值,然后除以列的最大值与最小值之差。这样,列中的归一化值将介于0和1之间。
假设“客户收入”特征的最小值为0美元,最大值为100,000美元。如果对“客户收入”列的前两个值应用最小-最大缩放,你会得到什么值?
对于第一个值,你会得到 (50,000 - 0) / 100,000 = 0.5。对于第二个值,你会得到 (40,000 - 0) / 100,000 = 0.4。
编码类别特征
如果你的原始数据包含非数值的类别值呢?例如,假设账户类型可以是“基础版”、“家庭版”或“白金版”,如下所示。
由于传统机器学习算法期望每个特征都是数值型的,你需要应用预处理步骤将此类别特征转换为数值特征。
将此列转换为数值列的一种方法是应用称为独热编码的方法。使用此方法,将“账户类型”列替换为三列:第一列代表“基础版”,第二列代表“家庭版”,第三列代表“白金版”。由于此处的第一位客户拥有“家庭版”账户类型,我将在“家庭版”列中用1表示,其他两列用0表示。你对所有其他行执行相同的操作。

独热编码的列易于解释,但如果列中唯一值的数量很大,它会显著增加数据集中的列数。
另一种编码方法是使用序数编码,当类别列的唯一值之间存在自然顺序时,这种方法很有用。因此,假设账户类型可以按其订阅费用排序,“基础版”最便宜,“家庭版”居中,“白金版”是最贵的账户类型。那么你可以用1替换“基础版”,用2替换“家庭版”,用3替换“白金版”。这样,你可以在不向数据添加列的情况下将类别列转换为数值列。
还有其他方法,例如哈希编码,即应用数学哈希函数将类别替换为计算出的哈希值;或者你可以创建嵌入,这将在本周晚些时候讨论。同样,你将与机器学习工程师合作,根据你的具体用例决定使用哪种方法。
总结与后续
以上是为训练机器学习算法准备数据时可能应用的一些预处理或特征工程步骤。你应该始终与机器学习团队紧密合作,以决定最适合给定项目的步骤和方法。
接下来,我将通过一个简短的演示,向你展示如何使用 Pandas 将这些特征工程步骤应用到实际数据中。如果你还不熟悉 Pandas,你可能需要查看本周结束时资源部分中的 Pandas 教程链接。
在我们深入演示之前,我还包含了一个可选视频,其中我与 Pandas 的创建者 Wes McKinney 进行了交谈。如果你想跳过那个视频,我们将在随后的演示中再见。

本节课中,我们一起学习了:
- 数据形式要求:传统机器学习算法通常期望接收数值表格形式的训练数据。
- 特征工程:将原始数据处理成算法所需形式的过程,包括创建新特征。
- 处理缺失值:理解缺失原因,并选择删除或填补等适当方法,需与团队协作决策。
- 特征缩放:通过标准化或最小-最大缩放,使不同特征的值处于相似范围,以帮助算法收敛并提高某些算法的准确性。
- 编码类别特征:使用独热编码、序数编码等方法将非数值类别数据转换为数值形式,选择取决于数据特性和项目需求。

这些步骤是构建有效机器学习管道的基础,与机器学习团队的持续沟通对于做出正确决策至关重要。
016:与Wes McKinney的对话 🎙️
在本节课中,我们将与Pandas库的创始人Wes McKinney进行对话,了解Pandas的起源、设计理念、适用场景,并探讨数据领域的未来趋势。
自我介绍
Wes McKinney是Pandas项目的原始创建者。他在2008年启动了该项目,并于2009年底将其开源。他撰写了《Python for Data Analysis》一书,该书已成为生态系统中重要的参考教材。此外,他还参与了许多其他开源项目,如Apache Arrow和Ibis,并致力于推动开源数据科学的发展。
什么是Pandas?📊
上一节我们介绍了Wes的背景,本节中我们来看看Pandas究竟是什么。
Pandas是一个用于数据管理和操作的工具包。它帮助你在Python内存中加载和操作数据集。如果你有CSV文件、Excel文件或数据库,在Python中进行任何数据工作的第一步通常是将数据加载到内存中,以便将其作为表格或类电子表格对象进行操作。
Pandas提供了一个名为DataFrame的核心数据结构。这是一个表格型数据结构,其API可用于执行类电子表格的操作、列操作、数据清洗操作。它还包含许多函数,帮助你从数据文件和外部数据源将数据加载到Pandas DataFrame中。
以下是Pandas的核心概念:
import pandas as pd
df = pd.read_csv('data.csv') # 从CSV文件加载数据到DataFrame
Pandas项目还包含许多其他功能。你可以用它来生成图表,或创建格式美观的表格以插入出版物中。它已成为在Python中进行交互式探索性数据分析的流行工具,并且通常是使用其他工具包进行Python统计或机器学习的第一步。
Pandas的起源与命名
了解了Pandas是什么之后,我们来看看它的诞生故事。
Wes在2008年金融危机期间,于一家量化对冲基金工作。当时团队面临巨大压力,需要进行大量探索性数据分析和研究以更快地做出决策。他们使用的工具包括MATLAB、R、Java、C++和大量SQL,但Wes对这些工具都不太满意。
他发现了Python,认为它是一种非常高效、优秀的编程语言,但当时缺少一个数据工具包。于是,他开始为自己的数据分析工作构建工具,并逐渐与同事分享。这个工具最初甚至没有名字。
关于“Pandas”这个名字的由来,有两点原因:
- Wes当时与许多计量经济学家合作,他们经常讨论“面板数据”。
- 他试图找到一个能唤起“Python数据分析”概念的缩写或字母组合。在写下各种可能性时,“pandas”这个词出现了。
Pandas的成功因素
Pandas并非一夜成名。那么,是什么促成了它的崛起和成功呢?
在2012-2013年左右,市场对数据科学技能的需求激增。能够进行数据科学工作的编程语言,是扩大数据科学普及度的未开发机会。使用开源软件意味着没有付费门槛,人们不必购买MATLAB等软件。
成功因素包括:
- 巨大的市场需求:对数据技能和数据科学家的巨大需求。
- 科技公司的接纳:许多新兴的、以数据为中心的科技公司乐于使用Python和开源软件,他们正在寻找数据工具包,于是发现了Pandas。
- 配套书籍的出版:《Python for Data Analysis》一书于2012年出版,帮助人们通过阅读学习如何在Python中进行基本的数据工作。
- 时机正确:处于正确的位置和正确的时间,结合了市场需求和行业对采用开源软件进行数据工作的前瞻性思考。
当时,大数据概念无处不在,每家公司都在试图弄清楚如何成为数据公司。Python由于各种原因,成为了数据科学和机器学习的主流语言,特别是谷歌和Facebook选择Python作为其机器学习框架(TensorFlow和PyTorch)的语言,这对巩固Python的地位起到了重要作用。
Pandas的适用场景
在课程中,学习者将探索的一个部分是:何时Pandas是一个合适的选择,何时可能需要尝试不同的方法。
Pandas在以下情况是一个很好的选择:
- 你的数据是表格型或矩形数据。
- 你有多个需要整合在一起的表格型数据集。
- 它适合处理类似电子表格或数据库的界面,即处理带有列名和列数据的表格。
当数据量非常大,无法装入内存,或者超出了你机器或笔记本电脑的处理能力极限时,你可能会考虑使用不同的方法。
以下是可能的选择:
- 使用某种类型的数据库。
- 尝试其他数据框库,例如一个名为Polars的新Python项目,它专为处理更大的Python数据而构建。
- 对于大多数机器学习数据集,它们可能是干净、规整的多维数组,存储在磁盘上。用户可能直接使用基于NumPy构建的Xarray等多维数组框架,或直接用于PyTorch或TensorFlow。在这些情况下,使用Pandas可能完全不必要。
给数据领域新手的建议
对于许多参加本课程、渴望成为数据从业者的人,Wes有以下建议:
除了学习数据操作、加载、清洗、整合、合并等数据整理的基础知识外,熟练掌握一些探索性数据分析技能也很重要。
以下是具体建议:
- 掌握绘图和可视化:学会查看数据。
- 熟练使用Jupyter Notebook:利用Jupyter Notebook的不同功能。
- 采用交互式、迭代式的工作流程:与在VS Code编辑器中编写Pandas脚本然后在命令行运行相比,围绕查看数据和可视化建立更交互、更迭代的方法,可以帮助你更快地发现错误,从而更快地掌握所需技能。
数据领域的未来展望
最后,我们来展望一下数据领域,特别是数据工程,在未来几年的发展趋势。
Wes认为,我们将继续看到Python工具和框架的演进。Python将继续是数据工作的主流编程语言。
AI助手(如Copilot、ChatGPT和大语言模型)在循环中的作用将变得越来越自然,成为在你需要时出现但不会妨碍你的工具。因为ChatGPT和其他大语言模型由于互联网上大量与Pandas相关的内容,对Pandas了解甚多。它们可以成为帮助你解决问题、修复代码错误或提出处理数据新想法的强大工具。
这将是工具箱中的又一个工具,它将增加可访问性,使人们的工作效率大大提高。我们可以自动化更多枯燥的工作,将更多精力投入到更具创造性和价值的模型开发和数据分析中。
总结
本节课中,我们一起学习了:
- Wes McKinney作为Pandas创始人的背景。
- Pandas的定义、核心DataFrame结构及其在数据工作流中的角色。
- Pandas诞生的背景和其名称的由来。
- 推动Pandas在2010年代初获得成功的市场和技术因素。
- Pandas最适合处理表格型数据,而在处理超大规模数据或特定格式的机器学习数据时,可能需要考虑数据库、Polars或其他多维数组库。
- 给数据新手的建议:掌握基础数据整理技能,并培养以可视化和Jupyter Notebook为核心的交互式、迭代式分析习惯。
- 对未来的展望:Python地位稳固,AI编码助手将进一步提升数据工作的效率和可及性。
感谢Wes McKinney的分享和他对数据科学领域做出的卓越贡献。
017:使用scikit-learn处理表格数据(第1部分)📊

在本节课中,我们将学习如何应用数据预处理步骤来处理一个示例数据集。我们将使用开源机器学习库scikit-learn,并重点演示如何对数值列进行标准化以及对分类列进行独热编码。
上一节我们介绍了数据预处理的基本概念,本节中我们来看看如何在一个具体的客户流失数据集上应用这些步骤。
数据集概览与探索
我将使用一个从Kaggle数据科学平台下载的客户流失数据集。如果你想跟随操作,可以在本视频的资源部分找到Notebook文件和CSV数据文件。
首先,使用pandas快速探索数据。导入pandas并读取本地CSV文件到一个名为data的pandas DataFrame中。
import pandas as pd
data = pd.read_csv('customer_churn.csv')
检查数据形状,可以看到它包含约440,000行和11列。

data.shape
使用head方法查看数据的前几行。
data.head()
每一行对应一个客户,包含一组特征,如客户年龄、使用频率、向服务提供商拨打的客服电话数量、订阅类型等。数据还包含churn列,这是每个客户的目标标签:值为1表示客户已流失,值为0表示客户未流失。
使用describe方法快速查看数值列的汇总统计信息。
data.describe()
可以看到客户的平均年龄接近40岁,客服电话数量范围是0到10,总花费的中位数是661美元。还可以看到每个特征的计数比总行数少1,这表明每个特征中都存在一个空值。

数据清洗:处理缺失值


你可以使用isnull方法验证这些数值列中确实存在缺失值。
data.isnull().sum()
从输出中可以看到,表中包含一整行的缺失条目。由于只有一行,并且该客户的所有列都是空值,我将使用dropna方法将其从数据集中删除。


data = data.dropna()
你可以再次验证所有列中是否已没有空值。
data.isnull().sum().sum()
探索分类列
为了快速探索分类列(即subscription_type和contract_length),我将在每个列上使用value_counts方法。该方法将返回唯一的类别以及属于每个类别的行所占的百分比。
以下是需要执行的步骤:


- 对
subscription_type列使用value_counts。 - 对
contract_length列使用value_counts。

print(data['subscription_type'].value_counts(normalize=True))
print(data['contract_length'].value_counts(normalize=True))


可以看到每个分类列都有三个唯一值。由于唯一值的数量较少,可以使用独热编码将这些分类列转换为数值列。
分离特征与标签
首先,由于大多数机器学习模型期望特征和标签分开存储,我将创建一个名为features的变量,并为其分配数据中代表客户特征的所有列。然后,创建一个名为labels的变量,并为其分配代表标签的单个列。
features = data.drop(columns=['churn'])
labels = data['churn']
你可以查看特征和标签的前几行,以确保它们已正确分离。
print(features.head())
print(labels.head())
数据预处理任务规划
现在假设你与机器学习团队进行了会议,他们要求你为训练机器学习模型准备这些数据。具体要求如下:
- 将数据按80%训练集和20%测试集的比例分割。
- 在每个数据集中需要保留客户ID。
- 对包含年龄、使用时长、使用频率、客服电话、付款延迟、总花费和最近交互的数值列进行标准化。
- 对分类列执行独热编码。
- 训练集和测试集应以Parquet文件格式存储。

根据这些要求,以下是准备训练数据将遵循的步骤:
- 将数据分割为训练集和测试集。
- 首先处理训练数据:
- 提取训练集的数值列,然后对每个数值列进行标准化。
- 提取训练集的分类列,并使用独热编码对其进行编码。
- 将处理后的数值列、编码后的分类列与客户ID合并到一个pandas DataFrame中。
- 将此DataFrame转换为Parquet文件。
- 对测试集重复相同的处理步骤。
将处理后的列连接成pandas DataFrame可以将元数据(即行和列标签)与值合并到一个对象中。这使得将数据存储为Parquet文件更加容易。
在评估或测试机器学习系统时,你应该使用在训练集上计算出的相同统计量,对测试集应用相同的预处理步骤。这是一个良好的实践,因为测试集用于评估机器学习算法在未见过的数据上的性能。因此,为了转换测试集,你应该使用用于转换训练集的任何统计量。
有了这个计划,让我们进入下一个视频,看看如何在scikit-learn中执行这些步骤。

本节课中我们一起学习了如何使用pandas探索和清洗客户流失数据集,包括检查数据形状、处理缺失值、查看统计摘要以及分离特征与标签。我们还为接下来的数据预处理步骤制定了详细的计划,包括数据分割、数值标准化和分类列编码。下一部分我们将具体实现这些预处理步骤。
018:使用scikit-learn处理表格数据(第2部分)📊

在本节课中,我们将学习如何使用scikit-learn库对表格数据进行预处理,具体包括将数据分割为训练集和测试集、对数值型特征进行标准化缩放、对分类型特征进行独热编码,并将处理后的数据保存为文件。这些步骤是为后续训练经典机器学习算法准备数据的关键环节。
数据分割


上一节我们介绍了数据预处理的整体计划,本节中我们来看看具体如何实施。首先,我们需要将原始数据集分割为训练集和测试集。这有助于我们在未见过的数据上评估模型的性能。
以下是使用scikit-learn的train_test_split方法进行数据分割的步骤:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=42)
features:代表特征的数据。labels:代表标签的数据。test_size:指定测试集所占的比例,例如0.2代表20%。random_state:随机种子参数,设置为一个整数(如42)可以确保每次运行代码时都能得到相同的分割结果,这对于实验的可复现性至关重要。
该方法返回训练集和测试集的特征(X_train, X_test)与标签(y_train, y_test)。

预处理训练集:数值型特征缩放
现在我们有了训练集和测试集,让我们首先专注于预处理训练数据集。在本节结束时,我们会将相同的处理步骤应用到测试集上。我们从数值型列开始,因为需要先对这些特征进行缩放。
以下是使用StandardScaler对数值型特征进行标准化的步骤:


from sklearn.preprocessing import StandardScaler
# 1. 指定数值型列
numerical_columns = ['age', 'tenure', 'usage_frequency', 'support_calls', 'payment_delay', 'total_spend', 'last_interaction']
# 2. 从训练特征中提取数值型列
X_train_numerical = X_train[numerical_columns]
# 3. 实例化StandardScaler对象
scaler = StandardScaler()
# 4. 在训练数据上拟合scaler(计算均值和标准差)
scaler.fit(X_train_numerical)
# 5. 使用拟合好的scaler转换训练数据
scaled_features = scaler.transform(X_train_numerical)
# 6. 将缩放后的NumPy数组转换回Pandas DataFrame,便于后续合并
X_train_scaled_df = pd.DataFrame(scaled_features, index=X_train.index, columns=numerical_columns)
fit方法:计算训练数据中每个数值列的均值和标准差。transform方法:使用计算出的统计量(均值和标准差)来缩放数据,使每个特征的值都转换为均值为0、标准差为1的分布。

现在,我们得到了一个包含缩放后数值列的数据框,所有特征都处于相似的数值范围内。
预处理训练集:分类型特征编码
接下来,我们处理分类型列。我们需要将它们转换为数值形式,以便机器学习算法能够理解。这里我们使用独热编码。
以下是使用OneHotEncoder对分类型特征进行独热编码的步骤:
from sklearn.preprocessing import OneHotEncoder



# 1. 指定分类型列
categorical_columns = ['subscription_type', 'contract_length']
# 2. 从训练特征中提取分类型列
X_train_categorical = X_train[categorical_columns]
# 3. 实例化OneHotEncoder对象
encoder = OneHotEncoder()
# 4. 在训练数据上拟合encoder(检查唯一值并准备输出列标签)
encoder.fit(X_train_categorical)

# 5. 使用拟合好的encoder转换训练数据
encoded_matrix = encoder.transform(X_train_categorical)
# 6. 获取编码后列的名称
encoded_column_names = encoder.get_feature_names_out(categorical_columns)
# 7. 将稀疏矩阵转换为常规矩阵,再转为DataFrame
X_train_encoded_df = pd.DataFrame(encoded_matrix.toarray(), index=X_train.index, columns=encoded_column_names)
fit方法:检查每个分类型列中的唯一值,以确定需要为每个特征创建多少列,并准备输出列的标签。transform方法:执行编码,返回一个稀疏矩阵(CSR格式)。稀疏矩阵高效存储了大量零值(独热编码的典型结果)。toarray()方法:将稀疏矩阵转换为常规的NumPy数组。get_feature_names_out()方法:获取编码后各列所代表的原始类别名称。
现在,分类型特征已被转换为数值形式。
合并处理后的特征
最后一步,我们将客户ID、处理后的数值型特征和编码后的分类型特征合并成一个完整的数据集。



以下是使用Pandas的concat方法进行水平合并的步骤:
# 假设customer_id是X_train中的一个列
processed_X_train = pd.concat([X_train[['customer_id']], X_train_scaled_df, X_train_encoded_df], axis=1)
参数axis=1指定了按列进行水平拼接。至此,我们得到了代表已处理训练数据的完整数据框。
应用相同步骤处理测试集
对于测试集,关键点在于我们不能重新拟合(fit)在训练集上使用的scaler和encoder。我们必须使用相同的、由训练集拟合好的对象来转换(transform)测试集数据。这确保了测试集数据与训练集数据使用相同的缩放标准(相同的均值和标准差)和相同的编码映射。


以下是处理测试集的简化流程:
# 1. 缩放测试集的数值型特征(使用训练集拟合的scaler)
X_test_numerical = X_test[numerical_columns]
X_test_scaled = scaler.transform(X_test_numerical) # 注意:这里调用transform,不是fit或fit_transform
X_test_scaled_df = pd.DataFrame(X_test_scaled, index=X_test.index, columns=numerical_columns)
# 2. 编码测试集的分类型特征(使用训练集拟合的encoder)
X_test_categorical = X_test[categorical_columns]
X_test_encoded = encoder.transform(X_test_categorical) # 注意:这里调用transform
X_test_encoded_df = pd.DataFrame(X_test_encoded.toarray(), index=X_test.index, columns=encoded_column_names)
# 3. 合并测试集的特征
processed_X_test = pd.concat([X_test[['customer_id']], X_test_scaled_df, X_test_encoded_df], axis=1)
保存处理后的数据
处理完成后,可以将数据保存为高效的格式(如Parquet)以供后续使用。
processed_X_train.to_parquet('train_features_processed.parquet')
processed_X_test.to_parquet('test_features_processed.parquet')
总结

本节课中我们一起学习了使用scikit-learn进行表格数据预处理的完整流程。我们首先将数据分割为训练集和测试集,然后对训练集的数值型特征进行标准化缩放,对分类型特征进行独热编码,并将处理后的部分合并。最重要的是,我们学习了如何将训练集上拟合的转换器正确地应用到测试集上,以确保数据转换的一致性。最后,我们将处理好的数据保存下来。这些处理后的数据集可以交付给机器学习工程师或数据科学家,用于训练和评估各种机器学习算法。
019:机器学习算法的图像数据建模 📸

在本节课中,我们将学习如何为机器学习算法准备图像数据。我们将探讨处理图像数据的不同方法,包括为传统算法和现代深度学习模型进行预处理的技术。
除了使用表格数据预测销售和细分客户,你也可以使用机器学习算法来识别图像中的模式。这类应用包括图像分类(例如从照片中识别植物种类)、目标检测(例如在十字路口照片中识别行人)以及像素分割(例如在X光图像中检测癌变区域)。在准备用于训练机器学习算法的图像时,你需要采取的措施取决于所使用的算法类型。
让我们来看看这些不同的情况。
为传统机器学习算法准备图像 📊
正如我们在上一课中讨论的,传统的机器学习算法甚至普通的神经网络都期望数据以表格形式呈现。例如,考虑这组灰度图像及其对应的像素表示。
假设机器学习团队希望使用一组图像来训练一个传统的机器学习算法,那么你需要将每张图像展平为一个长的像素值向量,然后将所有图像的向量连接成如下所示的表格形式,以供机器学习算法使用。
以下是此过程的关键步骤:
- 将每张图像展平为一个一维向量。
- 将所有图像的向量按行堆叠,形成一个二维表格。
- 表格中的每一行对应一张图像的展平像素值。
然而,这种方法存在局限性。当你展平图像时,会忽略图像的空间结构,从而丢失了可以从像素之间的相对位置提取的空间信息。此外,这种方法会为每张图像创建一个高维特征向量。例如,如果每张图像的大小是1000像素乘以1000像素,那么展平后你将得到一个大小为100万的向量。如果在一个普通的神经网络上进行训练,这将需要大量的计算和内存资源。在不深入过多技术细节的情况下,这也可能影响机器学习算法的性能,特别是当数据集包含的图像数量远少于100万张时。我在资源部分附上了吴恩达深度学习专项课程的链接,欢迎查看以获取更多细节。

为卷积神经网络准备图像 🧠
另一种方法是使用更先进的深度学习算法,例如卷积神经网络来训练这些图像。
卷积神经网络可以直接处理图像,而无需先将其展平。这类网络由若干层组成,每一层都试图识别图像中越来越多的、有助于机器学习任务的特征。第一层学习适用于任何图像的通用特征,例如线条、水平和垂直边缘以及简单的纹理。后面的层学习更复杂的特征,例如更复杂的图案和纹理,这些特征与手头的任务更相关。这就是为什么你会发现许多机器学习团队使用在大型图像集上预训练过的卷积神经网络算法,然后通过使用他们自己特定的图像集训练更深层的网络,来对这些模型进行微调以适应其特定任务。
因此,你可能需要负责向机器学习团队提供一组特定的图像,他们将用这些图像来微调卷积神经网络,甚至从头开始训练这些网络。无论如何,你仍然需要为训练算法准备图像。
以下是准备图像的关键预处理步骤:
- 调整尺寸:将图像调整为神经网络期望的形状。
- 缩放像素值:将像素值缩放到算法期望的数值范围内。
- 数据增强:通过应用几何变换(如翻转、旋转)或其他技术(如裁剪、调整亮度)来创建现有图像的新版本。数据增强有助于增加训练数据的大小和多样性,这通常有助于提升机器学习算法的性能。

你可以使用开源工具(如TensorFlow)来应用这些预处理步骤。TensorFlow是一个用于构建和部署深度学习模型的框架,它提供了可以直接应用于图像的预处理函数。在本视频后的阅读材料中,我包含了一个可选的代码示例,展示了如何使用TensorFlow来调整大小、缩放和增强一组图像。
以上简要概述了可用于预处理图像的技术。
处理其他非结构化数据 📄
除了预处理图像文件,你可能还需要预处理包含需要从中提取文本数据或表格的文档的PDF文件。同样有技术可以帮助你完成这项任务。更多信息,我附上了一个关于为大语言模型应用预处理非结构化数据的短期课程链接,其中展示了这些技术的示例。

在下一个视频中,我们将继续讨论如何为机器学习算法预处理非结构化数据,但重点将放在文本数据上。
总结


本节课中,我们一起学习了为机器学习算法准备图像数据的方法。我们首先了解了如何为传统算法将图像展平为表格形式,并讨论了其局限性。接着,我们探讨了为卷积神经网络准备图像的更优方法,包括调整尺寸、缩放像素值和数据增强等关键预处理步骤。最后,我们简要提及了处理PDF等其他非结构化数据的需求。掌握这些预处理技术是构建高效图像机器学习模型的重要基础。
020:文本分析与分类的预处理 📝

在本节课中,我们将学习如何为文本分析和分类任务准备数据。我们将探讨一系列文本预处理技术,这些技术对于将原始文本转换为机器学习模型能够理解的格式至关重要。
随着组织生成和收集的文本数据日益增多,包括产品评论、社交媒体帖子和客户支持互动,从这些文本数据中提取洞察以做出关键业务决策变得至关重要。作为数据工程师,你的任务可能是预处理文本数据并将其提供给机器学习工程师,以便他们分析数据并训练机器学习系统,用于各种应用,例如产品评论的情感分析、新闻文章分类、聊天机器人和虚拟助手、垃圾邮件检测、客户细分、产品推荐等。
自然语言处理(NLP)是人工智能的一个子领域,它使计算机能够处理、理解和生成人类文本。NLP是一个已有50多年历史的领域,它包含了随时间演变的多种文本分析技术。虽然经典机器学习算法在文本分类、情感分析等NLP任务中很有效,但NLP的最新发展,即大语言模型(LLMs),已显著增强了计算机以卓越的准确性和流畅性解释和生成文本的能力。
在本视频中,我们将探讨一些文本预处理技术,你可以使用这些技术为NLP任务的机器学习系统训练准备文本数据。
预处理的重要性 🤔

假设你的公司希望进行情感分析,以分析客户评论是正面还是负面。这项技术通常依赖于使用预训练的机器学习系统,或在你的客户评论上训练一个机器学习系统。作为数据工程师,除了收集和存储这些评论,你还可以帮助将这些文本数据转换为机器学习算法能够理解的格式。
你需要应用的预处理程度将取决于所使用的机器学习算法的类型。经典机器学习算法无法直接处理句子。因此,在这种情况下,你需要应用预处理技术来首先清理文本,然后将其向量化。
另一方面,LLMs可以直接处理来自句子的标记或单词序列,而无需你首先将句子向量化为数值形式。
无论如何,我认为熟悉各种预处理策略对于准备文本数据进行进一步分析非常重要。这是因为文本数据可能包含拼写错误、不一致和重复,你需要解决这些问题,以便为训练机器学习模型提供干净、高质量的数据,并确保其性能,无论机器学习算法是经典的还是像LLM这样的先进模型。
此外,并非文本数据中的所有单词或字符都与给定的NLP任务相关,因此为了降低处理和存储成本,你需要删除任何对你的用例不携带相关信息的单词或字符。
最后,确实LLMs目前在许多NLP任务上表现出色,但训练这样的算法仍然昂贵且耗时,因此它们可能并非所有用例的最佳解决方案。与你合作的机器学习团队可能仍然倾向于使用经典机器学习算法,在简单解决方案足以满足用例要求的情况下。

还有一些用例需要你在训练数据中结合数值、分类和文本特征。基于所有这些原因,让我们来看看一些常见的预处理技术,用于准备文本数据。
文本清理 🧹
要清理你的文本,你可以从删除标点符号、多余空格或任何不增加文本含义的字符开始。
以下是三个原始文本评论示例,以及清理数据后你希望得到的结果。我已在视频后的阅读材料中包含了用于清理数据的Python代码,因此如果你不熟悉处理字符串,请随时查看该代码的解释。
原始文本示例:
- "The product is great! I love it."
- "AMT of features is good, but price is high."
- "I don't like the customer service."
清理后目标:
- "The product is great I love it"
- "AMT of features is good but price is high"
- "I don't like the customer service"
文本规范化 📏
清理数据后,你可以应用规范化,通过将文本转换为一致的格式来标准化文本。这可能包括将字符转换为小写、将数字或符号转换为字符,或扩展缩写词以减少同一单词的拼写变体。
例如,查看我们的三个示例评论,第二位客户将“amount”写为“AMT”,而另一条评论可能将“amount”中的“a”大写。当你应用规范化时,你可以通过用单词“amount”的小写拼写替换所有这些实例来解决这些不一致问题。在第三条评论中,你还看到了缩写“don‘t”,你可以将其替换为“do not”。
其他文本规范化的例子包括将常见单位如“KG”转换为“kilograms”或“LBS”转换为“pounds”,或者将缩写如“DE”替换为“data engineering”。同样,你可以在随后的阅读材料中查看执行此规范化的Python代码。
规范化数据后,你应该得到如下所示的文本:
规范化后目标:
- "the product is great i love it"
- "amount of features is good but price is high"
- "i do not like the customer service"
分词与停用词移除 ✂️
为了为进一步分析准备文本,然后对每个文本评论应用分词,这意味着你将每个评论拆分为单个单元或标记,这些标记通常是单词,但也可以是任何有意义的文本单元,如子词或短句。为简单起见,我将每个评论拆分为一个单词向量。
这可以使用Python字符串方法split来完成,你将在阅读材料中看到。
接下来,你可以删除频繁使用的单词,如“is”或“the”,这些单词通常不会为数据增加任何含义。这些单词被称为停用词。你可以定义自己的停用词列表,或使用带有内置停用词集的NLP库,如Spacy、NLTK、Gensim和TextBlob等。
以下是在我移除属于该集合的停用词后,示例评论的样子:
移除停用词后目标:
- ["product", "great", "love"]
- ["amount", "features", "good", "price", "high"]
- ["not", "like", "customer", "service"]
词形还原 🔄
最后,你可以使用一种称为词形还原的技术,将每个单词替换为其基本形式,即其词元。例如,“getting”和“got”的基本形式是“get”。因此,当你对文本应用词形还原时,你将所有这些变体替换为其词元,即单词“get”。同样,你可以使用NLP库来获取每个单词的词元。
以下是我对所有三条评论应用词形还原后得到的结果:
词形还原后目标:
- ["product", "great", "love"]
- ["amount", "feature", "good", "price", "high"]
- ["not", "like", "customer", "service"]
预处理步骤的灵活性 🔧
根据机器学习工程师或数据科学家的需求,你可能不需要应用所有这些步骤,或者可能需要应用其他预处理步骤。这取决于你的最终用户要应用的模型以及他们希望自己执行多少处理。我在资源部分包含了一些链接,指向讨论其他文本预处理步骤的课程,因此请随时查看它们,以了解更多关于为训练LLM或其他算法准备文本数据的信息。
无论如何,在你执行了必要的步骤来清理和预处理数据之后,接下来你可能需要做的事情,特别是与期望表格形式数值数据的机器学习算法一起工作时,就是将数据向量化。
因此,在下一个视频中,我将介绍一些常见的向量化技术,在这些技术中,你将干净且分词的文本转换为数字向量。


总结 📚
本节课中,我们一起学习了为文本分析和分类准备数据的关键预处理步骤。我们了解了文本清理、规范化、分词、停用词移除和词形还原等技术。这些步骤对于将原始、杂乱的文本转换为适合机器学习模型(无论是经典算法还是现代大语言模型)使用的结构化格式至关重要。掌握这些预处理技术是数据工程师在自然语言处理项目中提供高质量数据的基础。
021:文本向量化与嵌入 📚

在本节课中,我们将学习如何将预处理后的文本数据转换为向量。我们将介绍传统的向量化方法,如词袋模型和TF-IDF,以及更先进的词嵌入和句子嵌入技术。这些技术是自然语言处理的基础,能够将文本转换为机器学习模型可以理解的数值形式。
从文本到向量:传统方法
上一节我们介绍了文本数据的常见预处理步骤。本节中,我们来看看如何将预处理后的文本数据转化为向量。
传统的文本向量化技术主要包括词袋模型和词频-逆文档频率方法。

- 词袋模型:将每个文档(例如一条客户评论)表示为一个向量,向量的长度等于整个语料库的词汇表大小。向量中的每个值对应词汇表中某个词在该文档中出现的次数。
- TF-IDF:在词频的基础上,引入了逆文档频率,以降低在整个语料库中频繁出现但信息量较少的词的权重,提升稀有且重要词的权重。
以下是这两种方法的简要说明:
-
词袋模型向量化:向量
V_document的第i个元素是词汇表中第i个词在文档中出现的次数。- 公式:
V_document[i] = count(term_i in document)
- 公式:
-
TF-IDF向量化:向量值由词频和逆文档频率共同决定。
- 词频公式:
TF(t, d) = (词t在文档d中出现的次数) / (文档d的总词数) - 逆文档频率公式:
IDF(t, D) = log(语料库中文档总数 / (包含词t的文档数 + 1)) - TF-IDF公式:
TF-IDF(t, d, D) = TF(t, d) * IDF(t, D)
- 词频公式:
这些传统方法简单、易于理解和解释,适用于快速实验和小型数据集。它们在文档分类或关键词检测任务上可能表现良好。然而,它们忽略了词语的语义和句子结构,并且可能产生高维稀疏向量。
捕捉语义:词嵌入与句子嵌入
上一节我们了解了基于频率的向量化方法。本节中,我们来看看如何更好地捕捉文本的语义信息。
为了捕捉词语的语义,可以使用词嵌入技术。词嵌入将每个词映射为一个稠密向量,语义相近的词在向量空间中的位置也更接近。
例如,有用 和 有帮助 的嵌入向量之间的距离,应该比 有用 和 树 的向量之间的距离更近。流行的词嵌入算法如 Word2Vec 和 GloVe,通过在大规模文本中学习词的共现模式来生成这些向量。
但是,简单地将句子中所有词的嵌入向量相加,会忽略词语在句子中的顺序。例如,“人咬蛇”和“蛇咬人”会得到相同的向量,但含义截然不同。

这时就需要句子嵌入。句子嵌入是一个能反映整个句子语义的向量,它考虑了词序和每个词的含义。语义相似的句子,其嵌入向量在空间中也更接近。此外,句子嵌入的维度通常远低于TF-IDF等方法产生的向量维度。
你可以使用在大规模数据集上预训练的NLP模型来获取句子嵌入。既有开源的框架(如 Sentence Transformers),也有闭源的API服务(如 OpenAI、Anthropic、Google 提供的嵌入模型)。
实践:应用句子嵌入
以下是使用Python的Sentence Transformers库为文本评论生成句子嵌入的示例步骤:

- 初始化模型:选择一个预训练的句子嵌入模型。
- 生成嵌入:将清洗和标准化后的文本输入模型。
- 计算相似度:使用余弦相似度等度量方法比较不同句子嵌入之间的相似性。
# 示例代码
from sentence_transformers import SentenceTransformer, util
# 1. 初始化模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 2. 准备文本数据(假设reviews是预处理后的评论列表)
reviews = ["这个产品物超所值。", "我收到的产品数量不足。", "包装非常精美。"]
# 3. 生成句子嵌入
embeddings = model.encode(reviews)
# 4. 计算相似度(例如,比较第一条和第二条评论)
similarity = util.cos_sim(embeddings[0], embeddings[1])
print(f"评论1与评论2的余弦相似度: {similarity.item():.2f}")
计算出的相似度可以帮助我们理解不同文本之间的语义关系。例如,关于“产品数量”的评论彼此之间会比与关于“包装”的评论更相似。

嵌入向量的应用
生成这些嵌入向量后,可以将其用作机器学习算法的特征,应用于多种任务:
- 产品推荐系统
- 文本聚类分析
- 语义相似度搜索
在本周的第二个实验练习中,你将有机会把嵌入技术应用到真实的文本评论数据上,并结合元数据处理技术,完成一个完整的数据处理流程。

总结
本节课中我们一起学习了文本向量化的核心方法。
我们首先回顾了词袋模型和TF-IDF这两种传统技术,它们基于词频将文本转换为向量。接着,我们探讨了词嵌入技术,它能更好地捕捉词语的语义信息。最后,我们介绍了句子嵌入,它通过考虑词序和上下文,生成了能代表整个句子语义的稠密低维向量,并演示了如何使用开源库快速实现句子嵌入及其在相似度计算中的应用。



掌握这些向量化技术,是将非结构化的文本数据转化为可供机器学习模型使用的结构化数据的关键一步。
022:第2周总结 📊

在本节课中,我们将回顾第2周的核心内容,总结表格数据、图像和文本的建模与预处理方法,为机器学习用例做好准备。
概述
本周我们探讨了为机器学习用例准备表格数据、图像和文本的各种方法。你了解了机器学习项目生命周期的样貌,以及如何在项目的各个阶段为数据科学或机器学习团队提供所需的数据支持。
表格数据的处理
许多机器学习算法期望数据以数值表格形式存在,但真实数据往往并非如此。它可能包含缺失值或分类列。
以下是处理表格数据的关键步骤:
- 处理缺失值:可以应用数据插补技术,或在某些情况下直接删除空记录或列。
- 转换分类列:可以使用编码技术,如
独热编码 (One-Hot Encoding)、序数编码 (Ordinal Encoding)或其他方法。 - 特征缩放:我们还讨论了缩放训练数据中数值特征的重要性,这有助于机器学习算法更快地收敛。
在本周的第一个实验中,我们使用 scikit-learn 库实现了这些处理步骤。
图像数据的处理
上一节我们介绍了表格数据的处理,本节中我们来看看图像数据。
如果你使用传统的机器学习算法,需要将图像展平为一长串像素序列。而更先进的神经网络技术,如卷积神经网络 (CNN),可以直接处理图像,并在每一层中学习图像的特征。
虽然使用卷积神经网络时不需要展平图像,但你仍然可能需要对原始图像应用预处理技术,例如图像重塑和归一化。或者,你可能被要求通过应用翻转、旋转或添加畸变等技术来帮助增强图像数据集。
文本数据的处理
接下来,我们转向文本数据的处理。
你学习了如何预处理文本。如果最终用例涉及更先进的NLP模型,那么你可能只需向机器学习工程师或数据科学家提供原始数据或清洗后的文本数据。
然而,如果最终用例涉及训练传统的机器学习算法,或者你希望在将文本输入大语言模型 (LLM) 之前通过预处理来降低处理和存储成本,那么你需要执行额外的预处理步骤,将文本转换为数值向量。
你了解了两种传统的向量化技术:词袋模型 (Bag of Words) 和 TF-IDF。你还看到了一个更先进的技术:句子嵌入 (Sentence Embedding)。
在本周的第二个实验中,你将句子嵌入应用于亚马逊评论数据集,并将其与其他产品特征相结合。
总结
本节课中我们一起学习了表格、图像和文本数据的关键建模与预处理技术。对于构建机器学习系统而言,对机器学习工作原理有一个基本的理解,将帮助你向利益相关者提供高质量的数据,并为你的组织创造价值。
至此,我们已经涵盖了用于分析用例的建模技术,以及用于机器学习用例的处理技术。


下周预告
下周我们将讨论更多的转换技术,并了解如 Spark 这样的转换框架。我们下周见。
023:第3周概览 🗓️

在本节课中,我们将要学习数据工程生命周期中的“转换”阶段。我们将探讨如何为不同的数据处理需求选择合适的转换框架和技术,包括批处理和流处理场景。
课程概述
到目前为止,在本课程中,你已经通过定义规范化模式、星型模式和一个大表模式,为分析用例进行了数据建模。你也为机器学习用例建模了数据,并使用Pandas数据框架对小数据集进行了一些简单的转换。

然而,当你处理需要复杂转换的大规模数据时,仅使用Pandas数据框架可能无法提供你所需的可扩展性和性能。因此,本周我们将深入探讨转换的细节,并研究可用于转换数据的各种数据处理框架的技术考量。
转换阶段的核心价值
正如前面提到的,数据工程生命周期的转换阶段是你为下游利益相关者操作和增强数据的时候,也是你作为数据工程师能够真正为组织增加价值的地方。
以下是几个具体的例子:
- 你可以清理和组合来自多个来源的数据,然后将其存储在数据湖或数据仓库中,为你的组织创建一个单一的真相来源。
- 在数据仓库内部,你可以利用其大规模并行处理能力,将数据转换为星型模式或数据仓库模式,以便用户能更轻松地查询数据。
- 在数据湖内部,你可以通过将数据从原始区域移动到清理或转换区域,最后到丰富区域,来应用一系列转换,使其准备好供数据使用者使用。
转换框架的技术考量
要应用这些转换,你需要考虑诸如数据大小、可用硬件规格和性能要求等因素。这些考量将帮助你决定是仅在单台机器上处理数据,还是使用像Spark这样的分布式处理工具;以及是应该用SQL还是用Python等其他语言来编写转换逻辑。
你也可以对流数据进行转换,使你的利益相关者能够进行实时分析。在这种情况下,你需要考虑系统的延迟要求,并确保你的转换不会造成任何延迟。
本周学习路线图
上一节我们介绍了转换阶段的价值和基本考量,本节中我们来看看本周的具体学习内容。
以下是本周的学习安排:
- 批处理转换用例:我们将从数据工程师可能遇到的一些批处理转换用例开始。
- 分布式处理框架:我们将涵盖两个分布式处理框架:使用HDFS存储和处理数据的Hadoop MapReduce,以及内存处理框架Spark。许多数据工程师可能认为Hadoop由于其复杂性、高昂的扩展成本和显著的维护要求而是一项遗留技术。但理解MapReduce范式仍然很重要,因为它影响了当今许多分布式系统。
- SQL与Python转换对比:我们将比较基于SQL的转换与用Python等其他语言实现的转换。
- 实践练习:在实验环节,你将有机会执行本课程之前用dbt做过的相同转换,但这次你将在数据仓库之外实现这些转换。
- 专家分享:本周你还将听到来自AWS的高级解决方案架构师、AWS Glue服务专家Noni Chla的分享。他将向你展示如何使用Glue Studio生成用于处理数据的Glue作业。
- 流式转换:在第二课中,我们将了解流式转换。你将使用Kafka和Flink实现一个变更数据捕获(CDC)管道,以捕获数据源中的更改并相应地更新系统中的数据。
总结

本节课中我们一起学习了数据工程第3周的核心内容——数据转换。我们概述了转换阶段的重要性、技术考量因素,并预览了本周将深入学习的批处理与流处理框架、工具及实践案例。这将是内容充实的一周,请在下一个视频中继续加入学习。
024:批处理转换模式与用例 📊

在本节课中,我们将学习批处理转换的核心模式及其典型应用场景。批处理是数据工程师处理离散数据块以支持分析报告和机器学习模型训练的关键工作。
概述
在与多家大型科技公司数据团队的交流中,我发现数据工程师的许多工作都涉及批处理。批处理转换是指在固定时间表(如每日、每小时或每15分钟)上操作离散数据块,以支持持续的报表、分析和机器学习用例。
批处理转换模式

上一节我们介绍了批处理的基本概念,本节中我们来看看几种常见的批处理转换模式。
假设你已经为数据创建了一个模型,它可能基于Kimball星型模式、数据仓库或其他数据建模方法。现在,你需要将数据摄入系统,并应用转换将源数据重构为预期形式。
以下是几种可选方案:
- ETL(提取、转换、加载):依赖外部转换工具,根据你创建的数据模型提取和转换数据,然后将转换后的数据加载到目标系统(如数据仓库)中。
- ELT(提取、加载、转换):从系统提取原始数据并直接导入数据仓库,然后利用仓库的存储和计算能力,在仓库内部清理和转换数据。
- ELT(混合方法):这里的“小t”指在将数据加载到数据仓库之前,为清理数据而应用的简单转换(如数据去重)。而“大T”指在数据仓库内部,根据你定义的模型重构数据所应用的转换。
事实上,你在之前的实验课中已经尝试过ETL和ELT两种方法。在课程1中,你使用AWS Glue ETL作为外部转换工具,在将数据加载到S3之前进行转换,实现了一个ETL管道。AWS Glue ETL基于一个名为Spark的分布式处理框架,可用于对更大规模的数据执行更复杂的转换。本周晚些时候我们将更详细地介绍Spark。
然后,在本课程的第一周,你使用DBT在数据库内部转换数据,实现了一个ELT管道。需要注意的是,DBT不像Spark那样是一个执行工具,它本身不提供计算资源。相反,它是一个SQL工具,你可以利用存储系统的计算资源,在数据库或数据仓库内促进转换任务的完成。
数据整理与清洗
除了将数据转换为目标模式外,你可能还需要应用转换来清理和规范化数据。例如,你提取的源数据可能存在缺失值、重复条目、异常值或其他不一致之处。

这个将混乱、格式错误的数据转化为干净数据的过程称为数据整理。你可以编写自己的代码来执行数据整理,就像上周为了将原始数据转化为可用于训练机器学习算法的形式所做的那样。但我强烈建议你使用数据整理工具,以避免重复繁重的工作。
以下是关于数据整理工具的说明:
- 有许多第三方数据整理工具可供选择。
- 许多主要云服务提供商通常也提供自己的此类工具版本。例如,AWS提供AWS Glue DataBrew作为一种可视化数据准备服务,用于在存储数据后对其进行清理、标准化和转换。
数据更新策略
将转换后的数据存储在数据管道中后,你可能需要定期或持续更新数据,以确保其与源系统中的数据同步。
你可以采用一种称为截断并重新加载的简单方法,即删除表中所有记录,然后从数据源重新加载数据,并重写任何必要的转换以使数据进入目标系统。当数据量较小且只需偶尔更新目标系统中的数据时,这种方法效果很好。
然而,如果你的数据量很大,这种方法可能会变得非常耗费资源。在这种情况下,你可能希望采用CDC(变更数据捕获) 方法,即首先识别源系统中发生的变更,然后仅基于这些变更来更新目标系统中的表。例如,你可以检查关系型源数据库中的“最后更新”列,或者检查数据库的事务日志。日志中的每一行都可以被标记为:I(如果行被插入)、U(如果行被更新)或D(如果行被删除)。
处理更新行时,你可以应用仅插入模式或更新合并模式来更新目标系统。
以下是处理更新和删除行的具体模式:
- 仅插入模式:插入新记录而不更改或删除旧记录,并在新记录中添加额外信息以区别于旧记录。
- 更新合并模式:获取一组源记录,并使用主键或其他逻辑条件在目标表中查找匹配项。当发生匹配时,用新记录替换目标记录来更新它。当不存在匹配时,则插入新记录。
- 硬删除模式:从目标系统中永久删除记录。
- 软删除模式:将记录标记为已删除。
出于性能原因(例如表太大),或者存在法律或法规原因时,你可能会使用硬删除。当你不想永久删除记录,但又想将其从查询结果中过滤掉时,可以使用软删除。你也可以以仅插入的方式删除记录,即插入一个带有删除标志的新记录,而不修改该记录的先前版本。
数据加载最佳实践
单行插入通常在面向行的数据库上执行。但我经常看到的一个问题是,一些数据工程师试图在面向列的OLAP数据库中执行单行插入。这是一种反模式,可能会给OLAP系统带来巨大负载。它还会导致数据被写入许多单独的文件中,这对于后续读取极其低效。
因此,我建议以周期性微批或批处理的方式加载数据。当你批量插入数据时,数据可以更有效地组织成行组并得到更好的压缩。如果OLAP系统是分布式的,你还可以利用分布式并行处理能力,而不是逐条加载记录。

总结与展望
本节课中我们一起学习了一些常见的批处理转换模式。正如前面提到的,如果你只处理简单的转换和小型数据集,或许可以在单台机器上完成转换。
然而,随着所需转换变得更加复杂且数据集变得更大,你将需要考虑使用分布式处理框架来满足可扩展性和性能要求。虽然云数据仓库利用了分布式处理能力,但你可能还需要在数据仓库外部或数据湖内部应用转换。

在下一个视频中,我们将开始讨论分布式处理框架。
025:分布式处理框架Hadoop 🗂️

在本节课中,我们将要学习Apache Hadoop,这是一个用于处理海量数据集的早期分布式计算框架。我们将了解其核心组件、工作原理以及它在现代数据生态系统中的历史地位和影响。
多年来,工程师们开发了许多大数据工具来处理日益增长的数据量。
让我们花点时间来理解那些塑造了当今数据生态系统的关键工具和框架的演变过程。
在本视频中,我们将聚焦于Apache Hadoop,它是处理大型数据集最早的框架之一。
尽管是早期技术,但在2000年代初数据爆炸的背景下,它至今仍具有惊人的相关性。
在1990年代,传统的单体数据库和数据仓库无法以经济高效、可扩展、可用且可靠的方式处理海量数据。与此同时,服务器、内存、磁盘和闪存驱动器等商用硬件也变得廉价且无处不在。
这一时期,多项创新催生了我们今天所见的大规模分布式计算和存储集群。2003年,谷歌发表了一篇关于谷歌文件系统(GFS)的论文,该系统提供了一个跨多个商用硬件服务器集群的容错分布式文件系统。
不久之后,在2004年,谷歌又发表了一篇关于MapReduce的论文,这是一种用于在GFS上分布式处理大规模数据的全新并行编程范式。
谷歌的这些论文构成了数据技术和数据工程文化根源的“大爆炸”。
谷歌的论文启发了雅虎的工程师,他们在2006年开发了开源框架Apache Hadoop。谷歌的GFS论文为Hadoop分布式文件系统(HDFS)提供了蓝图,而MapReduce则成为了该框架的一部分。
尽管Hadoop在今天不被视为尖端技术,但我仍然认为理解Hadoop背后的概念非常重要,因为MapReduce仍然影响着当今数据工程师使用的许多分布式系统,并且HDFS仍然是许多当前大数据引擎(如Amazon EMR和Spark)的关键组成部分。
上一节我们介绍了Hadoop的历史背景,本节中我们来看看它的核心组件之一:Hadoop分布式文件系统。
Hadoop分布式文件系统与对象存储类似,但有一个关键区别:Hadoop将计算和存储结合在相同的节点上,而对象存储通常对内部处理的计算支持有限。
Hadoop将大文件分解成数据块。每个数据块保存着大小通常小于几百兆字节的数据块。
文件系统由一个称为名称节点的组件管理,它维护文件元数据和一个详细的目录,描述文件块在集群中的位置。
在一个典型配置中,你会将每个数据块复制到三个称为数据节点的节点上。这既提高了数据的持久性,也提高了可用性。如果某个磁盘或节点发生故障,导致某些文件块的复制因子低于三,名称节点将指示其他数据节点复制这些文件块,使其再次达到正确的复制因子。
通过将计算资源与存储节点结合,Hadoop允许就地数据处理。这最初是通过MapReduce编程模型实现的。


在MapReduce编程模型中,你将计算代码发送到包含数据的节点,优先考虑数据本地性,而不是将数据移动到你的应用程序。
以下是MapReduce作业的核心步骤:
- Map阶段:计算代码由一组Map任务组成,这些任务读取单个数据块并生成一组键值对。
- Shuffle阶段:随后进行Shuffle操作,将结果在集群中重新分发,使得具有相同键的值被收集到同一个节点上。
- Reduce阶段:最后是Reduce步骤,在每个节点上聚合数据。
为了更具体地理解这个过程,让我们通过一个SQL查询的例子来演示MapReduce是如何工作的。
假设你想运行一个SQL查询:SELECT user_id, COUNT(*) FROM user_events GROUP BY user_id。结果将是所有用户ID及其关联的记录数量。
在HDFS中,用户事件表中的数据被分解成数据块并分布在许多节点上。让我们放大一个包含三个数据块的数据节点。
MapReduce作业为每个数据块生成一个Map任务。每个Map任务本质上在其对应的数据块上运行查询,并生成键值对,其中键代表出现在该块中的用户ID,值是该用户ID在该块内对应的记录计数。
例如,在第一个块中,有6条记录与user2关联,10条记录与user1关联,依此类推。虽然整个表可能包含PB级的数据,但每个块可能只包含几百MB。因此,在单个数据块上运行查询比在整个表上运行要快得多。
然后,你通过键(在本例中是用户ID)重新分发结果,使得每个键最终只落在一个节点上。例如,所有user1的键值对最终在一个数据节点,所有user2的键值对在另一个数据节点。这就是Shuffle步骤,通常使用键的哈希算法来执行。
一旦Map结果经过Shuffle,你就对每个键的结果进行求和。键及其计算出的总计数可以写入它们被计算的节点的本地磁盘。最后,你收集存储在各个节点上的结果,以查看完整的查询结果。
这个模型非常强大,但它也有一些缺点。它利用了大量短暂存在的MapReduce任务,这些任务从磁盘读取数据并将中间计算结果写入磁盘。具体来说,没有中间状态保留在内存中,所有数据都通过存储在磁盘上或通过网络推送在任务之间传输。
这简化了状态和工作流管理,并最小化了内存消耗,但也可能导致较高的磁盘带宽利用率,并增加处理时间。

因此,工程师们开发了其他数据处理框架,这些框架仍然包含Map、Shuffle和Reduce的一些元素,但放宽了MapReduce的限制,允许进行内存缓存。
由于RAM在传输速度和寻道时间上都比SSD和HDD快得多,即使将少量选定的数据持久化在内存中,也能显著加快特定的数据处理任务。
例如,我们将在下一个视频中讨论的Spark,就是围绕内存处理设计的。在Spark中,你将数据视为驻留在内存中的分布式集合,并将磁盘视为用于处理的二级数据存储层,只有在数据溢出可用内存时才会使用。
如今,Hadoop已不再是热门的前沿技术,MapReduce也未被数据工程师广泛使用。我认为,在可预见的未来,利用内存进行数据转换的进步将继续带来收益。
在下一个视频中,请与我一起了解更多关于Apache Spark的知识,这是一个分布式内存处理框架,你将在接下来的实验中使用它。


本节课中我们一起学习了Apache Hadoop框架。我们回顾了其诞生的历史背景,深入探讨了其核心组件HDFS和MapReduce编程模型的工作原理,并通过一个具体例子理解了MapReduce的执行流程。最后,我们分析了Hadoop的局限性,并引出了后续更先进的、利用内存计算的新框架(如Spark)的出现。理解Hadoop是理解现代大数据处理技术演进的重要基础。
026:分布式处理框架Spark 🚀

在本节课中,我们将要学习分布式处理框架Apache Spark。我们将了解Spark的设计目标、核心架构、工作原理以及其丰富的生态系统。通过学习,你将理解Spark如何克服Hadoop MapReduce的局限性,并成为一个统一、高效的大数据处理平台。
为了应对Hadoop MapReduce的不足,加州大学伯克利分校的研究人员于2009年启动了Spark项目。


其目标是构建一个借鉴MapReduce思想,但更简单、更快速的分布式框架。它支持中间结果的内存存储和数据的交互式处理。此后,Spark不断发展,已支持流处理、机器学习和图计算库。Apache Spark社区也在持续开发和为这个框架添加新功能。
我认为你获得一些Spark实践经验至关重要。与Hadoop通过HDFS集成计算和持久化存储不同,Spark只是一个计算引擎,专为处理分布式大数据集而设计。使用Spark,你可以执行并行计算,并将中间结果保留在内存中,这减少了磁盘I/O交互,使得计算速度比Hadoop MapReduce显著更快。你可以在本地、数据中心或云端使用Spark。你可以从独立的关系型数据库、键值数据库、对象存储甚至Hadoop文件系统等持久化存储系统加载数据并存储最终结果。
Spark提供了一个统一平台,允许你在一个处理引擎上以自包含的Spark应用程序形式运行不同类型的分析工作负载。例如,你可以使用Spark执行SQL查询、加载数据、训练和测试机器学习算法,并在同一计算引擎上对数据应用流式转换。你可以使用Spark核心API,用Python、Java、Scala和R编写工作负载。Spark还提供了内置库,例如用于编写SQL查询的SparkSQL、用于机器学习应用的MLlib、用于处理实时数据的Spark Structured Streaming以及用于图处理的GraphX。除了这些标准库,你还可以使用开源社区发布和维护的外部第三方库。这些库包括允许你连接到各种外部数据源和存储系统、监控性能等的连接器。
让我们深入了解一下Spark应用程序的底层组件,看看它们如何协同工作。
一个Spark应用程序由一个节点集群组成。它包含一个驱动节点,这是Spark应用程序的中央控制器;一个集群管理器节点,它与驱动节点通信,以在集群中分配计算和内存资源并管理这些资源;以及一组工作节点,每个节点包含一个Spark执行器,用于执行驱动节点分配给它们的任务。Spark在从磁盘加载数据时应用一种分区方案将数据分解为多个分区,并将网络中最接近每个Spark执行器的分区分配给它。因此,每个执行器的CPU核心都会处理一个数据分区。
当你编写Spark应用程序时,首先实例化一个SparkSession对象,它代表了访问所有Spark功能的单一统一入口点。通过这个入口,你可以定义数据帧、从数据源读取数据以及执行SQL查询。驱动节点将你用Python、Scala或其他语言编写的指令翻译成Spark作业,这些作业将根据优先级一个接一个地执行。
为此,驱动节点将每个作业转换为一连串的阶段,并将这些阶段表示为一个有向无环图。例如,这里的这个作业有三个阶段,并用这个DAG表示。每个DAG就像是相应作业的执行计划。每个阶段进一步分解为可以用Spark代码编写、能够并行运行的任务。这里,阶段1有四个可以并行运行的任务,阶段2和阶段3各有三个也可以并行运行的任务。你将串行运行具有共享依赖关系的阶段,而并行运行那些没有依赖关系的阶段。例如,在这个DAG中,阶段2和阶段3依赖于阶段1的结果,因此它们必须等到阶段1完成后才能开始。但阶段2和阶段3之间没有共享依赖关系,可以并行运行。因此,为了执行这个作业,Spark将从阶段1开始,并行运行所有四个任务。一旦这些任务完成,阶段2和阶段3将同时启动,每个阶段中的三个任务将并行运行。
回到我们的Spark应用程序,一旦DAG执行计划制定完成,驱动节点会与集群管理器通信,为执行器请求计算和内存资源。每个任务被分配给执行器内的单个核心,每个执行器处理单个数据分区。执行器执行任务,并将计算结果传回驱动节点。最后,驱动节点聚合这些计算,并将结果返回给你。
在与Spark交互时,你无需担心所有这些底层细节。因此,无论你是使用最喜欢的编程语言还是任何标准库编写应用程序,在幕后,你的代码都会被分解成任务并分配到各个Spark执行器上。
关于Spark,我们可以涵盖很多内容,但我想重点介绍PySpark,它是Apache Spark的Python API。PySpark支持所有Spark功能,包括SparkSQL、Spark数据帧、机器学习和结构化流处理。在实验环节,你将使用Spark数据帧和SparkSQL处理结构化数据。
因此,在下一个视频中,我将向你展示如何创建和使用Spark数据帧。在之后的视频中,我们将详细介绍Spark的SQL功能。
总结



本节课我们一起学习了Apache Spark分布式处理框架。我们了解到Spark是为了克服Hadoop MapReduce的局限性而设计的,它通过内存计算和统一的编程模型实现了更高的性能。我们探讨了Spark应用程序的核心架构,包括驱动节点、集群管理器、工作节点以及任务执行的DAG模型。最后,我们介绍了PySpark作为使用Spark的Python接口,为后续的实践操作奠定了基础。
027:Spark数据框 🚀

在本节课中,我们将要学习Apache Spark的核心数据结构——Spark数据框。我们将了解它与之前学习的Pandas数据框有何不同,其底层架构是什么,以及它如何通过惰性求值和容错性来处理大规模分布式数据。
上一周,你使用Pandas数据框在一个小型表格化的客户流失数据上执行了简单的转换。而使用Spark数据框,你可以处理分布在多个Spark执行器后端的、规模大得多的表格数据集。但Spark为你抽象了这些细节,因此你可以像与单个表格交互一样查看和操作数据。
Spark数据框实际上是构建在一个名为弹性分布式数据集的低级数据结构之上的,其英文为Resilient Distributed Dataset (RDD)。RDD代表了可以并行操作的实际分区记录集合。如果直接使用RDD,你需要手动定义和优化所有想在数据上执行的操作。但有了Spark数据框,你可以使用更简单、更具表现力的高级操作来与数据交互,例如:
以下是Spark数据框支持的一些常见高级操作:
- 过滤 (
filter) - 选择 (
select) - 计数 (
count) - 聚合与分组 (
groupBy,agg)
Spark会在后台将这些操作编译到RDD级别。Spark数据框及其底层的RDD都被认为是不可变的数据结构,这正是它们具有弹性(即容错性)的原因。

上一节我们介绍了Spark数据框的基本概念和高级操作。本节中,我们来看看对分布式数据的操作分类。
我们可以将对分布式数据的操作分为两种类型:转换和行动。
转换(例如过滤、选择、连接和分组)会从现有数据框创建新的数据框,而不会修改原始数据。这就是为什么数据框及其底层的RDD被认为是不可变的。
行动(例如计数、显示和保存)则会触发这些转换的执行。实际上,Spark的所有转换都是惰性求值的,这意味着它们不会立即执行。相反,它们被记录为一个血统图,只有在调用一个行动时才会执行。这种惰性求值允许Spark通过重新排列转换操作来优化执行计划,以提高效率。
此外,血统图和不可变性属性确保了容错性,因为这些属性允许你在发生故障时重现原始状态。
在下一个视频中,我将为你快速演示Spark数据框,并向你展示一些可以执行的常见数据框操作。当在Apache Spark中处理数据时,我们那里见。


本节课中,我们一起学习了Spark数据框。我们了解到它是用于处理大规模分布式数据的高级抽象,构建在RDD之上。我们区分了转换(惰性操作,创建新数据框)和行动(触发实际计算)两种操作类型,并理解了惰性求值和不可变性如何共同作用,为Spark带来了强大的优化能力和容错性。
028:使用Python处理Spark数据框 🐍

在本节课中,我们将学习如何使用Python对Spark数据框进行基本操作。我们将涵盖数据框的创建、数据操作、数据清洗、数据聚合以及如何定义和使用用户自定义函数。
概述
我们将从安装必要的库和创建Spark会话开始。接着,我们会学习如何手动创建数据框以及从外部文件(如CSV)读取数据。然后,我们将探索如何选择、操作和清洗数据框中的列。之后,我们会介绍如何进行数据聚合操作。最后,我们将了解如何定义和使用用户自定义函数,并讨论其性能考量。
创建Spark会话
在创建Spark数据框之前,你需要创建一个Spark会话,它作为处理Spark数据框的入口点。
首先,从pyspark.sql导入SparkSession类。
from pyspark.sql import SparkSession

然后,通过调用getOrCreate方法创建一个名为“example”的Spark会话。如果名为“example”的会话已存在,此方法将获取它;否则,它将创建一个新的会话。
spark = SparkSession.builder.appName("example").getOrCreate()
你可以查看会话的详细信息,包括Spark版本号和会话名称。
创建数据框
你可以手动创建数据框,也可以从外部数据源导入数据。
手动创建数据框
假设你想手动创建一个包含订单详情数据的数据框。你可以创建一个名为orders_df的数据框,指定之前创建的Spark会话对象,并对其调用createDataFrame方法。
此方法需要两个参数:第一个参数是数据,你可以提供一个元组列表,每个元组代表一行数据;第二个参数是模式,用于指定每列的名称和类型。
orders_df = spark.createDataFrame([
(1, "Product A", 10.0),
(2, "Product B", 15.0)
], ["order_id", "product", "price"])
你可以使用show方法查看数据框的行。
orders_df.show()
从CSV文件创建数据框
假设你想通过读取包含在线零售商店交易数据的CSV文件来创建数据框。你可以创建另一个名为transactions_df的数据框,使用相同的Spark会话对象,但这次调用read.csv方法,指定CSV文件的名称,并指明数据包含表头。
transactions_df = spark.read.csv("transactions.csv", header=True)
你可以通过调用show方法并设置n=5来查看数据的前五行。
transactions_df.show(5)
选择数据列
如果你只关心数据框中的某些列,可以先使用columns命令查看所有列名,然后使用select方法选择所需的列。
以下是查看所有列名并选择“price”、“quantity”和“country”列的示例。
print(transactions_df.columns)
selected_df = transactions_df.select("price", "quantity", "country")
selected_df.show(5)
如果你想快速探索这些列的内容,可以使用describe方法计算每列的基本摘要统计信息,包括值计数、均值、标准差和最大值。
selected_df.describe().show()
操作数据框
现在,我们来看看如何操作数据框,例如添加、更新、重命名或删除现有数据框中的列。
添加新列
假设你想创建一个新列,表示每个产品的支付金额。你可以调用withColumn方法,指定新列的名称(例如“amount”),并通过将现有数据框中的“price”和“quantity”列相乘来获取新列的值。
Spark会检查“amount”列是否已存在于数据框中。如果存在,它将用新值替换该列;如果不存在,它将使用这些值创建一个新列。
transactions_df = transactions_df.withColumn("amount", transactions_df["price"] * transactions_df["quantity"])
transactions_df.show()
重命名列
接下来,让我们将“invoice”列重命名为“ID”。为此,我将调用withColumnRenamed方法,并指定现有列名为“invoice”,新列名应为“ID”。
transactions_df = transactions_df.withColumnRenamed("invoice", "ID")
transactions_df.show()
删除列
最后,让我们删除“description”列。我将调用drop方法,并指定要删除的列的名称。
transactions_df = transactions_df.drop("description")
transactions_df.show()
清洗数据
在数据框中清洗数据时,你可以通过调用dropna方法轻松删除包含空值的行。
transactions_df = transactions_df.dropna()
你也可以调用filter方法,根据布尔条件删除行。例如,假设“quantity”列包含一些负值,而你只想考虑包含正值的行。你可以调用filter方法,并提供一个条件,即“quantity”列必须大于零。
transactions_df = transactions_df.filter(transactions_df["quantity"] > 0)
transactions_df.show()
这样,你将只得到不包含空值且包含正数量值的行。
数据聚合操作
接下来,我们来看看数据框上的一些聚合操作。
计算每个订单的总金额
假设你想找出每个订单的总花费。首先,我将使用groupBy方法按订单ID对行进行分组。然后,我将对“amount”列求和,以获取每个订单ID的总金额。
total_amount_df = transactions_df.groupBy("order_id").sum("amount")
total_amount_df.show()
统计每个国家的总行数并按降序排序
作为另一个例子,让我们统计每个国家的总行数,并按降序对计数进行排序。再次,我将调用groupBy方法按国家分组行,然后调用count方法进行计数。接着,我将调用orderBy方法按计数排序,并指定ascending=False,以便结果按降序排序。
country_count_df = transactions_df.groupBy("country").count().orderBy("count", ascending=False)
country_count_df.show()
用户自定义函数
虽然Spark提供了许多内置功能,你可以在文档中找到,但它也支持用户自定义函数。
定义和使用UDF
例如,假设你想将所有国家名称转换为大写。你可以定义一个名为to_upper的函数,该函数接收一个字符串并返回大写的字符串。然后,你需要将此函数包装在UDF对象中。
首先,从pyspark.sql.functions导入udf类。
from pyspark.sql.functions import udf
from pyspark.sql.types import StringType
然后,实例化一个名为udf_to_upper的UDF对象,指定刚刚定义的函数及其返回类型。
def to_upper(s):
return s.upper()
udf_to_upper = udf(to_upper, StringType())
现在,选择“ID”列和“country”列,并将udf_to_upper对象应用于“country”列。
uppercased_df = transactions_df.select("ID", udf_to_upper(transactions_df["country"]).alias("country"))
uppercased_df.show()
你可以看到,“country”列中的所有条目现在都已转换为大写字符串。
使用装饰器定义UDF
或者,你可以添加带有返回类型的UDF装饰器,而不是将定义的函数包装在UDF对象中。然后,你可以直接将to_upper函数应用于“country”列。
from pyspark.sql.functions import udf
from pyspark.sql.types import StringType
@udf(returnType=StringType())
def to_upper(s):
return s.upper()
uppercased_df = transactions_df.select("ID", to_upper(transactions_df["country"]).alias("country"))
uppercased_df.show()
请记住,Spark数据框是不可变的,因此使用UDF时,你实际上创建了一个新的数据框,其中包含大写的“country”列。
让我们用包含大写值的新列替换交易数据框中的“country”列。我将调用withColumn方法,并指定我想用大写值替换“country”列中的值。
transactions_df = transactions_df.withColumn("country", to_upper(transactions_df["country"]))
transactions_df.show()
这是新的交易数据框。
UDF性能考量
既然你已经了解了Python UDF的工作原理,在使用这些函数时应格外小心。这是因为Spark原生使用Scala,工作节点中的每个执行器都是一个Java虚拟机进程,托管一部分数据。当你使用Python UDF时,Spark会在工作节点内启动一个单独的Python进程,并需要从JVM传输数据以便由Python处理。Spark将数据序列化为Python可以理解的格式。然后,Python进程逐行对该数据执行函数,最后将行操作的结果返回给JVM。这个过程效率不高,因为JVM和Python进程会竞争内存资源,并且序列化和反序列化数据的成本很高。
为了获得更好的性能,你应该考虑用Scala或Java重写Python UDF,因为这些函数将直接在JVM内运行。好消息是,在Spark中注册这些UDF后,你仍然可以在Python中使用它们。

总结

在本节课中,我们一起学习了如何使用Python对Spark数据框进行基本操作。我们从创建Spark会话和数据框开始,然后探索了如何选择、操作和清洗数据。接着,我们介绍了数据聚合操作和用户自定义函数的定义与使用。最后,我们讨论了Python UDF的性能考量。在下一个视频中,我们将学习如何使用SparkSQL发出SQL查询。
029:数据工程(数据建模、转换和服务)第4课 - 使用Spark SQL 🚀

概述
在本节课中,我们将学习如何在PySpark环境中使用SQL代码来探索和处理数据。你将了解如何创建临时视图、执行SQL查询、注册自定义函数,以及如何通过SQL进行多表连接操作。
从DataFrame到SQL查询
上一节我们介绍了如何使用Spark DataFrame和Python代码处理数据。本节中我们来看看如何通过SQL与相同的数据进行交互。
在PySpark中,你可以选择使用SQL代码、Python代码或两者混合的方式来操作数据。这两种类型的代码都运行在相同的计算引擎上,并最终编译为相同的底层代码。

以下是使用SQL查询数据的基本步骤:
-
创建临时视图:要从DataFrame执行SQL查询,你需要先基于DataFrame创建一个临时视图。临时视图是一个虚拟表,它本身并不存储数据。只要Spark会话在运行,这个视图就会一直存在,并为你提供了一个使用SQL代码操作表数据的接口。
# 基于transaction_df创建一个名为'orders'的临时视图 transaction_df.createOrReplaceTempView("orders") -
执行SQL查询:创建视图后,你可以通过Spark会话对象的
.sql()方法执行SQL查询。该方法接收一个包含SQL查询语句的字符串,并返回一个表示查询结果的DataFrame。# 查询每个订单的总金额,并按总金额降序排列 result_df = spark.sql(""" SELECT ID, SUM(amount) AS total FROM orders GROUP BY ID ORDER BY total DESC """) result_df.show()
在SQL查询中使用自定义函数
与在DataFrame中类似,你也可以在SQL查询中使用自定义函数。但在SQL中,你需要先将函数注册到Spark会话中。
以下是注册和使用自定义函数的步骤:
-
定义并注册函数:首先定义一个Python函数,然后使用
spark.udf.register方法将其注册为一个可以在SQL中调用的UDF(用户自定义函数)。# 定义一个将字符串转换为小写的函数 def to_lower(word): return word.lower() # 将该函数注册为SQL可用的UDF,命名为'udf_to_lower' spark.udf.register("udf_to_lower", to_lower) -
在SQL查询中调用:注册后,你就可以在SQL语句中使用这个函数了。
# 查询orders表中所有不重复的国家,并将国家名称转换为小写 spark.sql(""" SELECT DISTINCT udf_to_lower(country) FROM orders """).show()
多表连接查询
你可以创建多个临时视图,并在SQL查询中对它们进行连接操作,这在进行复杂数据分析时非常有用。
以下是进行多表连接查询的示例:
-
创建第二个视图:假设我们有一个包含产品代码和类别的DataFrame,我们可以为其创建第二个临时视图。
# 假设 product_category_df 是另一个DataFrame product_category_df.createOrReplaceTempView("items") -
执行连接查询:现在,我们可以编写SQL查询来连接
orders表和items表。# 连接两个表,计算每个产品类别的平均订单金额 spark.sql(""" SELECT i.category, AVG(o.amount) AS avg_amount FROM items i LEFT JOIN orders o ON i.code = o.stock_code GROUP BY i.category """).show()这个查询通过
items表的code字段和orders表的stock_code字段进行左连接,然后按类别分组并计算平均金额。

总结
本节课中我们一起学习了在PySpark中使用SQL的核心技能。我们掌握了如何将DataFrame转换为临时视图以便执行SQL查询,如何在SQL中注册和使用自定义函数,以及如何进行多表连接来回答更复杂的业务问题。这些技能为你提供了处理数据的灵活性,你可以根据场景选择最合适的编程范式(SQL或Python)或混合使用它们。

在接下来的实验中,你将有机会使用PySpark将数据转换为星型模式。而在实验之后,我们将深入探讨数据转换过程中的技术考量。
030:深入理解Amazon EMR 🚀

在本节课中,我们将深入学习Amazon EMR,这是一个支持多种处理框架的大数据处理工具。我们将探讨其工作原理、核心特性,并通过一个简单的示例演示如何在EMR Studio笔记本中运行Spark作业。
概述:什么是Amazon EMR?
在第一门课程中,我们曾将Amazon EMR介绍为一种支持广泛处理框架的大数据工具。在即将进行的实验中,你将使用EMR在Amazon EMR Studio笔记本中运行Spark作业。
本视频旨在帮助你更深入地理解在Amazon EMR上运行Spark作业时后台发生的过程,以及EMR的其他一些特性。
EMR的工作原理:并行处理的力量
如果你还记得我们之前关于Amazon Redshift的课程,你了解到它如何利用大规模并行处理来处理大数据分析。EMR以类似的方式工作,它包含一个拥有多个节点的集群,每个节点负责处理一部分工作。
当你使用EMR提交一个作业时,该作业会在这些节点上并行运行,每个节点处理一部分数据。由于这种并行化,作业完成的速度比单台机器所能达到的要快得多。
你的集群规模会影响作业的运行速度。在EMR中,集群是弹性的,这意味着它可以根据需要扩展或收缩。
作业完成后,结果会存储在你指定的目的地。这可以是Amazon S3、HDFS或其他数据存储选项。然后,你可以分析这些结果,或将它们输入到另一个应用程序或工作流中进行进一步处理。

EMR支持的大数据框架

EMR支持众多流行的大数据框架,包括Apache Spark、Hadoop、Hive、Presto、Flink、HBase以及许多其他支持数据分析任务的工具和框架。

EMR提供了一个托管环境,简化了这些框架的设置和扩展,并原生集成了其他AWS服务。因此,使用EMR,你可以更专注于数据工作流,而不是底层基础设施。
与AWS服务的集成
例如,如果你想使用Hadoop分析存储在S3中的数据,你可以利用S3和Amazon EMR文件系统之间的集成来实现。这允许你将计算和存储解耦,并分析可能无法容纳在本地存储上的大量数据。
其工作方式是,当你启动集群时,EMR会将数据从S3流式传输到集群中的每个实例,并开始处理。

将数据存储在S3的另一个优势是,你可以使用多个EMR集群以不同方式处理相同的数据。EMR还可以与其他AWS数据源集成,例如Amazon DynamoDB、Amazon RDS和Amazon Redshift。
实践:在EMR Studio中运行Spark作业
在即将进行的实验中,你将使用Amazon EMR Studio,这是一个基于浏览器的Jupyter笔记本IDE,运行在EMR集群上。你将使用一个笔记本来运行Spark作业。
让我们通过一个使用笔记本的基本示例,为你完成实验做好准备。

以下是使用EMR Studio运行Spark作业的基本步骤:
- 创建EMR集群:首先,在AWS控制台中导航到EMR仪表板,创建一个EMR集群。对于本例,我们将启动一个无服务器集群。
- 创建EMR Studio和工作空间:你需要一个EMR Studio来管理工作空间。创建一个工作空间,并为其命名。
- 配置工作空间:创建工作空间后,需要对其进行配置以连接到计算资源。这包括选择之前创建的EMR无服务器应用程序,并设置交互式运行时角色。
- 创建并运行笔记本:在工作空间中,创建一个新的笔记本,并选择PySpark作为内核。然后,你可以编写并运行Spark脚本。
例如,你可以运行一个简单的PySpark脚本来分析S3桶中的数据。以下是一个计算特定日期范围内出租车行程平均费用的示例代码片段:
from pyspark.sql import SparkSession
from pyspark.sql.functions import avg
# 初始化Spark会话
spark = SparkSession.builder.appName("TaxiFareAnalysis").getOrCreate()


# 从S3读取数据
df = spark.read.csv("s3://your-bucket-name/taxi_data.csv", header=True, inferSchema=True)
# 过滤特定日期范围并计算平均费用
filtered_df = df.filter((df["date"] >= "2023-01-01") & (df["date"] <= "2023-01-31"))
average_fare = filtered_df.select(avg("fare_amount")).collect()[0][0]


print(f"平均费用为: ${average_fare:.2f}")
运行此脚本后,作业将在EMR无服务器集群上执行,结果将显示在笔记本中。
总结
本节课中,我们一起深入探讨了Amazon EMR。我们了解了其利用并行处理处理大数据的核心工作原理,认识了它支持的各种流行框架,并学习了它如何与S3等其他AWS服务无缝集成。

通过一个在EMR Studio中创建集群、配置工作空间并运行简单Spark作业的实践示例,我们直观地体验了EMR的使用流程。希望这能帮助你更好地理解EMR,并为你在接下来的实验中自己进行更有趣的数据分析做好准备。祝你实验顺利!
031:技术考虑因素 🧠

在本节课中,我们将学习在使用PySpark进行数据转换时,如何根据具体场景在Spark SQL和Python DataFrame API之间做出选择。我们还将探讨何时应该使用分布式计算框架(如Spark),以及如何通过优化数据提取来提升处理性能。
在Spark SQL与Python DataFrame之间选择
上一节我们介绍了使用PySpark进行数据转换的两种主要方式。本节中,我们来看看在选择使用Spark SQL还是直接在Spark DataFrame上使用Python时,需要考虑哪些技术因素。
使用PySpark转换数据时,你可以通过SparkSQL执行SQL查询,也可以直接使用Python操作Spark DataFrame。在选择这两种方式时,应考虑转换的复杂性、代码的可重用性与可测试性,以及团队的技术背景和技能。

以下是选择时需要考虑的几个关键点:
- 转换复杂性:对于简单的转换操作,例如过滤、分组和聚合,直接在Spark DataFrame上使用Python或将其编写为SQL查询,通常能获得相近的性能。这是因为两种方法最终都会被转换为相同的执行计划,并由底层的Spark计算引擎执行。然而,如果转换操作更为复杂,你可能无法用SQL实现,或者实现起来不够直接。例如,要对表格进行转置操作(交换行和列),你可以简单地调用
.T方法:df.T。但SparkSQL不支持转置操作。 - 代码质量:使用DataFrame有助于编写更易于测试、维护和模块化(或可重用)的代码。在Spark中创建可重用的库更容易,而SparkSQL对于更复杂的查询组件缺乏良好的可重用性概念。
- 团队技能:你可能会发现,对于团队而言,编写SQL查询比使用Python操作Spark DataFrame更简单、更容易。
因此,根据你的具体转换用例,你可能会发现其中一种方法比另一种更合适。你甚至可以尝试结合使用Spark DataFrame和SparkSQL方法,以兼得两者之长。
何时使用Spark分布式框架
既然我们已经讨论了使用SparkSQL与Python DataFrame的考量,现在让我们花点时间讨论一下,在什么情况下你会考虑使用像Spark这样的分布式框架。
正如上周所见,除了使用Spark DataFrame,你也可以直接使用Pandas DataFrame来处理数据。然而,Pandas不是一个分布式框架,它会将所有数据加载到运行Python代码的机器的内存中。
以下是选择工具时的考量:
- 数据量小:如果你的数据量不大(即整个数据集可以放入内存),那么你可以使用Pandas而不是Spark。事实上,对小数据使用Spark可能有些大材小用,因为你还需要管理节点集群。
- 数据量大或需高性能:如果你的数据量非常大,无法完全放入内存,或者你想利用分布式计算来提升处理性能,那么你应该选择Spark,并可能在云端的集群上运行它。
最佳实践:优化数据提取
无论如何,无论你是在单机上工作还是在节点集群上工作,最佳实践是仅从数据源提取你需要的数据。

你提取的数据越少,代码消耗的资源就越少,性能也就越高。因此,你可能需要在将数据提取到处理引擎之前,在数据库内部应用诸如连接、分组和过滤等转换操作,以减少需要处理的数据量。
总结与下节预告
本节课中我们一起学习了如何为批处理转换选择正确的编码方法,这需要在SQL编码的简洁性与使用Python等非声明性语言编码的灵活性和模块化之间取得平衡。选择正确的批处理转换工具取决于你想要转换的数据大小以及运行代码的硬件规格。请务必理解这些不同方法之间的权衡。

现在我们已经讨论了批处理转换,下一节课我们将一起学习流式转换,以及流式处理工具如何影响你系统的延迟。
032:流处理 🚀

在本节课中,我们将要学习流处理的核心概念,了解如何对实时数据流进行查询和转换,并比较不同的流处理工具。我们将探讨流处理的应用场景、技术选型,并最终通过一个实践项目来巩固所学知识。
流处理概述
在第三门课程中,我们讨论了如何对数据流应用查询,并使用 Flink 练习编写这些查询。
这些流式查询可以帮助利益相关者对数据流进行实时分析,同时也可以用于对数据流进行转换。
流式转换的目的
流式转换旨在通过将事件流转换为另一个流,为其添加额外信息或将其与另一个流连接,从而为下游消费准备数据。
例如,您可能有一个来自物联网(IoT)源的事件流。这些 IoT 事件携带设备 ID 和事件数据,您可能希望使用存储在单独数据库中的设备元数据动态地丰富这些事件。您可以使用流处理引擎查询包含此元数据的单独数据库,然后通过将此元数据添加到现有的 IoT 事件中来生成一个新的事件流。
事实上,在之前的实验中您已经有过一些经验。您应用了流式 ETL 来转换用户会话事件流,通过为每个事件添加一个表示处理时间的时间戳以及从每个用户数据计算出的额外指标来丰富事件。
流式转换的更多示例
作为流式转换的另一个例子,您可以使用窗口查询动态计算窗口上的汇总统计信息,然后将输出发送到目标流。
在连接两个流方面,例如,您可以使用流式转换将包含网站点击流数据的流与另一个包含 IoT 数据的流结合起来,以获得用户活动的统一视图。
在这些例子中,事件通常由 Kafka 或 Kinesis Data Streams 等流平台传输给您,然后您可以使用流处理器处理这些事件。
流处理工具
与批处理类似,当您拥有大型数据集时,可以使用 Spark Streaming 和 Flink 等分布式流处理工具。这两种工具都是开源的,允许您编写 Python 代码或 SQL 查询来处理大型数据流。
选择流处理工具时,了解您的用例、延迟要求以及相关框架的性能能力非常重要。
微批处理与真流处理
其中一些工具,如 Spark Streaming,以微批处理方式处理数据,提供近实时性能。它累积从几分钟到几秒不等的小批量输入数据,然后使用分布式任务集合并行处理每个批次,类似于批处理作业的执行。
另一方面,像 Flink 这样的真流系统被设计为一次处理一个事件。系统中的每个节点持续监听来自其他节点的消息,并向其依赖节点输出新的更新。真流系统可以以比微批处理系统更低的延迟处理事件。
然而,这会带来显著的开销。因此,根据您的用例和可接受的延迟,您可以选择其中之一。
如果您收集的是每隔几分钟发布一次的销售指标,只要设置了适当的微批处理频率,微批处理可能就足够了。
另一方面,如果您的运维团队需要每毫秒计算一次指标以检测恶意攻击,您可能需要真流处理。
深入学习与实验

为了了解更多关于 Spark Streaming 和 Flink 底层差异的信息,课程包含了一些可选材料,比较了两种工具的架构,并提供了展示如何使用 Spark Streaming 的代码示例。
之后,您将完成本周的最后一个实验。您将通过实现一个流式变更数据捕获(CDC)管道来练习执行流式转换。您将使用 Debezium 作为工具从源数据库捕获变更,然后将这些变更推送到一个 Kafka 流,并使用 Flink 处理变更。
要了解此实验的概览,您可以查看实验演练,或者直接进入实验。
总结

本节课中,我们一起学习了流处理的基本概念和应用。我们了解了流式查询和转换如何为实时分析和数据准备提供支持,比较了微批处理(如 Spark Streaming)与真流处理(如 Flink)在架构和适用场景上的差异,并介绍了通过实验来实践流式变更数据捕获(CDC)管道的构建。掌握这些知识将帮助您根据具体的延迟和性能需求,为数据工程项目选择合适的流处理工具。
033:第3周总结 📊

在本节课中,我们将回顾并总结第3周学习的核心内容,重点聚焦于数据转换的细节、不同数据处理框架的技术考量,以及如何将原始数据转化为对下游应用有价值的信息。
概述
本周我们深入探讨了数据转换的细节,以及可用于转换数据的各种数据处理框架的技术考量。我们了解到,数据转换不仅是为了根据目标模式对数据进行建模,还可以通过清洗、丰富数据以及在数据管道中保持其更新来提升数据的价值与质量。
上一节我们介绍了数据转换的基本目标,本节中我们来看看实现这些目标的具体技术框架和考量因素。
批处理转换的考量
在批处理转换方面,我们讨论了几个关键考量因素,包括需要转换的数据量大小以及运行代码的机器的内存规格。
以下是选择批处理框架时的主要考量点:
- 数据规模:需要处理的数据集大小。
- 内存规格:执行转换任务的机器的内存容量。
- 计算模式:是否需要利用并行计算来提升效率。
你了解到,如果需要处理的数据无法完全装入内存,或者希望利用并行计算,可以使用分布式处理框架。同时,你也学习了像 Spark 这样的大数据工具是如何从 Hadoop MapReduce(其中间结果存储在磁盘上)演化而来的。
Apache Spark 实践
你概览了 Spark 在后台的工作原理,并有机会练习使用 Spark DataFrames 和 SparkSQL 进行编码。
虽然对于简单的转换任务,编码和 SQL 可能很方便,但使用 DataFrames 进行编码能帮助你实现更复杂的转换,并使你的代码更具模块化。其优势可以用以下伪代码结构表示:
# 模块化的DataFrame操作示例
transformed_df = (raw_df
.filter(condition) # 步骤1:过滤
.select(columns) # 步骤2:选择列
.groupBy(key) # 步骤3:分组聚合
.agg(functions))
因此,代码更易于维护和测试。

流处理转换的选择
当涉及流处理转换时,理解你的用例和系统的性能要求将帮助你在微批处理框架(如 Spark Streaming)和真正的流处理系统(如 Flink)之间做出选择。
选择依据可归纳为:
- 微批处理(如 Spark Streaming):适合对延迟要求不极端严格,但需要与批处理代码库保持一致的场景。
- 真流处理(如 Apache Flink):适合需要极低延迟和精确一次(exactly-once)处理保证的场景。
实践项目:变更数据捕获(CDC)
在本周最后的实验中,你实现了一种基于日志的变更数据捕获方法,使用 Debezium 和 Apache Kafka 来捕获数据库变更,并利用 Flink 来处理这些数据库更新流。
这个项目综合应用了流处理、消息队列和CDC技术,是数据管道中实现实时数据同步的典型模式。
总结
本节课中我们一起学习了数据工程生命周期中关键的转换阶段,在这里你将数据转化为对下游用例有用的信息。我们探讨了批处理与流处理框架的技术选型,并动手实践了使用现代工具链(Spark, Flink, Kafka, Debezium)进行复杂数据转换和实时处理。
随着转换阶段的讨论结束,整个数据工程的核心流程已清晰呈现。下一周,你将学习如何将转换后的数据提供给下游的利益相关者,这是数据服务的环节。作为整个项目的最后一周,你还将有机会在一个顶点项目中,综合运用在整个课程中学到的许多数据工程概念。


我们下周课程再见。
034:数据工程(数据建模、转换和服务)第4课,完结 🎯

概述
在本节课中,我们将学习数据服务的核心概念,了解如何为不同的分析和机器学习应用场景提供数据。我们将探讨数据服务的多种形式,并回顾在整个课程中学到的数据工程知识框架。
恭喜你完成本课程的最后一周学习。在整个课程中,你已经学习了如何为分析和机器学习用例进行数据建模,以及在转换数据时应考虑的工具和技术因素。
在最后一周,我们将探讨如何为各种分析和机器学习用例提供数据。
例如,在商业分析用例中,你可能需要从数据仓库或数据湖中提供数据,以便最终用户可以使用这些数据创建仪表板、报告或进行临时分析。
你也可以为运营分析提供数据,在这种场景下,最终用户需要监控数据的即时趋势以指导立即行动。对于运营分析,你需要确保在要求的延迟时间内提供数据。

最后,你还可以为嵌入式分析提供数据,最终用户将数据输入面向客户的数据产品或仪表板。
在为机器学习应用提供数据方面,这在各类公司中正被广泛采用。你很可能需要与数据科学家或机器学习工程师合作,以获取、转换和交付模型训练所需的数据,并以适合目标应用的格式呈现。
在课程早期,我们看了一些为构建客户流失模型和推荐系统提供数据的例子。
此外,你可以通过在数据建模后构建的语义层来整合业务定义和数据逻辑。在语义层中,你可以记录诸如“活跃用户”含义等定义,并推导出“收入”等业务指标,从而为你所提供的数据创建一种通用语言。
你也可以将数据以表、视图或物化视图的形式提供。我们将在本周后续内容中详细讲解所有这些概念,并且在第一个实验课中,你将通过使用DBT创建视图获得大量实践。
在详细讲解数据服务之后,我将为你总结在本课程中学到的数据工程概念,回顾如何像数据工程师一样思考的框架,以及数据工程生命周期的所有阶段和潜在因素。
最后,你将在毕业项目中看到所有这些概念是如何结合在一起的。


在下一个视频中,请与我一起探讨为分析和机器学习用例提供数据的不同方式。
035:为分析和机器学习提供数据服务 📊

在本节课中,我们将学习如何将数据有效地提供给最终用户,以支持分析和机器学习任务。我们将探讨多种数据服务方式,包括文件共享、数据库查询、流式数据服务,并介绍确保数据一致性和可理解性的关键概念,如数据定义、数据逻辑和语义层。
通过文件共享数据 📁
向最终用户提供数据有多种方式。将数据作为文件共享是一种常见且直接的数据服务方法。
例如,数据科学家可能需要客户评论的文本文件来进行情感分析;分析师可能需要发票的CSV格式数值数据来进行统计分析;机器学习工程师则可能使用产品图像来开发产品分类系统。
当然,在某些情况下,你可以使用数据库或对象存储来提供文本、数值或图像数据。有时,你也可以直接通过电子邮件等方式共享单个文件。但这种方式难以管理文件的版本控制。
使用数据共享平台可以帮助你确保与最终用户共享的文件版本具有一致性和连贯性。
对于某些临时性需求,一次提供单个文件可能就足够了。但这种做法很难扩展。如果你需要共享半结构化或非结构化的大型文件,则需要通过对象存储或数据湖来扩展,而不仅仅是共享单个文件。
从数据库提供数据 🗄️
上一节我们介绍了文件共享,本节中我们来看看从数据库直接提供数据的方式。
你可以选择直接从你的OLAP数据库或数据仓库提供数据。在这种情况下,分析师或数据科学家可以使用SQL或其他查询语言查询存储系统,然后将结果导出到下游应用程序,或在笔记本中分析结果。
从数据库提供数据有其优势。数据库通过强制实施模式,为数据带来了秩序和结构。数据库在表、列和行级别提供了细粒度的权限控制,允许你为不同角色制定复杂的访问策略。此外,现代的OLAP数据库和查询引擎可以为复杂、计算密集型的查询提供高性能。
处理流式数据服务 ⚡
如果你正在处理流式数据,通过文件和数据库提供服务可能不切实际或无法实现,而数据库本身可能不具备你所需的功能。在这种情况下,你需要使用流式系统来实时提供数据。
例如,操作型分析数据库正变得越来越流行,因为它们允许最终用户以低延迟对大量历史数据以及最新的实时数据(精确到秒级)执行分析查询。当你从这些数据库提供数据时,你实际上是将OLAP数据库的功能与流处理系统的功能结合了起来。
确保数据定义与逻辑 📝
在数据管理方面,你需要确保利益相关者信任你提供的数据,并且他们能够正确解释并以一致的方式使用它。因此,你需要确保数据包含适当的数据定义和逻辑。
数据定义指的是在整个组织内被理解的数据含义。例如,“客户”一词的定义应被记录并可供所有使用该数据的人查阅。
数据逻辑包含从数据中推导指标的公式,例如增长率、销售额或客户生命周期价值。正确的数据逻辑依赖于正确的数据定义,并包含统计计算的细节。
以下是计算客户流失率指标的例子:
- 首先,需要理解“客户”对最终用户意味着什么。
- 然后,你可以编写一个SQL查询来定义该指标,该定义可以在整个组织内复用。
这有助于避免出现混乱且难以维护的SQL代码蔓延。
在组织内正式声明数据定义,对于确保数据的正确性、一致性和可信度大有裨益。

构建语义层 🔍
虽然你可以通过数据建模来帮助捕获数据定义和逻辑,但你也可以在数据模型之上构建一个语义层,将底层数据元素和结构转换为对最终用户来说更直观、更有用的业务术语。
语义层确保每个业务术语都有单一、一致的定义,并帮助你的最终用户更轻松地浏览数据以找到所需内容。
语义层可以存在于BI工具中,或者你也可以使用像DBT这样的工具来创建这个层。使用DBT,你可以使用YAML文件和SQL查询来定义标准的业务指标。
最终用户的数据使用 🛠️
一旦最终用户收到数据,他们可能会使用可视化工具或商业智能平台(如Amazon QuickSight、Apache Superset或Looker)来创建分析仪表板。数据科学家则可能使用笔记本来探索数据或训练模型。

作为数据工程师,你可能还需要负责帮助设置和管理专为处理云端数据科学工作负载而设计的云平台,例如Amazon SageMaker、Google Cloud Vertex AI和Microsoft Azure Machine Learning。

总结 ✨
本节课中,我们一起学习了为分析和机器学习提供数据服务的多种方式。我们探讨了通过文件共享、从数据库查询以及处理流式数据来服务数据的方法。我们还深入了解了确保数据质量与可用性的关键要素,包括数据定义、数据逻辑以及构建语义层的重要性。这些方法共同构成了一个健壮的数据服务体系,能够支持组织内各种分析和机器学习需求。
在下一个视频中,我们将深入探讨从数据库提供数据的具体方法,特别关注使用视图和物化视图。
036:视图与物化视图 👁️🗨️

在本节课中,我们将要学习两种重要的数据库对象:视图和物化视图。你将了解它们的定义、创建方法、使用场景以及它们之间的核心区别。掌握这些知识将帮助你更高效地为下游用户提供数据服务。
概述
当为下游利益相关者提供数据库直接访问权限时,除了以表的形式提供数据,你还可以使用视图和物化视图这类类似表的对象来提供数据。
你可以在数据管道的转换阶段,或在数据消费层将数据提供给最终用户之前,创建这些对象。
什么是视图?🔍
上一节我们介绍了为下游用户提供数据的不同方式,本节中我们来看看视图的具体概念。
视图本质上是一个可以存储在数据库中的查询。它为开发者和利益相关者提供了访问常用查询的便捷途径,并能帮助简化复杂查询的编写过程。
以下是创建视图的语法示例:
CREATE VIEW customer_info AS
SELECT first_name, last_name, email, phone, address
FROM customer, address, city, country;
视图代表一个虚拟表,而非物理表。最终用户可以像查询普通表一样从视图中进行选择。当你从视图中选择数据时,数据库会创建一个新查询,将视图定义与引用它的查询结合起来,然后查询优化器会对整个查询进行优化和执行。
假设一位营销分析师需要频繁地对连接customer、address、city和country表的结果运行查询。创建这个customer_info视图后,你就将四个表连接成了一个宽表。这样,营销分析师就可以简单地编写在此视图上进行过滤和聚合的查询,而无需每次都编写连接这些表的查询。
视图的安全与组织作用 🛡️
除了简化查询,视图还可以在提供数据时应用安全原则。
例如,你可以创建一个只选择特定列和行的视图。当你向下游利益相关者提供此视图时,你实际上将他们的数据访问权限限制在了他们所需的数据范围内。
你在课程3中学到的CTE(公共表表达式)是一个与视图类似的SQL概念。回忆一下,你可以使用WITH子句创建CTE,后跟CTE的名称和一个用括号括起来的查询。但该查询代表了你希望在后续SQL查询中引用的一些临时结果。
因此,CTE和视图都能通过使代码更清晰、更易于遵循来帮助组织代码。
然而,CTE仅存在于引用它们的主查询范围内。一旦主查询执行完毕,CTE就会被丢弃,并且无法在其他查询中被引用。

另一方面,视图是一个实际的数据库对象,可以被外部数据库用户访问。代表视图的查询主体实际上存储在数据库磁盘上,并且可以一直保留在数据库中,直到你显式地删除它。因此,视图可以被最终用户在不同的会话和查询中引用和使用。
视图的局限性 ⚠️
现在,如果使用视图,你无法执行和存储任何预计算。这意味着每次引用视图时,都需要执行视图所代表的查询。
因此,使用视图来存储最终用户需要频繁运行的复杂查询,其计算成本可能极其高昂。
什么是物化视图?⚡
为了解决视图的性能问题,我们来看看物化视图。
物化视图则不同,它会提前执行部分或全部的视图计算。然后,它会缓存查询结果,并允许你定期刷新数据。
以下是创建物化视图的语法示例:
CREATE MATERIALIZED VIEW rental_by_category AS
SELECT category.name, SUM(payment.amount)
FROM payment, rental, inventory, film, film_category, category
GROUP BY category.name;
该查询被执行,六个表连接的结果被保存在缓存中。当用户引用这个rental_by_category物化视图时,他们查询的是预先连接好的数据。
当你能够容忍刷新之间存在一定程度的延迟时,物化视图非常有用。
本周实验与课程总结 📋
在本周的第一次实验中,你将使用本课程第一周见过的星型模式模型。
你将使用DBT在该模型之上创建分析视图。
在此之后,你将准备好投入到下一课的顶点实验项目中。在下一课中,我将为你总结在整个课程中学到的所有关键概念。然后,在你开始进行那些实验之前,我会带你快速浏览一下顶点项目的内容。

我们下一课再见。
总结
本节课中我们一起学习了视图和物化视图。视图是存储的虚拟查询,用于简化访问和增强安全,但每次查询都会重新计算。物化视图则通过预先计算和缓存结果来提升频繁复杂查询的性能,但数据存在延迟。理解两者的区别和适用场景,对于设计高效的数据服务层至关重要。
037:数据工程基础概念总结 🎯

在本节课中,我们将回顾数据工程基础课程中涵盖的核心主题与概念。课程围绕数据工程生命周期及其底层支柱展开,探讨了在云端构建数据解决方案的技术与最佳实践。通过实验练习,你已亲手实践了这些概念。现在,我们将所有内容整合起来,为最终的顶点实验做好准备。
数据工程师的思维方式 🧠
上一节我们介绍了课程的整体框架,本节中我们来看看数据工程师的思维方式。启动新数据项目时,应始终采用逆向工作法。
以下是具体步骤:
- 首先识别利益相关者的需求,以及他们如何从你提供的数据中获取价值。
- 将这些需求转化为系统要求。
- 选择能够满足这些要求的合适工具与技术。
- 开始构建并迭代你的数据系统。
当你专注于最终用户及其数据需求时,你就能为组织创造价值。

数据系统的构建:摄取与处理 🔄
上一节我们介绍了如何从需求出发,本节中我们来看看如何构建数据系统。构建数据系统通常从从源系统摄取数据开始。
摄取方式取决于源系统的类型,例如:
- 数据库
- 文件系统或对象存储中的文件
- 流式系统
- API
你可以通过批处理摄取模式从文件或数据库摄取历史数据,也可以使用流式摄取从流式系统中实时摄取数据。虽然批处理和流式摄取常被分开讨论,但它们实际上存在于一个连续体中,范围从低频次、大批量的数据摄取到实时、逐条消息的流式传输。在这两者之间,存在广泛的微批处理和流式方法。摄取方法的选择应由利益相关者的需求和系统要求来指导。
批处理和流式摄取都可以作为ETL(提取、转换、加载)或ELT(提取、加载、转换)流程中的提取阶段。在ETL中,你在将数据加载到目标系统之前进行转换;而在ELT中,你在将数据加载到目标系统之后进行转换。这两种模式的选择取决于:
- 要对数据应用何种转换。
- 处理工具和目标系统的硬件规格。
- 数据的大小。
数据转换与建模 ⚙️
上一节我们讨论了数据摄取,本节中我们来看看数据的转换与建模。数据转换包括清理和合并来自多个源的数据,以及将数据转换为目标模式。
目标模式取决于你创建的数据模型。如果为分析服务数据,你可能会选择星型模式或单一大表模型。这些模型可以帮助最终用户更轻松、高效地编写分析查询。
如果为数据科学或机器学习服务数据,处理数据的方式和程度将取决于你的组织以及最终用户是想探索数据、使用数据训练机器学习模型、进行预测还是其他目的。
应用转换时,你可以执行简单的SQL查询,也可以用像Python这样的非声明性语言编写灵活、复杂的代码。根据数据量的大小,你可以使用非分布式处理工具(如pandas)或分布式框架(如Spark)来处理数据。
你还了解到,对于分析用例,可以在云数据仓库内部处理大量数据,以利用云计算的大规模并行处理能力。像DBT这样的工具也促进了数据仓库内部的数据建模。
数据存储:数据仓库、数据湖与湖仓一体 🏗️
上一节我们介绍了数据转换,本节中我们来看看不同的数据存储解决方案。与关系数据库类似,数据仓库期望具有明确定义模式的结构化数据集。当最终用户向数据仓库发出分析查询时,其查询优化器会寻找最佳执行计划,然后基于此计划返回结果。
数据仓库更适合分析工作负载,因为它们基于列式存储,这使得其在聚合查询方面比面向行的事务型数据库更高效。
另一方面,你可以使用构建在低成本对象存储之上的数据湖来支持需要海量结构化和非结构化数据的应用,例如机器学习和大数据处理。为防止数据湖变成无法使用的“数据沼泽”,你可以创建数据目录来跟踪和管理数据湖中的数据存储。
你还了解了湖仓一体架构,它结合了数据湖的低成本、可扩展存储与数据仓库的卓越结构化查询性能和数据管理功能,为提供低延迟分析和机器学习服务提供了一个统一平台。随着数据仓库采用类似数据湖的特性,数据湖融入类似数据仓库的能力,数据仓库、数据湖和湖仓一体之间的界限正变得模糊。
数据工程的底层支柱 🔐
上一节我们探讨了数据存储方案,本节中我们来看看支撑数据工程的底层支柱。在安全方面,你使用IAM来确保云上数据系统的安全,防止未经授权的用户访问你的数据和资源。VPC、路由表、网络ACL和安全组等网络概念也有助于保护资源。
在数据管理方面,你通过以下方式实践良好的管理:
- 对数据进行建模。
- 使用数据目录。
- 合理组织数据存储,使最终用户更容易找到数据。

你的最终用户也需要信任你的数据。通过在数据管道中使用像Great Expectations和Glue Data Quality这样的工具来测试和监控数据质量,确保你提供的数据能为利益相关者带来价值。
你还应用了DataOps的自动化支柱,使用像Terraform这样的基础设施即代码工具来自动化数据管道资源的创建和管理,并使用Airflow来编排整个数据管道。
课程总结与顶点实验 🚀
现在,你已经了解了如何设计和构建涵盖数据工程生命周期每个阶段并融合数据工程关键底层支柱的数据工程解决方案。
在本课程的最终实验中,你将有机会使用一个端到端的数据管道,将所有概念整合起来。顶点实验包括两部分:
- 在第一部分,你将创建并配置数据管道的资源。
- 在第二部分,你将把数据质量检查和编排集成到数据管道中。

为帮助你准备顶点实验,请在下一个视频中与我一起查看一些示例代码和补充材料,这些将有助于你完成实验。之后,你可以自由开始实验,或者如果你想在开始前了解实验任务概览,可以观看实验讲解视频。

本节课中我们一起学习了数据工程的核心生命周期阶段(摄取、转换、存储、服务)、关键数据存储架构(数据仓库、数据湖、湖仓一体)以及支撑整个流程的底层支柱(安全、数据管理、DataOps)。这些知识为你构建可靠、高效且有价值的数据解决方案奠定了坚实基础。
038:第4课 - 完结篇 🎓


在本节课中,我们将回顾整个数据工程专业证书课程,并探讨数据工程领域的未来发展趋势。课程最后,吴恩达老师将分享他对行业变化的见解,并为你的持续学习提供个人建议。
课程回顾与框架介绍


恭喜你完成了数据工程专业证书课程。希望你已经爱上了数据工程,并对在工作中应用所学知识充满信心。
在之前的课程中,我们一直在Matt H.和我所著的书中描述的“生命周期”和“潜流”背景下学习数据工程。


我们定义生命周期阶段和潜流的目标,是提供一个能够经受时间考验的框架。即使支撑生命周期每个阶段的基础工具和技术在不断变化和演进,这个框架依然有效。
数据团队的角色演变
在整个课程中,我们一直在数据团队的背景下审视数据工程师的角色,例如与软件工程、数据科学和机器学习等角色的关系。
然而,随着数据日益紧密地嵌入每一个业务流程,软件工程、数据工程、数据科学和机器学习之间的界限正变得越来越模糊。数据和算法领域将会出现新的角色。
虽然无法预测未来,但一种可能性是:随着机器学习工具集变得更易于使用和管理,云机器学习服务能力不断增强,数据工程和机器学习工程将融合在一起。机器学习正从临时的探索和模型开发,转变为一门运营学科。
这个融合角色的主要目标将是创建或利用能够自动训练模型、监控性能并使整个机器学习流程投入运营的系统。对于已被充分理解的模型类型,他们还将监控数据管道和质量,这与当前数据工程的领域有所重叠。
今天的机器学习工程师可能会变得更加专业化,专注于研究性质更强、尚未被充分理解的模型类型。
软件工程与数据工程的交汇
另一个职称可能发生变化的领域是软件工程和数据工程的交叉点。融合了传统软件应用与分析功能的“数据应用”将推动这一趋势。
因此,工程师需要对数据工程有更深入的理解。他们将发展在流式数据管道、数据建模、数据质量等方面的专业知识。数据工程师将被整合到应用开发团队中,而软件开发人员也将掌握数据工程技能。
应用后端系统和数据工程工具之间的界限也将被打破,通过流式和事件驱动实现深度集成。


数据工程的未来工具与技术趋势
在数据技术以令人疲惫的速度持续演进之际,数据工程的工具和技术将走向何方?
我认为数据工程领域将持续的一个主要趋势是:向简化易用的工具发展,并追求跨应用和系统的互操作性。抽象化可以简化开发流程,让工程师能更专注于解决复杂的、高附加值的问题,而不是管理底层基础设施和代码。
因此,如果工具变得更易用,数据工程师将向价值链上游移动,专注于更高层次的工作。
我还认为,流式管道和实时分析数据库将变得更易获取和普及。虽然这些技术已存在一段时间,但随着快速成熟的托管云服务,以及对流数据业务用例的清晰聚焦,你将能更轻松地部署它们。
当然,批处理转换不会完全消失。批处理对于模型训练、季度报告等场景仍然有用。但流式转换将成为常态。






人工智能与数据工程工作流的整合
现在,谈谈将人工智能整合到数据工程师工作流中的热门话题。似乎每家公司都想做人工智能(无论那具体意味着什么)。
当我深入研究那些能产生实际影响的人工智能用例时,像 GitHub Copilot 这样的工具浮现在脑海中。对许多公司来说,它是从人工智能中获取价值的“入门途径”。这就像为你的工程团队配备实习生和初级工程师,他们非常有说服力,但常常需要重写他们的代码。不过大多数时候,代码输出是过得去的,或者至少最终是可用的。
但有一个问题:我不认为人工智能辅助的工作流会很快取代手写代码,因为工程师仍然更喜欢编写代码。工程师想要“工程”。敲击键盘对工程师来说是一种非常具体且宣泄的体验。人工智能编码助手和智能体工作流,在大多数时候只是让完成工作变得更容易。
以上是我预计在不久的将来会在数据工程领域看到的一些趋势。
给学习者的建议与鼓励
最后,我想给你一些建议,供你在独自探索这个领域、努力构建数据工程技能和基础时参考。
我鼓励你着手进行自己的数据工程项目。在课程资源部分,你可以找到一些数据工程项目示例的链接,它们可以作为灵感来源,还有一些你可以深入阅读的博客,以继续自我教育。
同时,我鼓励你作为社区的一部分继续交流。参加线下聚会,提出问题,并分享你自己的专业知识。你还应该通过阅读书籍、博客文章、论文,以及聆听领域专家的演讲来了解最新发展,这能帮助你发现热门技术和实践的优势与陷阱。
课程到此结束,你已经完成了!
但如果你有兴趣听取行业专家的更多见解和职业建议,请查看本课程的最后一个可选课时。在那里,你会找到我与一些朋友(包括Jack Wilson, Carly Taylor和Ben Rojan)录制的访谈,他们谈论了如何开启数据领域的职业生涯,并提供了个人的技巧和建议。
感谢你与我一同踏上这段数据工程之旅。我祝愿你在职业生涯中一切顺利,并迫不及待想看到你运用在本课程中学到的知识所构建的成果。

总结

本节课中,我们一起回顾了整个数据工程课程,探讨了数据工程、软件工程、数据科学和机器学习等角色界限的模糊化趋势,以及未来工具向易用性、互操作性和流式处理发展的方向。我们还讨论了人工智能在工程工作流中的辅助作用,并获得了持续学习和参与社区实践的建议。希望这些内容能为你的数据工程之路提供清晰的指引和持续的动力。
039:与Zack Wilson的对话 🎙️
在本节课中,我们将与资深数据工程师Zack Wilson进行对话,探讨如何进入数据工程领域、如何规划职业发展、如何提升技能以及行业未来的趋势。Zack拥有在Facebook、Netflix和Airbnb等顶尖科技公司超过十年的数据工程经验,他将分享宝贵的实战见解。
1. 进入数据工程领域 🚪
上一节我们介绍了课程概述,本节中我们来看看如何迈出进入数据工程领域的第一步。Zack指出,数据工程是一个特殊的领域,初级职位相对较少,因为许多公司通常只需要一名能够独立负责数据管道的工程师。
以下是进入该领域的几种常见路径:
- 从相邻角色切入:许多人是先成为软件工程师或数据分析师,再转向数据工程。这些角色的技能有大量重叠(约70%),且初级职位更多。
- 掌握核心技能组合:无论从哪个角色切入,都需要掌握一套核心技能。这包括用于粘合服务的Python和Bash脚本,用于数据操作的SQL,以及像BigQuery、Airflow或Spark这样的云服务与技术。
- 构建完整管道:最终目标是学会如何将这些技术组合起来,构建可靠的数据管道。这是数据工程师的核心价值所在。
2. 成为优秀数据工程师的标志 ✅
在了解了入门路径后,我们来看看如何定义“优秀”。Zack认为,一个关键的转折点是从关注“如何构建管道”的技术细节,转变为思考“为何要构建这个管道”及其业务影响。
一个优秀数据工程师的重要标志是懂得何时拒绝构建管道。许多分析团队会提出大量一次性的数据需求,如果工程师总是说“是”,会导致资源浪费和 burnout。学会评估需求的价值,将精力集中在可重复、高影响力的项目上,是职业成熟的表现。Zack分享道,在Airbnb学会说“不”后,他才获得了更多时间进行创造性工作和个人发展。
3. 给年轻自己的职业建议 💡
基于丰富的经验,Zack为初入行的工程师提供了几条关键建议。
以下是几条核心建议:
- 主动展示你的工作:你的工作成果不会自动为你代言。你需要主动地展示和推销自己的工作。这不仅是获得认可的方式,也是获取反馈、促进协作的途径。
- 尽早学习关键技能:要有前瞻性,识别并尽早学习像Spark这样有潜力的关键技术。等待“合适的机会”可能会耗费比预期更长的时间。
- 识别成长停滞的信号:要能识别自己是否处于无法成长的角色中。如果工作内容重复(例如只写SQL查询),缺乏挑战,就需要主动寻求改变,比如转向能接触像Kafka、Flink等前沿技术的团队或项目。
4. 在工作中持续学习与成长 📈
持续学习是技术领域的必修课。Zack分享了他在职期间保持技能更新的策略。
主要策略包括主动寻找能挑战“3V”(数据量Volume、速度Velocity、多样性Variety)边界的机会。例如,在Netflix时,他主动请缨帮助处理公司最大的、日处理量达2PB的网络流量管道。此外,积极寻找公司内从事前沿技术(如实时流处理)的团队并参与其中,也是快速成长的有效方式。关键在于不要完全依赖经理分配任务,而要主动发现机会并向经理阐明其价值。
5. 如何判断工作的价值?⚖️
从被动执行到主动寻找高价值工作,是职业进阶的关键。Zack根据职业阶段的不同,提供了两套价值判断启发式方法。
- 职业早期(初级到高级):优先考虑可衡量的直接影响。例如:“我通过优化数据模型,将查询效率提升了30%”或“我创建的新数据集帮助业务指标提升了X%”。
- 职业后期(高级以上):追求杠杆化或横向影响。重点从“做什么工作”转向“如何改变工作方式”。例如,优化一个部署流程,将每个工程师的等待时间从8分钟缩短到1分钟,然后将此优化推广到整个团队甚至公司,其累积效应是巨大的。识别这类机会并做出正确的优先级判断,是更高阶工程师的价值所在。
6. 数据工程的未来展望 🔮
最后,让我们展望一下数据工程领域未来五年的可能趋势。Zack和课程讲师分享了几点看法。
未来趋势可能包括:
- 服务所有者与数据工程融合:如果后端服务开发者能更好地负责其服务产生的数据(日志、事实数据),那么数据工程师和软件工程师的角色界限会进一步模糊,可能出现更多“软件工程师(数据方向)”的混合角色。
- 分析工程师角色扩展:更贴近业务、专注于将已清理数据转化为指标、仪表板和实验的分析工程师角色将会扩大。
- 应对非结构化数据与LLM:随着大语言模型(LLM)的兴起,处理非结构化数据(如大段文本)的需求激增。如何评估这些非结构化数据的质量、应对LLM幻觉等问题,将是需要大量创新的领域。
- 技术栈的影响:公司的技术栈也会影响角色定义。拥有JVM后端技术栈的公司更容易实现软件与数据工程的融合。

本节课中,我们一起学习了从进入数据工程领域到规划长期职业发展的完整路径。关键点包括:通过相邻角色切入、掌握核心技能组合、懂得判断工作价值以避免 burnout、主动学习与展示、以及根据职业阶段调整价值判断标准。最后,我们对行业融合、分析工程兴起以及LLM带来的挑战等未来趋势进行了探讨。希望这些来自一线实战的经验能对你的数据工程职业之旅有所启发。
040:数据工程(数据建模、转换和服务)第4课,完结篇 🎓
章节:与Carly Taylor的职业对话 💼
在本节课中,我们将与Carly Taylor进行一场关于数据科学职业发展的对话。Carly是《使命召唤》的机器学习专家,同时也是一位全职的内容创作者。我们将探讨她进入数据领域的历程、日常工作、团队角色划分以及给初学者的宝贵建议。
对话开场与背景介绍
很高兴见到你,Carly。我也很好。
对于正在学习的同学们,这是我的朋友Carly Taylor。对于那些不认识你的人,你愿意做一个简短的自我介绍吗?
我的介绍可能无法简短。简而言之,我在《使命召唤》从事机器学习工作,同时我也是领英和Instagram上的全职内容创作者。我们即将讨论的话题之一是职业建议。我认为你会亲切地向新手和专家 alike 提供职业建议。
进入数据领域的历程
也许可以先谈谈你进入数据领域的历程?你是如何进入这个领域的?
首先说明,我的一些建议可能比另一些更好。所以请自行斟酌风险。
我和许多有STEM背景的人一样,在完成研究生学位时进入了数据科学领域。当时,我的许多朋友被金融科技公司雇佣,担任一个名为“数据科学”的新角色,主要是帮助进行定量分析和交易。那是在这个领域还很新的时候。我觉得这很有趣,听起来很令人兴奋,并且能很好地运用我的技能,因为我发现找一份化学家的工作相当困难。
你的背景是化学?是的。
然后你进入了数据科学领域。是的。
你是如何做到这一点的?
我实际上是从一家营销公司的营销分析师开始的。他们正在寻找具备我这种技术技能的人,并且愿意忽略我对营销几乎一无所知的事实。
这是一个很好的组合。但我想这是进入数据职业的一种常见方式,你可能不是领域专家,但拥有某种定量技能或工程能力,然后被雇佣等等。但你是如何花时间学习营销知识以提高工作能力的?
有很多是在职培训。那是一家营销咨询公司,他们的工作方式非常专有,所以我认为这对我帮助很大,因为他们意识到并习惯于在工作中为你进行他们特定领域的大部分培训。
我做的很多准备工作,实际上并没有帮助到我。如果我能重来一次,我会更认真地学习一些商业课程,并提前尝试学习一些行话。比如,什么是损益表?我当时完全不知道这些是什么。我的背景是非常硬核的科学,所以很多时候是“要么沉下去,要么游起来”,需要即时谷歌搜索。
然后你学会了所有的商业行话,比如“回头再聊”。哦,天哪,是的。然后你会想,哦,不,他们说要“回头再聊”,我现在该怎么办?这种情况经常发生。
机器学习工程师的日常工作
现在你已经在动视暴雪的《使命召唤》团队从事机器学习工作。作为一名工程师,你团队典型的一天是怎样的?
这真的取决于你是哪种工程师。对于我的机器学习工程师来说,他们的一天从站会开始,与项目经理讨论他们一直在做的工作。然后他们会深入研究他们正在处理的任何问题。我敢说,他们大约70%的时间都在进行探索性数据分析,只是为了弄清楚我们有什么、没有什么、我们需要什么。然后大约20%的时间用于建模,10%的时间用于投入生产和监控。但大部分时间只是实实在在地处理数据。
你的工程师们会和数据工程师一起工作吗?
我们有一些专门的DevOps人员。在另一家公司,我可能会称他们为数据工程师。在我们公司,我想他们被称为软件工程师,但他们主要做DevOps。我们团队中有些人可以灵活地担任这个角色。机器学习工程师也被期望具备一些数据工程技能。至于在流水线中交接发生在哪里,这取决于在任何一天谁更有主人翁意识。
这似乎总是一个模糊的领域。我认为这也是我希望学习者意识到的一点:在现实世界中,职位头衔可能在某些情况下非常模糊。正如你指出的,你团队的软件工程师可能在做数据相关的工作,机器学习工程师也可能在做数据相关的工作,数据工程师也可能在做机器学习相关的工作。
在你看来,数据工程师和机器学习工程师有什么区别?
我认为,对于一个数据工程师,我不会期望他能够构建一个端到端的机器学习模型。我会期望他能够从确保数据在流水线中无误、没有数据丢失、没有无意义的转换等角度来深入挖掘数据。但我不期望他理解数据在现实世界或业务影响方面的意义。
对于机器学习工程师,他们的工作是在业务背景下对数据进行合理性检查,然后在此基础上构建预测模型。所以我认为,数据可用性更多是数据工程,而数据理解对我来说更多是机器学习工程。
机器学习应用实例
你能给学习者举一些你在《使命召唤》中进行的机器学习类型的例子吗?
这真的、真的取决于具体问题。凭记忆来说,我们使用计算机视觉。我们做了很多异常检测,因为我从事安全领域的工作。所以,你能想到的任何大银行做的事情,比如欺诈分析和检测,寻找数据中的异常值,寻找数据集中的离群点,这基本上是我们的核心工作。
从个人贡献者到团队领导者的转变
你从个人贡献者一路晋升到现在管理一个团队。你能向学习者介绍一下从IC(个人贡献者)到领导者的转变过程吗?
这是一个很有趣的问题,因为我认为它总是很有争议。人们常常会问,我什么时候应该转向管理?或者我应该吗?我进入数据科学是因为我喜欢工作的技术方面。放弃这些去追求更多的管理职位是令人害怕的。所以对我来说,这个过程看起来像是在某个领域缺乏领导力时挺身而出,而我们恰好需要它。所以我认为,我进入管理层是出于必要性,而不是我有一个严格的计划。
但你常常会在职业生涯中发现,当你涉足那些可能缺乏某些东西的领域时,就是你成长的时候。但当你发现自己进入了一个你可能从未想过会进入的领域,只是因为你看到了某种需求而采取了行动。
在职业成长方面,你会给年轻的自己什么建议?
给自己多一点宽容,不要总是觉得自己必须知道一切,也不要感到有压力必须时刻跟上数据科学和机器学习领域发生的一切。当我刚开始的时候,我压力很大,经常把自己累垮,总是试图跟上所有领域的最新和最伟大的东西。这很累人,因为这个领域太广阔了。
所以我会更早地选择一个专业方向。现在我身处安全领域,异常检测世界,这个领域是我的专业方向。我喜欢在那个特定领域保持更新。但当我年轻的时候,我不理解“T型”理解水平的概念。我当时追求广度而不是深度。
你能向学习者解释一下“T型”概念吗?
当然。如果你把数据科学领域想象成在某个x轴上极其广阔,那么在任何给定的主题上,你只能深入那么深,对吧?如果你追求广度,你会在横向上发展。如果你追求深度,你会在纵向上发展。
我认为一个好的数据科学家具有“T型”经验水平。你可能对很多不同的事情都知道很多,但对其中任何一个主题都没有深度,除了那个你选择深入的专业领域。所以你的专业知识可能看起来更像一个“T”形,并且会集中在你发现是你专业领域的那个区域周围。所以你可能甚至对远处的东西一无所知,因为你的“T”没有延伸到那么远。这没关系。
不是每个人都必须知道所有事情的一切。事实证明,确实如此。尤其是现在有了AI,感觉每天都有新的进展,想要跟上所有新论文、新产品是不可能的。
给求职者的建议
对于那些试图找到第一份数据工作的人,你有什么建议或技巧吗?
至少让五个不同的人看你的简历,而且不要每次都找同样的人。如果你发现你的简历没有效果,就修改它。不要对你简历上的任何东西过于珍视,以至于如果它不起作用,你也不愿意删掉它。
我看到很多人,尤其是转行的人,因为我自己也经历过,觉得无法放弃之前职业生涯中的成就,因为那些对他们来说是定义职业生涯的时刻。作为一名化学家,我在顶级期刊上发表了很多出色的论文。猜猜什么不在我的简历上了?我不得不删掉它们,因为这对我的面试官来说不一定重要。当然,我可以在面试中提到,是的,我在一些前十的期刊上发表过文章。这实际上提出来是很好的。但就简历空间而言,这是在讲述我需要讲的故事,还是只是我因为无法放下过去的成就而感到不舍?因为当你进入一个新领域时,你真的没有那些成就了。感觉自己像个新手很难受,但有时你不得不放手。
如果你没有任何成就呢?那就更容易了。你只需要硬着头皮上。这种情况经常发生。
现在的就业市场,正如我们录制这段视频时,竞争总是很激烈,尤其是对于新人。你会说这些天竞争异常激烈。你建议听众如何在竞争激烈的就业市场中脱颖而出?
我一直在思考这个问题。对于求职者,我要说现在有一个新趋势,就是在获得面试之前提交视频。现在有很多平台会要求你通过视频回答问题,然后提交给公司。我看到很多人抱怨这一点,并且不想这样做。我理解当你感到不知所措,有很多事情要做时,坐下来录制视频是你最不想做的事情。
但我要说,利用这一点来发挥你的优势,因为这是你从所有其他人中脱颖而出的机会。不要只是照本宣科,要看起来专业,有好的灯光。我看到的99%的这些视频都是低努力、低质量的。你可以看出他们不在乎,也可以看出他们是在照着ChatGPT提供的答案念。
如果你哪怕只多做一点点,比如穿上一件好看的衬衫,你就会脱颖而出。所以,如果你开始做得比一般人好5%,你就会开始看到自己领先。你不必总是做到100%,你只需要比大多数人好就行。
这真是很好的建议。就像那个关于如何在熊的攻击中生存的建议:你不必是你小组里最快的人,你只是不想成为最慢的那个,对吧?
最后的鼓励
关于职业方面,你还有什么其他建议想给学习者吗?
我认为你已经参加了这门课程,这表明你对自己很认真,并且正在尽一切努力为自己铺就成功之路。对自己友善一点,给自己宽容,不要总是要求自己表现100%。即使在那些你只是出现了的日子里,那也比0%要好。
进入一个新领域很难。自学新东西很难。说服别人你已经做了所有这些工作,他们应该冒险雇佣你,甚至更难。所以,如果在任何时候,任何一点让你感到不知所措,没关系,你会挺过去的。只要坚持下去。
总结
本节课中,我们一起学习了Carly Taylor的职业历程和宝贵见解。我们探讨了如何进入数据科学领域、机器学习工程师的日常工作、数据工程师与机器学习工程师的角色区别,以及“T型”技能发展的重要性。我们还获得了关于简历优化、在竞争激烈的市场中脱颖而出以及保持学习动力的实用建议。记住,持续学习、保持专注并对自己保持耐心是职业成功的关键。
041:从入门到进阶 🚀
在本节课中,我们将跟随资深数据工程师本·罗格扬(Ben Rogojan,又名“西雅图数据侠”)的分享,了解数据工程领域的职业发展路径、核心技能以及如何成为一名成功的数据工程师。我们将探讨从数据科学转向数据工程的历程、在大公司(如Facebook)的工作体验,以及给初学者的宝贵建议。
职业起点:从数据科学到数据工程 🔄
上一节我们介绍了本·罗格扬的背景,本节中我们来看看他如何从数据科学领域转向数据工程。
本最初计划成为一名数据科学家。他在大学期间将许多课程转向生物信息学或统计学领域。毕业后,他开始从事数据科学工作,但逐渐发现自己更喜欢软件和编程方面的工作。他是在寻找新工作时,偶然发现了数据工程这个领域。
他当时只是输入了自己掌握的技能,如 SQL、Python、自动化、数据仓库等,从而获得了第一份正式的数据工程师职位。他当时在一家医疗保健分析初创公司工作。
早期实战:处理医疗数据 🏥
在初创公司的经历是数据工程实践的起点。以下是当时的主要工作内容:
- 数据来源:处理来自40或50家不同保险提供商的数据集。
- 核心任务:标准化这些数据。虽然医疗索赔数据本身是标准化的,但接收到的数据格式各不相同。
- 技术挑战:学习处理各种数据分隔方式,例如位置分隔字段或位置空格字段。这通常需要依赖模式文件来跟踪所有字段。
- 系统与产品:围绕这些数据构建系统,并在其上开发产品。例如,曾构建欺诈检测产品和阿片类药物追踪产品。
- 技术栈:主要使用 PowerShell 和 SQL Server 等工具。
平台跃迁:Facebook的成熟体系 🏢
从初创公司到Facebook,工作环境和挑战发生了显著变化。
Facebook的数据基础设施非常成熟。对于许多工程师来说,这可能显得有些“枯燥”,因为大部分困难的基础问题已经解决。并非每个人都在处理所谓的“大数据”问题,那只是很小一部分团队的工作。
在本的工作中,很多时候是编写封装了SQL的Python脚本,然后将其部署到某处。这更多地关乎沟通和发现值得解决的问题,而不仅仅是编码能力。所有困难的基础设施问题似乎都已得到解决。
成功关键:沟通与问题识别 🎯
在成熟的技术环境中,技术能力是基础,但区分优秀工程师的关键在于其他方面。
Facebook有数千名数据工程师。如果只做别人吩咐的事情,很难脱颖而出,也无法对业务产生实际影响。因此,本和他的团队会花大量时间与合作的团队沟通,了解他们正在解决或试图处理的问题,而不仅仅是数据问题。
他的经理曾指出:你不会因为解决了一千个任务而获得晋升,但会因找到方法消除一千个重复任务而获得晋升。不断重复出现的大问题是一个信号,表明需要思考如何从根本上解决它。这也是Facebook能够构建Presto和Hive等成熟基础设施的原因——他们看到了MapReduce的痛点(成本高、速度慢、迭代慢),并通过构建上层抽象让所有人的工作变得更轻松。
本在解决问题时也常采用这种思路:如何消除这个更大的根本性问题,而不仅仅是处理日常的小问题。
给初学者的建议:路径与心态 🧭
对于有兴趣进入数据工程领域的人,本给出了以下建议:
打好技术基础:无论从何处开始,都需要从技术层面入手。你需要学习 SQL、Python、数据仓库的概念以及如何构建数据管道。乔·赖斯(Joe Reis)的书籍是一个很好的起点,可以帮助你了解这个领域存在什么,以及从哪里开始。
深入理解“为什么”:在掌握技术的同时,更需要理解其背后的原因。要不断追问:我们为什么要构建数据管道?为什么要把数据从一个地方搬到另一个地方?仅仅是为了分析吗?业界在数据摄取(EL)上花费了巨大的成本和时间(可能有50亿年之多)。建立扎实的技能,并开始追问为什么需要这些技能,以及它们究竟为业务带来了什么价值。
从技术向业务延伸:职业生涯早期,应专注于提升工程能力,例如熟练构建管道。但同时,要开始提出探究性的业务问题:我们如何赚钱?这个管道是做什么用的?随着技术自信的增长,可以更多地向业务方向倾斜,与非技术人员交流。如果只与技术同行交流,很容易陷入自己的“气泡”,追逐酷炫的新工具,但这些工具解决的问题往往与核心业务无关。最终需要转向思考:我们给业务带来了什么?实时数据真的有实际好处,还是仅仅因为它听起来很酷?
行业洞察:定义与未来展望 🔮
数据工程领域的一些概念需要清晰定义,而未来发展则需回归基础。
厘清“实时”的定义:“实时”是一个定义不断变化的概念。当有人提出需要“实时”数据时,必须追问他们具体指什么。本曾遇到过客户所谓的“实时”实际上是指每12到24小时,甚至有一个月更新一次的极端案例。因此,作为优秀的数据工程师,必须通过提问来明确需求:你说的“实时”到底是什么意思?
未来的关注点:本希望行业能继续朝着构建长期可靠、可用的数据集的方向努力。即使未来所有数据都存在于某个同质化的数据源中,数据管理的问题依然存在。目前,像Tabular、Databricks等多引擎数据架构在理论上很有效,但现实中公司的数据栈往往非常分散(亚洲用BigQuery,美国用Snowflake,营销用Redshift),很少有公司能妥善实施统一架构。
随着AI的普及,数据管理和数据质量等基础问题正得到重新审视。虽然有人认为可以把AI套用在一切事物上,但许多企业数据集的质量并不理想,这是一个重大挑战。因此,未来需要回归并扎实实施这些数据基础。
总结与资源 📚
本节课中,我们一起学习了资深数据工程师本·罗格扬的职业经验与见解。
我们回顾了他从数据科学转向数据工程的历程,了解了在医疗初创公司处理复杂数据的实战经验,以及在大公司(如Facebook)工作所强调的沟通与问题识别能力。对于初学者,核心建议是:打好SQL、Python和数据管道等技术基础,同时不断追问每个技术决策的“业务原因”,并学会与非技术人员沟通。最后,我们探讨了如何明确定义需求(如“实时”),并认识到无论技术如何演进,数据质量和可靠的数据管理始终是基石。
如果你想了解更多关于本·罗格扬的内容,可以在 YouTube 或 Substack 上搜索“Seattle Data Guy”。他也在LinkedIn等其他平台活跃。

浙公网安备 33010602011771号