有史以来最极化电影的数据挖掘
使用 Python 和统计学寻找有分歧的电影
作者图片
有些电影你要么喜欢要么讨厌,没有折中。我们都可以想到一些分裂朋友和家人的电影,但有些人甚至努力整理了列表中最分裂的电影,如*【房间】、波拉特和拿破仑炸药*。对于这些电影,肯定普遍缺乏共识,但它们真的是有史以来最两极分化的电影吗?
IMDB 显示,超过一半(57.3%)的《房间》的评分要么是 1 分,要么是 10 分,虽然这肯定是一部很少有人能同意的电影,但我很好奇我们是否能做得更好。使用来自 IMDB.com 的 API 的数据,这一分析将挖掘成千上万部电影,以找到有史以来真正最极化的电影。
数据
IMDB,或者说是为门外汉准备的互联网电影数据库,是一个拥有数百万用户评分的网站。它也是一个 API 的所在地,允许开发者提取这些数据并在各种应用中使用,比如为其他网站提供后端数据,或者在这种情况下,寻找两极分化的电影。IMDB 的文档中列出了几十个 API 调用来获取不同类型的数据,但“UserRatings”调用可以让用户在一个请求中获取一部电影的所有用户评级,如下面的社交网络所示。
然而,对于这些调用,我们需要每个电影的 title_id。这可以在每部电影页面的 url 中找到,但 IMDB 也非常有用地提供了从每个标题到文件中每个 title_id 的映射,title.basics.tsv.gz可以在他们的网站上下载。不幸的是,这个文件包含了 IMDB 上的一切(电视剧集、短片、视频游戏、迷你剧集等。)而且数据质量可能相当参差不齐。经过一些数据探索,我写了下面的代码片段来转换。IMDB 提供的 tsv 文件转换成更干净,更有用的。用于此分析的 csv 文件。
下面是用于电影到 title_id 映射的已清理数据的示例。
作者图片
有了正确的 movie-to-title_id 映射和正确的 API 调用来获取每部电影的收视率数据,然后我创建一个助手函数,它将获取给定电影所需的所有相关信息。这个函数对 IMDB 的 API 进行初始调用,将响应转换成 JSON 对象,然后从该对象解析出相关信息。
调用 API 的函数
通过遍历我清理的电影数据中的所有 281,459 个 title _ ids 并调用上述函数(以及一些错误处理),我们最终得到如下所示的数据帧:
作者图片
总之,由于进行每个单独调用所花费的时间长度以及 IMDB 对 API 调用的强制每日限制,提取这些数据需要大约两周的时间。
衡量标准
接下来,我希望找到一个衡量标准,在给定评级分布的情况下,从几十万部电影中找出最两极分化的电影。幸运的是,之前的研究(检测收视率的两极分化)已经解决了这个问题。不幸的是,研究人员通过特征化电影评级分布来解决这一问题,让“多名专家[手动]将每个项目分为极化或非极化类别”,然后在这个带标签的数据集上训练随机森林,最终计算每个分布的极化分数。因为我在 IMDB 的 API 上花费的 40 美元让我超出了预算 40 美元,所以我需要一种不同的方法。
经过一番头脑风暴,我想出了 3 个可能的指标来检测一个极化的 U 型分布:
标准差:方差的平方根,即均值的方差的平均值。
下面我定义了三个函数,它们将获取汇总的评级信息,将其转换为数据分布,并计算该分布的汇总指标。将这些新特征添加到数据集中后,我检查了每个指标的极端最高值和最低值,以找出哪一个最能捕捉分布中的两极分化。注意:感谢 GitHub 用户 Olivia Guest 已经编写了一个从 numpy 数组计算基尼系数的函数。
创建指标的辅助函数
顺便提一下,与 for 循环相比,将这些函数作为 DataFrame.apply()方法的输入确实有助于加快在这种规模下显示数据的速度。当所有这些功能完成后,我绘制了至少有 5000 张选票的电影(以减少噪音),以及每个候选指标的最高或最低值:
最极化的电影
作者图片
最不两极分化的电影
作者图片
查看每个指标的定义以及每个指标中最极化和最不极化电影的两个图表,我选择使用峰度作为评论中极性的主要衡量标准。对我来说,它似乎在挑选两极分化的电影方面做得最好,喜欢或讨厌这些电影的人几乎各占一半,另一方面,它似乎发现有一致共识的电影比标准差或基尼不平等要好一点。
电影
在我看来,要让一部电影具有分裂性,必须有相当多的人不同意它,但 IMDB 上的绝大多数电影只有很少的评级(一部电影的评级中位数是 85)。为了找到有史以来最两极化的电影,我限制了 IMDB 上投票最多的前 5%的电影。如下图所示,这将我们限制在超过 8,049 票的电影。
作者图片
随着峰度作为我的极化指标和投票阈值的最终确定,我现在可以宣布有史以来最极化的电影是… 库马利·塞伯 2 !总共 10,736 张选票中的 10,326 张(超过 96%!)无论是 10 分还是 1 分,这部电影的观众都有着难以置信的分化。这部电影讲述的是库马利处理他祖父所在社区的养老院可能被拆除的异常情况,受到了批评,比如“这是我看过的最糟糕的电影”。我甚至不能完成它、这部电影是在浪费时间。看空烟囱更有益,”但显然不是每个人都有这种感觉。
作者图片
以峰度衡量,第二部最两极分化的电影是 国家之死:我们能第二次拯救美国吗? 。这部电影比较了唐纳德·特朗普和亚伯拉罕·林肯担任总统期间的政治气候。显然,它还将民主党与纳粹党相提并论,这就很容易理解为什么这部电影如此两极分化。
作者图片
以一种更轻松的方式结束,我给 一个方向:这是我们 在这些有争议的电影奖项中获得荣誉奖。一个 10 星评论家评论说“显然是 10/10,爱 1d 和想念他们很多。此外,哈利可以得到我的心,zayn 的声音是另一回事”而一位一星投票者回应道“没有任何语言可以解释这部电影有多糟糕。绝对难以置信。
作者图片
感谢您的阅读!要获得所有中型文章的完整访问权限,请点击此处!
数据建模 101:它是什么?
什么是数据建模,如何使用,如何实践?
托拜厄斯·菲舍尔在 Unsplash 上的照片
不久前,我在一家受欢迎的航运公司的数据架构部门实习。去实习的时候,我甚至不知道那意味着什么。我后来发现,数据架构的很大一部分被称为数据建模。
在那里的时候,我学会了设计数据库,映射这些数据库中的列和表之间的关系,最重要的是,在一个图表中可视化这一切。在最近的一次工作面试过程中,其中一次技术面试只关注数据建模,这让我重新审视了所有这些。
为了准备这次面试,深入挖掘四年前我在实习期间学到的一切,这是一次很好的学习经历。如果你也遇到围绕这个话题的技术面试,以下是你需要知道的。
什么是数据建模?
数据建模是为将来会存储在数据库中的数据创建数据模型的过程。好吧,那么什么是数据模型呢?数据模型将数据组织成表,并描述这些表之间的关系。
例如,假设我们有一个服装店,需要创建一个数据库来存储库存和客户信息。您将创建一个包含各种表的数据模型,比如采购、客户信息、项目详细信息和库存信息。
我们将所有这些数据组织到表中并定义它们之间的关系的原因与访问数据的方式有关。将信息拆分到不同的表中,使得系统在检索这些数据时更加高效。
数据模型的类型
有三种不同类型的数据模型,每一种都具有一定的复杂性。
- 概念数据模型
这是三个中最不专业的。它提供了您需要的不同表(也称为实体)以及该表中潜在的列(属性)的高级概述。
2.逻辑数据模型
这是一个更复杂的技术问题。该模型将包括实体之间的关系以及属性和实体的数据类型。
3.物理数据模型
这是在生成数据库本身之前创建的最后一个数据模型。这突出显示了数据库的实际模式,并包括前面的数据模型中提到的所有信息。
关系的类型
我前面提到过,数据模型可视化了实体之间的关系。数据模型中可以使用四种不同的关系。
- 一对一
这意味着对于另一个实体的每个实例,只能存在一个实体。以我的经验来看,这种关系相当罕见。
2.一对多和多对一
这意味着一个实体可以有另一个实体的许多实例。例如,一个雇主可以有许多全职员工,但每个员工只能有一个全职雇主。
3.多对多
这意味着一个实体的许多实例可以有另一个实体的许多实例。例如,许多航空公司可以有许多客户,许多客户可以有他们坐过的许多航空公司。
主键和外键
在实体之间创建任何类型的关系时,您总是会有一个主键和外键。实体的主键是实体的唯一标识符。这通常是某种身份证号码。一个实体中的外键是另一个实体中的主键。
换句话说,只要另一个实体中有一个属性是另一个实体中的主键,就应该将其标记为外键。下面有一个例子可以帮助理解这个概念。
创建数据模型
创建数据模型时,理解数据是关键。跟踪哪个领域很重要?这个数据库的目的是什么?回答这些问题将使建模过程变得更加容易。
- 你的实体是什么?
让我们回头看看我们的服装店例子。我们首先要确定我们的实体或表是什么。想想不同的更广泛的数据类别。我们需要一个购买实体来跟踪哪些客户购买了哪些商品。我们希望为客户提供一个实体,存储他们所有的个人信息,如姓名、地址和电话号码。我们还希望有一个实体来跟踪每件商品的信息,如唯一的 id,颜色,尺寸和服装类型。最后,我们希望有一个库存实体,这样我们就知道每件商品还剩多少。
2.这些实体中需要什么属性?
现在我们已经创建了实体,我们需要弄清楚在这些特定的实体中需要哪些属性。让我们先来看看购买。以下是我想到的与购买相关的信息:
- 唯一的采购 ID
- 购买日期
- 购买时间
- 采购人员的客户 ID
- 正在购买的项目的项目 ID
- 购买总成本
作者图片
考虑哪个实体对每个属性最有意义是很重要的。考虑这些实体之间的关系也很重要。我们需要在这个购买表中包含 customerId 和 itemId,以访问客户和项目实体。这些将作为这个表中的外键和其他表中的主键。
此外,您还可以在此步骤中包括这些字段的数据类型。然而,这在面试中通常是不必要的。其他概念更需要证明。
现在测试一下你自己,看看你是否能填写我提到的其他实体的属性。不准偷看!
当你完成后,将你的结果与我下面的结果进行比较。
作者图片
3.所有这些实体是如何联系起来的?
现在,我们需要确保拥有将不同实体连接在一起所需的所有主键和外键。为了在一个查询中提取所有需要的数据,这是非常重要的。如果这些表不能正确地相互连接,您的数据库将不会有效或正确地构建。
让我们回顾一下主键和外键之间的区别。主键是实体的唯一标识符。客户 ID 充当客户实体的主键,项目 ID 充当项目实体的主键,采购 ID 充当采购实体的主键。
外键是在另一个实体中充当主键的字段,但不是您当前正在查看的实体。例如,customer ID 和 item ID 都充当 purchases 实体中的外键,因为它们是其他表的唯一标识符(主键),而不是 purchases 实体本身的唯一标识符。
作者图片
4.这些实体之间的关系类型是什么?
现在让我们重温一下我们之前讨论过的关系类型。我们需要弄清楚哪一个将适用于这些实体。
一次采购可以有很多客户吗?不,但是一个顾客可以有许多购买。客户和购买之间是一对多的关系。
作者图片
测试您的技能,并尝试将正确的关系添加到其余的实体中。完成后,向下滚动,将你的答案与我的答案进行比较。
作者图片
Item 与 purchases 有多对一的关系,因为一个采购可以有多个 item,但是一个 item 只能有一个 purchase。
库存与项目有一对多的关系,因为一个唯一的 itemId 只能有库存信息,但多个项目可以有相同的库存信息。
通过思考不同的日常场景,您可以继续练习这些数据建模问题。电影院如何创建数据库?杂货店怎么样?想一想您每天接触到的必须有数据模型的业务。通常这类问题没有正确或错误的答案,只要确保你理解了概念,并能解释为什么你创建了某个实体。
祝你好运!请务必查看我的其他一些文章,为技术面试做准备,如应对每个 SQL 面试问题的顶级技巧和如何使用 SQL 超前和滞后函数。
中端市场公司的数据监控和可观察性基准
现代数据堆栈的五大观察工具
图片来自 Castor 的网站
现代组织比以往任何时候都更多地生产、收集和处理数据。一项针对数据专业人士的 IDG 调查显示,数据量正以平均每月 63%的速度增长。随着业务中数据的激增,我们用来移动这些数据的技术变得更加错综复杂,以至于我们完全看不到数据是如何处理的。结果,随着数据的移动,错误越来越多,我们最终得到的是无用的数据。谢天谢地,可观察性工具在过去几年里蓬勃发展,帮助公司重新获得对数据处理的控制权。今天,我们试图理解数据可观测性的概念,并理清可观测性工具的活跃生态系统。
The 的可观测性概念起源于控制理论,由鲁道夫·卡尔曼首创于线性动态系统。我们把它定义为一个系统的健康程度可以从它的输出中推断出来的度量。
数据可观察性是数据管道发展的结果。数据管道指的是负责将数据从一个系统转移到另一个系统的任何一组处理材料。它们是数据分析和数据科学活动的支柱,因为它们生成数据科学家和数据分析师使用的数据资产。直到 15-20 年前,数据管道还是相当基础的,为业务分析的稳定需求服务。商业智能团队需要对他们的财务状况、库存水平、销售渠道和其他运营指标进行历史测量。没什么大不了的。数据工程师使用 ETL(提取、加载、转换)工具来转换特定用例的数据,并将其加载到数据仓库中。由此,数据分析师使用 BI 软件创建了仪表板和报告,生活变得很美好。
传统数据管道—图片由 Louise de Leyritz 提供
In:近年来,我们见证了对数据需求的飞速增长。从数据分析师到业务用户,数据无处不在,每个人都在使用它。
为了确保数据用户完美地理解数据仓库中存在的所有数据,我们建议使用数据目录,如 Castor 或 Collibra (取决于您的规模)。你可以在这里找到所有数据目录工具的基准。
数据管道现在使用复杂工具(Spark、Kubernetes、Airflow)的组合运行,随着互联部件数量的增加,管道故障的风险也在增加。工具的多样性是必要的,因为它允许数据团队在其数据堆栈的每一层选择最佳平台。但是所有这些引擎的组合使得实际上不可能看到管道的不同部分。
现代数据管道 Louise de Leyritz 图片
现代数据管道不仅复杂,而且还具有黑盒特性。你知道进去的是什么,出来的是什么,但你不知道中间发生了什么。只要出现想要的结果就好。但如果没有,那就非常令人沮丧。当数据集从管道中出来时,通常会留下奇怪的值、缺失的列、本应是数字的字段中的字母等等。结果,数据工程师花了几个小时绞尽脑汁想到底哪里出了问题,哪里出了问题,如何解决。Forrester 估计,数据团队将超过 40%的时间花在数据质量问题上,而不是致力于为企业创造价值的活动。所有这些都面临着巨大的压力,因为在客户演示中使用这种低质量数据的数据分析师正在被他的经理痛打一顿。这是最重要的部分。收集、处理和分析数据的全部目的是为了创造商业价值。如果没有对管道的可见性,错误就会累积,数据会恶化,业务价值也会被破坏。这就是数据可观测性技术的用武之地。它告诉你坐下来放松,因为它将揭示每个管道故障的根本原因。不再流泪,不再抓头,不再挨打。但实际上,它是如何工作的呢?
可观察性与监控——广告中的标识
数据监控经常与数据可观察性混淆在一起。这两个术语是共生的,这解释了它们之间界限的模糊性。数据监控是实现数据可观测性的第一步,也是可观测性的一个子集。比方说,你正在观察一个数据管道系统。监控的基本目的是使用一组预定义的系统指标和日志来了解您系统的状态,利用这些指标和日志来获取有关事件的警报。它要么告诉你“系统是健康的”,要么告诉你“有问题”。因此,监控应用程序允许您检测****已知的一组故障模式。它不能解决黑匣子的问题。通过监控,您可以:
- 跟踪错误并在错误发生时立即得到通知
- 跟踪关于给定微服务消耗的资源数量的度量。
- 当您的系统遇到中断或故障时获得警报。
数据监控会标记您的数据问题,这很好。不利的一面是,识别问题只是旅程的开始,在了解管道中断的根本原因时,监控并不顺利。
数据可观察性带来了一种更主动的方法,通过提供系统操作的更深入的视图。它使用监控产生的数据和见解来全面了解您的系统、其健康状况和性能。
比方说,你拥有一家杂货店。每天早上,食品杂货都陈列在货架上,供顾客挑选。简单。监控的作用是提醒你产品可能出现的任何问题:当货架上有丢失的商品时,当产品洒得到处都是时等等。可观察性更进了一步,让你清楚地看到货架上货物堆积背后的供应链。如果你很容易理解为什么货架上的一件商品不见了,如果你能看到后面的房间里发生了什么,或者员工是如何把商品放在手推车上的,那么你的杂货店就是“可观察的”。
在数据领域,可观察性揭示了数据管道中发生的工作流,使您能够轻松地从结果导航到原因。借助可观察性,您可以:
- 了解请求所经历的服务,并定位性能瓶颈****
- 了解请求失败的原因****
- 观察特定的微服务如何处理请求****
- 识别错误或瓶颈发生时的模式**,并利用洞察采取行动,以防止将来出现此类情况**
****监控是可观测性的子集,你只能监控一个可观测的系统。监控告诉你“你的管道出故障了。”而可观察性告诉您“您有一个管道中断,因为一个 spark 作业由于一个无效行而失败”
有哪些可观察性工具?
有三代可观察性工具:
****第 1 代:传统的 APM(应用管理性能)工具。这些工具是为软件工程师和开发人员创建的,但却是用于数据可观察性的第一批工具。
****第二代:数据可观测性工具。这些工具允许您深入数据问题的根源,并了解问题的根本原因。
****第三代:自动化数据观察。这些智能工具可以预测并自动修复问题,以免影响性能。
如果您正在寻找数据可观察性解决方案,我们在这里列出了所有工具及其产品。
可观察性 1.0:用于 DevOps 的 APM 工具
可观察性并不新鲜。在 DevOps 世界中,这是一个非常成熟的概念。现代组织从整体架构向微服务架构的转变导致了开发运维团队的兴起,这些团队消除了传统的开发和运营团队之间的障碍。DevOps 团队持续关注其系统的健康状况,确保应用程序和基础架构正常运行。可观测性的概念源于这一发展。
**DevOps 团队使用应用管理性能(APM)工具来监控他们的系统和基础架构。APM 旨在检测和诊断复杂的应用程序性能问题,以保持服务水平。这些工具集合了三个监控组件来发现和解决系统故障:**度量、日志和跟踪。这些通常被称为“可观察性的三大支柱”
软件监控的支柱—图片由 Louise de Leyritz 提供
****指标:指标是在给定时间间隔内测量的数值。它包括特定的属性,如时间、名称、KPI 和值。度量可以使用数学建模和预测来获得系统在一段时间内的行为知识。
****日志:日志是在特定时间发生的事件的文本记录。在日志中,您可以找到事件的注释、事件发生的时间以及事件的一些背景。
****跟踪:跟踪表示请求通过分布式系统的端到端旅程。即在不同微服务中执行的每个操作的记录。跟踪是日志的表示,单个跟踪可以提供对请求所经过的路径及其结构的可见性。
将这三个监控组件集成到一个解决方案中,使开发人员能够了解他们的系统。这些系统被称为可观测的,因为可以根据监测输出来推断它们的健康状况。
随着数据激增,需要有可观察的管道,数据工程团队使用如上所述的标准 APM 工具,试图获得对其数据堆栈的可观察性。
然而,APM 工具是专门为软件工程师和开发人员构建的,而不是为了监控数据管道。尽管一些公司将它们用作数据观察工具,但它们并不总是奏效。原因是,技术系统和数据管道天生不同。例如,对于数据管道,在进程成功运行之前遇到许多故障是完全正常的。APM 工具不能理解这一点,也不能理解数据管道逻辑的其他细微差别。使用 APM 工具来获得管道可见性的数据团队通常以错误的警报结束,这带来了不必要的混乱。
可观察性 2.0:数据管道可观察性
为了获得数据管道上的可观察性,除了上述标准集之外,还需要监控一组额外的维度。APM 工具并不是为了数据的可观察性而设计的,它们已经逐渐被更好的工具所取代,用来监控与数据管道健康相关的组件。
数据可观察性的支柱 Louise de Leyritz 的图像
****数据质量:数据质量有问题吗?您的数据集是否具有您期望的容量,或者是否有丢失的数据?你的数据分布正确吗?
****时间表:管道是否按计划运行?您的数据是最新的,还是更新计划中有故障?
****依赖关系:上游的数据问题将如何向下游传播?管道的不同部分是如何关联的?
这是接近数据可观察性支柱的一种方式。巴尔·摩西提出了另一个方案,她在其中概述了数据可观察性的五大支柱。可观测性数据“支柱”的数量并不那么重要。其思想是:您可以通过监视一定数量的组件来获得对堆栈的可观察性,这些组件将告诉您数据管道的健康状况。不同的可观测性解决方案将根据它们想要实现的可观测性的种类来监控管道的不同组件。这就是为什么选择适合您需求的解决方案非常重要。
可观察性 3.0:自动化数据可观察性
第三代可观测性工具拥有广泛的自动化特性。这意味着这些工具根据提供给它们的数据集调整它们的监控方法,自动决定在哪里设置警报阈值。通过这种方式,您可以拥有可观察的管道,而不必经历为每个数据资产定义警报阈值的烦人任务。
使用机器学习模型,现代数据可观察性平台,如 Bigeye 分析数据趋势,并自动推荐数据质量指标,以开始跟踪特定资产。您不必花时间定义对您的业务至关重要的所有指标和日志,因为这是自动实现的。更重要的是,现代工具可以预测数据质量指标,并根据预测自动调整警报阈值。这节省了时间,因为数据团队不必手动调整阈值。它还确保团队总是收到相关的警报。
一些解决方案,如 databand.ai 提出了自动化功能,如动态管道运行,旨在改善数据管道的性能。该功能使数据团队能够根据不断变化的数据输入、参数或模型分数运行不同版本的管道。
您的组织使用哪种可观察性工具?
下面,你会发现一个可观察性工具的图景,它可以帮助你选择一个适合你公司需要的可观察性工具。我们从两个方面对解决方案进行了分类:
- 它们是否允许实时数据监控
- 他们是使用管道测试框架还是异常检测框架来调查数据的健康状况。
****实时数据监控指的是解决方案是否能够识别数据管道中发生的问题,从而有可能在加载坏数据之前停止管道。
流水线测试与异常检测框架
并非所有的数据可观测性解决方案都是相同的。我们特别根据解决方案是使用管道测试还是异常检测框架来区分它们。
一个流水线测试框架允许数据工程师测试二进制语句。例如,列中的所有值是否都是唯一的,或者模式是否与某个表达式匹配。当测试失败时,数据被标记为“坏的”。有了这些信息,数据工程团队可以诊断质量差的数据,并采取必要的措施来解决问题。测试在管道中的不同步骤重复进行,这使得数据工程师可以很容易地看到数据在管道的哪个阶段/层损坏,并找到最合适的人来解决问题。
在异常检测框架中,该解决方案扫描数据资产,从这些数据中收集统计数据,并关注这些统计数据的行为变化。设置警报阈值(自动、手动或两者兼有),当统计行为显示数据有问题时,解决方案会向您选择的平台发送警报。有了这个框架,解决方案通常使用数据沿袭找到数据问题的根本原因,允许他们追溯数据经历的所有事件。
可用解决方案的基准—图片由 Louise de Leyritz 提供
要获得完整的数据可观察性工具基准测试和更多分类标准,请单击此处的。
订阅我的简讯获取更多文章!
更现代的数据堆栈基准?
点击查看更多关于现代数据堆栈的性能指标评测和分析。我们写了利用数据资产时涉及的所有过程:从现代数据堆栈,到数据团队的组成,再到数据治理。我们的博客涵盖了从数据中创造有形价值的技术和非技术方面。
在 Castor,我们正在开发一个数据文档工具 Figma,Slack generation。或者对 Fivetran,Looker,Snowflake,DBT 的狂热爱好者来说是数据方面的。我们将我们的目录设计得易于使用、令人愉快且友好。
想去看看吗?联系我们,我们将向您展示一个演示。
原载于https://www.castordoc.com。**
机器学习中的数据噪声和标签噪声
思想和理论
在现实世界中应用机器学习需要考虑机器学习中的噪声,下面是为什么以及如何处理它。
1-自身图像:不对称标签噪声
动机
机器学习中为什么要关心数据噪声和标签噪声?
巨大的成就将机器学习带到了各种应用中。这将激励和加速研究和应用,因为我们现在可以致力于回答真正重要的问题——医学、心理学、犯罪学。然而,这些现实世界的应用往往比学术问题更嘈杂。以医学为例,观察者之间和观察者内部的高度可变性(医学样本中一个或多个医生的不同诊断)是一个众所周知的问题[1],尽管学术数据集也存在噪声[2]。另一方面,更大网络的趋势导致记忆而不是归纳的问题[3],这也包括记忆噪音。
这篇文章应该激励其他研究人员将数据和/或标签噪声纳入他们的考虑。它们很容易在现代框架(如 PyTorch)中实现,提高了可靠性和逼真的场景,如下所示。我的 github 知识库[4]为 PyTorch 中嘈杂的机器学习实验提供了一个简单的基础。
- 数据和标签噪声介绍
- 数据和标签噪声分布的描述
- 防御数据和标签噪声
- 未来的研究
数据和标签噪声介绍
数据和标签噪声是与真实数据集的假设偏差。因此,数据噪声反映了数据中的偏差,即图像和标签噪声反映了标签中的偏差。
对称标签噪声
随机选择的标签 I 的α%被改变为标签 j!= i(包括 I 在内的所有标签的改变也是可能的),遵循用于选择新标签的均匀分布(参见图 2)。这种类型的标签噪声反映了标签中的普遍不安全性,并且在α较小的情况下相对容易克服[5]。
2-自有图像:对称标签噪点
不对称标签噪声所有标签
所有标签 I 中随机选择的α%被切换到标签 i + 1,或者对于最大值 I 被切换到 0(见图 3)。这与现实世界中标签被随机破坏的情况一致,因为数据集中标签的顺序也是随机的[6]。
3-自身图像:不对称标签噪声
不对称标签噪声单标签
随机选择的标签 0 的α%被切换到标签 4,而所有其他标签保持不变(参见图 4)。这是在两张复杂图像被混淆的情况下发生的。从技术上讲,这些图像位于决策边界附近。
4-自有图像:不对称标签噪声,单开关
语义标签噪声
特别是模糊的图像被扰乱到那一类,弱模型预测它是。为此,一组训练样本 d 被分成小组 X ⊂ D 和大组 Y ⊂ D。弱模型在 x 上被训练并在 y 上被测试。y 中每个图像的损失被排序,使得具有最高损失的那些图像的α%被选择(最困难的样本)。它们的标签被切换到来自弱分类器的预测标签。包括扰动标签的集合 Y’被添加到 X,从而在 D’ [7,8]上训练模型。这种类型的标签噪声对于模型来说特别难以克服。它也接近于现实世界的应用,如医学成像,其中具有挑战性的案例会导致标签的模糊性[9]。
高斯数据噪声
将正态分布添加到测试图像中。相对于图像信噪比来计算其标准偏差和结果幅度。因此,对于每个图像,高斯噪声以相同的相对幅度被添加。这种噪声在现实应用中很明显,例如由于相机故障。
自有公式(1):高斯数据噪声。SNR:信噪比
盐和胡椒数据噪声
随机选择的α 2 %的像素被切换到 0,α/2 %被切换到 1。这种噪声可能是由相机中的故障像素引起的,并且在图像处理中得到充分研究[10]。
斑点数据噪声
在雷达图像中常见的 eg,这是一种乘性噪声,其中将图像的α x N(μ,σ2)倍加到图像上,其中 N 是正态分布。
泊松数据噪声
等同于高斯数据噪声,可以添加泊松分布而不是正态(高斯)分布。据我所知,这种方法应用较少。
防御标签噪声和数据噪声
知道了数据集中的噪声类型,仍然需要变得可靠以抵抗噪声。在文献中,噪声标签和噪声数据被广泛考虑。简要描述了一些防御策略,尤其是针对噪声标签的防御策略。还有更多的技术需要发现和开发。
不确定性估计
这本身并不是真正的防御,但是不确定性估计在数据样本中产生了有价值的见解。任意的、认知的和标签噪声可以检测某些类型的数据和标签噪声[11,12]。反映预测的确定性是自治系统的重要资产,尤其是在嘈杂的真实世界场景中。置信度也经常被使用,尽管它需要校准良好的模型。
稳健损失函数
一个研究分支的目标是损失函数不会过度拟合噪声样本。广泛使用的交叉熵损失由于将负类归零而倾向于过度自信。MAE 是一个简单且相当稳健的损失函数,尽管相对较慢[13]。
强健的训练
一种方法是在训练策略中更多地考虑鲁棒性和泛化能力。特别是在非常嘈杂的环境中,早期停止已经被证明是一种有效的策略,不会记住噪音[14]
未来的研究
以下是一些关于研究空白的想法。总的来说,我希望这篇文章激发了噪声在更广泛环境中的使用。
进一步的噪声分布及其对模型的影响
研究进一步的噪声分布是非常有趣的。我之前研究的另一个例子是敌对噪音。类似于一个对立的例子,类损失最大化扰动被添加,有或没有跨越决策边界,从而切换标签。因此,当分类非常接近决策边界时,数据(例如图像)看起来仍然相同。类似于语义噪声,我认为它对模型来说是困难的,尽管通过认知的不确定性很容易检测到。
噪声的应用和影响
由于实际应用中存在数据和标签噪声,因此也应该在存在噪声的情况下研究旨在解决这些应用的方法。在研究实际应用时,我鼓励将此类噪声特性包括在内。
机器学习中的可靠性
随着机器学习被引入越来越多的领域,包括非常敏感的领域,可信度是一项重要的资产。最近的作品展示了模特是如何造成伤害和侮辱的。记忆噪音可以增加这种效果。由于这是我自己目前的研究领域,我只能鼓励读者研究机器学习模型的可靠性和可信度。
文献学
[1]j . g .埃尔莫尔、C.K .韦尔斯、C.H .李、D.H .霍华德和 A.R .费恩斯坦,1994 年。放射科医生对乳房 x 光片解释的可变性。新英格兰医学杂志,331(22),第 1493-1499 页。
[2]:诺斯卡特,C.G .,江,l .和庄,I.L .,2019。自信学习:估计数据集标签的不确定性。arXiv 预印本 arXiv:1911.00068。
[3]:张,c .,本吉奥,s .,哈特,m .,雷希特,b .和维尼亚尔斯,o .,2016。理解深度学习需要重新思考泛化。 arXiv 预印本 arXiv:1611.03530 。
【4】:【https://github.com/richtertill/noisy_machine_learning
[5]: B. van Rooyen,A. K. Menon 和 R. C. Williamson,“用对称标签噪声学习:精神错乱的重要性”,CoRR,第 abs/1505.07634 卷,2015 年
[6]: G. Blanchard,M. Flaska,G. Handy,S. Pozzi 和 C. Scott,“非对称标签噪声分类:一致性和最大去噪”,2016 年
[7]: K. Lee,S. Yun,K. Lee,H. Lee,B. Li 和 J. Shin,“通过生成分类器处理噪声标签的鲁棒推理”,2019
[8]: J. Lee 和 S.-Y. Chung,“全体一致的强健训练”,2020 年。
[9]:欧文、拉杰普尔卡尔、M. Ko、Y. Yu、S. Ciurea-Ilcus、C. Chute、H. Marklund、B. Haghgoo、R. Ball、K. Shpanskaya、J. Seekins、D. A. Mong、S. S. Halabi、J. K. Sandberg、R. Jones、D. B. Larson、C. P. Langlotz、B. N. Patel、M. P. Lungren 和 A. Y. Ng
[10]: R. Pandey,“斑点噪声:建模与实现”,第 9 卷,第 8717–8727 页,2016 年 01 月。
[11]: Tomczack,a .,Navab,n .和 Albarqouni,s .,2019 年。学会评估标签的不确定性以保证质量。arXiv 预印本 arXiv:1909.08058。
[12]:肯德尔,a .,和 Y. Gal。"在计算机视觉的贝叶斯深度学习中,我们需要哪些不确定性?"神经信息处理系统进展,第 30 卷。2017.
[13]:张,z .和萨本库,M.R .,2018。训练带噪声标签的深度神经网络的广义交叉熵损失。arXiv 预印本 arXiv:1805.07836。
[14]: Song,h .,Kim,m .,Park,d .和 Lee,J.G .,2019。早期停止如何有助于抵抗标签噪声的泛化?。 arXiv 预印本 arXiv:1911.08059 。
使用 Python scikit 进行数据规范化-学习
在关于数据预处理的系列文章之后,在本教程中,我将讨论 Python scikit-learn
中的数据规范化。正如在我之前的教程中已经说过的,数据标准化包括将不同尺度上测量的值调整到一个共同的尺度上。
规范化仅适用于包含数值的列。有五种标准化方法:
- 单一特征缩放
- 最小最大
- z 分数
- 对数标度
- 剪报
在本教程中,我使用scikit-learn
库来执行规范化,而在我之前的教程中,我使用pandas
库来处理数据规范化。我使用了我之前教程中使用的相同数据集,因此可以比较结果。事实上,我们使用这两种方法获得了相同的结果。
无论如何,在本教程中我不处理日志缩放和剪辑,这将是未来教程的对象。
scikit-learn
库也可以用来处理缺失值,正如我在上一篇文章中所解释的。
本教程中描述的所有scikit-learn
操作都遵循以下步骤:
- 选择预处理方法
- 通过
fit()
功能进行安装 - 通过
transform()
功能将其应用于数据。
scikit-learn
库只对数组起作用,因此当执行每个操作时,dataframe 列必须被转换成数组。这可以通过numpy.array()
函数来实现,该函数接收 dataframe 列作为输入。fit()
函数接收数组的数组作为输入,每个数组代表数据集的一个样本。因此,reshape()
函数可以用来将标准数组转换成数组的数组。
本教程描述的所有代码都可以从我的 Github 库下载。
数据导入
作为数据集的一个例子,在本教程中,我们考虑由意大利 Protezione Civile 提供的数据集,该数据集与自新冠肺炎疫情开始以来登记的新冠肺炎病例数相关。数据集每天更新,可从此链接下载。
首先我们需要导入 Python pandas
库,通过read_csv()
函数读取数据集。然后我们可以删除所有带有NaN
值的列。这是通过dropna()
功能完成的。
**import** pandas **as** pd
df **=** pd.read_csv('https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-regioni/dpc-covid19-ita-regioni.csv')
df.dropna(axis**=**1,inplace**=True**)
df.head(10)
作者图片
单一特征缩放
单一特征缩放将列中的每个值转换为 0 到 1 之间的数字。新值的计算方法是当前值除以列的最大值。这可以通过MaxAbsScaler
类来完成。我们将缩放器应用于tamponi
列,它必须转换为数组并进行整形。
**import** numpy **as** np
**from** sklearn.preprocessing **import** MaxAbsScaler
X **=** np.array(df['tamponi']).reshape(**-**1,1)
scaler **=** MaxAbsScaler()
现在,我们可以安装缩放器,然后应用变换。我们通过应用逆reshape()
函数将其转换为原始形状,并将结果存储到 dataframe df
的新列中。
scaler.fit(X)
X_scaled **=** scaler.transform(X)
df['single feature scaling'] **=** X_scaled.reshape(1,**-**1)[0]
scikit-learn
库还提供了一个函数来恢复原始值,给定转换。这个函数也适用于本文后面描述的转换。
scaler.inverse_transform(X_scaled)
它给出了以下输出:
array([[5.000000e+00],
[0.000000e+00],
[1.000000e+00],
...,
[5.507300e+05],
[6.654400e+04],
[3.643743e+06]])
最小最大
与单个要素缩放类似,最小最大值将列中的每个值转换为 0 到 1 之间的数字。新值计算为当前值和最小值之差除以列值的范围。在scikit-learn
中,我们使用了MinMaxScaler
类。例如,我们可以对列totale_casi
应用 min max 方法。
**from** sklearn.preprocessing **import** MinMaxScaler
X **=** np.array(df['totale_casi']).reshape(**-**1,1)
scaler **=** MinMaxScaler()
scaler.fit(X)
X_scaled **=** scaler.transform(X)
df['min max'] **=** X_scaled.reshape(1,**-**1)[0]
z 分数
Z-Score 将列中的每个值转换为 0 左右的数字。通过 z 得分变换获得的典型值范围为-3 到 3。新值计算为当前值和平均值之差除以标准偏差。在scikit-learn
中,我们可以使用StandardScaler
功能。例如,我们可以计算列deceduti
的 z 值。
**from** sklearn.preprocessing **import** StandardScaler
X **=** np.array(df['deceduti']).reshape(**-**1,1)
scaler **=** StandardScaler()
scaler.fit(X)
X_scaled **=** scaler.transform(X)
df['z score'] **=** X_scaled.reshape(1,**-**1)[0]
摘要
在本教程中,我展示了如何使用scikit-learn
库的preprocessing
包来规范化数据集。我们可以通过手动应用公式来获得相同的结果,如我之前的教程中所解释的那样。
使用scikit-learn
的主要优点如下:
- 您不需要提前知道公式,因为您有预打包的类来执行操作
- 您可以通过
inverse_transform()
功能恢复原始数据。
如果您想学习如何使用 scikit-learn 库执行数据预处理的其他方面,请继续关注…
如果你想了解我的研究和其他活动的最新情况,你可以在 Twitter 、 Youtube 和 Github 上关注我。
专业人工智能开发的数据营养标签
来源于美国美国食品药品监督管理局政府网站。
负责任的人工智能不仅仅是一套原则。期望是你会有证据证明他们被支持。
我们在LexisNexis Legal&Professional的使命是让所有人都能获得法律信息。我们从超过 50,000 个公共来源收集文档和数据,管理着超过 2Pb的数据。因为这些文件代表了我们国家的成文法,所以我们完全按照它们的写法提供。我们小心翼翼地不采用任何要求我们改变内容的个人判断或道德标准。然而,支持对社会有积极影响的决策也是我们公司的核心使命。
是的,但是你们的科学家太专注于他们能不能做到,他们没有停下来想想他们是否应该。——伊恩·马尔孔博士,《侏罗纪公园》
正如所有的技术进步都是出于好意,它们也可能被用来产生负面影响。例如,社交网络的发明源于将人们聚集在一起并创造更紧密的社会联系的积极愿望,但它现在可以用来羞辱、欺负甚至操纵社会。思考用例并试图确定长期的道德和社会影响应该是寻求开发专业级人工智能的公司的一部分。
责任人工智能是一个新兴的治理框架,专注于人工智能的道德使用和民主化。为了确保今天开发的人工智能保持道德和社会责任,许多公司开始采用一套人工智能原则。比如 谷歌 和 微软 都在自己的网站上公开发布了自己的 AI 原理。正在开发一套人工智能原则的公司将包括以下三个主题的一些变体:
- 最大化积极的社会影响,同时最小化消极影响(例如,在消除消极偏见的同时保持公平和包容性)
- 透明度(例如,解释人工智能系统如何运行和构建)
- 人类责任(例如,确保计算机和人工智能对人类负责)
但是仅仅说自己的公司有原则是不够的。专业人士的期望是,你会有证据表明他们得到了支持。
与当今大多数企业一样,LexisNexis 利用数据科学和人工智能为我们的客户构建智能分析和解决方案。我们的每个智能应用程序都依赖于从上述法律数据资产中获得的训练数据。因此,这些模型具有潜在加剧包含在训练数据中的任何负面偏差并通过模型表达它们的风险。
负责任的人工智能关注的一个领域是最小化负面偏见,特别是对受保护的社会阶层,如性别、种族或年龄的偏见。一种可能尝试的天真的方法是在他们的模型中不使用变量,如种族、性别或年龄。但是,如果训练数据是有偏差的,例如,偏向一种性别类型,仅仅从模型中排除变量仍然会导致有偏差的模型,因为这是训练数据中的所有信息。一个更好的方法是通过对数据进行分层或者在回归模型中包含社会阶层变量来控制这些影响。然而,使用这两种方法中的任何一种,都需要建模者对训练数据的来源和构成有一个透彻的理解。
不幸的是,数据科学模型开发中的大部分时间和精力都花在了模型选择和调整上,很少关注训练数据评估。更令人担忧的是,如果你查看任何应用程序的 github 库,关于数据是如何收集和评估的训练数据和文档是不存在的。
在专业级人工智能开发中,我们想要改变这一点。
**在 LexisNexis,我们认识到训练数据是模型中主要的驱动偏差。在专业级人工智能开发中,我们要求数据科学家努力实现透明,并提供支持公司负责任的人工智能原则的实证评估。为此,我们创建了一个轻量级解决方案,让我们的数据科学家在为面向客户的产品开发模型时使用。
首先,我们需要沿着关键的受保护类别对训练数据进行剖析。此外,我们列出了也可以用来近似这些类别的代理。例如,如果所有数据都来自佛罗里达州,那么它可能会偏向于比该国其他地区年龄更大的人群。下图显示了我们在分析过程中检查的几个类别和代理。在代理的情况下,很难确定它们可以代表的所有可能的类别,所以我们建议只报告它们在数据和/或原始计数中的存在。
作者图片
虽然检查和分析训练数据对开发模型的数据科学家很有用,但是如果他们选择继续使用数据开发模型,那么该模型的消费者如何知道数据科学家知道什么呢?如果该模型是仅使用来自佛罗里达的数据开发的,它是否应该带有一个警告标签:“小心!用佛罗里达数据开发的模型,如果在另一个州使用风险自担"?
我们会争辩,是的!
嗯,不完全是这样。但我们确实认为,数据科学家不仅有责任测量,而且有责任报告他们的训练数据概况。正如我们前面提到的,大多数代码库不提供关于数据的细节。然而,他们有需求文档,列出了模型开发中使用的代码模块和版本。我们建议代码库也包含一个训练数据清单文件,或者我们喜欢称之为 数据营养标签 。下面是一个标准清单文件的示例,该文件存储在代码存储库的根文件夹中:
**DataNutritionLabel.txt**## Source
[ Training data was queried on 2/10/2020 from 2018-2019 Case Law - CA, TX, FL ]## Protected Category Stats[ Male: Observed 70%, Expected 50%Female: Observed 30%, Expected 50%]## Proxies[ Zipcodes: Observed 7500, Expected 41,692Names: Observed]## Decision[ Include Weight Variable in model to adjust for protected cat skew]## Alternatives considered[ Any alternatives that were considered during the decision making process ]
作为一名寻求开发负责任的人工智能的专业级数据科学家,不仅仅需要找到完美的数据和完美的模型。它要求人们思考和理解使用模型及其应用可能产生的社会和伦理影响。它涉及对训练数据的彻底评估和评价。它还可能需要涉及额外的主题专家和产品经理,以帮助考虑可能的应用程序和用例。最后,这意味着数据科学家对透明度负责;用于记录和共享该信息,以告知模型的未来开发人员和用户他们所使用的训练数据中的偏差。正如营养标签出现在你购买的每一种食品上,使你能够做出消费决定一样, 我们建议这些训练数据清单成为支持数据科学模型的每个代码库的一部分。
数据可观察性及其重要性
播客
Kevin Hu 谈如何确保你的数据集不是问题
编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。
想象一下,你正在经营一家盈利的企业,你的销售策略的一部分是偶尔向那些已经注册加入你的邮件列表的人发送大量电子邮件。在一段时间内,这种方法带来了可靠的新销售流,但有一天,这种情况突然停止了。发生了什么事?
你翻遍日志,寻找解释,但结果发现问题不在你的软件上;它和你的数据在一起。也许新来的实习生不小心给你数据集中的每个电子邮件地址添加了一个字符,或者打乱了你邮件列表上的名字,这样克里斯蒂娜就收到了一封写给“约翰”的邮件,反之亦然。这种故事的版本出人意料地经常发生,当它们发生时,成本可能是巨大的:收入损失、客户失望,或者更糟——不可逆转的信任损失。
今天,整个产品都是建立在数据集之上的,而这些数据集没有被适当地监控以发现关键故障,并且越来越多的产品在高风险的情况下运行。这就是数据可观察性如此重要的原因:能够跟踪任务关键型数据的来源、转换和特征,以便在问题导致下游损害之前发现问题。
这也是我们将与 Kevin Hu 对话的原因,他是全球首批数据可观测性创业公司之一 Metaplane 的联合创始人兼首席执行官。Kevin 对数据管道有着深刻的理解,如果没有适当的监控,cap 就会出现问题。在这一集的 TDS 播客中,他和我一起谈论了数据的可观测性,为什么它很重要,以及它如何与负责任的人工智能联系起来。
以下是我在对话中最喜欢的一些观点:
- Kevin 提出了他所谓的“数据可观察性的四个支柱”,这是一组维度,它们共同给出了数据集和数据基础架构健康状况的完整画面。这四个支柱是指标、元数据、沿袭和日志。度量传达有关数据集的信息,该数据集取决于它包含的特定样本,例如,列的平均值、标准偏差和偏斜度。元数据描述数据集的外部特征,从结构上更好地描述数据集:数据有多新,它有多少列,等等。谱系指的是应用于数据集中每个样本的转换的起源和历史(根据 Kevin 的说法,谱系是公司最容易纠结或完全忽略的四大支柱之一)。最后,日志捕获数据和现实世界之间的交互,无论是机器对机器的交互(例如数据复制)还是人机交互(例如数据团队创建新的模型或仪表板)。你可以在这里阅读 Kevin 关于数据可观察性四大支柱[的帖子。](http://The Four Pillars of Data Observability)
- Kevin 指出,虽然数据可观测性和数据操作比软件工程中的同行落后了大约十年,但软件工程中的许多概念可以用来实现更好的数据可观测性,因此没有人需要在这里重新发明轮子。例如,他强调了这样一个事实,即数据血统可以使用数据版本控制工具进行跟踪,如 dbt ,这本身就是受软件领域版本控制工具的启发。
- 人工智能的新时代以在大型数据集上训练的高度规模化、多用途模型为特征。这些有时被称为“基础模型”,它们会产生新的安全风险:因为一个基础模型可以用于许多应用程序(例如,GPT-3,它可以翻译语言,撰写文章,代码等),它们可能成为基于它们构建的大型工具和服务生态系统的单点故障。这意味着我们在训练他们的时候需要特别小心:破坏一个用来训练基础模型的数据集的后果是非常严重的,这个基础模型的输出被数百万人消费。随着人工智能能力的增加,数据可观察性游戏的赌注也在增加。
你可以点击这里在 Twitter 上关注凯文。
章节:
- 0:00 介绍
- 2:00 什么是数据可观测性?
- 8:20 数据集的内部和外部特征之间的差异
- 12:20 为什么数据如此难以记录?
- 17:15 追溯模型
- 22:00 日期的算法分析
- 五年内实现 26:30 的数据运营
- 33:20 与前沿人工智能工作的关系
- 39:25 软件工程和启动资金
- 42:05 较小规模的问题
- 46:40 需要解决的未来数据运营问题
- 48:45 总结
数据可观测性:如何大规模地固定数据质量
引入一种新方法来防止分析仪表板损坏,并增强对您数据的信任。
图片由 Shutterstock 提供。
公司每年花费超过1500 万美元 处理 数据停机 ,换句话说,在数据丢失、损坏或其他错误的时间段内,每 5 家公司中就有 1 家https://www.zdnet.com/article/companies-are-losing-revenue-opportunities-and-customers-because-of-bad-data-practices/因数据不完整或不准确而失去了一个客户。
好在 还有希望数据的下一个前沿:可观测性 。 以下是全球电子商务公司 Yotpo 的数据工程师和 BI 分析师如何利用大规模数据可观察性来提高成本节约、协作和生产率。
Yotpo 与世界各地的电子商务公司合作,通过评论、视觉营销、忠诚度和推荐计划以及短信营销来帮助他们加速在线收入增长。
对于业务绩效总监 Yoav Kamin 和数据工程团队负责人 Doron Porat 来说,始终拥有准确可靠的数据是这项任务成功的基础。
挑战:破损的数据管道和仪表板
从第一天起,Yotpo 就为支持公司多样化数据需求的内部团队投资了一个分布式数据平台,从生成营销报告到授权产品开发团队为他们的用户建立更好的服务。
在过去的几年里,Yotpo 呈指数级增长,在全球范围内扩张业务,并收购了包括 Swell Rewards 和 SMSBump 在内的公司。随着 Yotpo 的发展,数据源的数量和数据管道的复杂性也在增加。随着时间的推移,跟踪数据完整性、沿袭和质量变得越来越困难,这是可靠数据的三个关键特征。这种的数据停机时间,换句话说,就是数据丢失、不准确或其他错误的时期,导致了耗时且成本高昂的数据消防演习,从而导致了 Yotpo 的业务绩效和数据工程团队之间的摩擦。
“一次又一次,我们的工作人员会找到我的团队,告诉我们数据是错误的,但我们根本不知道数据是如何泄露的,”多伦说。“我们很清楚,我们必须更好地控制我们的数据管道,因为我们不能让我们的数据消费者就数据问题向我们发出警报,并不断被意外捕获,没有人会以这种方式信任我们的分析。”
为了解决这个问题,Yotpo 需要一种更好的方法来管理数据健康和发现。最终,他们需要一种解决方案,能够在正确的时间为他们提供正确的信息,以便在数据管道和商业智能仪表板中的异常情况影响业务之前发出警报并加以预防。
解决方案:数据可观察性
为了帮助他们消除数据停机并释放数据的潜力,Yotpo 选择了 一种主动的数据可观察性方法 ,该方法将自动监控 Yotpo 数据生态系统的关键功能,包括数据新鲜度、分布、卷、模式和沿袭。
无需手动设置阈值,他们的团队就能快速获得以下问题的答案:
- 我的表上次更新是什么时候?
- 我的数据是否在可接受的范围内?
- 我的数据完整吗?2000 排突然变成 50 排了?
- 谁有权访问我们的营销表并对其进行了更改?
- 我的数据在哪里损坏了?哪些表或报表受到了影响?
结果:通过在数据异常影响下游消费者之前捕捉它们来节约成本
开箱即用的数据可观测性使 Yotpo 能够概述他们的红移环境,包括所有数据资产、模式和表。他们的机器学习算法自动生成规则,通知数据停机监控和警报,提供即时价值。
调整到 Yotpo 数据生态系统的 ML 算法在数据异常影响下游数据资产之前检测到它们。图片由巴尔·摩西提供。
数据可观察性对团队的影响的一个最近的例子是,当 Yotpo 的 Segment 实例中的一个错误数据点生成了比预期多 6 倍的行时,即使考虑了季节性和正常的数据波动。
Doron 说:“当峰值发生时,我们的数据可观察性引擎立即向我的团队发出警报,使我们能够在异常影响营销团队的下游数据消费者之前进行调查和故障排除。“因为我们马上发现了这一点,所以我可以放心,任何重要的业务指标都不会受到影响。”
由于数据工程团队在问题影响到他们的利益相关者之前就得到通知,他们能够修复他们的管道,并防止未来的异常危及他们数据的完整性。
结果:通过跟踪外地一级的传承改善了合作
数据可观察性对 Yotpo 的日常运营变得至关重要的另一个用例是,当 Yotpo 的业务应用程序团队(负责集成和维护 Salesforce 等内部运营系统的团队)想要用新的字段替换过时的字段时。他们的许多仪表板严重依赖于这一领域,因此他们必须提前为这一变化做好准备。
数据可观察性使 Yotpo 能够了解关键数据资产的依赖性。图片由巴尔·摩西提供。
使用信息丰富的查询日志,数据可观察性有助于 Yotpo 了解 1)该字段的下游依赖关系是什么,2)谁在使用该表以及如何使用,3)哪些仪表板当前正在使用旧字段(沿袭),以及 4)在何处添加了新字段以跟踪此更新的进度。
“我们使用 lineage 来突出我们数据生态系统中的上游和下游依赖关系,包括 Salesforce,让我们更好地了解我们的数据健康状况,”Yoav 说。“我们不需要被动应对并在控制面板出现故障后进行修复,而是需要主动出击。”
结果:通过监视废弃数据集提高工作效率
数据可观察性使 Yotpo 对重要数据集和表的健康状况有了更大的透明度。图片由巴尔·摩西提供。
此外,数据可观察性使 Yotpo 能够更加透明地了解https://www.montecarlodata.com/how-to-solve-the-youre-using-that-table-problem/重要数据资产的相关性和使用模式,并在不同属性(如记录类型 ID 和特定数据集)被否决时通知他们。数据可观察性使他们能够跟踪新字段添加和使用的位置,以便您可以跟踪哪些仪表板需要更新。这些知识确保了 Yotpo 可以相信他们的数据是准确可靠的,即使他们的数据平台在不断发展。
“一旦你对你的数据失去信任,你就失去了可靠性,”多伦说。“有了数据可观察性,数据停机时间更少,数据可靠性更高。痛苦的模式更改和破损仪表板的日子已经一去不复返了。”
Yotpo 数据可观测性的影响
对于 Yotpo 来说,数据可观察性使他们能够快速解决数据质量问题,因此他们可以开始相信他们的数据能够为业务提供可靠、可操作的见解。
“我们的高管依靠我团队的仪表盘来做出决策。有了数据可观察性,当数据发生变化时,我们确切地知道应该更新什么,因此没有停机时间,也没有消防演习。我们的决策者更高兴了,我晚上可以睡觉了,”Yoav 说。
除其他优势外,数据可观察性使 Yotpo 能够:
- 通过减少解决繁琐的数据消防演习的时间来增加成本节约 并恢复对数据的信任,以便做出重要决策
- 数据工程和数据分析师团队之间更好地协作 以了解数据资产之间的关键依赖关系
- 通过获得对数据资产的运行状况、使用模式和相关性的端到端可见性,提高效率和生产力
有了数据可观测性,Yoav、Doron 和他们的数据团队已经做好充分准备,可以消除数据停机时间,并继续释放数据的潜力。
有兴趣学习更多数据可观测性? 巴尔摩西 。
本文由 会知更鸟 共同撰写。特别感谢 Yoav、Doron 和 Yotpo 数据团队的其他成员!
数据可观察性:如何防止大规模数据管道破裂
介绍了解数据可靠性的新方法
图片由foximage于Shutterstock上,通过 Shutterstock 的标准许可批准使用。
公司每年花费超过1500 万美元 处理 数据停机 ,换句话说,在数据丢失、损坏或出现其他错误的时间段内,超过 88%的美国企业 因数据质量而遭受损失
幸运的是,数据工程的下一个前沿领域 还有希望:数据可观察性。 以下是图书汇总订阅服务 Blinkist 的数据工程团队如何通过大规模的数据可观察性来提高成本节约、协作和生产率。
Blinkist 在全球拥有超过 1600 万用户,它通过电子书订阅服务帮助时间紧张的读者将学习融入他们的生活。
工程总监 Gopi Krishnamurthy 领导着负责数据工程、基础设施、云卓越中心、增长和货币化的团队。
对于 Blinkist 来说,拥有值得信赖和可靠的数据是其业务成功的基础。
挑战:损坏的数据管道影响增长、用户体验和可靠性
缺乏实时数据跟踪导致关键分销渠道的营销支出减少。图片由 Blinkist 提供。
作为一家高速增长的公司,Blinkist 利用付费绩效营销来获得客户。他们的 2020 年战略——雄心勃勃的 40%增长目标——包括对脸书和谷歌等渠道的重大投资,这些渠道将根据 Blinkist 应用程序和渠道本身之间共享的行为数据自动优化活动。
当然,像 2020 年的许多公司一样,新冠肺炎疫情改变了一切。现在,历史数据无法反映受众日常生活的当前现实,实时数据变得至关重要——不仅是为了确定广告支出,也是为了了解用户如何与 Blinkist 应用和网络内容进行交互的当前状态。
这些数据中的任何不准确都可能影响决策,从活动支出到更新产品路线图。至关重要的是,不要错过任何创新的机会,从添加新功能到简化入职到测试新广告——因为围绕“改善通勤”的活动已经不再相关。
随着首席执行官和活动经理越来越依赖实时洞察来推动营销战略、预算支出和投资回报,Gopi 和他的团队正在努力解决数据停机问题,包括数据质量、仪表板更新延迟和管道中断等问题。
“每周一,我们都会接到高管的电话,”戈皮说。“几乎每周一,我都会在电话中尝试回答为什么我们无法扩展,问题是什么,我们在跟踪数据方面面临多少问题…尝试解释问题的严重性,并尝试增强管理层利益相关者的信心。”
Gopi 估计他的团队花费了50%的工作时间进行数据演习,试图解决数据停机问题,同时重建与组织其他部门的信任。这是不可持续的——必须有所改变。
解决方案:端到端的数据可观察性
Blinkist 利用数据可观察性在整个公司范围内恢复对数据的信任,进而确保营销支出得到合理分配。图片由 Blinkist 提供。
于是在 2020 年秋天,Gopi 和他的团队重组,重新聚焦。他们建立了一个以 Spotify 推广的深思熟虑的执行框架为模型的计划,设定了一个明确的目标,在他们的公司建立对数据的信任。
Gopi 说:“这个框架的核心是数据可靠性工程,我们将数据可靠性视为一等公民,就像工程团队在过去十年中开始对待 DevOps 和站点可靠性工程一样。”
实现数据可靠性的基础是关注数据治理、数据质量和重构系统。
“当我们转向尝试引入数据可靠性工程原则时,数据可观察性对我们在短时间内轻松采用并满足这三个期望起到了关键作用,”Gopi 说
成果:通过自助工具和明确的数据可靠性 SLA,更快地解决数据事件
通过制定明确的数据可靠性服务级别协议和利用自助工具,Blinkist 能够在数据宕机影响下游消费者之前对其进行补救。图片由 Blinkist 提供。
在无代码的情况下,他们的数据可观察性平台在不到两周的时间内建立并运行,提供了对其数据管道和关键资产运行状况的即时可见性,大大加快了事件响应时间。
“我们可以立即看到发生了什么,”戈皮说。“每天,我们都可以看到是否有一个破裂的管道,一个没有更新的表,或者一个因为在上游添加或删除了某些内容而改变了数据模型的表。”
随着 Gopi 和他的团队努力重建破裂的信任以及破裂的管道,他们与公司领导合作,建立对数据可靠性原则的共同理解,并制定具体的数据 SLA(服务级别协议)。
数据利益相关方也获得了访问数据报告的权限,从而提高了整个公司数据运行状况的透明度。
“数据可观察性的自助服务功能有助于重建对数据的信任,因为用户看到了我们的行动:从红色警报到蓝色的“工作进行中”,再到绿色的“已解决”,Gopi 说。“他们知道谁应该负责,他们知道团队正在努力,一切都变得非常清楚。”
成果:通过对关键数据资产的自动监控和警报,每周节省 120 个小时的时间
数据可观察性为 Blinkist 提供了一个关于其数据资产真相的集中来源,包括浮现相关异常和跟踪血统到现场级别。图片由 Blinkist 提供。
数据可观察性可检测 Blinkist 数据环境中的异常,使用机器学习算法来生成控制数据停机警报的阈值和规则。这种自动化监控为 Gopi 的团队节省了每个工程师每周 20 个小时的时间,这在内部开发是不切实际的。这为 Gopi 的团队带来了每周 120 小时的累计时间节省,现在这些精力可以用于构建他们的产品或其他创新。
“特别是考虑到我们正在工作的时间框架,数据可观察性平台不是我们可以建立的,”Gopi 说。“这基本上是数据可观察性背后的人工智能的力量——要建立这种工具,你需要有大量的内部知识来建立这些业务规则和创建这些警报。”
由于前面提到的自助式报告和数据 SLA,数据可观察性也有助于利益相关者更高效地工作。
例如,当渠道经理注意到营销活动表现不佳时,他们可以轻松访问数据报告,并查看数据可靠性 SLA 是否得到满足以及数据管道是否正常工作。如果是这样的话,他们可以排除不良数据这一罪魁祸首,并寻找其他解决方案,如更换广告创意或调整目标受众——而无需要求数据团队的同事付出时间或精力。
成果:通过防止数据管道和仪表板损坏增加收入
数据事件的自动警报确保了 Blinkist 的数据团队在管道破裂或仪表盘失效时第一个知道。图片由 Blinkist 提供。
由于 Blinkist 能够更快地检测和解决数据停机问题,他们的营销渠道蓬勃发展,从而增加了收入。
“如果我们能够在 24 小时内发现并解决问题,脸书或谷歌就可以自动更正,永远不会缩减广告活动,”戈皮说。
随着更准确的分析和新近恢复的对数据的信任,Blinkist 营销人员现在能够迅速做出决策,优化他们的广告支出,以获得更好的目标和绩效。
“我们今年看到的增长规模是压倒性的,”Gopi 说。“虽然数据团队不能独享全部荣誉,但我绝对认为我们能够做到的事情——在数据可观察性和将透明度引入数据运营方面——改善了我们锁定受众和渠道的方式。”
Blinkist 数据可观测性的影响
数据可观察性恢复了人们对数据的信任,确保营销支出得到合理分配,进而增加收入。图片由 Blinkist 提供。
数据可观察性帮助 Blinkist 增加了收入,节省了时间,并在整个组织中重建了数据的信任和透明度。随着损坏的数据管道得到控制,他们的数据工程师正专注于创新和解决核心业务问题,而不是救火。
除其他优势外,数据可观察性使 Blinkist 能够:
- 每位工程师每周节省 20 多个小时、通过消除对繁琐数据消防演习的故障排除需求
- 通过获得对数据资产的运行状况、使用模式和相关性的端到端可见性,提高效率和协作
“数据可观察性通过在新鲜度、数据量和数据模型变化方面自动化异常检测,使生活变得更加轻松,”Gopi 说。“这对于我们在正确的时间采取行动并确保减少甚至防止数据宕机非常有帮助。”
有兴趣了解数据可观察性如何帮助您获得更可靠的数据?伸出手去 巴尔 和 蒙特卡洛!
特别感谢 Gopi 和 Blinkist 团队的其他人!
本文由Will Robins共同撰写。
数据可观察性:使用 SQL 构建数据质量监视器
辅导的
如何构建自己的数据质量监控器,以识别数据管道中的新鲜度和分布异常
图片由像素上的 faaiq ackmerd 提供。
在本系列文章中,我们将介绍如何从头开始创建自己的数据可观察性监视器,映射到数据健康的五大支柱*。本系列的第 1 部分改编自 Barr Moses 和 Ryan Kearns’ O’Reilly 培训,* 管理数据停机时间:将可观测性应用于您的数据管道 ,这是业界首个关于数据可观测性的课程。关联练习在 这里 可用,本文所示改编代码在 这里 可用。
从空值和重复行,到建模错误和模式更改,数据可能由于多种原因而中断。数据测试通常是我们抵御不良数据的第一道防线,但是如果数据在其生命周期中出现问题,会发生什么呢?
我们将这种现象称为数据宕机,它指的是数据丢失、出错或不准确的时间段。数据停机提示我们提出如下问题:
- 数据是最新的吗?
- 数据是否完整?
- 字段是否在预期范围内?
- 零利率是高于还是低于它应有的水平?
- 模式改变了吗?
为了在数据中断时触发警报并防止数据停机,数据团队可以利用我们在软件工程领域的朋友们的一个屡试不爽的策略: 监控和可观察性 。
我们将 数据可观察性 定义为组织回答这些问题并评估其数据生态系统健康状况的能力。反映数据健康的关键变量,数据可观察性的五个支柱是:
- 新鲜度:我的数据是最新的吗?我的数据是否有未更新的时间间隔?
- 分布:我的数据在现场级别的健康程度如何?我的数据是否在预期范围内?
- 卷:我的数据接收是否达到预期的阈值?
- 模式:我的数据管理系统的正式结构改变了吗?
- 血统:如果我的部分数据宕机,对上下游有什么影响?我的数据源如何相互依赖?
以这种概念性的方式谈论数据可观测性是一回事,但完整的处理应该拉开帷幕— 数据可观测性在代码中实际上是什么样子的?
很难完全回答这个问题,因为细节将取决于个人对数据仓库、数据湖、BI 工具、首选语言和框架等的选择。即便如此,使用 SQLite 和 Jupyter 之类的轻量级工具解决这些问题可能是有用的。
在本文中,我们将通过一个示例数据生态系统来创建我们自己的 SQL 数据质量监视器,并探索数据可观测性在实践中是什么样子的。
让我们来看看。
实践中的数据可观测性
本教程基于我们奥莱利课程 练习 1 , 管理数据停机 。欢迎您使用 Jupyter 笔记本和 SQL 自行尝试这些练习。我们将在以后的文章中更详细地讨论,包括练习23和 4 。
我们的样本数据生态系统使用关于可居住外行星的模拟天文数据。出于本练习的目的,我使用 Python 生成了数据集,对我在生产环境中遇到的真实事件的异常进行建模。这个数据集完全可以免费使用,如果您感兴趣的话,存储库中的 utils 文件夹包含了生成数据的代码。
我使用的是 SQLite 3.32.3 ,它应该可以通过简单的设置从命令提示符或 SQL 文件访问数据库。这些概念实际上可以扩展到任何查询语言,这些实现可以扩展到 MySQL、Snowflake 和其他数据库环境,只需做很小的改动。
**$ sqlite3 EXOPLANETS.db
sqlite> PRAGMA TABLE_INFO(EXOPLANETS);
0 | _id | TEXT | 0 | | 0
1 | distance | REAL | 0 | | 0
2 | g | REAL | 0 | | 0
3 | orbital_period | REAL | 0 | | 0
4 | avg_temp | REAL | 0 | | 0
5 | date_added | TEXT | 0 | | 0**
EXOPLANETS
中的数据库条目包含以下信息:
0._id
:对应行星的 UUID。
1。distance
:距离地球的距离,以光年为单位。
2。g
:表面重力为 g 的倍数,引力常数。
3。orbital_period
:单个轨道周期的长度,以天为单位。
4。avg_temp
:平均表面温度,单位为开尔文度。
5。date_added
:我们的系统发现这颗行星并将其自动添加到我们的数据库中的日期。
请注意,由于数据缺失或错误,对于给定的行星,distance
、g
、orbital_period
和avg_temp
中的一个或多个可能是NULL
。
**sqlite> SELECT * FROM EXOPLANETS LIMIT 5;**
请注意,这个练习是追溯性的—我们正在查看历史数据。在生产数据环境中,数据可观察性是实时的,并应用于数据生命周期的每个阶段,因此将涉及与此处略有不同的实现。
出于这个练习的目的,我们将为新鲜度和分布建立数据可观察性算法,但是在以后的文章中,我们将解决我们的五个支柱的其余部分——以及更多。
新鲜
我们监控的数据可观察性的第一个支柱是新鲜度,它可以为我们提供重要数据资产上次更新时间的有力指示。如果一个整点定期更新的报告突然看起来非常陈旧,这种类型的异常应该给我们一个强烈的信号,表明有什么不对劲。
首先,注意DATE_ADDED
列。当添加单个记录时,SQL 不存储元数据。因此,为了在这种追溯设置中可视化新鲜感,我们需要自己跟踪这些信息。
按DATE_ADDED
列分组可以让我们深入了解EXOPLANETS
每天是如何更新的。例如,我们可以查询每天添加的新 id 的数量:
你可以自己用库中的$ sqlite3 EXOPLANETS.db < queries/freshness/rows-added.sql
运行这个。我们得到以下数据:
基于我们数据集的这个图形表示,看起来EXOPLANETS
每天持续更新大约 100 个新条目,尽管存在连续多天没有数据的间隙。
回想一下,对于新鲜感,我们想问“我的数据是最新的吗?”—因此,了解表更新中的这些缺口对于了解我们数据的可靠性至关重要。
新鲜度异常!
这个查询通过为DAYS_SINCE_LAST_UPDATE
引入一个度量来操作新鲜度。(注意:由于本教程使用的是 SQLite3,所以计算时差的 SQL 语法在 MySQL、雪花和其他环境中会有所不同)。
结果表显示“在日期 X ,在EXOPLANETS
中的最新数据是 Y 天前的数据。”这是从表格的DATE_ADDED
栏中无法明确获得的信息——但是应用数据可观测性为我们提供了发现它的工具。
现在,我们有了检测新鲜度异常所需的数据。剩下要做的就是为 Y 设置一个阈值** 参数—多少天算多?参数将查询变成了检测器,因为它决定了什么算异常(读:值得警告),什么不算异常。(关于设置阈值参数的更多信息,请参阅后面的文章!).**
新鲜度异常!
返回给我们的数据代表新鲜事件发生的日期。
在 2020 年 5 月 14 日,表中的最新数据是 8 天前的!这样的中断可能代表我们的数据管道中的一个破损,了解我们是否将这些数据用于任何有价值的事情将是很好的(如果我们在生产环境中使用这些数据,很有可能我们正在使用)。
请特别注意查询的最后一行:DAYS_SINCE_LAST_UPDATE > 1;
。
这里,1 是一个 模型参数——这个数字并不“正确”,尽管改变它会影响我们认为是事件的日期。数字越小,我们将捕捉到的真正的异常就越多(高召回),但是很有可能,这些“异常”中有几个不会反映真实的停机。数字越大,我们捕捉到的所有异常反映真实异常的可能性就越大(高精度),但是我们可能会遗漏一些。****
出于本例的目的,我们可以将 1 改为 7,从而只捕捉 2020 年 2 月 8 日和 2020 年 5 月 14 日两次最严重的停机。这里的任何选择都将反映特定的用例及目标,并且是在生产环境中大规模应用数据可观测性时反复出现的重要平衡。
下面,我们利用相同的新鲜度检测器,但是用DAYS_SINCE_LAST_UPDATE > 3;
作为阈值。两个较小的中断现在没有被发现。
请注意两次未检测到的停机—这两次停机之间的间隔必须少于 3 天。
现在我们设想相同的新鲜度检测器,但是现在用DAYS_SINCE_LAST_UPDATE > 7;
作为阈值。除了两次最大的停电外,其他都没有被发现。
就像行星一样,最佳模型参数位于过低和过高值之间的“黄金地带”或“最佳点”。这些数据可观察性概念(以及更多!)将在后面的文章中讨论。
分配
接下来,我们想要评估我们的数据在现场级别的分布状况。分布告诉我们数据的所有期望值,以及每个值出现的频率。一个最简单的问题是,“我的数据NULL
多久一次”?在许多情况下,某种程度的不完整数据是可以接受的——但是如果 10%的无效率变成了 90%,我们就想知道了。
该查询返回大量数据!这是怎么回事?
通式CAST(SUM(CASE WHEN SOME_METRIC IS NULL THEN 1 ELSE 0 END) AS FLOAT) / COUNT(*)
,当按DATE_ADDED
列分组时,告诉我们SOME_METRIC
的NULL
值在EXOPLANETS
的每日批量新数据中的比率。通过查看原始输出很难获得某种感觉,但视觉可以帮助说明这种异常情况:
********
这些图像清楚地表明,我们应该检测零利率“峰值”事件。现在让我们只关注最后一个指标AVG_TEMP
。我们可以用一个简单的阈值来检测最基本的零尖峰:
我们的第一个分布异常。
就检测算法而言,这种方法有点生硬。有时,我们数据中的模式足够简单,这样的阈值就能达到目的。然而,在其他情况下,数据会有噪音或有其他复杂因素,比如季节性,这要求我们改变方法。
例如,检测 2020–06–02、2020–06–03 和 2020–06–04 似乎是多余的。我们可以过滤出紧随其他警报之后的日期:
注意,在这两个查询中,关键参数是0.9
。我们实际上是在说:“任何高于 90%的空率都是一个问题,我需要知道它。”
在这种情况下,我们可以(也应该)通过应用带有更智能参数的滚动平均值的概念来变得更智能一点:
一个澄清:注意在第 28 行,我们使用数量AVG_TEMP_NULL_RATE — TWO_WEEK_ROLLING_AVG
进行过滤。在其他情况下,我们可能希望得到这个误差量的ABS()
,但不是在这里——原因是如果一个NULL
率“峰值”代表了从先前平均值的增加,那么它就更加令人担忧。每当NULL
频率突然降低时,可能不值得进行监控,而检测NULL
频率增加的价值是显而易见的。
当然,还有越来越复杂的异常检测指标,如https://en.wikipedia.org/wiki/Standard_score和 自回归建模 超出了本文的范围。本教程只是为 SQL 中的现场健康监控提供了基本的框架;希望能给你自己数据的思路!
下一步是什么?
这个简短的教程旨在说明“数据可观察性”并不像其名称所暗示的那样神秘,并且通过一个整体的方法来理解您的数据健康状况,您可以在您的管道的每个阶段确保高度的数据信任和可靠性。
事实上,使用普通的 SQL“检测器”可以实现数据可观察性的核心原则,前提是保留一些关键信息,如记录时间戳和历史表元数据。还值得注意的是,对于随生产环境增长的端到端数据可观测性系统,ML 驱动的关键参数调整是强制性的。
请继续关注本系列的后续文章,这些文章将重点关注分布和模式中的异常监控、血统和元数据在数据可观察性中的作用,以及如何大规模地一起监控这些支柱以获得更可靠的数据。
在此之前,祝您没有数据宕机!
有兴趣了解有关如何大规模应用数据可观测性的更多信息吗?伸出手去 瑞恩巴尔 ,以及剩下的 蒙特卡洛团队 。**
数据可观察性,第二部分:如何使用 SQL 构建自己的数据质量监视器
辅导的
使用模式和沿袭来理解数据异常的根本原因
图片由卢卡斯·佩泽塔在像素上提供。
在本系列文章中,我们将介绍如何从头开始创建自己的数据可观察性监视器,映射到数据健康的五大支柱。第一部分可以在这里找到*https://www.montecarlodata.com/data-observability-in-practice-using-sql-1/。***
本系列的第二部分改编自 Barr Moses 和 Ryan Kearns’ O’Reilly 的培训, 管理数据停机时间:将可观测性应用于您的数据管道 ,这是业内首个关于数据可观测性的课程。关联练习可在 这里 获得,本文所示改编代码可在 这里 获得。
随着世界对数据需求的增加,强大的数据管道变得更加必要。当数据中断时——无论是由于模式更改、空值、重复还是其他原因——数据工程师需要知道。
最重要的是,我们需要在破坏影响到下游系统和消费者之前,尽快评估破坏的根本原因。我们使用“”来指代数据丢失、出错或不准确的时间段。如果您是一名数据专业人员,您可能很熟悉提出以下问题:****
- 数据是最新的吗?
- 数据是否完整?
- 字段是否在预期范围内?
- 零利率是高于还是低于它应有的水平?
- 模式改变了吗?
为了有效地回答这些问题,我们可以从软件工程师的剧本中抽出一页: 监控和可观察性 。
回顾一下第 1 部分,我们将 数据可观察性 定义为组织回答这些问题并评估其数据生态系统健康状况的能力。反映数据健康的关键变量,数据可观察性的五个支柱是:
- 新鲜度:我的数据是最新的吗?我的数据是否有未更新的时间间隔?
- 分布:我的数据在现场级别的健康程度如何?我的数据是否在预期范围内?
- 卷:我的数据接收是否符合预期的阈值?
- 模式:我的数据管理系统的正式结构改变了吗?
- 血统:如果我的部分数据宕机,对上下游有什么影响?我的数据源如何相互依赖?
在本系列文章中,我们感兴趣的是揭开帷幕,研究数据可观测性是什么样子的——代码中的。**
在第一部分中,我们看了前两个支柱,新鲜度和分布,并展示了一点 SQL 代码如何操作这些概念。这些是我们称之为更“经典”的 异常检测问题——给定一个稳定的数据流,是否有任何东西看起来不正常?良好的异常检测当然是数据可观测性难题的一部分,但不是全部。
同样重要的是 语境 。如果发生了异常,很好。但是在哪里?什么上游管道可能是原因?哪些下游仪表板会受到影响?我的数据的形式结构改变了吗?良好的数据可观察性取决于我们正确利用元数据来回答这些问题(以及许多其他问题)的能力,这样我们就可以确定根本原因并在问题变成更大的问题之前解决它。
在本文中,我们将研究两个数据可观察性支柱,它们旨在为我们提供这种关键的环境— 模式和血统。同样,我们将使用像 Jupyter 和 SQLite 这样的轻量级工具,因此您可以轻松地构建我们的环境并亲自尝试这些练习。让我们开始吧。
我们的数据环境
本教程基于奥莱利课程 练习 2 和练习 3 , 管理数据停机 。欢迎您使用 Jupyter 笔记本和 SQL 自行尝试这些练习。我们将在以后的文章中更详细地讨论,包括练习【4】*。*
如果你读过这个系列的第一部分,你应该熟悉我们的数据。像以前一样,我们将使用关于可居住外行星的模拟天文数据。我们用 Python 生成了数据集,对我在生产环境中遇到的真实事件的数据和异常进行建模。这个数据集完全可以免费使用,如果您感兴趣的话,存储库中的 utils 文件夹包含生成数据的代码。
我使用的是 SQLite 3.32.3,它应该可以通过简单的设置从命令提示符或 SQL 文件访问数据库。这些概念可以扩展到任何查询语言,这些实现可以扩展到 MySQL、Snowflake 和其他数据库环境,只需做很小的改动。
再一次,我们有了系外行星表:
****$ sqlite3 EXOPLANETS.db
sqlite> PRAGMA TABLE_INFO(EXOPLANETS);
0 | _id | TEXT | 0 | | 0
1 | distance | REAL | 0 | | 0
2 | g | REAL | 0 | | 0
3 | orbital_period | REAL | 0 | | 0
4 | avg_temp | REAL | 0 | | 0
5 | date_added | TEXT | 0 | | 0****
系外行星中的数据库条目包含以下信息:
0._id
:对应行星的 UUID。
distance
:距离地球的距离,以光年为单位。
2.g
:表面重力为 g 的倍数,引力常数。
3.orbital_period
:单个轨道周期的长度,以天为单位。
4.avg_temp
:平均表面温度,单位为开氏度。
5.date_added
:我们的系统发现这颗行星并自动将其添加到我们的数据库的日期。
注意,对于给定的行星,由于数据缺失或错误,一个或多个distance
、g
、orbital_period
和avg_temp
可能是NULL
。
sqlite> SELECT * FROM EXOPLANETS LIMIT 5;
请注意,这个练习是追溯性的—我们正在查看历史数据。在生产数据环境中,数据可观察性是实时的,并应用于数据生命周期的每个阶段,因此将涉及与此处略有不同的实现。
看起来我们最早的数据是 2020-01-01(注意:大多数数据库不会存储单个记录的时间戳,所以我们的DATE_ADDED
栏会跟踪我们)。我们的最新数据…
****sqlite> SELECT DATE_ADDED FROM EXOPLANETS ORDER BY DATE_ADDED DESC LIMIT 1;
2020–07–18****
…看起来来自 2020 年 7 月 18 日。当然,这是我们在过去的文章中使用的同一张表。如果我们想要探索模式和血统的更多上下文相关的支柱,我们将需要扩展我们的环境。
现在,除了EXOPLANETS
之外,我们还有一个叫做EXOPLANETS_EXTENDED
的表,它是我们过去的表的超集。把这些看作是在不同时刻的同一张桌子是很有用的。事实上,EXOPLANETS_EXTENDED
的数据可以追溯到 2020 年 1 月 1 日…**
****sqlite> SELECT DATE_ADDED FROM EXOPLANETS_EXTENDED ORDER BY DATE_ADDED ASC LIMIT 1;
2020–01–01****
…但也包含截至 2020 年 9 月 6 日的数据,比EXOPLANETS
更远:
****sqlite> SELECT DATE_ADDED FROM EXOPLANETS_EXTENDED ORDER BY DATE_ADDED DESC LIMIT 1;
2020–09–06****
可视化模式更改
这些表之间还有一些不同之处:
****sqlite> PRAGMA TABLE_INFO(EXOPLANETS_EXTENDED);
0 | _ID | VARCHAR(16777216) | 1 | | 0
1 | DISTANCE | FLOAT | 0 | | 0
2 | G | FLOAT | 0 | | 0
3 | ORBITAL_PERIOD | FLOAT | 0 | | 0
4 | AVG_TEMP | FLOAT | 0 | | 0
5 | DATE_ADDED | TIMESTAMP_NTZ(6) | 1 | | 0
6 | ECCENTRICITY | FLOAT | 0 | | 0
7 | ATMOSPHERE | VARCHAR(16777216) | 0 | | 0****
除了EXOPLANETS
中的 6 个字段外,EXOPLANETS_EXTENDED
表还包含两个附加字段:
6.eccentricity
:行星围绕其主星的轨道偏心。
7.地球大气的主要化学成分。
请注意,与distance
、g
、orbital_period
和avg_temp
一样,eccentricity
和atmosphere
都可能是给定行星的NULL
,这是数据缺失或错误的结果。比如流氓行星轨道偏心率未定义,很多行星根本没有大气层。
还要注意,数据没有回填,这意味着从表格开始的数据条目(数据也包含在EXOPLANETS
表格中)没有偏心率和大气信息。
****sqlite> SELECT
...> DATE_ADDED,
...> ECCENTRICITY,
...> ATMOSPHERE
...> FROM
...> EXOPLANETS_EXTENDED
...> ORDER BY
...> DATE_ADDED ASC
...> LIMIT 10;
2020–01–01 | |
2020–01–01 | |
2020–01–01 | |
2020–01–01 | |
2020–01–01 | |
2020–01–01 | |
2020–01–01 | |
2020–01–01 | |
2020–01–01 | |
2020–01–01 | |****
添加两个字段是一个 模式 变更 的例子——我们的数据的正式蓝图已经被修改。当对数据结构进行更改时,会发生架构更改,手动调试可能会令人沮丧。模式更改可以表明关于数据的许多事情,包括:
- 添加新的 API 端点
- 假定已弃用但尚未弃用的字段
- 列、行或整个表格的增加或减少
在一个理想的世界中,我们想要一个这种变化的记录,因为它代表了我们管道中可能出现的问题的一个向量。不幸的是,我们的数据库并没有自然地配置来跟踪这样的变化。它没有版本历史。
我们在第一部分中查询个人记录的年龄时遇到了这个问题,并添加了DATE_ADDED
列来应对。在这种情况下,我们将做一些类似的事情,只是增加了一个完整的表:
****sqlite> PRAGMA TABLE_INFO(EXOPLANETS_COLUMNS);
0 | DATE | TEXT | 0 | | 0
1 | COLUMNS | TEXT | 0 | | 0****
在任何给定的日期,EXOPLANETS_COLUMNS
表通过记录EXOPLANETS_EXTENDED
中的列来“版本化”我们的模式。查看第一个和最后一个条目,我们看到这些列在某一点上确实发生了变化:
****sqlite> SELECT * FROM EXOPLANETS_COLUMNS ORDER BY DATE ASC LIMIT 1;
2020–01–01 | [
(0, ‘_id’, ‘TEXT’, 0, None, 0),
(1, ‘distance’, ‘REAL’, 0, None, 0),
(2, ‘g’, ‘REAL’, 0, None, 0),
(3, ‘orbital_period’, ‘REAL’, 0, None, 0),
(4, ‘avg_temp’, ‘REAL’, 0, None, 0),
(5, ‘date_added’, ‘TEXT’, 0, None, 0)
]sqlite> SELECT * FROM EXOPLANETS_COLUMNS ORDER BY DATE DESC LIMIT 1;
2020–09–06 |
[
(0, ‘_id’, ‘TEXT’, 0, None, 0),
(1, ‘distance’, ‘REAL’, 0, None, 0),
(2, ‘g’, ‘REAL’, 0, None, 0),
(3, ‘orbital_period’, ‘REAL’, 0, None, 0),
(4, ‘avg_temp’, ‘REAL’, 0, None, 0),
(5, ‘date_added’, ‘TEXT’, 0, None, 0),
(6, ‘eccentricity’, ‘REAL’, 0, None, 0),
(7, ‘atmosphere’, ‘TEXT’, 0, None, 0)
]****
现在,回到我们最初的问题:模式到底是什么时候改变的?因为我们的列列表是按日期索引的,所以我们可以用一个快速的 SQL 脚本找到更改的日期:
以下是返回的数据,为了便于阅读,我对其进行了重新格式化:
****DATE: 2020–07–19
NEW_COLUMNS: [
(0, ‘_id’, ‘TEXT’, 0, None, 0),
(1, ‘distance’, ‘REAL’, 0, None, 0),
(2, ‘g’, ‘REAL’, 0, None, 0),
(3, ‘orbital_period’, ‘REAL’, 0, None, 0),
(4, ‘avg_temp’, ‘REAL’, 0, None, 0),
(5, ‘date_added’, ‘TEXT’, 0, None, 0),
(6, ‘eccentricity’, ‘REAL’, 0, None, 0),
(7, ‘atmosphere’, ‘TEXT’, 0, None, 0)
]
PAST_COLUMNS: [
(0, ‘_id’, ‘TEXT’, 0, None, 0),
(1, ‘distance’, ‘REAL’, 0, None, 0),
(2, ‘g’, ‘REAL’, 0, None, 0),
(3, ‘orbital_period’, ‘REAL’, 0, None, 0),
(4, ‘avg_temp’, ‘REAL’, 0, None, 0),
(5, ‘date_added’, ‘TEXT’, 0, None, 0)
]****
通过这个查询,我们返回令人不快的日期:2020–07–19。像新鲜度和分布可观察性一样,实现模式可观察性遵循一种模式:我们识别发出管道健康信号的有用元数据,跟踪它,并构建检测器来警告我们潜在的问题。提供一个类似于EXOPLANETS_COLUMNS
的附加表是跟踪模式的一种方式,但是还有很多其他方式。我们鼓励您考虑如何为自己的数据管道实现模式更改检测器!
可视化血统
我们将世系描述为数据可观察性的 5 个支柱中最全面的一个,这是有充分理由的。
Lineage 通过告诉我们(1)哪些下游来源可能会受到影响,以及(2)哪些上游来源可能是根本原因,将事件联系起来。虽然用 SQL 代码“可视化”血统并不直观,但一个简单的例子可以说明它是如何有用的。
为此,我们需要再次扩展我们的数据环境。
简介:宜居
让我们向数据库添加另一个表。到目前为止,我们一直在记录系外行星的数据。这里有一个有趣的问题要问:这些行星中有多少可能有生命?
HABITABLES
表从EXOPLANETS
获取数据来帮助我们回答这个问题:
****sqlite> PRAGMA TABLE_INFO(HABITABLES);
0 | _id | TEXT | 0 | | 0
1 | perihelion | REAL | 0 | | 0
2 | aphelion | REAL | 0 | | 0
3 | atmosphere | TEXT | 0 | | 0
4 | habitability | REAL | 0 | | 0
5 | min_temp | REAL | 0 | | 0
6 | max_temp | REAL | 0 | | 0
7 | date_added | TEXT | 0 | | 0****
HABITABLES
中的条目包含以下内容:
0._id
:对应行星的 UUID。
1.perihelion
:在一个轨道周期内离天体最近的距离。
2.aphelion
:一个轨道周期内到天体的最远距离。
3.地球大气的主要化学成分。
4.habitability
:一个介于 0 和 1 之间的实数,表示该星球有多大可能孕育生命。
5.min_temp
:星球表面的最低温度。
6.max_temp
:星球表面最高温度。
7.我们的系统发现这颗行星并将其自动添加到我们的数据库中的日期。
与EXOPLANETS
中的列一样,perihelion
、aphelion
、atmosphere
、min_temp
和max_temp
的值允许为NULL
。事实上,perihelion
和aphelion
将是EXOPLANETS
中任意_id
的NULL
,其中eccentricity
是NULL
,因为你使用轨道偏心率来计算这些度量。这解释了为什么这两个字段在我们的旧数据条目中总是NULL
:
****sqlite> SELECT * FROM HABITABLES LIMIT 5;****
因此,我们知道HABITABLES
依赖于EXOPLANETS
(或者,同样地,EXOPLANETS_EXTENDED
)中的值,并且EXOPLANETS_COLUMNS
也是如此。我们数据库的依赖图如下所示:
图片由蒙特卡洛提供。
非常简单的血统信息,但已经很有用了。让我们在此图的上下文中查看HABITABLES
中的异常情况,看看我们能了解到什么。
调查异常现象
当我们有了一个关键指标,比如HABITABLES
中的可居住性,我们可以用几种方法来评估这个指标的健康程度。首先,某一天新数据的habitability
平均值是多少?
看着这些数据,我们发现有些不对劲。habitability
的平均值通常在 0.5 左右,但在记录的数据中后来减半至 0.25 左右。
分布异常…但是是什么引起的呢?
这是一个明显的分布异常,但是到底发生了什么呢?换句话说,这个异常的根源是什么?**
为什么我们不像我们在第一部分中做的那样,看看适合居住的比率呢?
幸运的是,这里看起来没有什么不符合角色的:
但这看起来不像是我们问题的起因。如果我们看看另一个分布健康指标,零值比率,会怎么样?****
这里似乎有更明显的问题:
从历史上看,habitability
实际上从未为零,但在后来的日子里,它平均飙升至近 40%。这具有降低磁场平均值的检测效果。
分布异常…但是是什么引起的呢?
我们可以修改我们在第一部分中构建的一个分布检测器,以在habitability
场中获得可感知的零速率的第一个日期:
我通过命令行运行了这个查询:
******$ sqlite3 EXOPLANETS.db < queries/lineage/habitability-zero-rate-detector.sql
DATE_ADDED | HABITABILITY_ZERO_RATE | PREV_HABITABILITY_ZERO_RATE
2020–07–19 | 0.369047619047619 | 0.0******
2020 年 7 月 19 日是零利率开始显示异常结果的第一天。回想一下,这与EXOPLANETS_EXTENDED
中的模式变更检测是同一天。EXOPLANETS_EXTENDED
在HABITABLES
的上游,所以这两起事件很有可能是有关联的。
通过这种方式,沿袭信息可以帮助我们识别事件的根本原因,并更快地解决它们。比较HABITABLES
中对此事件的以下两种解释:****
- 在 2020 年 7 月 19 日,表格
HABITABLES
中可居住性一栏的零比率从 0%跃升至 37%。 - 在 2020 年 7 月 19 日,我们开始跟踪
EXOPLANETS
表中的另外两个字段eccentricity
和atmosphere
。这对下游工作台HABITABLES
有不利影响,每当eccentricity
不为NULL
时,经常将字段min_temp
和max_temp
设置为极值。反过来,这导致了零速率的habitability
场尖峰,我们检测到这是平均值的异常降低。
解释(1)仅使用了发生异常的事实。解释(2)使用沿袭,根据表和字段之间的依赖关系,将事件放在上下文中并确定根本原因。顺便说一句,( 2)中的所有内容实际上都是正确的,我鼓励你去改变周围的环境,自己去理解发生了什么。虽然这些只是简单的例子,配备(2)的工程师会更快地理解和解决潜在的问题,这都归功于适当的可观察性。****
下一步是什么?
跟踪模式更改和沿袭可以让您对数据的运行状况和使用模式有前所未有的了解,提供有关数据使用人、内容、位置、原因和方式的重要上下文信息。事实上,在理解数据停机的下游(通常是真实世界)含义时,模式和沿袭是两个最重要的数据可观察性支柱。
总结一下:
- 观察我们数据的模式意味着理解我们数据的形式结构,以及它何时以及如何变化。****
- 观察我们数据的血统意味着理解我们管道中的上游和下游依赖关系,并把孤立的事件放在更大的背景中。****
- 数据可观察性的这两个支柱都涉及跟踪适当的元数据,并以一种使异常可以理解的方式转换我们的数据。
- 更好的可观察性意味着更好地理解数据损坏的原因和方式,减少检测时间和解决时间。****
我们希望“上下文中的数据可观察性”的第二部分是有用的。
直到第三部分,这里祝你没有数据停机!
有兴趣了解更多有关蒙特卡洛数据可观测性方法的信息吗?将手伸向 瑞恩巴尔 ,以及蒙特卡洛团队***。*****
本文由瑞安·卡恩斯和巴尔·摩西撰写。
压缩分区的数据优化
TL;速度三角形定位法(dead reckoning)
借助基于使用模式和领域专家确定的高优先级列的智能数据优化,ORC 和 Parquet 等列数据能够创建更小的数据占用空间。这个存储操作是幂等的,不变的。根据数据当前的存储方式,节省的成本可能非常可观。在下面的案例中,我们在存储大小和查询持续时间方面实现了高达 70%的缩减。
介绍
因此,您已经创建了一个表。花了几个小时和几天来设置流和/或批处理管道,定义逻辑,解决错误和边缘情况,您已经成功了,祝贺您!您已经有了生产就绪的代码,并且正在用数据填充表分区。剩下的工作就是监控管道,收集统计数据,让用户构建他们的模型并报告任何问题,对吗?在这篇博文中,我们将在这样的假设下工作
- 我们正在处理 Pb 级的大型数据湖:具有许多 GB 的分区和 TB 级的目录,
- 潜在的问题与小的非最佳叶文件有关,与集群设置、架构等无关。
小活页文件
过了一段时间,我们的表已经写了许多小时、天、月等。我们的用户报告说,即使在访问单个分区时,表也很慢。经过调查,我们注意到管道正在分区中写入许多小的叶子文件。
小叶子文件问题是什么?
小文件是指比 HDFS 块大小小得多的文件。对于 parquet 和 orc 文件,大型数据的最佳文件大小在 512MB 到 1024MB 之间。由于我们处理的是大数据,我们的 HDFS 块大小将是 256MB。也就是说,我们的叶文件是块大小的 2–4 倍,这是最佳设置。
为什么我们会看到非最佳分区大小?
默认情况下,Spark 将在每个分区写入 200 个文件,因为spark.sql.shuffle.partitions=200
会引入一个会产生混洗的动作。在仅映射写入中,文件的数量将与初始分区的数量相关。在下面的示例中,假设正在从数据工程/模型管道写入数据,该管道在写入数据之前应用了一些逻辑,从而引入了洗牌(对于大多数产生业务使用表的管道逻辑来说是常见的)。
但是,根据您的集群设置或用户指定的设置,该数字可能会更高或更低。假设我们的表是按日和小时划分的。对于给定的一天,我们有 24 个小时,每个分区在 10GB 到 25GB 之间。我们的叶文件将在 51.2MB 和 128MB 之间。
天真的方法
对于此表,我们保留了 90 天的滚动数据。该表包含 432,000 个不大于 128MB 的叶文件,我们知道平均每天的数据量约为 2TB,因此该目录约为 180TB。使用 3 因子复制,我们看到的数据占用空间为 540TB。根据我们的同事和/或以前的经验,我们知道我们需要建立一个数据压缩工作。要做到这一点,我们可以使用联合或重新划分,但重新划分的使用将涉及洗牌。我们应该使用哪一个?因为不需要洗牌而合并?考虑 8GB 的分区:
图片拍摄于https://tech . true analytics . ai/posts/data-optimization-for-compacted-partitions/
在简单的方法中,我们设置一个全局联合或重新分区值,而不是每个分区的值。
有了 coalesce,我们的运行时间会更快,因为它不需要洗牌,数据大小也不会改变。但是,coalesce 不能保证它将数据平均分配到文件中,因此,我们不能使用值 8,这将导致文件大小介于 128MB 和 1024MB++之间,具体取决于使用全局值的最佳分区。
再分
对于重新分配,我们引入洗牌。不过,混洗的好处是,当 Spark 写入数据时,叶文件的大小将相对相等。一个有趣的值是分区的数量。如果重新分区将产生相同大小的叶文件,为什么我们不应该使用 8?由于重新分区会改变数据,如果数据有自然的顺序,列压缩的好处可能会被破坏。这意味着重新分区会产生 3 种潜在结果,它可以将数据打乱更好的自然顺序,数据将会减少,它可能不会立即影响数据大小,或者它可能会使数据变得更糟,实际上导致数据大小增加。
优化方法
怎样才能做得更好?首先,我们需要了解我们的数据。由此,哪些列对于模型构建、数据分析、过滤等是最重要的。使用该表时,某些列是否比其他列更重要?在过滤的情况下,我们不考虑分区列,因为当用户指定 where partition_col = some_partition
时,分区修剪会处理这些列。
线性排序
对于线性排序,我们希望按照层次结构中最有影响力的列来编写数据。要确定感兴趣的列,最好咨询表所有者、该表的高级用户以及这些数据源的主题专家(SME)。此外,对多列进行线性排序时,优先级基于它们在子句中的指定顺序,也就是说,列表中第一列的影响比最后一列大。因此,只需要挑选少数几个(不超过 10 个,但我建议少于 10 个)列。
大约在 2020 年 3 月至 4 月,我们 True Analytics Tech 在我们最大的数据集上展示了线性排序的优势。我们查看了两周的数据。当我们在下图中谈论运行时间时,这是为了运行计数。在每种情况下,我们在 JVM 预热后收集 10 次运行的结果。在一个更复杂的运行时测试中,这里没有显示,我们聚集了一些数据,并将其连接回原始数据 2 周。在优化的数据上,它运行了大约 5 到 10 分钟。然后,我们用克隆数据(2 周)和表中的数据对它进行了测试。两者都花了 1 个小时才完成 50%。我们克隆了 2 周的数据来运行,因此我们可以确定较长的运行时间与表的完整大小无关。
图片取自https://tech . true analytics . ai/posts/data-optimization-for-compacted-partitions/
我们的数据占用空间减少了 77%,在 2 周的采样数据中,运行时间减少了约 90%。由于这一结果,我们决定在我们最大的数据集上全面应用这一点,并在 2020 年优化我们的传入数据和旧数据。
该表中的数据已经增长到每天大约 2TB,通过应用线性排序,我们每天能够减少 1.5TB 的数据占用空间,并且通过 3 因子复制,我们每天可以节省 4.5TB 的数据。下面,您将看到由数据模型团队生成的数据监控,他们对我们的一个较大的数据集进行压缩。我们在删除非最佳数据之前等待了 8 天,因此尚未清理的数据将显示大约 2.5TB,因为两个源都存在。
我们对此基础层的保留策略是 90 天,由此,我们有 3 个按小时、天、月派生的聚合层,目前我们将保留大约 1-2 年。我们在这张单个表格上展示的累计节省为 1.322PB。此外,我们的受众团队还优化了他们的数据存储,又节省了 1PB。也就是说,截至 2021 年 6 月 17 日,我们已经保存了 2.331PB 的数据。
在 True Analytics,我们在内部存储数据。然而,为了说明优化压缩流程的好处,我们来看看亚马逊的定价层。我们将只关注存储节省,但请记住,最终用户将体验到运行时间的减少。对于新加坡地区,标准 S3 的成本是第一个 50TB 0.025 美元,第二个 450 TB 0.024 美元,之后 0.023 美元,这是每月的费用。对于亚马逊定价,我们基于 1 个因素;即 2.331 * 1024 / 3 = 795TB。通过减少数据占用空间,我们每月将节省 19,287.04 美元(623,164 泰铢)的成本。
空间填充曲线
空间填充曲线是我们可以用来优化数据的另一种广泛使用的方法。同样,我们希望确定最有影响的列,使用的列数规则在这里仍然适用。
空间填充曲线有多种风格:
- 阿砣
- 科赫
- 希尔伯特
- 莫顿
举几个大的例子。
为什么我们要使用空间填充曲线,而不仅仅是使用线性排序?如上所述的线性排序涉及多维索引,其中每个索引对查询结果的权重越来越小。如果您正在寻找存储数据的最佳方式,那么我们需要做的就是减少数据占用空间并能够将多维索引映射到一维。这就是空间填充曲线发挥作用的地方。这里我们来看一个在一组(x, y)
坐标上使用莫顿曲线(Z 排序)的例子。考虑设置{(1, 1), (1, 8), (2, 1), (3, 1)}
。这个集合目前按 x,y 线性排序,在 Z 排序中,会发生什么?
图片摘自https://tech . true analytics . ai/posts/data-optimization-for-compacted-partitions/
如果我们根据比特交织的结果对数据进行排序,我们将得到{(1, 1), (2, 1), (3, 1), (1, 8)}
。在 x 完全排序之前,y 在排序中起作用,允许其他变量列对排序后的数据产生影响。
当与表列统计一起使用时,空间填充曲线可以加快数据检索速度。发生这种情况时,查询可以跳过较大的数据块,从而最大限度地减少需要遍历的文件数量。在这里,在分析平台上,我们运行了 Z 排序,并将其与我们的原始和线性结果进行了比较。我们看到线性排序的数据占用空间减少了 16%。在这种情况下,我们的原始数据是 1.5TB,线性数据是 398GB,应用 Z 排序时是 334GB。
我们还没有对带有表统计的大型数据集进行完整的性能测试。我们无法显示使用 Z 排序对数据的好处。然而,Amazon 和 Databricks 都有文章概述了使用 Z 排序时的性能提升。对于我们的用例,我们既没有使用 Amazon 集群,也没有在 Deltalake 中使用 Databricks 优化。一切都是内部构建的,用于我们的内部集群。
确认
我们运行了一个验证套件来验证数据优化在数据方面是一个等幂变换。首先,我们在原始数据和优化数据的列上运行计数、最小值、最大值等标准指标,最后对两个数据集进行哈希运算以生成校验和。
结论
在我们开始使用这种优化压缩的一年半时间里,我们已经在最大的数据集上节省了 50–70%的数据。这提高了我们的集群利用率,减缓了我们的服务器扩展速度,使我们的运营团队不必经常重新平衡节点,并实现了性能提升,使我们的数据模型和数据科学团队能够以更少的资源更快地运行他们的管道。然而,我们还没有完成。我们正在将数据压缩扩展到本地集群中的大多数数据集。
原载于 2021 年 7 月 22 日https://tech . true analytics . ai。
多云未来的数据模式
多重云正在成为常态。当您的数据分布在多个云和本地时,这对数据科学和数据工程意味着什么?
弗兰基·查马基在 Unsplash 上拍摄的照片
对于任何组织来说,最重要的资产就是他们的数据。但这也是最难管理的资产。我已经写了云计算如何发展以适应世界经济和贸易的不断变化,以及为什么每个组织都必须有一个云计算战略。虽然跟上不断变化的动态对于企业的繁荣和发展非常重要,但它也带来了一系列新的挑战。
尤其是当您引入多个云平台,使您的企业数据分布在不同的云中时,它会如何改变数据科学等式?您是否仍然能够利用来自所有云和本地的所有数据来连接这些点,并从中提取有意义的分析?
Gartner 预测了,这并不奇怪,
到 2021 年,超过 75%的大中型组织将采用多云和/或混合 IT 战略。
随着我们与一些最大的企业合作,实现他们的云计算和云计算驱动的数据分析之旅,我们在谷歌看到了这一预测的现实已经展开。
在这篇文章和下一篇文章中,我们将讨论不同的数据模式,您可以在跨多个云的数据科学、数据工程和 BI 任务中采用这些模式。
通过跨多个云连接数据实现商业智能
作者图片
Looker 是谷歌的云端商业智能和数据分析平台。Looker 的强大之处在于它能够连接到跨不同云或内部的多个数据源,并创建报告和仪表板。它的 LookML 有一套丰富的 API,可以让你在网站中嵌入数据、可视化和见解,并使用工作流集成,在 Looker 本身的基础上构建应用程序。
Looker 可以部署在各种位置,它不一定是谷歌云。如果仪表板上显示的大部分数据位于另一个云中或内部,那么 Looker 最好部署在靠近主数据源的地方。
使用 BigQuery-Omni 访问多个云中的数据
作者图片
适用于查询 BigQuery 托管存储的相同控制机制可以应用于驻留在 AWS 中的数据。另一个有趣的模式是将 Looker 与 BigQuery Omni 结合使用。
我们来看看这个图和之前的图有什么不同。Looker 连接到 BigQuery,而不是从 Looker 连接到 Athena,这是查询 S3 存储桶中的数据所需要的。从技术上讲,它连接到控制平面,控制平面转到驻留在 AWS 中的数据平面,并根据 S3 存储桶中的数据处理查询。
使用这种模式的优点是,适用于查询 BigQuery 托管存储的相同控制机制可以应用于驻留在 AWS 中的数据,这将是一个更受控制和更简单的仪表板设置。从旁观者的角度来看,BigQuery Omni 的连接性与托管存储或 Google Cloud 上的联合数据源中的 BigQuery 表的连接性完全相同。当数据科学家或 BI 分析师无法通过 SQL 接口访问 Athena 或其他方式来查询 S3 存储桶中的数据时,这也会变得非常方便。BigQuery Omni 成为 SQL 引擎,从熟悉的 Google 平台控制它既方便又安全。
之前模式的其余部分保持不变——您仍然可以连接到其他数据源,包括 Google Cloud 上的 BigQuery 数据和本地数据仓库,并在同一个仪表板上可视化它,并构建相同类型的应用程序和工作流。
在下一篇文章中,我们将讨论一些更高级的架构模式,它们可以让您跨多个云运行您的分析。
数据视角——如何更理智地看待数据
关于堆栈和队列的概述和讨论,以及为什么了解它们有助于编程。
(src =https://pixabay.com/images/id-1954920/
介绍
至少从软件的角度来看,数据的概念在现代社会中根深蒂固。每个人至少知道数据的基本知识,我们知道如何保存它,我们知道如何使用它。作为数据科学家和计算机程序员,我们与数据的关系要复杂得多,有时甚至是浪漫的。我们可以用许多不同的方式来看待数据的概念,但通常情况下,在我们的计算机下发生的大量数据传输都远离用户。当然,无论用户是否是软件工程师,情况都是如此。
作为数据科学家,我们在不断学习,数据科学家当然需要学习的一件事就是数据。人们可以从很多不同的角度来看待数据。例如,让我们考虑一个软件工程师,他在查看数据框之前从来没有做过任何分析工作——他可能不知道关于他们正在查看的特性的许多事情。今天,我想揭示一些关于数据的观点,一些在数据科学领域取得成功的思考方式。使用这些想法和思维方式,人们可能会发现在不同的场景中处理数据要容易得多。
№1:硬件视角
我想向大家展示的第一个视角是硬件视角。许多数据科学家在查看数据时擅长于其他角度,但从更面向硬件的角度查看数据可能会成为任何真正想要创建令人敬畏的东西的有抱负的工程师的绊脚石。不用说,如果你想在任何定制的机器学习网络上工作,你可能想首先了解它运行的硬件。
也就是说,以这种方式查看数据对于一个更注重分析的数据科学家来说可能有些不和谐。每当我们从不同的角度看问题时,我们都在看功能和观察结果,但在更面向硬件的角度,我们主要看的是书面存储器,更重要的是编程,我们的随机存取存储器,或 RAM。让我们从堆栈和队列的角度来看待数据,而不是将数据视为特征和观察值。
堆栈是内存中一堆数据的名称。这堆数据有几个不同的层次。它本质上只是一堆字节。例如,我们在一台计算机上有 4GB 的内存,这将是 4000 兆字节,或 4e+9 字节。每次内存被移动到堆栈时,一个新的“堆”被放在旧堆栈的顶部。在某种程度上,我们的筹码就像是一堆叠在一起的华夫饼。除此之外,每块华夫饼都是独一无二的。关于堆栈,要记住的最重要的事情是,当我们考虑到这一点时,它是没有组织的。我们可以添加华夫饼干,也可以去掉华夫饼干。如果我们想在中间放一个华夫饼干,我们需要在访问它之前把数据从上面拿走。每当我们从内存中删除一部分字节时,就称为弹出,当我们添加它时,我们采用注册表术语“推送数据”
队列就像一个堆栈,唯一的区别是最新的版本应该先放。理解数据是如何存储在内存中的肯定会应用到一个人的编程技能中。尤其是当一个人在用 C 或汇编这样的低级编程语言工作时。
№2:软件视角
另一种在给定时刻查看数据的方式是从软件的角度。在我看来,这是纯分析视角和硬件视角之间的一个很好的中间点。在大多数情况下,以这种方式查看数据可能是最理想的,因为我们处理的数据类型通常不是简单的特性集。有时,数据甚至根本无法被解释,除非计算机正在这样做,例如分解。如果你对分解不太了解,我还有一篇文章解释了奇异值分解的数学细节,你可以在这里读到:
[## 深入奇异值分解
towardsdatascience.com](/deep-in-singular-value-decomposition-98cfd9532241)
每当我们从软件的角度考虑数据时,我们都在处理文件,有时是数据类型,但通常不是结构化数据集。换句话说,这种思维方式经常在分析数据集之前就被使用了。如果有人对数据科学世界完全陌生,我可能会建议先从软件角度学习数据,因为该领域的大多数初级数据科学家最有可能以这种方式工作。
№3:分析视角
最后一种看待数据的方式是从分析的角度。这是更典型的科学方法,但是对于那些对软件比对科学更感兴趣的人来说,这也是一个缺点。然而,分析视角非常重要,因为它允许数据科学家将他们的数据视为特征和观察结果,而不是位和字节。为了正确理解数据,与数据和数据本身的特性建立某种联系是必不可少的。
这部分数据的一个重要方面是理解该领域内部通常使用的语言。正如我们之前在我的文章中提到的,数据科学是许多不同领域和专业知识的混搭。虽然这是它如此有趣的部分原因,但一个重要的问题是所有这些职业对不同的事物都有自己的术语。也就是说,从分析的角度来看,我们不想作为软件工程师,而是作为数据科学家来看待事物。这些不是“列”和“行”,而是特性和它们各自的观察结果。
结论
数据是一种复杂的工具,不用说,它是数据科学领域的一个非常大的部分。在成功构建许多项目时,理解来自多个领域的数据是绝对重要的。在一些企业环境中,有些人可能只能勉强度日,而对他们所处理的数据的每一部分只有最低限度的了解,但是,为了成为比这些人更好的数据科学家,我认为在这方面扩展一点肯定是一个好主意。非常感谢您阅读我的文章!
了解如何使用 Kafka Connect 建立从 PostgreSQL 到 Cassandra 的数据管道
关于如何使用 Kafka Connect 进行实时数据同步的教程
昆腾·德格拉夫在 Unsplash 上的照片
Apache Kafka 通常作为整体数据架构的核心组件,其他系统将数据注入其中。但是,Kafka(主题)中的数据只有在被其他应用程序使用或被其他系统接收时才有用。尽管使用您选择的语言和客户端 SDK 可以使用 Kafka 生产者/消费者API构建解决方案,但是 Kafka 生态系统中还有其他选择。
其中之一是 Kafka Connect ,它是一个平台,以可伸缩和可靠的方式在 Apache Kafka 和其他系统之间传输数据。它支持几个现成的连接器,这意味着您不需要定制代码来集成外部系统与 Apache Kafka。
本文将演示如何使用 Kafka 连接器的组合来建立一个数据管道,以便将关系数据库(如 PostgreSQL )中的记录实时同步到Azure Cosmos DB Cassandra API。
该应用程序的代码和配置可在 GitHub repo 中获得—https://github.com/abhirockzz/postgres-kafka-cassandra
这是一个高层次的概述…
…本文中介绍的端到端流程。
针对 PostgreSQL 表中数据的操作(在本例中适用于INSERT
s)将作为change data
事件被推送到 Kafka 主题,这要感谢 Debezium PostgreSQL 连接器,它是一个 Kafka Connect source 连接器——这是使用一种称为 Change Data Capture (也称为 CDC)的技术实现的。
变更数据捕获:快速入门
这是一种用于跟踪数据库表中行级变化以响应创建、更新和删除操作的技术。这是一个强大的功能,但只有在有办法利用这些事件日志并使其对依赖于该信息的其他服务可用时才是有用的。
Debezium 是一个开源平台,构建于不同数据库中可用的变更数据捕获特性之上。它提供了一组 Kafka Connect 连接器,这些连接器利用数据库表中的行级更改(使用 CDC)并将它们转换成事件流。这些事件流被发送到 Apache Kafka 。一旦变更日志事件在 Kafka 中,它们将对所有下游应用程序可用。
这与 卡夫卡连接 JDBC 连接器 所采用的“轮询”技术不同
第二部分
在管道的后半部分, DataStax Apache Kafka 连接器(Kafka Connectsinkconnector)将变更数据事件从 Kafka topic 同步到 Azure Cosmos DB Cassandra API 表。
成分
这个例子使用 Docker Compose 提供了一个可重用的设置。这非常方便,因为它使您能够用一个命令在本地引导所有组件(PostgreSQL、Kafka、Zookeeper、Kafka Connect worker 和示例数据生成器应用程序),并允许迭代开发、实验等更简单的工作流。
使用 DataStax Apache Kafka 连接器的特定功能,我们可以将数据推送到多个表中。在这个例子中,连接器将帮助我们将变更数据记录持久化到两个 Cassandra 表中,这两个表可以支持不同的查询需求。
下面是组件及其服务定义的分类——你可以参考 GitHub repo 中完整的docker-compose
文件。
- 卡夫卡和动物园管理员使用 debezium 图像。
- Debezium PostgreSQL Kafka 连接器在Debezium/connectDocker 图片中开箱即用!
- 为了作为 Docker 容器运行,DataStax Apache Kafka 连接器是在 debezium/connect 映像之上构建的。这个图像包括 Kafka 及其 Kafka Connect 库的安装,因此添加定制连接器非常方便。可以参考 Dockerfile 。
data-generator
服务将随机生成的(JSON)数据植入 PostgreSQL 的orders_info
表中。可以参考GitHub repo中的代码和Dockerfile
在继续之前,
首先创建 Cassandra 键空间和表
使用与下面相同的键空间和表名
CREATE KEYSPACE retail WITH REPLICATION = {'class' : 'NetworkTopologyStrategy', 'datacenter1' : 1};CREATE TABLE retail.orders_by_customer (order_id int, customer_id int, purchase_amount int, city text, purchase_time timestamp, PRIMARY KEY (customer_id, purchase_time)) WITH CLUSTERING ORDER BY (purchase_time DESC) AND cosmosdb_cell_level_timestamp=true AND cosmosdb_cell_level_timestamp_tombstones=true AND cosmosdb_cell_level_timetolive=true;CREATE TABLE retail.orders_by_city (order_id int, customer_id int, purchase_amount int, city text, purchase_time timestamp, PRIMARY KEY (city,order_id)) WITH cosmosdb_cell_level_timestamp=true AND cosmosdb_cell_level_timestamp_tombstones=true AND cosmosdb_cell_level_timetolive=true;
使用 Docker Compose 启动所有服务
git clone https://github.com/abhirockzz/postgres-kafka-cassandracd postgres-kafka-cassandra
正如承诺的那样,使用一个命令来启动数据管道的所有服务:
docker-compose -p postgres-kafka-cassandra up --build
下载和启动容器可能需要一段时间:这只是一个一次性的过程。
检查是否所有容器都已启动。在不同的终端中,运行:
docker-compose -p postgres-kafka-cassandra ps
数据生成器应用程序将开始将数据注入 PostgreSQL 中的orders_info
表。你也可以做快速检查来确认。使用[psql](https://www.postgresql.org/docs/current/app-psql.html)
客户端连接到 PostgreSQL 实例…
psql -h localhost -p 5432 -U postgres -W -d postgres
当提示输入密码时,输入
*postgres*
…并查询表格:
select * from retail.orders_info;
此时,您所拥有的只是 PostgreSQL、Kafka 和一个向 PostgreSQL 写入随机数据的应用程序。您需要启动 Debezium PostgreSQL 连接器来将 PostgreSQL 数据发送到 Kafka 主题。
启动 PostgreSQL 连接器实例
将连接器配置(JSON)保存到文件示例pg-source-config.json
{
"name": "pg-orders-source",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"database.hostname": "localhost",
"database.port": "5432",
"database.user": "postgres",
"database.password": "password",
"database.dbname": "postgres",
"database.server.name": "myserver",
"plugin.name": "wal2json",
"table.include.list": "retail.orders_info",
"value.converter": "org.apache.kafka.connect.json.JsonConverter"
}
}
要启动 PostgreSQL 连接器实例,请执行以下操作:
curl -X POST -H "Content-Type: application/json" --data @pg-source-config.json [http://localhost:9090/connectors](http://localhost:9090/connectors)
要检查 Kafka 主题中的变更数据捕获事件,请查看运行 Kafka connect worker 的 Docker 容器:
docker exec -it postgres-kafka-cassandra_cassandra-connector_1 bash
一旦您进入容器外壳,只需启动通常的 Kafka 控制台消费者进程:
cd ../bin./kafka-console-consumer.sh --bootstrap-server kafka:9092 --topic myserver.retail.orders_info --from-beginning
注意,根据 连接器约定 ,题目名称为
*myserver.retail.orders_info*
您应该会看到 JSON 格式的变更数据事件。
到目前为止一切顺利!数据管道的前半部分似乎在按预期工作。对于下半年,我们需要…
启动 DataStax Apache Kafka 连接器实例
将连接器配置(JSON)保存到文件 example,cassandra-sink-config.json
,并根据您的环境更新属性。
*{
"name": "kafka-cosmosdb-sink",
"config": {
"connector.class": "com.datastax.oss.kafka.sink.CassandraSinkConnector",
"tasks.max": "1",
"topics": "myserver.retail.orders_info",
"contactPoints": "<Azure Cosmos DB account name>.cassandra.cosmos.azure.com",
"loadBalancing.localDc": "<Azure Cosmos DB region e.g. Southeast Asia>",
"datastax-java-driver.advanced.connection.init-query-timeout": 5000,
"ssl.hostnameValidation": true,
"ssl.provider": "JDK",
"ssl.keystore.path": "<path to JDK keystore path e.g. <JAVA_HOME>/jre/lib/security/cacerts>",
"ssl.keystore.password": "<keystore password: it is 'changeit' by default>",
"port": 10350,
"maxConcurrentRequests": 500,
"maxNumberOfRecordsInBatch": 32,
"queryExecutionTimeout": 30,
"connectionPoolLocalSize": 4,
"auth.username": "<Azure Cosmos DB user name (same as account name)>",
"auth.password": "<Azure Cosmos DB password>",
"topic.myserver.retail.orders_info.retail.orders_by_customer.mapping": "order_id=value.orderid, customer_id=value.custid, purchase_amount=value.amount, city=value.city, purchase_time=value.purchase_time",
"topic.myserver.retail.orders_info.retail.orders_by_city.mapping": "order_id=value.orderid, customer_id=value.custid, purchase_amount=value.amount, city=value.city, purchase_time=value.purchase_time",
"key.converter": "org.apache.kafka.connect.storage.StringConverter",
"transforms": "unwrap",
"transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState",
"offset.flush.interval.ms": 10000
}
}*
启动连接器:
*curl -X POST -H "Content-Type: application/json" --data @cassandra-sink-config.json [http://localhost:8080/connectors](http://localhost:8080/connectors)*
如果一切都已正确配置,连接器将开始从 Kafka 主题向 Cassandra 表泵送数据,我们的端到端管道将开始运行。
很明显你会想…
查询 Azure Cosmos DB
检查 Azure Cosmos DB 中的 Cassandra 表。如果您在本地安装了cqlsh
,您可以简单地像这样使用它:
*export SSL_VERSION=TLSv1_2 &&\
export SSL_VALIDATE=false &&\
cqlsh.py <cosmosdb account name>.cassandra.cosmos.azure.com 10350 -u <cosmosdb username> -p <cosmosdb password> --ssl*
如果没有,Azure 门户中的托管的 CQL shell 也相当好用!
以下是您可以尝试的一些查询:
*select count(*) from retail.orders_by_customer;
select count(*) from retail.orders_by_city;select * from retail.orders_by_customer;
select * from retail.orders_by_city;select * from retail.orders_by_city where city='Seattle';
select * from retail.orders_by_customer where customer_id = 10;*
结论
总之,您学习了如何使用 Kafka Connect 在 PostgreSQL、Apache Kafka 和 Azure Cosmos DB 之间进行实时数据集成。由于样品采用基于 Docker 容器的方法,您可以根据自己的独特要求轻松定制,冲洗并重复!
以下主题可能也会引起您的兴趣…
如果您觉得这很有用,您可能还想浏览以下资源:
- 使用 Blitzz 将数据从 Oracle 迁移到 Azure Cosmos DB Cassandra API
- 使用 Azure Databricks 将数据从 Cassandra 迁移到 Azure Cosmos DB Cassandra API 帐户
- 快速入门:构建一个 Java 应用程序来管理 Azure Cosmos DB Cassandra API 数据(v4 驱动程序)
- Azure Cosmos DB Cassandra API 支持的 Apache Cassandra 特性
- 快速入门:用 Python SDK 和 Azure Cosmos DB 构建 Cassandra 应用
自动驾驶汽车试驾结果|数据管道
自动驾驶汽车已经在公共道路上行驶了数百万英里。目前为止结果如何?
你是一个自动驾驶汽车(AV)爱好者吗?你想知道 AV 发展到什么程度了吗?你是否在一家影音公司工作,想知道自己的表现与竞争对手相比如何?
没有单一的答案,没有单一的来源。挖掘法规、安全标准、市场研究、业务案例接受脉冲、独立和关联的汽车子系统报告、算法基准、测试报告、燃烧率分析,等等。
需要一个以上的职位来全面分析所有这些问题。这篇文章占了一大块。它利用公开可用的数据,以定量的方式关注 AV 道路测试报告。它为您提供了代码和指南,以便您可以开始使用 AV 试驾结果数据管道(请记住,数据不是现成的可消费格式)。最后是对未来改进的思考和基于整合数据的简要分析。
系好你的安全带,跟着读…
公开的 AV 试驾结果数据
虽然 Udacity、加州大学伯克利分校、Nvidia 和更多机构以专门构建的格式公开提供了大量 AV 传感器数据来试验算法,但缺乏公开提供的 AV 测试结果数据来全面评估当前状况,甚至是公共政策!AV 测试分三个阶段进行:模拟、赛道测试和道路测试。道路测试是在现场部署 AV 之前的最后一英里,监管机构会查看道路测试数据。
加州车管所公布了 AV 碰撞报告和脱离报告。在 CA 持有 AV 测试许可的公司向 DMV 提交报告。安全性是反车辆地雷可行性的首要标准。碰撞报告提供了直接的见解。脱离接触报告是一个替代指标。假设是,如果 AV 已经脱离,它就没有为无人驾驶做好准备。如果安全驾驶员因为 AV 没有完全遵循他/她的想法而担心,并且他/她启动了脱离,该怎么办?如果场景在模拟中运行,结果是安全的,但没有脱离,会怎么样?关键是这些报告并非没有缺陷。
那评价的备选选择是什么?目前没有。**除了加州,美国没有其他州公布过此类数据。**一些州,例如亚利桑那州、佛罗里达州已经在 PowerPoint 演示文稿中分享了 AV 文学。
了解加州车管所 AV 数据管道
数据管道的目的是以可用于分析的格式访问数据。
谁可以使用数据管道?在 AV 行业工作的开发人员可以访问特定公司的丰富数据,但缺乏其他公司的数据。他们可以用它作为基准。市场研究公司和影音爱好者也可以使用它。
是否需要数据管道?脱离报告以 excel 格式提供,可由任何分析工具获取,并且不需要数据管道。由脱离度驱动的直觉本身就是一个有争议的话题。碰撞报告实际上是一个以 pdf 格式存储的表单。每年都有数百份碰撞报告提交。手动分析数据是不可能的。
数据管道在处理不同 AV 公司在不同日期提交的数百份多页表格后,生成一个整洁的 excel 文件。以下是字段/列标题列表和字段子集上的字段值片段。
字段列表(来源:作者)
字段列表(来源:作者)
字段列表(来源:作者)
字段值的片段(来源:作者)
上述输出的输入是从 CA DMV 站点下载的一堆 pdf 表单文件。下面的截图显示 1) Waymo、Cruise、苹果、Zoox、Aurora 购买了优步的自动驾驶设备,以及更多提交 AV drive 崩溃报告的 AV 公司 2)先睹为快 Lyft 提交的 pdf 表格。
输入=不同公司在不同日期的 pdf 表单(来源:作者)
在 Lyft 自动驾驶实验室提交给 CA DMV 的 pdf 表格中先睹为快(来源:作者)
数据管道入门指南
有几点值得注意。这两种光学字符识别(OCR)技术都用于从表单中提取信息,它们相互补充。
空间用于自然语言处理(NLP)。表单中有一个描述部分,如下所示。并不是所有的影音公司都以同样的方式填写。如果有,速度和相关的对象值将被捕获到 excel 的速度和上下文字段中。在下面的截图中:1) Waymo 填写了自动驾驶车辆的速度。2)提取速度、动词和对应的名词对象,从而实现词性依存分析。图中显示了 spaCy 文档中的图表。这是一个有趣的实验。但由于数据的不一致性和 NLP 的复杂性,这部分并不是很有成果。
Waymo 在 CA DMV 表格中输入了 19 年 2 月 21 日撞车时 AV 速度的详细信息(来源:作者)
使用 spaCy 的依赖解析器可视化(来源: spaCy 开发者文档
AV 测试结果数据管道的未来改进
让某样东西工作,测试它,让它变得更好。当前版本是最低可行版本。它允许按照碰撞报告的当前格式创建一个 excel 文件。
以下是未来改进的想法:
捕捉多选选项。举例来说,天气可能同时下雨、有雾和刮风。表格可以通过勾选所有这些选项来填写。到目前为止,没有表单选择一个以上的选项,代码只支持一个选项。
自动收集输入文件。从 CA DMV 网站下载文件需要一段时间。考虑使用网络爬虫和自动下载。爬虫可以第一次运行,然后以每季度或每年的频率收集新文件。代码应该只快速增量地处理新文件。
拥有易于维护且健壮的代码库。CA DMV 将来可能会改变表格的格式。这一特点强调了 AV 领域对标准的需求。这将要求不可避免的代码变更。目前在图像处理和最终格式检查时需要一些人工干预。代码应该没有任何人工干预。
NLP 识别相关信息:如果数据和管道继续有用,最后要改进的是准确和相关的文本提取。
结果分析一瞥
使用数据管道生成的 excel 文件,可以进行数据分析。下面是对 2019 年数据的分析;报告了 104 起事件。
CA DMV AV 碰撞报告 2019 |损坏程度|碰撞时的速度(来源:作者)
CA DMV AV 碰撞报告 2019 |碰撞类型| AV 碰撞前的移动(来源:作者)
在深入分析之前,看一下数据是值得的。数据有偏差。**所有记录都属于城市道路上的撞车和自动驾驶汽车。**没有关于高速公路驾驶或大型车辆(如自动驾驶卡车和公共汽车)的数据。
速度(mph)数据不足以推断结论,因为 75%的情况下没有报告速度。
令人惊讶的是,在预碰撞阶段,自动驾驶汽车直线行驶,然后发生碰撞。AV 在崩溃前停止似乎是一个常见的场景。AV 是否预见到了碰撞并停止了?还是在停车标志前停下来被撞了?当中等强度的碰撞发生时,AV 是否试图使其变得轻微?如果是,交换是什么?为了进一步了解情况,需要访问 AV 的事件日志和情况的原因分析。
有大量的追尾事故。汽车主机厂是否应该在车尾材料上进行创新和实验?
你注意到 AV 准备就绪的好消息了吗?虽然撞车确实发生了,但 70%的撞车是轻微的。让我们以积极的态度到达终点。
数据管道——可重用性、可扩展性
为质量、灵活性、透明度和增长发展数据管道
图片:去飞溅
设计可扩展、模块化、可重用的数据管道是一个更大的主题,与数据工程密切相关,因为这类工作涉及处理不同层的不断变化,如数据源、接收、验证、处理、安全、日志记录和监控。这些层之间的变化速度各不相同,根据管道的抽象级别和设计,它们对数据管道的影响也各不相同。在本帖中,我们将讨论横切关注点,特别是日志和监控,并讨论一些用例及模式,它们可能为在管道中构建模块化和可重用性提供基础
为了在数据管道的层中获得一些上下文,并开始映射配置,这里有一个概念性视图:
图:作者数据管道高级架构
这是一个简化的视图,因为这些层可以用许多不同的方式表示,但是在一个提炼的形式中,管道可以被认为是摄取、处理和结果层。对于每一层,我们可以从功能的角度来考虑,因为图像显示了功能块。块的内容根据层的要求而变化。这有助于我们从可以表示管道 DAG 的模板和配置方面进行思考。例如,下面的 yaml 配置:
图:为 DAG 层创作概念 Yaml 配置
接收、处理和结果层可以根据需求映射到不同的记录器和监视器。例如,在接收层,文件日志可以是 S3,事件日志可以是自定义的,监视器可以是 Stackdriver 和 Redash。但是,结果层可以作为数据狗映射到事件日志数据狗和事件监视器。
如果我们采用一个通用的管道,特别是查看日志和监控,这种表示将作为 Dag 方法实现,其中日志记录器和监控器将与 Dag 代码耦合。这涉及到更多的编码,它是脆弱的,难以重用的,并且违反了 SRP、DRY、Open Closed 的设计原则,使得整个管道不稳定和不可靠。如果我们将这个问题陈述扩展到监控和日志记录之外,我们将在不同的功能块中看到类似的问题—数据质量/验证、安全性/隐私等。
当我们看到问题的相似性时,这是识别共同主题的标志。例如,这里我们有特定于 DAG 和特定于功能(日志记录、监控)的职责。此外,我们希望消除这些之间的耦合,并增加这些之间的内聚力。这给了我们足够的背景来开始考虑设计模式。参考 设计模式:可重用面向对象软件的元素“四人帮”(Gamma 等人)。对于我们的上下文,我们将展示这些模式如何在我们的设计中发挥作用。下图显示了适用于不同层的模式的管道 DAG 的高级视图:
图:适用于不同 DAG 层的作者模式
第一组模式是创造性的或结构性的。它们允许我们将横切关注点的创建和结构分开,例如日志记录、来自 Dag 特定区域的监控。工厂和抽象工厂有助于从 Dag 代码中抽象和分离不同的记录器和监视器,允许 Dag 代码库在不依赖于日志记录和监视代码库的情况下发展
*from loglib import LogConfigConst
from abc import ABC, abstractmethod
from boto3 import Session
class Logger(metaclass=ABC.ABCMeta):
@abstractmethod
def log(self,message):
pass
class S3Logger(Logger):
*""" log to AWS S3 """* _initialized = False
_instance = None
_s3session = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(S3Logger, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self, *args, **kwargs):
if self._initialized:
return
super(S3Logger, self).__init__(*args, **kwargs)
#get the kwarg config that has details of S3 connection params -- Region, Public Key, Secret Key
logconfig=kwargs["logconfig"]
logConfigConst=LogConfigConst()
#establish s3 session, use boto3
self.__class__._s3session=Session(aws_access_key_id=logconfig[logConfigConst.AWS_ACCESS_KEY_ID],
aws_secret_access_key=logconfig[logConfigConst.AWS_SECRET_ACCESS_KEY],
region_name=logconfig[logConfigConst.AWS_REGION])
self.__class__._initialized = True
@classmethod
def log(cls, message):
#log to S3
return*
显示 Logger 和 LogFactory 类示例的代码示例:
*class LogFactory:
_dictlogger={}
@classmethod
def create_logger(config):
logger=None
if config.name == LogConfigConst.filelog.names.S3:
logger=S3Logger(logconfig=config)
elif config.name == LogConfigConst.eventlog.names.DataDog:
logger=DataDogLogger(logconfig=config)
elif config.name == LogConfigConst.databaselog.names.PostGresDB:
logger=PostGresDBLogger(logconfig=config)
return logger
@classmethod
def get_logger(cls,config):
if cls._dictlogger[config.name] is None:
cls.dict_logger[config.name] = cls.create_logger(config)
return cls.dict_logger[config.name]*
第二组模式是行为模式。它们允许我们在保持 SRP、DRY 和 SOLID 原则的同时指定行为。装饰器模式广泛用于修改或添加现有函数的行为。正如我们在下面看到的,日志记录和监控是直接适用的。
*import functools,sys,traceback
from loglib import LoggingFacade
class LoggingDecorator:
def __init__(self,config):
self.logFacade=LoggingFacade(config_template=config)
def log_error(self,func=None):
def error_log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
# Execute the called function
return func(*args, **kwargs)
except Exception as e:
error_msg = 'And error has occurred in '.join(func.__name__)
self.logFacade.log_filestore(error_msg)
raise e
return wrapper
return error_log
@LoggingDecorator.log_error
def push_exception_to_xcom(kwargs):
*"""To push exception occuring from dag task to xcom ; this will be used in alert/reporting"""* exc_type, exc_value, exc_traceback = sys.exc_info()
exception_details= ''.join(traceback.format_exception(etype=type(exc_type),value=exc_value, tb=exc_traceback))
kwargs['ti'].xcom_push(key='exception_details', value=exception_details)*
显示 LoggingDecorator 示例的代码示例:
当客户端或消费者需要更窄或特定的 api 时, Facade 模式非常有用。例如,由不同的记录器和监视器展示的 api 或方法的广泛集合不需要向 Dag 层展示。Facade 模式有助于定义对日志记录、监控层的访问或接口,如下所示
*from loglib import LogConfigConst
from loglib.LoggerFactory import LogFactory
class LoggingFacade:
_file_logger, _event_logger, _database_logger = None
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(LoggingFacade, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self, *args, **kwargs):
if self._initialized:
return
super(LoggingFacade, self).__init__(*args, **kwargs)
#get the kwarg config that has config for all the loggers
config=kwargs["config_template"]
logconfig=LogConfigConst()
for conf in config.loggers:
if conf.type == logconfig.FILELOG:
self.__class__._file_logger = LogFactory().get_logger(conf)
elif conf.type == logconfig.EVENTLOG:
self.__class__._event_logger = LogFactory().get_logger(conf)
elif conf.type == logconfig.DATABASELOG:
self.__class__._database_logger = LogFactory().get_logger(conf)
self.__class__._initialized = True
#filestore could be S3 or other providers
@classmethod
def log_filestore(cls,message):
cls._file_logger.log(message)
#event logger could be DataDog or other providers
@classmethod
def log_event(cls,message):
cls.event_logger.log(message)
@classmethod
def log_database(cls,message):
cls.database_logger.log(message)*
显示 LoggingFacade 示例的代码示例:
当我们组合这些模式时,我们意识到设计原则的好处,因为职责的分离允许在多个级别上模块化代码库:Dag 级别、横切关注点级别(用于监控、日志记录的单独的包)、跨 Dag 级别(公共模板可以被抽象为例如领域特定的)。这提供了构建模块,用于将数据管道一般化,从编写定制代码转移到更通用的模块、模板、配置。不同的部分遵循它们的开发周期和部署包,与 Dag 代码库完全分离。虽然添加这些设计原则确实增加了抽象性和某种程度的复杂性,但是这对于在保持质量和速度的同时扩大管道开发来说是一个很小的代价
尽管数据工程领域在不断发展,DAG 管道设计和架构也面临着一系列挑战,但仍有一些关键原则可用于提高管道的稳定性、可靠性和可维护性。我希望您喜欢阅读这篇文章,并找到一些有用的信息应用到您的数据工程计划中。我期待您的评论和反馈。
数据管道:什么,为什么,哪些
不同管道框架如何相互关联的示例和解释
克林特·王茂林在 Unsplash 上拍摄的照片
如果您在数据科学领域工作,您可能会在各种文章和教程中不断看到术语“数据管道”。您可能还注意到,术语“管道”可以指代许多不同的事物!有跨越 IT 堆栈不同部分的管道,特定工具的管道,以及特定代码库中的管道。例如,我工作的公司 UbiOps 也提供自己的管道形式。
跟踪所有这些不同的管道是什么,以及它们彼此之间有什么不同,可能会非常令人困惑。在本文中,我们将列出并比较一些常见的管道,并阐明 UbiOps 管道在总体情况中的位置。
什么是数据管道?
让我们从头开始,什么是数据管道?总的来说,数据流水线就是简单的对数据执行的自动化操作链。它可以将数据从 A 点带到 B 点,它可以是从多个来源聚合数据并将其发送到某个数据仓库的流程,或者它可以对检索到的数据执行某种类型的分析。基本上,数据管道有多种形状和大小。但是它们都有三个共同点:它们是自动化的,它们引入了可重复性,并且有助于将复杂的任务分解成更小的、可重用的组件。
您可能熟悉 ETL ,或者它的现代对应物 ELT ,它们是常见的数据管道类型。ETL 代表提取、转换、加载,它就是这样做的。ELT 管道提取、加载,然后才进行转换。它们是许多处理数据的公司使用的通用管道模式。尤其是当他们处理需要存储在数据仓库中的来自不同来源的数据时。ETL 或 ELT 管道是数据管道的子集。换句话说,每个 ETL/ELT 管道都是数据管道,但不是每个数据管道都是 ETL 或 ELT 管道。
在数据管道的另一端,我们有更侧重于分析的管道。这些通常出现在数据科学项目中。它们是处理传入数据的管道,这些数据通常已经以某种方式进行了清理,以提取洞察力。它不仅仅是加载和转换数据,而是对数据进行分析。
数据管道不局限于这种或那种类型,它更像一个光谱。一些管道完全专注于 ETL 端,另一些专注于分析端,还有一些两者兼而有之。建立数据管道有许多不同的方法,但最终最重要的是它要符合你的项目需求。
为什么需要数据管道?
好了,我们已经讨论了什么是数据管道,但是也许你仍然想知道它们的额外好处是什么。毕竟建立管道需要时间。出于几个原因,我可以保证这段时间花得很值。
首先,自动化管道最终会节省您的时间,因为您不必手动地一遍又一遍地做同样的事情。它允许您在可重复的任务上节省时间,因此您可以将更多的时间分配给项目的其他部分。
不过,使用自动化管道最重要的原因可能是,您需要思考、计划并在某个地方写下您计划放入管道的整个流程。换句话说:它迫使你预先做一个设计,并考虑必需品。反思过程并记录下来对于防止错误和允许多人使用管道非常有用。
此外,管道允许您将一个大的任务分成更小的步骤。这提高了效率、可扩展性和可重用性。这有助于针对他们必须做的事情优化不同的步骤。例如,有时不同的框架或语言更适合管道的不同步骤。如果它是一个大的脚本,你将不得不坚持一个,但是使用大多数管道工具,你可以为管道的每个单独部分选择最好的框架或语言。
最后,管道引入了再现性,这意味着几乎任何人、几乎任何地方都可以再现结果(当然,如果他们可以访问数据的话)。这不仅引入了安全性和可追溯性,还使调试变得更加容易。每次运行管道的过程都是相同的,所以每当出现错误时,您可以轻松地追溯步骤,并找出错误出在哪里。
UbiOps 管道在这幅图中是怎样的?
如果你熟悉 UbiOps ,你会知道 ubi ops也有流水线功能。UbiOps 管道是模块化工作流,由称为部署的对象组成。每个部署都为 UbiOps 中的一段 Python 或 R 代码提供服务。每个部署都有自己的 API 端点,并根据使用情况进行动态扩展。通过管道,您可以将部署连接在一起,以创建更大的工作流。这种设置有助于模块化,允许您将应用程序分成小的独立部分,以便随着时间的推移构建更强大的软件。UbiOps 将负责这些部署之间的数据路由,整个管道将通过自己的 API 端点公开,供您使用。
UbiOps 中的管道截图,图片由作者提供
当我们回顾我之前讨论的管道频谱时,UbiOps 更多地是在分析方面。最终,无论您在代码中指定什么,您都可以使用 UbiOps 管道,但它更多地意味着数据处理和分析,而不是在其他技术之间路由数据。
不同管道框架的比较
正如我前面提到的,有很多不同的管道框架,都有各自的优点和用例。在数据科学领域不断出现的几个例子是:Luigi、Airflow、scikit-learn 管道和 Pandas 管道。让我们看看它们的异同,并检查它们与 UbiOps 管道的关系。
Luigi 是 Spotify 为其数据科学团队打造的,用于构建数千个任务的长期运行管道,这些任务跨越数天或数周。它旨在帮助将任务整合到流畅的工作流程中。这是一个基于 Apache 的开源许可的 Python 包。
Luigi 的目的是解决通常与长时间运行的批处理相关的所有问题,其中许多任务需要链接在一起。这些任务可以是任何事情,但通常是长时间运行的事情,如 Hadoop 作业、向/从数据库转储数据或运行机器学习算法。
Luigi 有 3 个步骤来构建管道:
requires()
定义任务之间的依赖关系output()
定义了任务的目标run()
定义每个任务执行的计算
在 Luigi 中,任务与提供给它们的数据错综复杂地联系在一起,这使得创建和测试一个新任务变得困难,而不仅仅是将它们串在一起。由于这种设置,也很难更改任务,因为您还必须单独更改每个相关任务。
air flow air flow最初是由 AirBnB 创建的,旨在帮助他们的数据工程师、数据科学家和分析师掌握建设、监控和改造数据管道的任务。气流是一个非常通用的系统,能够处理各种工具的气流。Airflow 将工作流定义为有向无环图(Dag),任务是动态实例化的。
气流围绕着:
- 钩子,是连接外部平台的高级接口(如 Postgres 钩子)
- 操作符,它们是成为 DAG 节点的预定义任务
- 执行者(通常是芹菜)远程运行作业,处理消息队列,并决定哪个工人将执行每个任务
- 调度器,负责触发预定的工作流,以及将任务提交给执行器运行。
有了气流,就有可能创建高度复杂的管道,这是很好的编排和监测。对于 Airflow 来说,最重要的因素是它与其他系统(如数据库、Spark 或 Kubernetes)良好连接的能力。
然而,气流的一个大缺点是它陡峭的学习曲线。要很好地利用气流,你需要 DevOps 知识。一切都是高度可定制和可扩展的,但是以简单性为代价。
scikit-learn 管道 scikit-learn 管道与 Airflow 和 Luigi 有很大不同。它们不是编排不同服务的大型任务的管道,而是一个可以让您的数据科学代码更加清晰、更具可重复性的管道。scikit-learn 管道是 scikit-learn Python 包的一部分,该包在数据科学领域非常流行。
scikit-learn 管道允许您连接一系列转换器,后跟一个最终估计器。通过这种方式,您可以将模型训练或数据处理等特定步骤连接在一起。借助 scikit-learn pipelines,您的工作流程变得更加易于阅读和理解。正因为如此,发现数据泄露这样的事情也变得容易多了。
但是请记住,scikit-learn 管道只能与 scikit-learn 库中的转换器和估算器一起工作,并且它们需要在相同的运行时中运行。因此,这些管道与您在 Airflow 或 Luigi 中创建的编排管道非常不同。例如,使用 Airflow 或 Luigi,您可以在不同的工作节点上运行管道的不同部分,同时保持一个控制点。
熊猫管道
熊猫管道是特定 Python 包管道的另一个例子,在本例中是熊猫。Pandas 是一个流行的数据分析和操作库。你的数据分析变得越大,你的代码也会变得越乱。Pandas 管道提供了一种清理代码的方法,允许您在一个函数中连接多个任务,类似于 scikit-learn 管道。
Pandas pipes 有一个标准:所有的步骤都应该是一个函数,一个数据框作为参数,一个数据框作为输出。只要遵循这个标准,您可以根据需要添加任意多的步骤。您的函数可以在数据帧后面带额外的参数,这些参数也可以传递给管道。
按照数据帧进,数据帧出的原则,Pandas 管道非常多样化。就用例而言,它们与 scikit-learn 管道相当,因此也与 Airflow 和 Luigi 有很大不同。它们也可能是管道,但类型完全不同。
对比 很明显,所有这些不同的管道都适合不同类型的用例,甚至可以很好地结合使用。例如,完全有可能在 UbiOps 管道的部署中使用 Pandas 管道,以这种方式结合它们的优势。让我们把前面提到的管道并排放在一起,勾勒出更大的图景。
在管道频谱中,Luigi 和 Airflow 位于更高级别的软件编排端,而 Pandas 管道和 scikit-learn 管道位于特定分析的代码级别,UbiOps 位于两者之间。Luigi 和 Airflow 是创建跨堆栈中多个服务的工作流或在不同节点上调度任务的绝佳工具。Pandas 管道和 scikit-learn 管道对于更好的代码可读性和更具重现性的分析来说是非常棒的。UbiOps 非常适合在 Python 或 r 中创建分析工作流。
UbiOps、Airflow 和 Luigi 之间有一些重叠,但它们都面向不同的用例。UbiOps 面向数据科学团队,他们需要以最少的开发运维麻烦将其分析流程投入生产。Luigi 面向长时间运行的批处理过程,其中许多任务需要链接在一起,这些任务可能持续几天或几周。最后,气流是三者中最通用的,允许您监控和协调复杂的工作流,但以简单性为代价。随着其多功能性和功能的增加,也带来了很多复杂性和陡峭的学习曲线。
scikit-learn 和 Pandas 管道实际上无法与 UbiOps、Airflow 或 Luigi 相提并论,因为它们是专门为这些库设计的。scikit-learn 和 Pandas 管道实际上可以与 UbiOps、Airflow 或 Luigi 结合使用,只需将它们包含在这些管道中各个步骤的代码运行中即可!
结论
数据管道是将自动化、可再现性和结构化引入项目的一个很好的方式。有许多不同类型的管道,每一种都有自己的优点和缺点。希望这篇文章有助于理解所有这些不同的管道是如何相互关联的。
使用 Apache Beam 的数据管道
利用 Beam 实施大数据
如何借助 Beam 实现数据管道
Apache Beam 是 Apache 的最新项目之一,这是一个整合的编程模型,用于表达高效的数据处理管道,正如 Beam 的主网站[ 1 所强调的那样。在本文中,我们将更深入地研究这个特定的数据处理模型,并探索它的数据管道结构以及如何处理它们。此外,我们还将举例说明。
什么是阿帕奇光束
Apache Beam 可以表示为分布式数据处理的编程模型[ 1 ]。它只有一个 API 来处理数据集和数据帧这两种类型的数据。当您建立束管线时,您并不关心您所建立的管线类型,无论您是建立批次管线还是串流管线。
就其侧面而言,顾名思义,它可以调节到任何位置。在 Beam 上下文中,这意味着开发您的代码并在任何地方运行它。
装置
要在 Python 中使用 Apache Beam,我们首先需要安装 Apache Beam Python 包,然后将其导入到其网页[ 2 ]上描述的 Google Colab 环境中。
! pip install apache-beam[interactive]
import apache_beam as beam
什么是管道
管道通过改变输入来封装信息处理任务。
Apache Beam 的体系结构
在本节中,将介绍 Apache Beam 模型的体系结构、它的各种组件以及它们的作用。主要是用于合并处理的 Beam 概念,这是 Apache Beam 的核心。Beam SDKs 是用户可以用来创建管道的语言。用户可以选择自己喜欢的、舒适的 SDK。随着社区的发展,新的 SDK 正在被整合。
一旦用任何支持的语言定义了管道,它将被转换成通用语言标准。这种转换由一组运行程序 API 在内部完成。
我想提一下,这种通用格式并不完全是语言通用的,但我们可以说是部分通用的。这种转换只是概括了核心转换的基本内容,这些内容对于所有人来说都是通用的,如映射函数、分组和过滤。
对于每个 SDK,都有一个相应的 SDK 工作人员,他们的任务是理解特定于语言的东西并解决它们。这些工人提供了一个一致的环境来执行代码。
来源:图片由作者提供
对于每种语言的 SDK,我们都有一个特定的 SDK 工作人员。所以现在,如果我们有这个 runner 或 Beam API 和特定于语言的 SDK 工作器,我们使用哪个 Runner 就无关紧要了。任何跑步者都可以执行其指南页[ 4 ]上提到的相同代码。
阿帕奇波束的特点
阿帕奇波束包括四个基本特征:
- 管道
- p 集合
- p 转换
- 跑步者
Pipeline
负责读取、处理和保存数据。这整个循环是一个从输入开始直到输出的整个循环的管道。每个 Beam 程序都能够生成一条流水线。
光束的第二个特征是一个Runner
。它决定了这条管道将在哪里运行[ 5 ]。
光束的第三个特征是PCollection
。它相当于 Spark 中的 RDD 或数据帧。管道通过从数据源读取数据来创建一个PCollection
,之后,随着 PTransforms 被应用于它,更多的 PCollections 继续发展[ 6 ]。
每个PCollection
上的PTransform
都会产生一个新的PCollection
,使其不可改变。构造后,您将无法配置 PCollection 中的单个项目。在PCollection
上的转换将产生一个新的PCollection
。PCollection
中的特征可以是任何类型,但必须是同一类型。然而,为了保持分布式处理,Beam 将每个元素编码为一个字节串,以便 Beam 可以将项目传递给分布式工作器,如其编程页面[ 6 中所述。
Beam SDK 包还作为所用类型的编码机制,支持自定义编码。此外,PCollection 不支持粒度操作。因此,我们不能对 PCollection 中的某些特定项目应用转换。我们使用所有的转换来应用于整个PCollection
而不是某些方面[ 6 ]。
通常,当读取或添加项目时,源通常会为每个新元素分配一个时间戳。如果PCollection
持有有界数据,我们可以强调每个特性都将被设置为相同的时间戳。您可以显式指定时间戳,或者 Beam 将提供自己的时间戳。在任何情况下,如果源没有为我们指定时间戳,我们可以手动为元素指定时间戳。
光束的第四个特征是PTransform
。它将一个样本PCollection
作为数据源,并生成一个带有时间戳的相同的 PCollection。它们并行操作,同时进行诸如开窗、分配水印等操作。
梁的管道结构
在本节中,我们将使用 Python 实现 Beam 的管道结构。第一步从“给管道分配一个名称”开始,这是一行强制代码。
pipeline1 = beam.Pipeline()
第二步是通过读取任何文件、流或数据库来“创建”初始的PCollection
。
dept_count = (
**pipeline1**
|beam.io.ReadFromText(‘/content/input_data.txt’)
)
第三步是根据您的使用案例“应用”PTransforms
。我们可以在这个管道中使用几个转换,每个转换都由管道操作符应用。
dept_count = (
**pipeline1**
|beam.io.ReadFromText(‘/content/input_data.txt’)
**|beam.Map(lambda line: line.split(‘,’))
|beam.Filter(lambda line: line[3] == ‘Backend’)
|beam.Map(lambda line: (line[1], 1))
|beam.CombinePerKey(sum)**
)
要请求一个转换操作,您需要对输入PCollection
实现它。对于每个转换,都存在一个非专有的应用方法。我们可以通过. apply '或
| '管道运算符来使用 apply 操作。
在所有转换之后,第四步是将最终的PCollection
写入外部源。它可以是文件、数据库或流。
dept_count = (
**pipeline1**
|beam.io.ReadFromText(‘/content/input_data.txt’)
|beam.Map(**lambda** line: line.split(‘,’))
|beam.Filter(lambda line: line[3] == ‘Backend’)
|beam.Map(**lambda** line: (**line**[1], 1))
|beam.CombinePerKey(sum)
**|beam.io.WriteToText(‘/content/output_data.txt’)**
)
最后一步是运行管道。
pipeline1.run()
用 Python 生成变换操作
转换是每个数据处理结构的基本元素。Apache Beam 包含内置的转换,可以很容易地用封闭的形式应用,如 Beam 的主编程文档[ 6 中所述。让我们在接下来的章节中介绍这些转换。
从文本中读取
Beam 支持多种文件格式的“读”和“写”操作,如文本、 Avro 、 Parquet 。第一个转换是“ReadFromText”。这种格式将文本文件解析为换行符分隔的元素,这意味着默认情况下,文件中的每一行都将被视为单个元素。“ReadFromText”共有六个参数可供编辑,如果您希望在读取 Beam 的包模块页面[ 7 ]上列出的文件时有完全的控制权。让我们来看看这些参数。
import apache_beam as beam
reading = beam.Pipeline()
content_read = (
reading
**|beam.io.ReadFromText(‘/content/input_data.txt’)**
|beam.io.WriteToText(‘/content/output_data.txt’)
)
reading.run()
首先是file_pattern
。它指定输入文件的完整路径。当从一个目录中读取多个文件时,我们可以用*操作符来设置它。这个路径意味着它将读取所有以 input 关键字开始的文件。
第二个参数是minimum_bundle_size
。此参数指定将源拆分成束时应该生成的束的最小大小。一个 PCollection 在内部被分成许多批,称为 bundle[8]。它们在不同的机器上并行处理。这个参数的值决定了 PCollection 的最小包大小,它的参数应该是一个整数值。
第三个参数是compression_type
。它处理压缩的输入文件,以防输入文件被压缩。我们不提供,因为 Beam 将使用提供的文件路径的扩展名来检测输入文件的压缩类型。例如,如果我们有一个. gzip 文件,那么输入路径将从这个路径检测压缩类型。但是,如果您希望自己处理压缩的输入文件,您可以显式地提供压缩类型。
第四个参数是strip_trialing_newlines
,一个布尔字段。它指示源是否应该移除换行符。如果设置为“真”,则结束行被移除并且不被读取。如果设置为“假”,则不绘制结束线,并作为空行读取。默认情况下,其值为“True”。
第五个参数是validate
。它还是一个布尔标志,用于确认文件在管道创建期间是否存在。如果设置为“真”,它将控制输入文件是否存在。如果没有创建管道,那么 Beam 将抛出一个错误。如果设置为“false ”, Beam 不检查文件是否存在,而是生成管道。在这种情况下,您将看到空的输出文件。建议将该参数设置为“真”。因此,其默认值为“True”。
最后一个参数是skip_header_lines
。它帮助处理加载了头文件的文件。我们不希望处理标题,所以我们可以使用此参数跳过阅读它们。您可以在输入文件中提供想要忽略的行数。
从 Avro 读取
此操作用于读取一个或一组 Avro 文件。它有四个参数。ReadFromAvro
的前三个参数与 ReadFromText 的参数相同。它的第四个不同的参数是“use_fastavro”。该参数接受一个布尔值,以从 Avro 文件[ 7 ]中读取数据。由于该参数是强制性的,ReadFromAvro
应将其设置为‘真’以使用该库。
import apache_beam as beam
import avro.schema
from avro.datafile import DataFileReader, DataFileWriter
from avro.io import DatumReader, DatumWriter
from apache_beam.io import ReadFromAvro
from apache_beam.io import WriteToAvro
schema = avro.schema.parse(open(“parquet_file.parqet”, “rb”).read())
parquet_write = beam.Pipeline()
content_4 = ( parquet_write
|beam.Create({‘dict1’:[24,45,68],’dict2':[32,54,75]})
|beam.Map(lambda **element**: element)
|beam.io.WriteToAvro(‘/content/output.avro’,schema=schema))
parquet_write.run()
阅读拼花地板
第三个输入变换是ReadFromParquet
。该操作受益于读取拼花文件。前三个参数是与ReadFromText
相同的参数,第四个是“columns”。该参数指定了ReadFromParquet
将从输入文件[ 8 ]中读取的列列表。
import apache_beam as beam
import pandas as pd
import pyarrow
from apache_beam.options.pipeline_options import PipelineOptions
parquet_data = pd.read_parquet(‘/content/parquet_data.parquet’, engine=’pyarrow’)parquet_schema = pyarrow.schema([])
schema_map = {
‘STRING’: pyarrow.string(),
‘FLOAT’: pyarrow.float64(),
‘STRING’: pyarrow.string(),
‘DATE’: pyarrow.date64()
}
for item in parquet_data.schema:
parquet_schema = parquet_schema.append(pyarrow.field(item.name, schema_map[item.field_type]))
parquet_write = beam.Pipeline()
content = ( parquet_write |beam.beam.io.ReadFromParquet(‘/content/parquet_data.parquet’)
|beam.io.parquetio.WriteToParquet(‘/content/output5.parquet’,schema=parquet_schema))
parquet_write.run()
从 TFRecord 读取
拼花之后,最后一个文件 I/O 是ReadFromTFRecord
。该操作读取 TensorFlow 记录。TFRecord 格式是一种用于存储二进制形式序列的简单格式。这些记录之所以出名,是因为它们被序列化,因此在网络上传输速度更快。这种格式还有助于捕捉任何数据预处理。要读取 TensorFlow 记录,我们有ReadFromTFRecord
[ 9 。它有一个包含四个参数的列表。
它的三个参数与以前的类型相同。其他参数包括指定用于解码每个 TFRecord 的“编码器”名称的“编码器”。
这些是各种基于文件的读取转换。
import apache_beam as beam
from apache_beam.io.tfrecordio import ReadFromTFRecord
from apache_beam import coders
reading_tf = beam.Pipeline()
data_path = ‘/content/input_data’
content_read = (
reading_tf
|beam.io.ReadFromTFRecord(data_path, coder=beam.coders.BytesCoder(),
compression_type=’auto’, validate=True) |beam.io.WriteToText(‘/content/output_tfrecord.txt’)
)reading_tf.run()
从 PubSub 读取
下一个主题是从消息队列中读取。梁整体支持阿帕奇卡夫卡、亚马逊 Kinesis 、 JMS 、 MQTT 、 Google Cloud PubSub 。 Java 支持其中的每一种;但是,Python 只支持 Google Cloud PubSub。我们有一个转换操作。它有一个大约五个参数的列表,如下所示。
import apache_beam as beam
from apache_beam.options.pipeline_options import PipelineOptions
import os
from apache_beam import windowproject = 'SubscribeBeam'
pubsub_topic = 'projects/qwiklabs-gcp-01-7779ab5fa77e/topics/BeamTopic'path = "C:\\Users\ersoyp\qwiklabs-gcp-01-7779ab5fa77e-2d40f7ded2a8.json"os.environ["GOOGLE_APPLICATION_CREDENTIALS"]=path
input_file = "C:\\Users\ersoyp\data.csv"output_file = "C:\\Users\ersoyp\output.csv"
options = PipelineOptions()options.view_as(StandardOptions).streaming = Trueprocess = beam.Pipeline(options=options)output_file = '/content/outputs/'pubsub_data = ( process
| 'Read from PubSub' >> beam.io.ReadFromPubSub(subscription= input_file)
| 'Write to PubSub' >> beam.io.WriteToPubSub(output_file)
)
final_file = process.run()
第一个参数是topic
。对于这个参数,我们必须提供主题名称。然后,我们指定要发布的消息,Beam 将从这些消息中读取,如数据流文档[ 9 中所述。
第二个参数是subscription
。现有的发布-订阅订阅附加到特定的主题。以上两个参数是相互矛盾的。在这种情况下,我们提供一个主题作为参数。
第三个参数是id_label
。它指定传入 PubSub 消息的哪个属性应被视为 Beam 的模块页面[ 10 ]中指定的记录标识符。设置后,该属性的值将用于消息的重复数据删除。否则,如果不提供,Beam 将不保证数据的唯一性。
第四个参数是with_attributes
。这是一个布尔型字段。如果设置为“True ”,则输出元素将是 objects 类型。如果设置为“False ”,输出元素将为字节类型。默认情况下,该参数设置为“假”[ 10 ]。
最后一个参数是timestamp_attribute
。因为 Beam 中的每个元素都附有时间戳。该参数是从 PubSub 转换中读取的,用于从 Google Cloud PubSub [ 10 ]中提取消息。
创建转换
为了生成我们的数据,Beam 支持创建转换操作。我们可以生成各种形式的数据,如列表、集合、字典等。创建转换将在下面的示例中显示“创建转换”操作的一些状态。我们将简单地使用 create 生成数据,并将其写入输出文件。
要生成元素列表,请使用’ beam.Create ',然后使用方括号,在方括号内,您可以指定用逗号分隔的项目。在下面的示例中,我们没有对生成的数据应用任何转换。
import apache_beam as beamcreate_transform = beam.Pipeline()content = (**create_transform**
|beam.Create(['Beam create transform'])
|beam.io.WriteToText('/content/outCreate1.txt')
)create_transform.run()
作为第二个例子,可以创建一个列表。为此,我们生成一个数字列表。管线段应该在直线的最开始,前面没有空格。当我们创建管道或预期的缩进时,情况也是如此。它是无缩进的。
import apache_beam as beam
create_transform_2 = beam.Pipeline()
content_2 = (create_transform_2
|beam.Create([10,22,38,47,51,63,78])
|beam.io.WriteToText('/content/output2.txt')
)create_transform_2.run()
如果您想要两列或更多列的数据,那么传递一个元组列表。它是一个键值元组。如果在元组中进一步应用映射转换,每个元素都表现为一个单独的列。
import apache_beam as beam
create_transform_3 = beam.Pipeline()
content_3 = (create_transform_3
|beam.Create([(“DataScience”,10), (“DataEngineering”,20),(“ArtificialIntelligence”,30), (“BigData”,40)])
|beam.io.WriteToText(‘/content/output3.txt’)
)create_transform_3.run()
对于字典,可以传递键值对。对于键值对,你用花括号传递它们。您可以使用圆形、方形和花括号来生成各种形式的数据。使用这些括号的不同组合,你会得到额外的数据。它在“beam.Create”操作的帮助下创建一个变换。
import apache_beam as beam
create_transform_4 = beam.Pipeline()content_3 = ( create_transform_4
|beam.Create({'dict1':[24,45,68],'dict2':[32,54,75]})
|beam.Map(lambda element: element)
|beam.io.WriteToText('/content/output4.txt'))
create_transform_4.run()
写入文本
WriteToText
将 PCollection 的每个元素作为一行写入输出文件。
第一个参数是file_path_prefix
。它指定写入 PCollection 的文件路径。如果我们将其定义为一个参数,那么 Beam 将生成文件或者数据目录[ 11 ]中的项目。
beam.io.WriteToText(‘/content/output.txt’)
num_shards
和file_path_suffix
是第二个和第三个参数。我们文件的全名如下所示。
<prefix><num_shards><suffix>content-0000-of-0001-departments
第一部分,“内容”是一个前缀。第二个“0001 的 0000”属于“碎片数”。此参数指定作为输出写入的碎片数或文件数。如果我们将“number_of_shards”参数设置为 3,那么我们得到的文件将由 3 部分组成。当我们不设置这个参数时,服务将决定最佳碎片。
在本例中,“部门”代表由名为“文件名后缀”的参数控制的后缀。
第四个参数是append_trailing_newlines
。此参数接受一个布尔值,该值指示输出文件是否应该在写入每个元素后写入一个换行符。即输出文件是否应该用换行符分隔。默认情况下,它被设置为“真”[ 12 ]。
第五个参数是coder
。它指定了用于编码每一行的编码器名称。
第六个参数是compression_type
,一个字符串值。此参数用于处理压缩输出文件。
第七个参数是header
。它指定一个字符串作为头写在输出文件的开头。
写信给 Avro
WriteToAvro
的参数包括file_path_prefix
、file_path_suffix
、num_shards
、compression_type
,如刚才对WriteToText
的解释。
import apache_beam as beam
from avro import schema
import avro
from apache_beam.io import ReadFromAvro
from apache_beam.io import WriteToAvro
schema = avro.schema.parse(open("avro_file.avsc", "rb").read())
create_transform_5 = beam.Pipeline()content_4 = ( create_transform_5
|beam.Create(['Beam create transform'])
|beam.Map(lambda **element**: element) **|beam.io.WriteToAvro('/content/output5.avro',schema=schema)**)create_transform_5.run()
WriteToAvro
的第五个参数是schema
。写入 Avro 文件需要指定模式。
第六个参数是codec
。它是用于块级压缩的压缩编解码器。
第七个参数是设置为“真”的use_fastavro
。你可以使用“fastavro 库”来加快写作速度。
最后一个参数是mime_type
。如果文件系统支持指定的 MIME 类型,它将传递生成的输出文件的 MIME 类型。
写信给拼花地板
它用于将 PCollection 的每个元素写入 Parquet 文件。file_path_prefix
、file_path_suffix
、num_shards
、codec
、mime_type
、schema
的参数与WriteToAvro
相同。
import apache_beam as beam
import pandas as pd
import pyarrow
from apache_beam.options.pipeline_options import PipelineOptions
parquet_data = pd.read_parquet(‘/content/parquet_data.parquet’, engine=’pyarrow’)parquet_schema = pyarrow.schema([])
schema_map = {
‘STRING’: pyarrow.string(),
‘FLOAT’: pyarrow.float64(),
‘STRING’: pyarrow.string(),
‘DATE’: pyarrow.date64()
}
for item in parquet_data.schema:parquet_schema =parquet_schema.append(pyarrow.field(item.name, schema_map[item.field_type]))
parquet_write = beam.Pipeline()
content = ( parquet_write
|beam.beam.io.ReadFromParquet(‘/content/parquet_data.parquet’) **|beam.io.parquetio.WriteToParquet(‘/content/output.parquet’,
schema=parquet_schema**))
parquet_write.run()
第七个参数是row_group_buffer_size
。它指定行组缓冲区的字节大小。行组可以被接受为 parquet 文件的一部分,它保存列输入的序列化数组。这是一项高级功能,用于调整拼花文件的性能。
第八个参数是record_batch_size
。它指定了每个record_batch
的记录数。记录批次可以定义为用于在行组缓冲区中存储数据的基本单位。该参数纯粹与拼花文件相关。
写入 TFRecord
它具有file_path_prefix
、file_path_suffix
、num_shards
、compression_type
参数,这些参数已经在上面的写操作中解释过了。
import apache_beam as beam
from apache_beam import Create
from apache_beam import coders
from apache_beam.io.filesystem import CompressionTypes
from apache_beam.io.tfrecordio import ReadFromTFRecord
from apache_beam.io.tfrecordio import WriteToTFRecord
reading_tf = beam.Pipeline()
data_path = ‘/content/input_data’
content_read = (reading_tf
|**beam.io.ReadFromTFRecord(data_path, coder=beam.coders.BytesCoder(), compression_type=’auto’, validate=True**)
|**beam.io.WriteToTFRecord(data_path, compression_type=CompressionTypes.GZIP, file_name_suffix=’.gz’**)
)
reading_tf.run()
写入 PubSub
该操作将 PCollection 作为消息流写入 Google Cloud PubSub 服务。
import os
import apache_beam as beam
from apache_beam import window
from apache_beam.options.pipeline_options import PipelineOptionsproject = ‘SubscribeBeam’pubsub_topic = ‘projects/qwiklabs-gcp-01–7779ab5fa77e/topics/BeamTopic’path = “C:\\Users\ersoyp\qwiklabs-gcp-01–7779ab5fa77e-2d40f7ded2a8.json”os.environ[“GOOGLE_APPLICATION_CREDENTIALS”]=pathinput_file = “C:\\Users\ersoyp\data.csv”output_file = “C:\\Users\ersoyp\output.csv”options = PipelineOptions()options.view_as(StandardOptions).streaming = Trueprocess = beam.Pipeline(options=options)output_file = “/content/outputs/”pubsub_data = ( process
| ‘Read from PubSub’ >> beam.io.ReadFromPubSub(subscription= input_file)
| ‘Write to PubSub’ ‘ >> beam.io.WriteToPubSub(output_file)
)final_file = process.run()
第一个参数是topic
。它被用作写入输出的位置。
第二个参数是with_attributes
,决定输入元素的类型。如果它被设置为“真”,那么输入元素将是对象类型。如果为“假”,则特征的格式为字节。
第三个参数是id_label
。它为每个具有给定名称和新颖内容的 Cloud PubSub 消息设置一个属性。它可以在ReadFromPubSub
和PTransform
中应用该属性,以对消息[ 14 ]进行重复数据删除。
第四个参数是timestamp_attribute
。它被用作每个具有给定名称的云发布订阅消息的属性,其发布时间为 Beam 的模块页面[ 15 中提供的值。
地图变换
Map
transform 将一个元素作为输入,一个元素作为输出。它对集合中的每个项目执行一对一的映射功能。该示例应该将整个字符串作为单个输入,基于逗号对其进行分割,并返回元素列表。
import apache_beam as beam
map_transform = beam.Pipeline()
content = ( map_transform
|beam.io.ReadFromText(([‘data.txt’]))
|beam.Map(lambda element: element)
|beam.io.WriteToText(‘/content/output_1.txt’)
)
map_transform.run()
平面图变换
功能方面FlatMap
与Map
几乎相同,但有一个显著的区别。虽然Map
只能为单个输入输出一个元素,但FlatMap
可以为单个组件发出多个元素。以下示例生成一个列表作为输出。
import apache_beam as beam
flatMap_transform = beam.Pipeline()
content = ( flatMap_transform
|beam.io.ReadFromText(([‘data.txt’]))
**|beam.FlatMap(lambda element: element)**
|beam.io.WriteToText(‘/content/output_1.txt’)
)
flatMap_transform.run()
过滤变换
filter
操作将过滤指定部门的元素。这个过滤函数将前面的列表作为输入,并返回匹配条件中所有需要的特征。
import apache_beam as beamfiltering = beam.Pipeline()
dept_count = (
filtering
|beam.io.ReadFromText(‘/content/input_data.txt’)
|beam.Map(lambda line: line.split(‘,’))
**|beam.Filter(lambda line: line[3] == ‘Backend’)**
|beam.Map(lambda line: (line[1], 1))
|beam.io.WriteToText(‘/content/output_data.txt’)
|beam.CombinePerKey(sum)
)
filtering.run()
管道分支操作
大多数管道只是用一对一的映射来表示操作的线性流程。在第一个 PCollection 之后,一个过滤操作产生一个新的 PCollection。在 PCollection 上,一个映射转换在队列中创建附加 PCollection,直到它被写入文件。
然而,对于大多数用例来说,您的管道可能非常复杂和分支。这种类型的管道在 Beam 中称为分支管道,我们可以使用同一个 PCollection 作为多个转换的输入。
来源:图片由作者提供
以下是管道分支结构的实现示例流程。
import apache_beam as beambranched = beam.Pipeline()input_collection = (
branched
| “Read from text file” >> beam.io.ReadFromText(‘data.txt’)
| “Split rows” >> beam.Map(lambda line: line.split(‘,’)))
backend_dept = (input_collection
| ‘Retrieve Backend employees’ >> beam.Filter(lambda record: record[3] == ‘Backend’)
| ‘Pair them 1–1 for Backend’ >> beam.Map(lambda record: (“Backend, “ +record[1], 1))
| ‘Aggregation Operations: Grouping & Summing1’ >> beam.CombinePerKey(sum))
ai_dept = ( input_collection
|’Retrieve AI employees’ >> beam.Filter(lambda record: record[3] == ‘AI’)
|’Pair them 1–1 for HR’ >> beam.Map(lambda record: (“AI, “ +record[1], 1))
|’Aggregation Operations: Grouping & Summing2' >> beam.CombinePerKey(sum))output =(
(backend_dept , ai_dept)
| beam.Flatten()
| beam.io.WriteToText(‘/content/branched.txt’)
)branched.run()
在上面的例子中,第一个 transform 操作在后端部门应用了一个过滤器,Transform B 过滤了 AI 部门的所有雇员。
用 Python 生成 ParDo 变换运算
ParDo 可以作为并行处理的转换机制[ 16 ]。
第一个是Filtering
,数据集。您可以使用 ParDo 获取 PCollection 中的每个元素,或者将该元素输出到一个新的集合中,或者按照 Beam [ 16 ]的编程指南中的规定将其丢弃。
第二个是数据集中每个元素的Formatting
或Type Converting
。ParDo
可用于对输入 PCollection [ 17 ]上的每个组件进行转换。
第三个是每个项目的Extracting Individual Parts
。如果存在具有多个字段的元素集合,您可以使用ParDo
或提取单个项目。
第四个是对 PCollection 的每一项执行Computations
。我们可以将这个函数应用到 PCollection 的各个方面。
此外,我们可以利用 ParDo 以各种方式分割 PCollection。在下面的脚本中,我们使用了Map
、FlatMap
和Filter
变换。当您应用一个ParDo
转换时,您将需要以一个DoFn
对象的形式提供用户代码。
在内部,Map
和FlatMap
也继承了DoFn
类。为了实现代码中的ParDo
,用ParDo
替换Map
和。“DoFn”类中有许多函数,我们只需覆盖其中的一部分,即流程函数。
import apache_beam as beam
class EditingRows(beam.DoFn):
def process(self, element):
return [element.split(‘,’)]
class Filtering(beam.DoFn):
def process(self, element):
if element[3] == ‘Finance’:
return [element]
class Matching(beam.DoFn):
def process(self, element):
return [(element[3]+”,”+element[1], 1)]
class Summing(beam.DoFn):
def process(self, element):
(key, values) = element
return [(key, sum(values))]
pardo = beam.Pipeline()
department_operations= (pardo
|beam.io.ReadFromText(‘data.txt’)
|beam.ParDo(EditingRows())
|beam.ParDo(Filtering())
|beam.ParDo(Matching())
|’Grouping’ >> beam.GroupByKey()
|’Summing’ >> beam.ParDo(Summing())
|beam.io.WriteToText(‘data/output_pardo.txt’) )
pardo.run()
合成变换操作的生成
CompositeTransform
顾名思义是一个内部有一系列内置转换的转换。
在管道中使用复合转换可以使您的代码更加模块化,更容易理解。在复合转换中,我们将多个转换组合成一个单元。为此,我们将创建一个类CompositeTransform
,和 Beam 中的每个其他类一样,它应该继承其对应的基类。
import apache_beam as beam
class CompositeTransform(beam.PTransform):
def expand(self, columns):
x = ( columns |’Grouping & Summing’ >> beam.CombinePerKey(sum)
|’Filtering’ >> beam.Filter(Filtering))
return x
def EditingRows(element):
return element.split(‘,’)
def Filtering(element):
name, count = element
if count > 30:
return element
composite = beam.Pipeline()
input_data = ( composite
| “Reading Data” >> beam.io.ReadFromText(‘data.txt’)
| “Editing Rows” >> beam.Map(EditingRows))frontend_count = (input_data
| ‘Get Frontend Employees’ >> beam.Filter(lambda record: record[3] == ‘Frontend’)
| ‘Matching one-to-one’ >> beam.Map(lambda record: (“Frontend, “ +record[1], 1))
| ‘Composite Frontend’ >> MyTransform()
| ‘Write to Text’ >> beam.io.WriteToText(‘/content/composite_frontend.txt’))
ai_count = (input_data
| ‘Get AI Employees’ >> beam.Filter(lambda record: record[3] == ‘AI’)
| ‘Pairing one-to-one’ >> beam.Map(lambda record: (“AI, “ +record[1], 1))
| ‘Composite AI’ >> MyTransform()
| ‘Write to Text for AI’ >> beam.io.WriteToText(‘/content/composite_ai.txt’))composite.run()
为了创建复合变换,我们可以使用函数“Beam.PTransform”。这个 PTransform 是我们使用的每个 PTransform 的基类。PTransform 有一个需要重写的扩展方法。此方法将一个 PCollection 作为输入,将对其应用几个转换。要在我们的管道中使用这个转换,只需用它的惟一标签调用它的对象。
侧面输入和侧面输出
顾名思义,side input
是可以贡献给DoFn
对象的额外信息。除了输入“PCollection”之外,您还可以以侧面输入[ 19 ]的形式将附加信息引入到ParDo
或其子转换中,如Map
、FlatMap
。
让我们为侧面输入实现一个示例脚本。我们可以将侧面输入移动到ParDo
转换。
import apache_beam as beam
side_inputs = list()
with open (‘id_list.txt’,’r’) as my_file:
for line in my_file:
side_inputs.append(line.rstrip())
sideInput = beam.Pipeline()
class Filtering(beam.DoFn):def process(self, element, side_inputs, lower, upper=float(‘inf’)):
id = element.split(‘,’)[0]
name = element.split(‘,’)[1]
items = element.split(‘,’)
if (lower <= len(name) <= upper) and id not in side_inputs:
return [items]
small_names =( sideInput
|”Reading Data” >> beam.io.ReadFromText(‘data.txt’)
|”Side inputs & ParDo” >> beam.ParDo(Filtering(), side_inputs,3,10)
|beam.Filter(lambda record: record[3] == ‘Frontend’)
|beam.Map(lambda record: (record[0]+ “ “ + record[1], 1))
|beam.CombinePerKey(sum)
|’Write to Text’ >> beam.io.WriteToText(‘/content/side_inputs.txt’))
sideInput.run()
在 Apache Beam 中实现 windows
Beam 中的窗口可以说是其数据处理理念中的一个关键元素。窗口逻辑是任何流处理环境的关键概念。没有它,处理实时数据几乎是不可能的。
流式传输中有两种时间概念。这些是事件时间和处理时间。这些时间在处理过程中起着至关重要的作用,因为它们决定了窗口中要处理的数据。
event time
可以表示为特定事件的时间。这个时间嵌入在记录中。所有生成和发送事件的源都嵌入了带有值的时间戳。
processing time
可以描述为特定事件开始被处理时的处理时间。它是指执行相应操作的机器的系统时间。通过网络将信息发送到服务器需要一些时间,甚至是几毫秒或几秒钟。
翻滚的窗户
tumbling window
的意思是一旦创建了一个窗口,窗口将继续处理数据,直到特定的时间过去。用户必须在创建窗口时分配该时间。一旦给定了指定的时间量,窗口将发出直到该时间的计算结果。
import apache_beam as beam
fixed_window = beam.Pipeline()
content = ( fixed_window
|beam.Create({‘dict1’:[24,45,68],’dict2':[32,54,75],‘dict3’:[56,78,92]})
|beam.Map(lambda element: element)
|beam.WindowInto(window.FixedWindows(20))
|beam.io.WriteToText(‘/content/output_1’)
)
fixed_window.run()
推拉窗
创建一个sliding window
的基本原理类似于一个tumbling window.
,一旦完成,窗口将继续执行数据,直到一段特定的时间过去;然而,这是一个不同之处,因为滑动窗口可以重叠。单个窗口可能会与另一个窗口的时间重叠。为此,多个窗口有可能重叠。最终,数据中的大多数元素将属于多个窗口。
import apache_beam as beam
sliding_window = beam.Pipeline()
content = ( sliding_window
|beam.Create({‘dict1’:[24,45,68],’dict2':[32,54,75],‘dict3’:[56,78,92]})
|beam.Map(lambda element: element)
|beam.WindowInto(window.SlidingWindows(30,10))
|beam.io.WriteToText(‘/content/output_2’)
)
sliding_window.run()
水印
可以在事件时间戳上处理窗口。为了让 Beam 跟踪事件时间,会有一个额外的操作与之对应。如果我们用声明的时间定义一个窗口,那么应该有一些实体可以跟踪已经过去的指定数量的时间戳元素。测量事件时间进度的波束机制称为水印。水印声明流中已经过了指定的事件时间量。当前窗口不会接受时间戳小于当前水印值的任何元素。
用编码器进行编码操作
本节重点介绍 Beam 的数据编码机制。因此,您应该理解有两种数据模式。第一种是面向对象的,用户可以理解。另一种是机器可以理解的字节形式的序列化数据。
Beam 中的编码器类别
在每个生态系统中,对象数据在通过网络传输时被序列化为字节串。对于目标机器,它们被反序列化为对象形式。在 Beam 中,当运行者执行您的管道时,他们需要具体化您的 PCollections 的中间数据,这需要将组件从字节格式转换为字符串。
编码人员不一定与数据类型有一对一的关系。一种数据类型可以有多个编码器。
波束中的数据编码
创建定制编码器的最重要的步骤在下面作为一个例子来实现。
import parquet
from apache_beam.coders import Coder
from apache_beam.transforms.userstate import ReadModifyWriteStateSpec
class ParquetCoder(Coder):
def encode(self, item):
return parquet.dumps(item).encode()
def decode(self, item):
return parquet.loads(item.decode())
def is_deterministic(self) -> bool:
return Trueclass EncodeDecode(beam.DoFn):
data_source = ReadModifyWriteStateSpec(name=’data_source’, coder=ParquetCoder())
def execute(self, item, DataSource=beam.DoFn.StateParam(data_source)):return DataSource
第一种方法是Encode
。它接受输入值并将它们编码成字节串。
第二种方法是Decode
,它将编码后的字节串解码成相应的对象。
第三种方法是is_deterministic
。它决定该编码器是否按照 Beam [ 21 ]文档中的规定对值进行确定性编码。
阿帕奇光束触发器
Apache Beam 触发器提示窗口发出结果。如[ 22 和[ 23 所述,在对窗口结构中的元素进行分组的情况下,Beam 受益于触发器来决定何时转换每个窗口的聚合结果。即使你没有指定,每个窗口都有一个“默认触发器”。
您可以为您的窗口设置触发器来更改此默认行为。Beam 提供了几个预置的触发器供您选择。除此之外,您还可以创建自定义触发器。根据触发类型,您的窗口可以在水印穿过您的窗口之前发出早期结果,也可以在任何后期元素到达时发出后期效果。
事件时间触发器
EventTimeTrigger
表现为AfterMarkTrigger
。这些是传输窗口内容的默认触发器。当缺省窗口设置和缺省触发器一起使用时,缺省触发器精确地发出一次,并且后期数据被丢弃[ 24 ]。
import apache_beam as beam
from apache_beam import window
from apache_beam.options.pipeline_options import PipelineOptions, StandardOptions
from apache_beam.transforms.trigger import AfterWatermark, AfterProcessingTime, AccumulationMode, AfterCount
after_watermark_trigger = beam.Pipeline()
content = ( after_watermark_trigger
|beam.Create({‘dict1’:[24,45,68],’dict2':[32,54,75], ‘dict3’:[56,78,92]})
|beam.Map(lambda element: element)
|beam.WindowInto(window.FixedWindows(20),
trigger=AfterWatermark(
early=AfterProcessingTime(5),
late=AfterCount(5)),
accumulation_mode=AccumulationMode.DISCARDING)
|beam.io.WriteToText(‘/content/after_watermark_trigger.txt’)
)
after_watermark_trigger.run()
处理时间触发器
第二个是ProcessingTimeTrigger
俗称AfterProcessingTime
[ 25 。顾名思义,这个触发器在处理时间上运行。经过一定的处理时间后,触发器会提示窗口发出结果。执行时间受到系统日期的限制,最好是数据项的时间戳。此触发器有助于从窗口中触发早期结果,尤其是具有重要时间范围的窗口,如单个全局窗口。
import apache_beam as beam
from apache_beam import window
from apache_beam.options.pipeline_options import PipelineOptions, StandardOptions
from apache_beam.transforms.trigger import AfterWatermark, AfterProcessingTime, AccumulationMode, AfterCount
after_processing_time_trigger = beam.Pipeline()
content = ( after_processing_time_trigger
|beam.Create({‘dict1’:[24,45,68],’dict2':[32,54,75], ‘dict3’:[56,78,92]})
|beam.Map(lambda element: element)
|beam.WindowInto(window.FixedWindows(20), trigger=AfterProcessingTime(10), accumulation_mode=AccumulationMode.DISCARDING) |beam.io.WriteToText(‘/content/after_processing_time_trigger.txt’))
after_processing_time_trigger.run()
数据驱动触发器
第三个是DataDrivenTrigger
,名字叫AfterCount
。它在现有窗口收集了至少 N 个元素后运行。如果用“N = 5”指定计数触发器,当窗口的窗格中有五个功能时,它将提示窗口再次发出结果。
import apache_beam as beam
from apache_beam import window
from apache_beam.options.pipeline_options import PipelineOptions, StandardOptions
from apache_beam.transforms.trigger import AfterWatermark, AfterProcessingTime, AccumulationMode, AfterCount
after_count_trigger = beam.Pipeline()
content = ( after_count_trigger
|beam.Create({‘dict1’:[24,45,68],’dict2':[32,54,75], ‘dict3’:[56,78,92]})
|beam.Map(lambda element: element)
|beam.WindowInto(window.GlobalWindows(), trigger=AfterCount(5), accumulation_mode=AccumulationMode.DISCARDING)
|beam.io.WriteToText(‘/content/after_count_trigger.txt’)
)
after_count_trigger.run()
复合触发器
复合触发器是多个触发器的组合。它允许用谓词合并不同类型的触发器。它们允许同时使用多个触发器。光束包括以下类型[ 26 ]。
第一个是Repeatedly
。该条件指定一个运行到无穷大的触发器。建议将Repeatedly
与其他一些可能导致该重复触发停止的条件结合使用。下面添加了一个示例代码片段。
import apache_beam as beam
from apache_beam import window
from apache_beam.options.pipeline_options import PipelineOptions, StandardOptions
from apache_beam.transforms.trigger import AfterWatermark, AfterProcessingTime, AccumulationMode, AfterAny, Repeatedly
composite_repeatedly = beam.Pipeline()
content = ( composite_repeatedly
| beam.Create({‘dict1’:[24,45,68],‘dict2’:[32,54,75], ‘dict3’:[56,78,92]})
| beam.Map(lambda element: element)
|beam.WindowInto(window.FixedWindows(20), trigger=Repeatedly(AfterAny(AfterCount(50), AfterProcessingTime(20))),
accumulation_mode=AccumulationMode.DISCARDING)
| beam.io.WriteToText(‘/content/composite_repeatedly’))
composite_repeatedly.run()
第二个是AfterEach
。这种状态将多个触发器组合在一起,以特定的顺序一个接一个地触发。每当触发器发出一个窗口,过程就前进到下一个窗口。
第三个是AfterFirst
。它使用多个触发器作为参数。当它的任何参数触发器被满足时,它处理窗口发出结果。它类似于多个触发器的“或”运算。
第四个是AfterAll
。它保存多个触发器作为参数,并使窗口在其所有参数触发器都满足时发出结果。对于许多触发器来说,它相当于“与”运算。
第五个是Finally
。它作为最终条件,使任何触发器最后一次触发,并且不再触发。
Beam 中流式数据管道的结构
Beam 的核心思想是提供整合的大数据处理管道。正如其官方文档[ 6 ]中所述,其和谐的本质用单个 API 构建了批处理和流管道。
当您创建您的Pipeline
时,您还可以设置一些与之相关的配置选项,比如管道运行器,它将执行您的管道,以及所选择的运行器所需的任何运行器特定的配置。
您可以考虑通过硬编码来分配管道的配置参数。尽管如此,通常建议从命令行读取它们,然后将其传递给Pipeline
对象。出于这个原因,如果我们可以构建一个从命令行获取运行器信息、输入输出文件路径信息的管道,那么我们的问题就解决了,我们可以说我们将获得一个通用管道。
import apache_beam as beam
import argparse
from apache_beam.options.pipeline_options import PipelineOptions, StandardOptions
parser = argparse.ArgumentParser()
parser.add_argument(‘ — input’, dest=’input’, required=True, help=’/content/data.txt/’)parser.add_argument(‘ — output’, dest=’input’, required=True, help=’/content/output.txt/’)
path_args, pipeline_args = parser.parse_known_args()
input_arguments = path_args.input
output_arguments = path_args.output
options = PipelineOptions(pipeline_args)
pipeline_with_options = beam.Pipeline(options = options)
dept_count = (pipeline_with_options
|beam.io.ReadFromText(input_arguments)
|beam.Map(lambda line: line.split(‘,’))
|beam.Filter(lambda line: line[3] == ‘AI’)
|beam.Map(lambda line: (line[1], 1))
|beam.io.WriteToText(output_arguments)
)
pipeline_with_options.run()
部署数据管道
Beam 将通过 Google PubSub 提供流媒体数据。为了在 Google PubSub 中处理流数据,我们需要创建一个项目,并获得它的“服务 _ 账户 _ 认证”密钥[ 27 ]。
在 Google PubSub 中创建主题
首先,我们需要点击https://cloud.google.com/主页右上角的按钮进入“控制台”。
来源:图片由作者提供
第二,谷歌云控制台将帮助你创建一个新项目。启动此项目可能需要几秒钟时间。创建项目后,您可以在“项目信息”部分查看“项目名称”。
来源:图片由作者提供
要获得其“服务身份验证密钥”,我们需要转到“IAM & Admin”部分下的服务帐户。
来源:图片由作者提供
填写完必填字段后,我们可以点击“创建并继续”。
来源:图片由作者提供
或者,您可以在该身份验证密钥中授予您想要的权限。从选项中,继续“项目>编辑器”。
来源:图片由作者提供
您可以通过单击“完成”按钮来完成初始化部分。
来源:图片由作者提供
要创建一个`. json '格式的密钥,您可以单击“密钥”选项卡,然后选择“添加密钥”下的“创建新密钥”。
来源:图片由作者提供
这是我们希望为服务帐户生成的密钥。下载并保存在一个非常安全的地方。任何拥有此密钥的人都可以查看您的项目。
来源:图片由作者提供
您将需要一个出版商、一个主题和一个订阅。发布者将发布关于某个主题的消息。为此,我们将使用“PubSub”。
来源:图片由作者提供
我们已经创建了主题。一些统计数据是“发布消息请求计数”和“发布消息操作计数”。我们将在 publisher 脚本中使用这个主题路径。
来源:图片由作者提供
您需要通过将订阅名称和“交付类型”填写为“拉”来创建订阅主题。
来源:图片由作者提供
在创建了主题和订阅之后,我们可以查看统计图表,该图表没有显示任何内容,因为我们还没有发布任何消息。
来源:图片由作者提供
假设您希望通过使用接口本身来发布消息。您可以点击“发布消息”并提供可选的消息属性作为键值对。这些属性用于发送有关消息的附加信息。对于“添加属性”,您可以添加“语言”作为关键字,添加“英语”作为其值。
来源:图片由作者提供
消息已发布,我们可以手动提取此消息,因为没有正在运行的订阅者。您可以选择您想要“拉”它的订阅。您可以选中“启用确认”按钮,在收到确认后发送确认。
来源:图片由作者提供
然后,您可以点击“拉”。除了查看消息的属性之外,您还可以查看消息。我们可以观察到该消息已被确认。
来源:图片由作者提供
整个活动中包括一些连接器,用于将客户的提供者连接到我们的发布者应用程序。这种方法在一些现实场景中使用,在这些场景中,他们希望我们逐行读取文件并进行处理,而不是对文件进行批处理。
创建的 PubSub 主题可以像下面这样在脚本中定义来使用它。您可以用您的特定路径替换引用的字符串。应该填写这些路径,以便在 Google Cloud 的指南页面[ 28 ]中提到的正确主题上发布消息。
import os
from google.cloud import pubsub_v1
project = ‘SubscribeBeam’
topic_for_pubsub = ‘projects/qwiklabs-gcp-01–7779ab5fa77e/topics/BeamTopic’
service_account_path = “C:\\Users\ersoyp\Documents\ApacheBeam\qwiklabs-gcp-01–7779ab5fa77e-2d40f7ded2a8.json”
os.environ[“GOOGLE_APPLICATION_CREDENTIALS”] = service_account_path
data_path = “C:\\Users\ersoyp\Documents\ApacheBeam\data.csv”
用 GCP 处理数据管道
在上一节中,我们定义了 PubSub 主题和相关的“service_account_path”信息。在接下来的步骤中,我们将使用 PubSub 凭证通过 Beam 读写数据。我们一起来实施吧。
以下脚本定义了发布订阅主题路径、服务帐户路径、输入和输出文件路径。此外,我们添加了“GOOGLE_APPLICATION_CREDENTIALS”作为环境变量。在分配了这些路径之后,我们初始化了将要处理的射束管道。在输入和输出路径的帮助下,我们很容易从 Google Cloud PubSub 中读取数据,然后将结果写回给它。
import osimport os
import apache_beam as beam
from apache_beam.options.pipeline_options import PipelineOptions, StandardOptions
from apache_beam import window
project = ‘SubscribeBeam’
pubsub_topic = ‘projects/qwiklabs-gcp-01–7779ab5fa77e/topics/BeamTopic’
path_service_account = “C:\\Users\ersoyp\Documents\ApacheBeam\qwiklabs-gcp-01–7779ab5fa77e-2d40f7ded2a8.json”
os.environ[“GOOGLE_APPLICATION_CREDENTIALS”] = path_service_account
input_file = “C:\\Users\ersoyp\Documents\ApacheBeam\data.csv”
output_file = ‘C:\\Users\ersoyp\Documents\ApacheBeam\output.csv’
options = PipelineOptions()
options.view_as(StandardOptions).streaming = True
process = beam.Pipeline(options=options)
output_file = ‘/content/outputs/’
pubsub_data = ( process
|’Read from Google PubSub’ >> beam.io.ReadFromPubSub(subscription= input_file)
|’Write to Google PubSub’ >> beam.io.WriteToPubSub(output_file))
final_file = process.run()
向 GCP 订购数据管道
作为部署数据管道的最后一步,我们需要用 PubSub 创建一个“SubscriberClient”对象。订阅服务器初始化后,将被分配到相应的订阅路径。您可以使用下面的脚本查看实现。
脚本首先将“GOOGLE_APPLICATION_CREDENTIALS”指定为操作系统中的一个环境变量。分配的路径包括从 GCP IAM & Admin 界面生成的服务帐户密钥。之后,我们在“args”的帮助下创建一个订阅路径。然后,我们用 GCP 公共订阅创建一个 SubcriberClient。最后,我们将构建的订阅路径分配给 GCP 的订阅者。
from google.cloud import pubsub_v1
import time
import os
os.environ[“GOOGLE_APPLICATION_CREDENTIALS”] = ‘C:\\Users\ersoyp\Documents\ApacheBeam\qwiklabs-gcp-01–7779ab5fa77e-2d40f7ded2a8.json’
path_for_subcription = args.subscription_path
pubsub_subscriber = pubsub_v1.SubscriberClient()
pubsub_subscriber.subscribe(path_for_subcription, callback=callback)
监控数据管道
在上面的章节中,我们在 Google Cloud PubSub 的帮助下发布、处理和订阅了带有示例脚本的数据管道。由于我们使用了 GCP,我们可以使用谷歌云监控工具跟踪监控活动。
为此,我们可以通过使用此窗格选择监控来查看“概述”、“仪表板”、“服务”和“指标浏览器”。
来源:图片由作者提供
我们创建的任何指标都将添加到“指标浏览器”选项卡下。我们可以选择“资源类型”和“指标”来过滤出正确的数据。此外,我们可以使用“Group by”和“Aggregator”的聚合操作和“Alignment period”。
来源:图片由作者提供
阿帕奇光束 vs 阿帕奇火花
阿帕奇梁生产各种环境下的管道。它只是分布式数据的另一种编程模型。与 Apache Spark 一样,Apache Beam 有 RDD 或数据帧来执行批处理,还有数据流来进行流处理。Beam 用 Java 、 Python 和 Go 语言实现。
另一方面,Apache Spark 是一个用于海量数据处理的综合引擎。它是在 2012 年开发的,最初只是为批处理而设计的。Spark 将流分成几个小批量,并处理这些小批量。
如果我们保持小批量,就好像我们在执行实时流数据。这就是为什么 Spark 被认为接近实时流处理引擎,而不是有效的流处理引擎。Spark 是用 Scala 语言实现的。也兼容 Spark 官方页面[ 29 ]中描述的 Hadoop 平台。
最后的想法
在整篇文章中,在最初描述概念和用示例脚本实现解决方案的结构中呈现了广泛的主题。主题包括介绍 Apache Beam,然后在 Beam 中构建管道。标题包括但不限于:
- Apache Beam 的体系结构
- 阿帕奇波束的特点
- 阿帕奇梁的管道结构
- 帕尔多变换
- 复合转换
- 侧面输入和侧面输出
- 在 Apache Beam 中实现 Windows
- 用编码器进行编码操作
- 阿帕奇光束触发器
- 流式数据管道的结构
- 部署数据管道
- 监控数据管道
- 阿帕奇光束 vs 阿帕奇火花
非常感谢您的提问和评论!
附加参考
- 阿帕奇光束:https://beam.apache.org/documentation/
- Apache Beam Pipeline:https://Beam . Apache . org/documentation/pipelines/design-your-Pipeline/
- 阿帕奇火花:【https://spark.apache.org/documentation.html】的,的 https://beam.apache.org/documentation/runners/spark/
- 阿帕奇弗林克:https://ci.apache.org/projects/flink/flink-docs-master/
- 阿帕奇萨姆扎:http://samza.apache.org/startup/quick-start/1.6.0/beam.html
- 谷歌云数据流:https://Cloud . Google . com/data flow/docs/concepts/beam-programming-model
- 大数据描述:https://www . SAS . com/en _ us/insights/big-Data/what-is-big-Data . html
- Apache Beam |构建大数据管道的实践课程:https://www . udemy . com/course/Apache-Beam-A-Hands-On-course-build-Big-data-Pipelines/
- 窗口累加模式,https://beam . Apache . org/documentation/programming-guide/# triggers
- 谷歌云控制台,https://console.cloud.google.com