Python-和-R-的-PowerBI-扩展指南-全-
Python 和 R 的 PowerBI 扩展指南(全)
原文:
zh.annas-archive.org/md5/f6aea8ba61e4cab42d5fa7c6d0418307译者:飞龙
第一章:使用 Python 和 R 扩展 Power BI:使用分析语言的力量进行摄取、转换、丰富和可视化
欢迎来到 Packt 早期访问。在本书上市之前,我们为您提供独家预览。写一本书可能需要数月时间,但我们的作者今天有前沿信息要与您分享。早期访问通过提供章节草案,让您深入了解最新的发展。目前章节可能有些粗糙,但我们的作者将随着时间的推移进行更新。当新版本准备就绪时,您将收到通知。
这个标题正在开发中,还有更多章节有待撰写,这意味着您有机会对内容发表意见。我们希望出版对您和其他客户都有用的书籍,因此我们将定期向您发送问卷。所有反馈都很有帮助,所以请畅所欲言您的想法和意见。我们的编辑将对书籍的文本进行修改,因此我们希望您对技术元素和作为读者的经验提供反馈。我们还将提供关于作者如何根据您的反馈修改章节的频繁更新。
您可以随时阅读这本书或从头到尾跟随;早期访问旨在具有灵活性。我们希望您喜欢了解更多关于 Packt 书籍编写过程的信息。通过贡献您的想法加入对新主题的探索,并看到它们在印刷品中变为现实。
-
在哪里以及如何使用 R 和 Python 脚本在 Power BI 中
-
在 Power BI 中配置 R
-
在 Power BI 中配置 Python
-
导入未处理的数据对象
-
在 Power BI 中使用正则表达式
-
在 Power BI 中匿名化和伪匿名化您的数据
-
从 Power BI 记录数据到外部来源
-
在 Power BI 中加载超过可用 RAM 的大型数据集
-
调用外部 API 以丰富您的数据
-
使用复杂算法计算列
-
添加统计洞察:关联
-
添加统计洞察:异常值和缺失值
-
无需高级或嵌入式容量即可使用机器学习
-
探索性数据分析
-
高级可视化
-
交互式 R 自定义视觉
第二章:1 在 Power BI 中使用 R 和 Python 脚本的哪里和如何使用
Power BI 是微软的旗舰自助式商业智能产品。它由一套本地应用程序和基于云的服务组成,通过用户友好的界面帮助组织从各种源系统中整合、转换和分析数据。
平台不仅限于数据可视化。当考虑到其分析引擎(Vertipaq)与SQL Server Analysis Services(SSAS)和Azure Analysis Services相同,并且它还使用Power Query作为其数据提取和转换引擎时,Power BI 远不止于此。我们可以在 Analysis Services 和Excel中找到这个引擎,它配备了一个非常强大且多功能的公式语言(M)和 GUI,这使得您可以将任何类型的数据“磨碎”并塑造成任何形式。
此外,Power BI 支持DAX作为数据分析公式语言,它可以用于对已加载到表格数据模型中的数据进行高级计算和查询。
这样一个多功能且强大的工具对于需要进行数据摄取和转换以构建仪表板和报告来总结公司业务的人来说是上帝赐予的礼物。
最近,大量数据的可用性以及机器计算能力的可扩展性使得高级分析领域更具吸引力。因此,为了提供丰富的见解,新的数学和统计工具变得必要。因此,在 Power BI 中集成了分析语言,如Python和R。
在 Power BI 中,R 或 Python 脚本只能使用特定的功能。了解哪些 Power BI 工具可以将 R 或 Python 脚本注入 Power BI,对于理解您想要解决的问题是否可以通过这些分析语言实现至关重要。
本章将涵盖以下主题:
-
将 R 或 Python 脚本注入 Power BI
-
使用 R 和 Python 与您的数据交互
-
R 和 Python 在 Power BI 产品中的限制
技术要求
本章要求您已在您的计算机上安装了Power BI Desktop(您可以从这里下载:aka.ms/pbiSingleInstaller)。
将 R 或 Python 脚本注入 Power BI
在本节的第一部分,将详细介绍允许您使用 Python 或 R 脚本的 Power BI Desktop 工具。具体来说,您将了解如何在数据加载、数据转换和数据查看阶段添加自己的代码。
数据加载
在 Power BI Desktop 中处理数据的第一步之一是从外部源导入它:
-
根据各自的数据源,有许多连接器允许你这样做,但你也可以通过 Python 和 R 的脚本来实现。实际上,如果你点击功能区中的获取数据图标,不仅会显示最常用的连接器,而且你可以通过点击更多...来选择一个更完整的列表中的其他连接器:
![图 1.1 – 浏览更多连接器以加载数据]()
图 1.1 – 浏览更多连接器以加载数据
-
在弹出的新获取数据窗口中,只需在搜索文本框中输入字符串
script,立即就会显示通过 Python 或 R 导入数据的两个选项:![图 1.2 – 显示 R 脚本和 Python 脚本到“获取数据”窗口]()
图 1.2 – 显示 R 脚本和 Python 脚本到“获取数据”窗口
-
阅读通过将鼠标悬停在Python 脚本选项上获得的工具提示内容,两个应该立即跳入你的眼帘:a)需要本地安装 Python。B)可以通过 Python 导入的是数据框。当选择R 脚本时,同样的两个观察结果也适用。唯一的区别是,当使用 Python 时,可以导入pandas DataFrame(DataFrame 是 pandas 包提供的数据结构),而 R 使用的是默认提供的类似二维数组的R 数据框数据结构。
-
点击Python 脚本选项后,将显示一个包含用于编写 Python 代码的文本框的新窗口:
![图 1.3 – 显示 Python 脚本编辑器的窗口]()
图 1.3 – 显示 Python 脚本编辑器的窗口
如你所见,这确实是一个非常简陋的编辑器,但在第三章,使用 Power BI 配置 Python中,你将看到如何使用你喜欢的 IDE 来开发你自己的脚本。
-
查看警告信息,Power BI 提醒我们尚未检测到 Python 引擎,因此必须安装。点击如何安装 Python链接将打开一个 Microsoft Docs 网页,解释安装 Python 的步骤。Microsoft 建议安装基础 Python 发行版,但为了遵循一些关于环境的最佳实践,我们将安装Miniconda发行版。如何进行此操作以及原因将在第三章中介绍。
-
如果你点击了R 脚本,则会弹出一个用于在 R 中输入代码的窗口,类似于图 1.4中所示:
![图 1.4 – 显示 R 脚本编辑器的窗口]()
图 1.4 – 显示 R 脚本编辑器的窗口
与 Python 一样,为了在 R 中运行代码,你需要在你的机器上安装 R 引擎。点击如何安装 R链接将打开一个 Microsoft Docs 页面,其中 Microsoft 建议安装Microsoft R Open或经典的CRAN R。第二章,使用 Power BI 配置 R将向你展示如何选择引擎以及如何配置你喜欢的 IDE 以编写 R 代码。
为了使用 Python 或 R 导入数据,您需要在图 1.3和图 1.4中显示的编辑器中编写代码,将 pandas DataFrame 或 R 数据框分配给变量。您将在本书中看到具体的示例。
接下来,让我们看看如何转换数据。
数据转换
可以使用 R 或 Python 中的脚本对已导入或正在导入的数据应用转换。如果您想即时测试,可以直接从网络上导入以下 CSV 文件:bit.ly/iriscsv。请按照以下步骤操作:
-
简单地点击获取数据,然后点击网页以直接从网页导入数据:
![图 1.5 – 选择 Web 连接器从网页导入数据]()
图 1.5 – 选择 Web 连接器从网页导入数据
-
您现在可以在弹出的窗口中输入之前提到的 URL:
![图 1.6 – 从网络上导入 Iris 数据]()
图 1.6 – 从网络上导入 Iris 数据
点击确定后,将弹出一个窗口,显示即将导入的数据预览。
-
在这种情况下,不是直接导入数据,而是点击转换数据以访问 Power Query 数据转换窗口:
![图 1.7 – 导入数据预览]()
图 1.7 – 导入数据预览
-
在此阶段,您可以通过在Power Query 编辑器中选择转换选项卡来添加使用 Python 或 R 脚本的转换步骤:
![图 1.8 – R 和 Python 脚本工具集成到 Power Query 编辑器]()
图 1.8 – 将 R 和 Python 脚本工具集成到 Power Query 编辑器中
-
通过点击运行 Python 脚本,将弹出一个与数据导入阶段中您已经看到的类似的窗口:
![图 1.9 – 运行 Python 脚本编辑器]()
图 1.9 – 运行 Python 脚本编辑器
如果您仔细阅读文本框中的注释,您会看到
dataset变量已经初始化,并包含 Power Query 编辑器中当前时刻存在的数据,包括已应用的所有转换。在此阶段,您可以在文本框中插入 Python 代码以将数据转换为所需的形式。 -
如果您点击运行 R 脚本,将打开一个类似的窗口:
![图 1.10 – 运行 R 脚本编辑器]()
图 1.10 – 运行 R 脚本编辑器
此外,在这种情况下,
dataset变量已经初始化,并包含 Power Query 编辑器中当前时刻存在的数据。然后您可以添加自己的 R 代码,并引用dataset变量以最合适的方式转换您的数据。
接下来,让我们看看如何可视化数据。
数据可视化
最后,您可以将自己的 Python 或 R 脚本添加到 Power BI 中,以创建新的可视化,而不仅仅是工具中已经存在的可视化:
-
假设我们继续上一节开始的数据导入活动,一旦加载了
Iris数据集,只需在运行 R 脚本窗口中点击取消,然后在 Power Query 编辑器的主页选项卡中点击关闭并应用:![图 1.11 – 点击关闭并应用以导入 Iris 数据]()
图 1.11 – 点击关闭并应用以导入 Iris 数据
-
数据导入完成后,您可以在 Power BI 的可视化窗格中选择R 脚本可视化或Python 脚本可视化选项:
![图 1.12 – R 和 Python 脚本可视化]()
图 1.12 – R 和 Python 脚本可视化
-
如果您点击Python 脚本可视化,会弹出一个窗口请求允许启用脚本代码执行,因为可能存在安全或隐私风险:
![图 1.13 – 启用脚本代码执行]()
图 1.13 – 启用脚本代码执行
-
启用代码执行后,在 Power BI Desktop 中,您可以在报告画布上看到 Python 可视化图像的占位符,以及在底部有一个 Python 脚本编辑器:
![图 1.14 – Python 可视化布局]()
图 1.14 – Python 可视化布局
您现在可以在 Python 编辑器中编写自己的自定义代码,并通过图 1.14中突出显示的运行脚本图标运行它以生成 Python 可视化。
选择R 脚本可视化时,会出现几乎相同的布局。
使用 R 和 Python 与您的数据交互
在上一节中,您看到了所有通过 R 或 Python 脚本在 Power BI 中与您的数据交互的方式。除了知道如何以及在哪里将代码注入 Power BI 之外,了解您的代码将如何与数据交互也非常重要。在这里,我们看到通过 Power Query 编辑器注入的脚本和用于可视化的脚本之间存在很大的差异:
-
通过 Power Query 编辑器的脚本:此类脚本将转换数据并在模型中持久化转换。这意味着始终可以从 Power BI 中的任何对象检索转换后的数据。此外,一旦脚本执行并生效,除非数据刷新,否则它们将不会重新执行。因此,当您打算在其他可视化或数据模型中使用生成的洞察时,建议通过 Power Query 编辑器注入 R 或 Python 代码。
-
可视化中的脚本:R 和 Python 脚本可视化中使用的脚本从数据中提取特定的洞察,并通过可视化仅让用户明显看到。像报告页面上所有其他可视化一样,R 和 Python 脚本可视化也与其他可视化相互连接。这意味着脚本可视化会受到交叉过滤的影响,因此每次您与报告中的其他可视化交互时,它们都会刷新。尽管如此,无法在数据模型中持久化从可视化脚本中获得的结果。
提示
由于 R 和 Python 脚本可视化具有跨过滤器的交互性,因此可以注入用于从数据中提取 实时洞察 的代码,也可以从外部来源(你将在 第八章,调用外部 API 丰富您的数据 中看到)中提取。重要的是要记住,正如之前所述,那时只能可视化此类信息,或者最多将其写入外部存储库(你将在 第七章,将 Power BI 数据记录到外部存储库 中看到)。
在本章的最后部分,让我们看看使用 R 和 Python 在各种 Power BI 产品中的局限性。
Power BI 产品上的 R 和 Python 局限性
一旦您清楚在 Power BI 中注入 R 和 Python 脚本的位置,第一个问题可能是:“在所有 Power BI 产品中允许使用 R 和 Python 代码吗?”为了简要回顾各种 Power BI 产品及其一般用途,以下是一个简明的列表:
-
Power BI 服务:有时被称为 Power BI 在线,它是 Power BI 的 软件即服务(SaaS)版本。它的创建是为了通过仪表板和报告促进用户之间可视化分析的共享。
-
Power BI 报告服务器:这是 Power BI 的本地版本,它扩展了 SQL Server 报告服务的功能,使得可以在 Power BI 桌面版(用于报告服务器) 中创建的报告之间共享。
-
Power BI 嵌入式:一个 Microsoft Azure 服务,允许将仪表板和报告嵌入到没有 Power BI 账户的用户的应用程序中。
-
Power BI 桌面版:一个免费的 Windows 桌面应用程序,允许您使用 Power BI 提供的几乎所有功能。这不是用户之间共享结果的正确工具,但它允许您在 Power BI 服务和 Power BI 报告服务器上共享它们。允许在两个提到的服务上发布桌面版本是不同的。
-
Power BI 移动版:一个可在 Windows、Android 和 iOS 上使用的移动应用程序,允许安全访问 Power BI 服务和 Power BI 报告服务器,并允许您浏览和共享仪表板和报告,但不能编辑它们。
除了许可证(我们在这里不讨论),以下是之前提到的产品之间关系的摘要图:

图 1.15 – Power BI 产品之间的交互
不幸的是,在这些产品中,只有 Power BI 服务、Power BI 嵌入式 和 Power BI 桌面版 允许您通过代码在 R 和 Python 中丰富数据:

图 1.16 – Power BI 产品与 R 和 Python 的兼容性
重要提示
从现在开始,当我们谈论与分析语言兼容性的 Power BI 服务 时,我们所说的 也将适用于 Power BI Embedded。
因此,如果您需要通过 R 和 Python 进行高级分析来开发报告,请确保目标平台支持它们。
摘要
本章详细概述了您可以在 Power BI Desktop 中使用 R 和 Python 脚本的所有方法。在数据摄取和数据处理阶段,Power Query 编辑器允许您添加包含 R 或 Python 代码的步骤。您还可以利用 Power BI Desktop 提供的 R 和 Python 脚本可视化在数据可视化阶段使用这些分析语言。
了解 R 和 Python 代码如何与已加载或正在加载到 Power BI 中的数据进行交互也非常重要。如果您使用 Power Query 编辑器,无论是在加载数据还是转换数据时,脚本处理的结果都将持久保存在数据模型中。另外,如果您想再次运行相同的脚本,您必须刷新数据。另一方面,如果您使用 R 和 Python 脚本可视化,代码结果只能显示,并不会保存在数据模型中。在这种情况下,脚本执行会在报告中的其他可视化触发交叉筛选时发生。
很遗憾,在撰写本文时,您无法在其他任何 Power BI 产品中运行 R 和 Python 脚本。唯一提供运行分析脚本的是 Power BI Desktop 和 Power BI 服务。
在下一章中,我们将了解如何最佳配置 R 引擎和 RStudio 以与 Power BI Desktop 集成。
第三章:2 配置 R 与 Power BI
Power BI Desktop 默认没有配备上一章中提到的分析语言引擎。因此,有必要安装这些引擎并正确配置 Power BI Desktop 以正确地与它们接口。还建议安装一个集成开发环境(IDE),让你能够以最舒适的方式工作。
我们将探讨如何启动这些引擎,并给你一些关于如何根据你的需求选择最合适的引擎的一般指南。之后,我们将探讨如何使这些引擎与 Power BI Desktop 和 Power BI 服务接口。
最后,我们将提供一些关于如何克服 Power BI 服务上 R 可视化严格限制的重要提示。
尤其是本章将详细讨论以下主题:
-
可用的 R 引擎
-
选择要安装的 R 引擎
-
安装 R 开发用的 IDE
-
配置 Power BI Desktop 以使用 R
-
配置 Power BI 服务以使用 R
-
R 可视化的限制
技术要求
本章要求你有一个工作的互联网连接,并且你的机器上已经安装了Power BI Desktop。它还要求你在本章的最后部分注册了 Power BI 服务(这里有一个教程:bit.ly/signup-powerbiservice)。一个Power BI 免费版许可证就足够测试本书中的所有代码,因为你将仅在个人工作区中共享报告。
可用的 R 引擎
市面上有多个 R 发行版可供免费使用,你可以用于你的高级分析项目。在本节中,我们将探索每个的主要细节。
CRAN R 发行版
当提到安装 R 引擎时,我们几乎总是想到由一群贡献者多年共同开发的开源软件环境出类拔萃,这个环境被称为CRAN R,也称为base R(cran.r-project.org)。更确切地说,综合 R 存档网络(CRAN)是一个全球范围内的网络,包括网页服务器和 FTP 服务器,其目标是保存多个相同且最新的 R 源代码版本以及社区开发的整个 R 包生态系统,以及所有的 R 文档。
CRAN R 最大的优点之一是其非常活跃的开发者社区。他们对 CRAN 上新包创建的贡献是无价的。这就是为什么如果你认为你需要某个特定功能来处理你的数据,几乎可以肯定的是,它已经被 R 社区开发并作为免费可用的 R 包发布。
然而,并非每个人都了解 CRAN R 不是市场上唯一的 R 发行版。
微软 R Open 发行版和 MRAN
即使微软也通过发布适用于 Windows 和 Linux 的自己的开源 R“发行版”,在通用公共许可证版本 2的条款下对社区做出了贡献。从 2016 年开始,微软发布了其自己的 R 发行版,称为Microsoft R Open(MRO),有时简称为Microsoft R,它与 CRAN R 发布的版本相同,并且 100%兼容。
重要提示
如果你使用任何 CRAN 包编写的代码,并使用相同版本的 MRO 引擎运行,一切都会正常工作。
此外,只有少数由微软拥有的函数(包括RevoUtils和RevoUtilsMath)返回有关引擎安装(如路径和内存使用)以及数学内核库(MKL)可以运行的最大线程数的信息。
MRO 发布的优势在于它为 R 社区带来了重要的改进:
-
预装的多线程Intel MKL
-
Microsoft R 应用程序网络(MRAN,
mran.microsoft.com)是 CRAN 仓库的高性能镜像,它提供了一个“时间机器”工具,让您能够获取选定时间的 CRAN 包快照
让我们详细看看它们是什么。
MRO 中的多线程
虽然 CRAN R 最初是作为单线程创建的,但它也有能力链接到多线程的基本线性代数子程序(BLAS)和线性代数包(LAPACK)库,但这些手动操作并不简单。
安装 MRO 的好处是,由于 MKL,引擎已经预先配置并针对英特尔处理器进行了优化,目的是并行执行数学计算。为了了解计算时间上的收益,请查看图 2.1(来源:bit.ly/msropen-bnchmrk)中比较 R-3.4.1 和 MRO 3.4.1 的基准结果:

图 2.1 – 同一台机器上每个基准测试的总耗时
如您所见,CRAN R(未链接 BLAS 库直接安装)在矩阵计算中所需的时间显著高于 MRO。因此,如果您在数据科学、工程、金融分析等领域大量使用数学函数,强烈建议使用 MRO。
使用检查点重现结果
当使用 R 进行复杂分析时,结果脚本很可能基于一个或多个 CRAN 软件包。R 中最常见的问题之一是结果的可重复性,因为 CRAN 上的软件包每天都在变化。因此,可能会发生这种情况:几周前运行良好的脚本在更新软件包后开始产生之前从未见过的错误,或者当您在另一台机器上运行它时。更隐蔽的是,由于软件包更新,可能会在不经意间得到错误的结果。
因此,从 2014 年 9 月开始,微软推出了自己的 CRAN R 软件包分发版,这些软件包按整个存储库的每日快照组织,由 checkpoint 服务器 在 UTC 午夜进行快照并持久化在 MRAN 上:

图 2.2 – 在 MRAN 中持久化 CRAN 每日快照的过程
从那时起,R 社区能够访问 CRAN 软件包的特定快照,其版本被冻结在特定日期。
重要提示
您不需要使用 MRO 来利用 checkpoint 服务器提供的免费服务。CRAN R 用户也可以使用它。
通常,当您在 R 中调用 install.packages 来安装一个或多个软件包时,会安装每个指定软件包的最新 CRAN 版本。使用 checkpoint 软件包,您有权引用过去某一天 CRAN 存储库的特定快照,如下所示:
library(checkpoint)
checkpoint("YYYY-MM-DD") # Replace the data you want here
install.packages("dplyr")
使用前面的代码示例,您和任何其他用户都确保始终使用从您想要使用的 CRAN 存储库快照中下载的 dplyr 软件包的相同版本。
MRO 安装包括 checkpoint 软件包,并默认将快照日期设置为正在安装的 MRO 版本的发布日期。您可以通过 MRAN 门户上可用的 CRAN 时间机器 工具轻松验证由特定版本的 MRO 设置的快照日期(mran.microsoft.com/timemachine):

图 2.3 – 在 MRAN 中持久化 CRAN 每日快照的过程
您也可以按以下方式以编程方式检查默认软件包存储库:
getOption("repos")
您将得到类似以下输出:
CRAN
"https://mran.microsoft.com/snapshot/2020-07-16"
使用此示例代码,如果您安装任何 R 软件包,安装的版本将是 2020 年 7 月 16 日午夜在 CRAN 中存在的版本。
微软 R 客户端
微软还发布了一个名为Microsoft R 客户端的 R 版本,免费提供给 Windows 和 Linux 用户。这个版本完全基于 MRO,允许您运行在 CRAN R 上运行的任何代码。此外,还有一些微软的 R 函数库(bit.ly/ml-r-funcions)没有开源。这些函数是多线程的,允许您即使与通常无法包含在机器内存中的数据集(使用微软的本地产品SQL Server和Machine Learning Server)一起工作。是的,没错:如果您不知道,微软的关系型数据库管理系统(RDBMS)SQL Server 允许您在其存储过程中使用 R、Python 和 Java 代码。因此,如果您的数据源是 SQL Server 本身,并且您需要从那里持久化的数据中提取洞察,请考虑直接使用 SQL Server 中的存储过程应用高级分析算法的可能性,将 R 和 Python 代码嵌入其中。
使用 Microsoft R 客户端,由于之前提到的性能良好的MicrosoftML和RevoScale包,可以将需要非平凡计算复杂性的算法委托给 SQL Server 或 Machine Learning Server。
与 MRO 不同,Microsoft R 客户端不是与每个 CRAN R 版本并行更新,而是遵循在 Machine Learning Server 发布时包含的 MRO 版本。例如,截至今天,CRAN R 是 4.0.2 版本,MRO 也是 4.0.2 版本,而 Microsoft R 客户端是 3.5.2 版本,这是由 Machine Learning Server 9.4 支持的版本。
在描述了最流行的 R 发行版的主要功能后,让我们继续选择要在您的机器上安装的引擎。
选择要安装的 R 引擎
安装 R 引擎时的第一个问题是,“我应该安装哪个版本?我是选择标准的 CRAN R,还是选择任何 Microsoft R 版本?”这类问题的通常答案是,“视情况而定!”在我们的情况下,目标是使用 Power BI 中的 R 引擎,因此我们需要了解哪些引擎被允许在它们内部使用 R 的不同产品所使用。
Power BI 使用的 R 引擎
在第一章,在哪里以及如何使用 Power BI 中的 R 和 Python 脚本中,我们了解到只有两个 Power BI 产品允许使用 R 和 Python 脚本:Power BI 桌面版和 Power BI 服务(记住,当提到 Power BI 服务时,Power BI 嵌入式是隐含包含在内的)。因此,答案“视情况而定!”现在有了更明确的含义:如果您需要与组织内部的人共享报告,那么您必须安装与 Power BI 服务兼容的引擎;如果您需要为自己创建报告,甚至不需要在我的工作区上发布,您也可以安装适合 Power BI 桌面版的引擎。
这两个产品在分析引擎的使用上存在重大差异。Power BI Desktop 依赖于安装在运行 Power BI Desktop 的同一台机器上的 R 引擎。用户选择安装哪些引擎版本和哪些包。Power BI Desktop 确保通过其界面输入的任何 R 代码都直接在该引擎上运行。
Power BI 服务 是 Power BI 覆盖的 软件即服务(SaaS)产品之一。用户不需要承担其底层 IT 基础设施的维护工作,也不能随意决定在其上安装组件。
重要注意事项
Power BI 服务用于 R 可视化 的 R 引擎和包是预先安装在云上的,因此用户必须适应服务采用的版本。
确切了解 Power BI 服务中 R 可视化使用的 R 发行版也很重要。
重要注意事项
到目前为止,Power BI 服务在实现 R 可视化时依赖于 MRO 3.4.4 运行时。每次发布时,都要密切关注 R 引擎和 Power BI 服务提供的包的版本,以确保要发布的报告能够正常工作。更多信息请见以下链接:
bit.ly/powerbi-r-limits。
如果您必须使用 R 脚本进行数据摄取或数据转换,并且需要刷新您的数据,Power BI 服务在这种情况下不会使用相同的引擎用于 R 可视化。
重要注意事项
在数据刷新阶段,Power BI 服务用于 Power Query 中的 R 脚本 的 R 引擎必须在服务之外您选择的任何机器上安装,并且在该同一台机器上您必须安装 本地数据网关 的 个人模式。请注意,即使要刷新的数据不通过网关流动,而是来自网关本身未引用的数据源,您也必须使用外部引擎。
只要要通过数据网关引用的 R 引擎只有一个,那么它们都安装在同一台机器上就足够了。否则,以下注意事项适用。
重要注意事项
如果您需要在机器上安装的多个 R 引擎中为 Power Query 转换使用,您还必须安装 Power BI Desktop。它允许您通过其选项切换数据网关的路由到所选引擎,更新配置文件
C:\Users\<your-username>\AppData\Local\PowerBIScripting\RSettings.xml。此文件允许覆盖数据网关默认引用的 R 引擎。
如果您需要了解更多关于本地数据网关(企业和个人模式)的信息,我们建议阅读以下链接的 Microsoft Docs 页面:bit.ly/onprem-data-gateway。
总结来说,考虑到前面的场景,如果你需要在桌面上进行个人使用的报告,你对使用的 R 引擎没有任何限制,因此你可以安装最适合你的 R 版本和包。然而,如果你事先知道你将要创建的报告包含 R 可视化,并且打算在 Power BI 服务中与同事共享,那么对版本和预安装在服务中的包都有严格的限制。
现在让我们继续进行引擎的安装。
安装建议的 R 引擎
长期来看,管理为 Power BI 服务开发的报告中注入的 R 脚本的依赖项可能很复杂。考虑到可以在同一台机器上安装多个 R 引擎,我们建议以下技巧。
提示
我们建议你 专门为 Power BI 报告运行 R 引擎的机器。我们的建议是为 Power Query 中开发 R 脚本或在 R 可视化中可能出现的每个可能的需求安装一个 R 引擎。
数据转换的 R 引擎
你已经看到,在 Power Query 中用于更改数据模型的 R 脚本必须通过个人模式中的本地数据网关使用外部 R 引擎,即使你使用 Power BI 服务。
提示
我们建议安装 CRAN R 或 MRO 的最新可用版本。鉴于相同的版本在代码操作方面可以互换,我们认为安装 MRO 可以从 MKL 库的性能优势中受益。
安装 MRO 非常简单:
-
点击对应你操作系统的 Download 按钮。在我的情况下,这是 Windows:
![图 2.4 – Windows 下载 MRO]()
图 2.4 – Windows 下载 MRO
-
下载完
microsoft-r-open-x.x.x.exe文件(其中x.x.x对应实际选择的版本)后,双击它,然后在弹出的 Let’s get started 窗口中点击 Continue。 -
下一个屏幕,配置安装,要求你确认或更改安装路径,以及选择一些安装选项:
![图 2.5 – MRO 安装选项]()
图 2.5 – MRO 安装选项
唯一的可选选择是安装 MKL。由于你现在已经非常清楚的原因,我们建议安装它。然后点击 Continue。
-
在下一个窗口中,你可以阅读安装 MRO 的 Microsoft 许可协议。勾选 I acknowledge the above licensing information 后,点击 Continue。
-
类似地,在下一个窗口中,你可以阅读 MKL 的许可协议。勾选 I accept these terms 后,点击 Continue。
-
现在出现一个摘要窗口,要求你点击 Install 以开始安装 MRO。
-
安装结束时,一个 完成 窗口将通知您安装已成功完成。
就这样!您现在就可以在 MRO 上编写和运行您的 R 代码了。
重要提示
通常,您在 Power BI Desktop 上开发报告的安装位置与选定的 Power BI 服务 R 引擎机器分开,数据网关也经常安装在该机器上。在这种情况下,您还必须在安装 Power BI Desktop 实例的机器上安装 R 引擎。
如果您愿意,您现在就可以在 MRO 默认安装的非常基础的 GUI 上编写代码:

图 2.6 – MRO 安装的 rGUI
我们将在本章后面看到如何安装所有 R 开发者都偏好的 IDE:RStudio。
Power BI 服务上 R 可视化的 R 引擎
如前所述,在 Power BI 服务上发布的 R 可视化脚本在云中预安装的 R 引擎上运行,其版本可能会根据 Power BI 服务的最新发布而更改。如果您需要与同事分享包含 R 可视化的报告,您需要确保您的 R 代码在预安装的引擎上正确运行。
提示
我们强烈建议您在自己的机器上也安装与 Power BI 服务用于 R 可视化的相同版本的 MRO。
为了安装 Power BI 服务提供的相同版本的 MRO,过程非常简单:
-
访问
bit.ly/powerbi-r-limits并检查 Power BI 服务使用的实际 MRO 版本:![图 2.7 – Power BI 服务使用的实际 MRO 版本]()
图 2.7 – Power BI 服务使用的实际 MRO 版本
-
然后转到
mran.microsoft.com/release-history并检查与步骤 1 中看到的相同版本的 MRO(在我们的案例中,3.4.4):![图 2.8 – 下载选定的 MRO 版本]()
图 2.8 – 下载选定的 MRO 版本
-
下载可执行文件后,只需遵循上一节中概述的步骤来安装此特定版本的 MRO 引擎。
当 Power BI 服务升级 R 引擎时应该做什么
可能的情况是,您已经在某个日期安装了一个特定版本的 MRO,以与 Power BI 服务上的 R 可视化引擎保持一致,之后微软更新了 Power BI 服务,同时也升级了预安装的 R 引擎。这种情况会导致两个独立的事件:
-
R 引擎比其前身更新,可能包含可能导致先前开发的报告不可用的破坏性更改。这种情况非常罕见,因为通常通过引入“已弃用”函数或参数来缓解这些更改,从而确保向后兼容性。
-
在上一个 R 引擎发布日期安装的大多数包都将更新到较新版本,对应于新版本 R 引擎的发布日期。将包更新到新版本是因脚本与包的旧版本不兼容而导致错误的最常见原因之一。
在这种情况下,您应验证所有包含 R 可视化并发布到 Power BI 服务的报告也能与对服务进行的更新兼容。
提示
考虑到前面的观察结果,以及预安装的 Power BI 服务 R 引擎很少更新的事实,最好直接在您的机器上安装新的 MRO 版本,并测试您的报告,确保 Power BI Desktop 引用了新版本的引擎。您需要修复那些 R 可视化中存在的任何代码问题,之后您可以将这些报告重新发布到 Power BI 服务。
一旦您确认所有前面的报告都运行正常,您就可以决定是否要卸载 MRO 的旧版本以释放磁盘空间并避免在处理这些特定报告时产生混淆。
到此为止,我们可以继续安装一个比 MRO 默认安装的 rGUI 功能更丰富的 IDE。
安装 R 开发 IDE
在 Power BI 中开发代码需要安装最先进的 IDE,这源于需要所有必要的工具来识别任何错误,并快速测试代码片段的结果。
提示
强烈建议在 IDE 中测试您的 R 代码并验证结果,然后再在 Power BI 中使用它。
市场上有很多用于 R 开发的 IDE。一些例子是 R-Brain IDE(RIDE)、IntelliJ IDEA 和 Jupyter Lab,但据估计,超过 90% 的 R 程序员使用 RStudio 作为他们的主要 IDE。这就是为什么我们建议您也使用这个 IDE 来测试本书中会遇到的所有代码。
安装 RStudio
在您的机器上安装 RStudio 非常简单:
-
前往
rstudio.com/products/rstudio/download/并在 RStudio Desktop 列表下点击 下载。![图 2.9 – 前往 RStudio 下载页面]()
图 2.9 – 前往 RStudio 下载页面
-
然后点击 下载 RStudio for Windows 按钮:
![图 2.10 – 下载 RStudio for Windows]()
图 2.10 – 下载 RStudio for Windows
-
下载成功完成后,双击可执行文件,并在 RStudio 安装欢迎窗口中点击 下一步 按钮。
-
在下一个窗口中,您会被要求选择安装 RStudio 的文件夹。保留默认设置并点击 下一步。
-
以下窗口会要求您选择创建快捷方式的开始菜单文件夹。保留默认设置并点击 安装。
-
安装完成后,点击完成。
为了确保一切安装顺利,请检查你是否有从 RStudio 选项中选择两个新安装的 R 引擎之一的选项:
-
从开始菜单打开新安装的 RStudio:
![图 2.11 – 从开始菜单运行 RStudio]()
图 2.11 – 从开始菜单运行 RStudio
-
一旦打开,你可能会被问及是否希望匿名发送任何崩溃报告给 RStudio 以改进产品。根据你的选择选择是或否。
-
如你所见,RStudio 已经默认选择了一个 R 引擎。在我们的例子中,它是我们机器上安装的最新版本。此外,请注意 MKL 已经启动:
![图 2.12 – RStudio 和全局选项中默认选择的 R 引擎]()
图 2.12 – RStudio 和全局选项中默认选择的 R 引擎
现在,点击工具菜单,然后点击全局选项…以查看可用的引擎。
-
在选项窗口的常规菜单中,你可以通过点击更改...然后选择一个可用的引擎来选择你想要在 RStudio 中使用的引擎:
![图 2.13 – 选择您首选的 R 引擎与 RStudio 一起使用]()
图 2.13 – 选择您首选的 R 引擎与 RStudio 一起使用
通常,我们不会建议将包含“
“C:\PROGRA~1\...””的引擎视为路径的根。它只是当前在 RStudio 中选择的引擎。只需从列表中选择其他引擎之一。 -
现在,选择最新的 R 版本(在我们的例子中,是 4.0.2),然后在选择 R 安装窗口中点击确定。你将收到通知,表示你需要重启 RStudio 以使所选更改生效。
-
在更改 R 版本窗口中点击确定,然后在选项窗口中点击确定。有时你将有机会通过在随后显示的对话框中选择是来重启。如果没有,只需从 RStudio 中退出并重新打开即可。
干得好!现在你已经准备好在 Power BI Desktop 中正确配置 R 引擎,并设置你需要发布到 Power BI 服务的相关内容。
配置 Power BI Desktop 以与 R 一起工作
一旦你安装了用于报告开发和 RStudio IDE 的必要 R 引擎,你必须配置 Power BI Desktop 以正确引用这些工具。这实际上是一个非常简单的任务:
-
在 Power BI Desktop 中,转到文件菜单,点击选项和设置选项卡,然后点击选项:
![图 2.14 – 打开 Power BI Desktop 选项和设置窗口]()
图 2.14 – 打开 Power BI Desktop 选项和设置窗口
-
在选项窗口中,点击左侧的R 脚本选项卡。右侧面板的内容将更新,为你提供选择要引用的 R 引擎和使用于 R 可视化的 R IDE 的选项:
![图 2.15 – 在 Power BI Desktop 中选择要使用的引擎和 IDE]()
图 2.15 – 在 Power BI Desktop 中选择要使用的引擎和 IDE
-
如您所见,Power BI Desktop 自动识别已安装的 R 引擎和 IDE。目前,请选择引擎的最新版本(在我们的例子中是 4.0.2),以确保与 RStudio 中已选择的版本保持一致。
当我们在 第十二章,数据探索分析 中介绍 R 和 Python 脚本可视化时,您将了解如何从 Power BI Desktop 与 IDE 交互。
配置 Power BI 服务以使用 R
如您在本章的 Power BI 使用的 R 引擎 部分所学,Power BI 服务根据脚本是在 R 可视化中还是 Power Query 数据转换中使用,使用不同的 R 引擎。在前一种情况下,引擎在云上预先安装;在后一种情况下,您需要在任何您选择的机器上以个人模式安装 本地数据网关,以便 Power BI 服务与您在该机器上安装的 R 引擎通信。
以个人模式安装本地数据网关
我们强调您需要以个人模式安装数据网关,这是出于一个重要原因:在 企业模式 下,企业数据网关 不支持 R 脚本。
在您的情况下,您将在已安装 R 引擎和 Power BI Desktop 的同一台笔记本电脑上安装数据网关。执行此操作的步骤如下:
-
确保您能登录到 Power BI 服务 (
app.powerbi.com)。您将看到如下主页,带有您公司的标志:![图 2.16 – Power BI 服务主页]()
图 2.16 – Power BI 服务主页
-
在主页右上角,您会注意到一个向下指的箭头,允许您访问下载菜单。点击此箭头,然后选择 数据网关:
![图 2.17 – Power BI 服务主页上的下载菜单]()
图 2.17 – Power BI 服务主页上的下载菜单
-
您将被重定向到一个网页,您可以从该网页下载数据网关。请确保下载 个人模式 版本:
![图 2.18:下载数据网关的个人模式版本]()
图 2.18:下载数据网关的个人模式版本
-
立即运行新下载的可执行文件将打开一个初始窗口,要求您检查链接中给出的最小安装要求,设置软件的安装文件夹,并接受软件的使用条款:
![图 2.19 – 数据网关安装窗口]()
图 2.19 – 数据网关安装窗口
您可以选择默认文件夹,点击使用条款接受复选框,然后点击 安装 以继续。
-
数据网关安装完成后,将打开一个登录窗口,您需要输入您在 Power BI 服务上注册时使用的电子邮件地址,然后输入您的密码:
![图 2.20 – 数据网关登录窗口]()
图 2.20 – 数据网关登录窗口
-
当登录操作成功时,会出现一个绿色的勾选标记,并显示文字网关已上线并准备好使用:
![图 2.21 – 数据网关运行中]()
图 2.21 – 数据网关运行中
-
您可以关闭本地数据网关窗口。
在此时刻,Power BI 服务直接连接到您的机器,并在需要刷新与您新安装的数据网关关联的已发布数据集时可以访问它。
重要提示
请记住,Power BI 用户只能关联一个数据网关。如果您尝试在多台不同的机器上以个人模式安装多个本地数据网关,登录其中一台将强制断开所有其他数据网关的连接。
在 Power BI 服务中共享使用 R 脚本的报表
在前面的章节中,您了解到,由于在个人模式下新安装的数据网关,您当然可以将使用 R 脚本的报表发布到您的个人工作区。但是,假设您拥有 Pro 许可证,您是否也可以将此报表发布到共享工作区?对此经常存在很多混淆。
重要提示
没有人禁止您在共享工作区发布您的报表!尽管您已在个人模式下使用数据网关,但其他用户可以查看您的报表。不仅如此!其他用户还可以刷新报表背后的数据集。他们将代表您这样做,指向由您的数据网关引用的机器。重要的是您的机器是开启的!
那么,如果您仍然可以使用个人模式下的数据网关与他人共享报表,使用企业模式下的数据网关的优势是什么?
使用单个数据网关在企业模式下,您的组织中的多个用户可以访问他们已经具有访问权限的本地数据。不言而喻,其他用户可以查看和刷新您的报表,但他们不能通过使用 R 引擎通过您的个人数据网关引用您的机器来开发他们自己的报表。这是真正的限制。
鉴于上述情况,公司中经常使用一种非官方架构,允许所有使用 R 代码在 Power Query 中的报表引用已使用个人数据网关安装 R 引擎的单个机器。
提示
这种非官方架构利用了一个与物理人员无关的个人数据网关,而是与一个 虚构的“服务”用户 相关。该用户的凭据在所有使用 R 代码将数据转换为报告的分析员之间共享。此外,由于数据网关引用的 R 引擎的机器是共享的,它必须在计划活动期间保持开启状态。因此,在这个架构中,经常使用一个同时运行 R 引擎和数据网关的 Azure Windows 虚拟机。
由于“一图胜千言”,图 2.22 总结了此架构:

图 2.22 – 使用 R 进行数据转换的企业架构
多亏了这个架构,才使得一组分析师能够在他们的报告中使用 R 脚本,尽管个人模式下的本地数据网关施加了限制。
话虽如此,除了 Power Query 中 R 脚本的局限性之外,还有一些重要的局限性需要注意。
R 可视化局限性
R 可视化在处理数据方面有一些重要的局限性,无论是作为输入还是输出:
-
R 可视化可以处理一个 只有 150,000 行的 dataframe。如果有超过 150,000 行,则只使用前 150,000 行,并在图像上显示相关消息。
-
R 可视化的 输出大小限制为 2MB。
您还必须小心不要超过 R 可视化的 5 分钟运行时计算,以避免超时错误。此外,为了避免性能问题,请注意 R 可视化图表的分辨率固定为 72 DPI。
如您所想象,R 可视化的局限性取决于您是在 Power BI Desktop 还是 Power BI 服务上运行该可视化。
要在 Power BI Desktop 中创建报告,您可以执行以下任何一项操作:
-
在您的引擎中为 R 可视化安装任何类型的软件包(CRAN、GitHub 或自定义)。
-
在您的引擎中仅安装 CRAN 软件包以用于自定义 R 可视化(您将在 第十四章,R 中的交互式自定义可视化 中看到一个示例)。
-
从 R 可视化和自定义 R 可视化访问互联网。
在 Power BI 服务 中创建报告时,请注意以下事项:
-
对于 R 可视化和自定义 R 可视化,您可以使用 此链接中列出的 CRAN 软件包:
bit.ly/powerbi-r-limits。 -
您无法从 R 可视化或自定义 R 可视化访问互联网。
如您可能已经注意到的,尽管在 Power BI 服务上可用于 R 可视化的 CRAN 软件包有限,但您仍然可以数出列表中的 900 多个软件包!除了 R 是第一个被引入 Power BI 的分析语言之外,这还表明 R 实际上被认为是创建引人入胜的可视化中最灵活和最专业的语言之一。
如你所知,一旦你的报告在 Power BI 服务上发布,你可以决定通过“发布到网页”选项将其分享到你的博客、你的网站或社交媒体上。R 可视化不允许发布到网页。
重要提示
自定义 R 可视化克服了发布到网页的限制。
因此,如果你必须将报告发布到网页上,并且该报告包含使用 R 代码创建的可视化,你必须开发一个自定义 R 可视化。
摘要
在本章中,你学习了社区中最受欢迎的免费 R 引擎。特别是,你了解了微软将其 R 分发版推向市场所引入的优势。
注意到 Power BI Desktop 和 Power BI 服务的独特功能,你已经学会了如何正确选择引擎以及如何安装它们。
你还了解到了 R 社区中最受欢迎的集成开发环境(IDE)以及如何安装它。
此外,你还了解了在开发或企业环境中,如何使用 R 正确配置 Power BI Desktop 和 Power BI 服务的最佳实践。
最后,你了解了一些使用 R 与 Power BI 相关的限制,这些知识对于避免在开发和部署报告时犯错误至关重要。
在下一章中,我们将看到在 Power BI 中需要安装和配置哪些 Python 引擎和 IDE,以便在舒适的环境中开发和测试 Python 脚本。
第四章:3 配置 Python 与 Power BI
正如 第二章,使用 Python 配置 R 中所述,你必须安装 R 引擎才能与 Power BI 交互,同样,你也将需要在你的机器上安装 Python 引擎。你还将了解如何配置一些 IDE,以便在将 Python 代码用于 Power BI 之前,你可以舒适地开发和测试 Python 代码。因此,类似于我们在 第二章,使用 Python 配置 R 中已经看到的,本章将讨论以下主题:
-
可用的 Python 引擎
-
我应该安装哪个 Python 引擎?
-
安装 Python 开发环境的 IDE
-
配置 Power BI Desktop 以与 Python 一起工作
-
配置 Power BI 服务以与 Python 一起工作
-
Python 可视化的限制
技术要求
本章要求你拥有一个正常工作的互联网连接,并且你的机器上已经安装了 Power BI Desktop。本章的最后部分还要求你注册 Power BI 服务(这里有一个教程:bit.ly/signup-powerbiservice)。一个 Power BI 免费版 许可证就足够测试本书中的所有代码,因为你将仅在个人 工作区 中共享报告。
可用的 Python 引擎
与 R 语言一样,你可以为 Python 安装几个发行版:标准 Python、ActivePython、Anaconda 等等。通常,“纯”开发者会从 www.python.org/ 下载 Python 引擎的最新版本,然后从 Python 包索引(PyPI)安装各种社区开发的、对他们的项目有用的包。其他供应商,如 ActiveState 和 Anaconda,会预先打包特定版本的 Python 引擎和一系列包,以加速项目的启动。虽然标准 Python 和 ActiveState 发行版更针对通用开发者,但 Anaconda 是数据科学家和与机器学习项目紧密合作的人士首选的发行版。反过来,Anaconda 本身也有两个不同的发行版:Anaconda 和 Miniconda。
Anaconda 发行版,包含超过 150 个内置包,可以被认为是数据科学家最佳的 DIY 超市,其中所有东西都已准备好并配置好以供使用。另一方面,Miniconda 发行版被认为是数据科学家寻求将资源削减到适当水平的最小必需工具箱。
但 Anaconda 和 Miniconda 有一个共同的基本工具:那就是Conda,它是 Python 最受欢迎的包管理器之一。Conda 为开发者提供了一个易于使用的系统来管理所谓的虚拟环境。虚拟环境,或简称环境,旨在封装一组特定版本的 Python 引擎和包的安装。目标是创建一个隔离的环境,通常与项目或任务相关联,可以保证结果的再现性。这是一个非常重要的概念,对于确保 Python 项目在处理一个由独立创建和维护自己包的开发者群体时能够顺利运行至关重要。
注意
与您在第二章,使用 Power BI 配置 R中看到的情况相反,Python 社区没有“时间机器”可以轻松引用发布日期的特定 Python 引擎版本以及 PyPI 包在那天版本的整个生态系统快照。您需要自己使用环境来构建自己的“时间胶囊”,以确保代码的可再现性。
Conda 是一个非常通用的工具。除了管理环境之外,它还可以安装各种包(无论使用的编程语言是什么,而不仅仅是 Python),并仔细管理它们的所有依赖关系。但官方推荐的从 PyPI 安装 Python 包的工具是pip,它只安装用 Python 编写的包,通常与 Python 引擎一起安装。
话虽如此,除了围绕 Python 引擎安装的“车身”之外,各种 Python 发行版并没有添加显著提高引擎性能的功能,这与我们在第二章,使用 Power BI 配置 R中看到的 Microsoft R 引擎不同。因此,我们不会详细介绍每个发行版安装的功能。
选择要安装的 Python 引擎
回到我们的场景,为了在 Power Query 或 Python 可视化中使用 Python 代码,您肯定需要以下内容:
-
一个 Python 引擎
-
包管理器,用于安装转换数据或适当可视化的所需的最小数量的包
为了选择最适合您需求的产品,您需要更详细地了解您的 Power BI 需求。
Power BI 使用的 Python 引擎
正如 Power BI 服务中的 R 可视化一样,以下说明也适用于 Python 可视化。
重要说明
Power BI 服务用于Python 可视化的 Python 引擎和包是预先安装在云上的,因此用户必须适应服务采用的版本。
如您所想,Power BI 服务采用的引擎版本略落后于最新发布版(现在为 3.9.1)。有关更多详细信息,请参阅以下说明。
重要说明
到目前为止,Power BI 服务在实现 Python 可视化时依赖于 Python 3.7.7 运行时。始终关注 Power BI 服务每次发布提供的 Python 引擎和包的版本非常重要,以确保要发布的报告能够正常工作。更多信息请参阅以下链接:
bit.ly/powerbi-python-limits。
在 Power Query 中进行数据转换时,Power BI 服务的表现与我们已经看到的 R 脚本相同。
重要提示
在数据刷新阶段,Power BI 服务使用的 Python 引擎必须安装在任何您选择的机器上(服务之外),并且在该同一台机器上,您必须以 个人模式 安装 本地数据网关。请注意,即使要刷新的数据不通过网关流动,而是来自网关本身未引用的数据源,您也必须使用外部引擎。
只要通过数据网关引用的 Python 环境是基本环境,只要两者都安装在同一台机器上就足够了。否则,以下注意事项适用。
重要提示
如果您需要在使用机器上的 Power Query 转换中使用多个已安装的环境,您还必须安装 Power BI Desktop。它允许您通过其选项切换数据网关的路由到所选环境,更新位于
C:\Users\<your-username>\AppData\Local\PowerBIScripting\PythonSettings.xml的配置文件。此文件允许覆盖数据网关默认引用的 Python 环境。
简而言之,无论您是想运行 R 脚本还是 Python 脚本,Power BI Desktop 和 Power BI 服务所需的基础设施都以相同的方式进行管理。因此,再次强调,如果您需要为个人桌面上的使用生成报告,您对使用的 Python 引擎没有限制,因此您可以安装最适合您的 Python 版本和包。另一方面,如果您事先知道您将创建的报告将包含 Python 可视化并打算在 Power BI 服务上与同事共享,那么对版本和预先安装在服务中的包都有严格的限制。
但让我们言归正传,开始安装 Python 相关内容!
安装建议的 Python 引擎
在长期管理嵌入报告中的 Python 脚本的依赖关系可能会变得复杂。考虑到在同一台机器上可以创建多个环境,我们建议以下技巧。
技巧
我们建议您专门为 Power BI 报告使用的 Python 引擎分配一台机器。我们的建议是为在 Power Query 中开发 Python 脚本或为 Python 可视化可能出现的每个需求创建一个 Python 环境。如果您已经为运行 R 脚本准备了一台专用机器,如第二章,使用 Power BI 配置 R中所示,那么您也可以使用同一台机器来安装 Python 引擎。请记住,在这种情况下,您需要确保机器的资源足够运行所有引擎,并满足来自各个报告的各种请求。
让我们先安装最新版本的 Python 引擎,用于数据转换。
数据转换的 Python 引擎
当然,为了使用 Python 丰富您的报告,您不需要 150 个预安装的包。此外,为了轻松管理您的环境,Conda 是您应该包括在您的工具箱中的工具。考虑到我们即将安装的引擎将被 Power BI 服务用作外部 Python 引擎,通过 Power Query 在本地数据网关以个人模式转换数据,以下提示适用。
提示
我们建议采用最新版本的Miniconda作为默认发行版。这是因为,除了预安装非常少的包,让您有机会选择要安装的包之外,它还包括了 Conda 在发行版中。
Miniconda 的安装非常简单:
-
点击您操作系统可用的最新版本(截至写作时间为 3.8):
![图 3.1 – 下载 Miniconda 的最新版本]()
图 3.1 – 下载 Miniconda 的最新版本
-
文件下载完成后,双击它,在弹出的欢迎窗口中点击下一步,然后点击我同意以接受许可协议。
-
在下一个窗口中,您将被询问是否要为仅您或所有用户安装 Miniconda。保留默认设置(仅为您)并点击下一步。
-
在下一屏上保留安装的默认文件夹,并点击下一步。请记住,安装路径是这样的形式:
C:\Users\<你的用户名>\miniconda3。 -
在下一个窗口中,勾选将 Miniconda3 注册为我的默认 Python 3.8并点击安装:
![图 3.2 – 将 Miniconda 设置为默认 Python 3.8 引擎]()
图 3.2 – 将 Miniconda 设置为默认 Python 3.8 引擎
-
安装结束时,一个安装完成窗口会通知您安装已成功完成。然后,点击下一步。
-
最后一屏为您提供打开包含提示和资源的文档的可能性,以开始使用 Miniconda。您可以取消选中两个选项,然后点击完成。
就这些了!现在你可以使用 Miniconda 编写和运行你的 Python 代码了。
重要提示
通常,你开发报告的 Power BI Desktop 安装位于与选定的 Power BI 服务 Python 引擎机器不同的机器上,其中数据网关也经常安装。在这种情况下,你必须在安装 Power BI Desktop 的机器上安装 Miniconda。
安装完成后,在开始菜单中的 Anaconda3 (64-bit) 文件夹下,你会找到两个命令行界面(标准 命令提示符 和 PowerShell)的快捷方式,这确保了你可以在后台激活 Conda 并与 Miniconda 提供的工具交互:

图 3.3 – 与 Miniconda 交互的有用 Anaconda 提示
我们最喜欢的命令行是 Anaconda Prompt,我们很快就会向你展示如何使用它。
如我们在 可用的 Python 引擎 部分所述,conda 和 pip 都是很好的包管理器。作为包依赖关系求解器,conda 更好,尽管比 pip 略慢。但 pip 常用作包管理器的原因是它直接从 PyPI 拉取包,PyPI 是一个比 Anaconda 的更完整的仓库。出于同样的原因,我们将使用 pip 作为我们的默认包管理器。
创建用于数据转换的环境
与你看到的 R 引擎不同,对于 R 引擎,已经安装了两个不同版本的独立引擎,在 Python 的情况下,安装是唯一的,只有环境会变化。
在这里,我们将创建一个专门用于数据转换的环境,并包含 Miniconda 提供的最新版本的 Python 和少量对进行首次转换至关重要的包。
首先,你必须找到你刚刚安装的分布中存在的最新版本的 Python:
-
如前所述,从开始菜单打开 Anaconda Prompt。
-
如果提示符中的字体很小,只需右键单击其标题栏,选择 选项,然后在 字体 选项卡中更改你喜欢的字体。首先要注意的是当前路径前的单词 (base)。路径前的字符串表示 当前环境的名称。base 环境是在 Miniconda 分发安装期间创建的默认环境。
-
输入
conda search python命令并按 Enter。 -
你将看到可用的 Python 版本列表:
![图 3.4 – 所有可用的 Python 版本列表]()
图 3.4 – 所有可用的 Python 版本列表
我们案例中可用的最新版本是 3.9.1。
一旦我们找到了可用的最新版本的 Python,我们就可以创建我们的环境,该环境专门用于 Power Query 中的数据转换,我们将称之为 pbi_powerquery_env:
-
输入以下命令以创建一个名为
pbi_powerquery_env并包含 Python 版本3.9.1的新环境:conda create --name pbi_powerquery_env python==3.9.1如果您使用的是
=3.9(单个=)而不是==3.9.1,您将实现相同的效果,让 conda 找到最新的微版本。 -
Anaconda Prompt 将要求您安装创建环境所需的某些包。在
Proceed ([y]/n)?提示下,输入y并按Enter键。
当包安装完成时,您仍然会看到(base)作为提示前缀:

图 3.5 – 创建新环境后,您仍然在旧的环境中,名为“base”
这意味着您仍然在基础环境中。您确定您正确创建了新环境吗?让我们检查一下:
-
通过输入
conda env list命令尝试列出系统上现有的环境:![图 3.6 – 系统中 conda 环境的列表]()
图 3.6 – 系统中 conda 环境的列表
幸运的是,新环境被列出来了,但它不是活动环境。活动环境由一个星号标识。
-
为了在新建的环境中安装我们的包,您必须首先使用
conda activate pbi_powerquery_env命令激活它:![图 3.7 – 激活新环境]()
图 3.7 – 激活新环境
现在您的提示前缀正确地表明您已进入您的新环境。
-
为了安全起见,使用
python --version命令检查新环境中的 Python 版本是否如您预期:

图 3.8 – 检查新环境中安装的 Python 版本
您已进入您的新环境,Python 已正确安装!您现在可以开始安装您稍后需要的某些包。要安装的包如下:
-
NumPy:Python 中用于处理数组和线性代数、傅里叶变换和矩阵函数的最广泛使用的库。
-
SciPy:用于解决科学和数学问题;它基于 NumPy 扩展,并允许用户操作和可视化数据。
-
Pandas:一个 Python 包,提供快速、灵活和表达式的表格、多维和时间序列数据。
-
Requests:允许您极其容易地发送 HTTP 请求。
-
BeautifulSoup:一个库,使从网页中抓取信息变得容易。
-
PyYAML:允许您轻松地读取和写入 YAML 文件。
在下一节中,您将使用前面列表中的最后三个包,在那里您将隐式地使用网络爬取程序!
但让我们回到正题,通过pip安装每个包:
-
输入以下命令安装NumPy:
pip install numpy。 -
输入以下命令安装 SciPy:
pip install scipy。 -
输入以下命令安装 Pandas:
pip install pandas。 -
输入以下命令安装 Requests:
pip install requests。 -
输入以下命令安装 BeautifulSoup:
pip install beautifulsoup4。 -
输入以下命令安装 PyYAML:
pip install pyyaml。 -
使用
conda list命令检查所有包是否已正确安装:

图 3.9 – 检查所有选定的 Python 包是否已安装
太棒了!您的新环境现在已正确配置。现在让我们为 Power BI 服务上的 Python 视觉效果配置另一个环境。
在 Power BI 服务上创建 Python 视觉效果的环境
如前所述,Power BI 服务上发布的 Python 视觉脚本在云上预安装的 Python 引擎上运行,其版本可能会随着 Power BI 服务本身的更新而变化。如果您需要与同事共享包含 Python 视觉效果的报告,您需要确保您的 Python 代码在预安装的引擎上正确运行。
提示
我们强烈建议您在您的机器上也安装 Power BI 服务用于 Python 视觉效果的 相同版本 的 Python。
请记住,如果您使用 Python 视觉效果的报告不用于共享,并且您仅通过 Power BI Desktop 使用它们,那么这些限制将不会存在。在这种情况下,是您机器上的引擎被视觉效果使用。
要创建新环境,您必须检查 Power BI 服务支持的 Python 和允许的包的版本。您可以通过此链接检查这些要求:bit.ly/powerbi-python-limits。如您所见,截至目前,支持的 Python 版本是 3.7.7:

图 3.10 – Power BI 服务上用于视觉效果的 Python 版本
此外,截至目前,允许的包仅限于以下这些:
-
matplotlib 3.2.1 -
numpy 1.18.4 -
pandas 1.0.1 -
scikit-learn 0.23.0 -
scipy 1.4.1 -
seaborn 0.10.1 -
statsmodels 0.11.1 -
xgboost 1.1.0
最引人注目的是,与 Power BI 服务提供的 R 包相比,Python 包的数量要少得多(8 个 Python 包与 900 多个 R 包相比!)。这种明显的包可用性不平衡主要归因于两个原因:
-
Python 相比 R(2019 年 2 月引入)较晚被引入,因此引入的 Python 包主要是那些用于数据转换和可视化的基本包。
-
R 是一种主要用于数据分析的语言,这一点立即就很明显,因为它提供了各种用于科学可视化的包。另一方面,Python 是一种通用编程语言,也可以用于数据分析。
提示
由于 Power BI 服务支持的 Python 包数量很少,我们建议创建一个专门的环境来运行 Power BI 服务上的 Python 脚本,直接安装所有当前允许的包。
请记住,不安装一些默认包就无法正确运行 Python 可视化。请参阅以下说明。
重要提示
为了正确运行 Python 可视化,无论您是在 Power BI Desktop 上还是在 Power BI 服务上运行,都必须安装 pandas 和 Matplotlib 软件包。
话虽如此,您已经可以继续创建另一个环境,满足上述版本规格,并遵循创建先前环境时使用的步骤。尽管 Power BI 服务引擎更新不频繁,但这项手动任务仍然很繁琐。不幸的是,没有现成的“快照”可以即时安装以重现环境,正如您在 R 引擎案例中看到的那样。
提示
为了避免不必要的手动工作,我们创建了一个 Python 脚本,该脚本可以抓取 Power BI 服务上包含 Python 引擎要求的网页,并自动生成用于创建新环境的 YAML 文件。
YAML(由某个使用递归缩写词 YAML Ain't Markup Language 的有趣人物定义)是一种用于序列化数据(它与 JSON 有点竞争关系)且可读性强的语言。它通常用于序列化计算机系统配置文件的内容。
在我们的情况下,一个 YAML 文件帮助我们收集创建新环境所需的所有规格。我们之所以考虑使用 YAML 文件,是因为 conda 也允许使用 YAML 文件作为参数来创建环境。我们将要创建的新环境,我们将称之为 pbi_visuals_env,应该具备以下特性:
-
Python 引擎版本 3.7.7
-
pip 软件包管理器
-
所有之前看到的 8 个包,每个都安装到所需的版本,使用 pip 安装
前述要求可以总结如下 YAML 文件:
name: pbi_visuals_env
dependencies:
- python==3.7.7
- pip
- pip:
- matplotlib==3.2.1
- numpy==1.18.4
- pandas==1.0.1
- scikit-learn==0.23.0
- scipy==1.4.1
- seaborn==0.10.1
- statsmodels==0.11.1
- xgboost==1.1.0
因此,让我们使用我们的 Python 脚本生成 YAML 文件,如下所示:
-
打开您的 Anaconda Prompt(如果您之前没有关闭它,它应该仍然打开),确保
pbi_powerquery_env是激活的环境。如果不是,请使用conda activate pbi_powerquery_env命令激活它。 -
您的当前路径应该是
C:/Users/<your-username>的形式。在我的情况下,我有以下内容:![图 3.11 – 我们默认的 Anaconda Prompt 路径]()
图 3.11 – 我们默认的 Anaconda Prompt 路径
如果没有,请使用以下命令转到您的用户名文件夹:
cd C:/Users/<your-username>。 -
让我们使用
md py-environments命令创建一个名为py-environments的新文件夹(这将包含用于网络爬取的 Python 脚本以及 YAML 文件)。 -
现在,前往书籍的 GitHub 仓库
github.com/PacktPublishing/Extending-Power-BI-with-Python-and-R。 -
如果你已经下载了整个仓库的
.zip文件并在你的硬盘上解压,你将在Chapter03文件夹中找到我们感兴趣的 Python 脚本文件,文件名为01-create-pbi-service-py-packages-env-yaml-file.py。 -
如果你还没有下载整个仓库,请点击前一个链接页面右上角的 Code,然后点击 Download ZIP:
![图 3.12 – 下载整个压缩仓库]()
图 3.12 – 下载整个压缩仓库
解压后,你会在
Chapter03文件夹中找到我们需要的文件。 -
现在将文件
01-create-pbi-service-py-packages-env-yaml-file.py复制到C:/Users/<your-username>/py-environments文件夹中,使用文件资源管理器完成此操作。 -
返回 Anaconda Prompt 并使用此命令将当前文件夹更改为
py-environment:cd py-environment。 -
现在,你可以最终运行这个网络爬取并生成 YAML 文件的 Python 脚本,使用以下命令:
python 01-create-pbi-service-py-packages-env-yaml-file.py。你将在提示符中看到 YAML 文件的内容,如下所示:![图 3.13 – 执行 Python 脚本以创建 YAML 文件]()
图 3.13 – 执行 Python 脚本以创建 YAML 文件
你也可以通过再次查看
C:/Users/<your-username>/py-environments文件夹的内容来验证 YAML 文件是否正确生成:![图 3.14 – 正确创建的 YAML 文件]()
图 3.14 – 正确创建的 YAML 文件
-
在此阶段,我们可以直接使用以下命令创建新环境:
conda env create -f visuals_environment.yaml。 -
当 Python 和包的安装都完成后,使用以下命令激活新创建的环境:
conda activate pbi_visuals_env。 -
然后通过输入
python –version命令检查 Python 版本是否与 YAML 文件中定义的此环境版本一致。你应该会看到以下输出:

图 3.15 – 新环境包含正确的 Python 版本
太棒了!你终于创建了一个新环境,这个环境将在你为 Power BI 服务发布 Python 可视化时派上用场。如果你对详细了解之前的 Python 脚本是如何捕获创建环境所需的所有信息感兴趣,你可以在任何代码编辑器中打开它,并阅读代码上的详细注释。
当 Power BI 服务升级 Python 引擎时该怎么做
正如我们在 第二章,配置 R 与 Power BI 中所做的那样,让我们假设您已经使用之前创建的新环境开发和发布了包含 Python 可视化的报告。假设微软决定升级 Power BI 服务支持的 Python 版本,以及相应地升级当前支持的包版本。正如您可能已经猜到的,这些更新可能会使代码失败(这是一个罕见的事件,因为通常向后兼容性是有保证的)。
提示
在这种情况下,通常更方便的是在 Python 脚本中即时创建一个新环境,以适应微软更新的要求。接下来,您需要在 Power BI Desktop 上包含 Python 可视化的服务中进行测试报告,确保它引用了新创建的环境。您需要修复那些 Python 可视化中存在的任何代码问题,之后您可以将这些报告重新发布到 Power BI 服务。
一旦您确认所有报告都运行正常,您就可以决定是否要卸载“旧”环境以释放磁盘空间并避免在处理这些特定报告时产生混淆。
在这一点上,我们可以继续配置和安装一些便于 Python 脚本开发的 IDE。
安装 Python 开发 IDE
在 第二章,配置 R 与 Power BI 中,您安装了 RStudio 以方便地开发您自己的 R 脚本。您知道吗?从版本 1.4 开始,您可以直接在 RStudio 中编写和运行 Python 代码,利用高级工具来查看实例化的 Python 对象。
让我们看看如何配置您的 RStudio 安装以运行 Python 代码。
使用 RStudio 配置 Python
为了允许 RStudio 与 Python 世界进行通信,您需要安装一个名为 reticulate 的包,该包包含了一组用于 Python 和 R 之间互操作性的工具,这是由于 R 会话中嵌入的 Python 会话。之后,在 RStudio 中配置 Python 就变得非常简单。让我们看看如何操作:
-
打开 RStudio 并确保引用的引擎是最新版本,在我们的例子中是 MRO 4.0.2。正如在 第二章,配置 R 与 Power BI 中所见,您可以通过访问 工具 菜单然后 全局选项... 来在 RStudio 中设置您的 R 引擎。
-
让我们通过在命令行界面中输入
getOption("repos")来检查当前快照被冻结的日期:![图 3.16 – 使用 MRO 4.0.2 的当前快照日期]()
图 3.16 – 使用 MRO 4.0.2 的当前快照日期
如您所见,快照日期并不十分近期。
-
由于安装最新 R 引擎的目标是使用最新包来开发代码,我们可以通过覆盖包仓库 URL 来移除快照限制。你可以通过创建一个新的 R 脚本,点击左上角的绿色 + 图标,然后选择 R Script 来完成此操作:
![图 3.17 – 将仓库 URL 重置为 CRAN 镜像]()
图 3.17 – 将仓库 URL 重置为 CRAN 镜像
-
然后,将以下脚本复制到 RStudio 添加的新脚本选项卡中:
local({ r <- getOption("repos") r["CRAN"] <- https://cloud.r-project.org/ options(repos = r) })现在,只需高亮显示脚本,然后在右上角点击 Run:
![图 3.18 – 在 RStudio 中运行你的第一个 R 脚本]()
图 3.18 – 在 RStudio 中运行你的第一个 R 脚本
-
如果你现在去底部的控制台,再次输入
getOption("repos"),你会看到更新的仓库 URL:![图 3.19 – 包仓库已更新]()
图 3.19 – 包仓库已更新
现在,你肯定能始终安装 CRAN 上的最新版本的包。
-
现在,你可以通过点击 RStudio 右下角的 Packages 选项卡,然后点击 Install,在文本框中输入
reticulate字符串,并最终点击 Install 按钮来安装 reticulate 包:![图 3.20 – 安装 reticulate 包]()
图 3.20 – 安装 reticulate 包
-
之后,转到 Tools 菜单,然后选择 Global Options…。在弹出的 Options 窗口中,点击 Python,然后点击 Select… 来选择你的 Python 解释器可执行文件。请注意,你将有多少个可执行文件,就有多少个环境你创建了:
![图 3.21 – 在 RStudio 中设置你的首选 Python 解释器]()
图 3.21 – 在 RStudio 中设置你的首选 Python 解释器
-
选择最新版本的 Python 解释器(在我们的例子中是 3.9.1)。你将被要求重新启动当前的 R 会话。选择 Yes。
-
现在,你可以通过点击左上角的绿色 + 图标,然后选择 Python Script 来打开一个新的 Python 脚本文件:
![图 3.22 – 在 RStudio 中创建一个新的 Python 脚本]()
图 3.22 – 在 RStudio 中创建一个新的 Python 脚本
-
在新的脚本文件中编写代码
a = [1, 2],将其高亮显示,然后点击右上角的 Run 图标:![图 3.23 – 在 RStudio 中运行你的第一个 Python 脚本]()
图 3.23 – 在 RStudio 中运行你的第一个 Python 脚本
你可以从控制台看到,RStudio 在幕后使用 reticulate。此外,在右上角的面板中,你可以检查代码执行后创建的 Python 变量。
太好了!你已经成功配置了 RStudio 来运行你的 Python 代码。
使用 Visual Studio Code 配置 Python
如果您是 R 语言爱好者,那么您可能会更喜欢在 RStudio 上运行 R 和 Python 代码。然而,如果您有真正的Pythonista精神,那么您一定会喜欢使用最近备受追捧的其中一个高级编辑器:Visual Studio Code(VSCode)。让我们来安装和配置它!
-
通过点击下载 Windows 版按钮,从此链接(
code.visualstudio.com/)下载 VSCode。 -
运行可执行文件,当它询问是否继续使用用户安装程序时点击确定。
-
接受协议并在下一个窗口上点击下一步。
-
然后保持默认目标文件夹并点击下一步。
-
还要在下一个窗口中保持默认的启动菜单文件夹并点击下一步。
-
在附加任务窗口中,选择您喜欢的任务并点击下一步。
-
在摘要窗口上点击安装。
-
安装完成后,保持启动 Visual Studio Code复选框选中,并点击完成。
-
VSCode 启动后,点击左侧的扩展图标,然后开始输入
python字符串并点击Python扩展:![图 3.24 – 在 RStudio 中运行您的第一个 Python 脚本]()
图 3.24 – 在 RStudio 中运行您的第一个 Python 脚本
-
在扩展的主面板上的欢迎页面上点击安装。
-
现在转到顶部的文件菜单并点击打开文件夹…。
-
从打开文件夹窗口中创建一个名为Hello的新测试文件夹,然后选择该文件夹:
![图 3.25 – 在打开文件夹窗口中创建新文件夹并选择它]()
图 3.25 – 在打开文件夹窗口中创建新文件夹并选择它
-
现在您必须通过使用Ctrl + Shift + P访问命令面板来选择 Python 解释器。然后开始输入
interpreter字符串并点击Python:选择解释器:![图 3.26 – 在命令面板中选择 Python:选择解释器选项]()
图 3.26 – 在命令面板中选择 Python:选择解释器选项
-
您将被提示选择您机器上实际拥有的其中一个环境。选择pbi_powerquery_env环境:
![图 3.27 – 选择您首选的环境]()
图 3.27 – 选择您首选的环境
-
您可以在左侧的状态栏上看到所选环境:
![图 3.28 – 在状态栏上检查所选环境]()
图 3.28 – 在状态栏上检查所选环境
-
返回左侧的资源管理器面板中的HELLO文件夹。点击HELLO标签旁边的新建文件图标,并将新文件命名为
hello.py:![图 3.29 – 在 Hello 文件夹中创建一个名为 hello.py 的新文件]()
图 3.29 – 在 Hello 文件夹中创建一个名为 hello.py 的新文件
-
在新文件中输入以下代码:
msg = "Hello World" print(msg)然后在主面板右上角点击运行图标(绿色的三角形):
![图 3.30 – 输入示例代码并运行]()
图 3.30 – 输入示例代码并运行
-
您可以在以下终端输出中看到结果:

图 3.31 – 在 VSCode 中运行您的第一个 Python 脚本
很好!您的 VSCode 现在已正确配置,可以运行 Python 脚本。
配置 Power BI 桌面版以与 Python 协同工作
由于您已经安装了所有必需的组件,您现在可以配置 Power BI 桌面版以与 Python 引擎和 IDE 交互。这实际上是一个非常简单的任务:
-
在 Power BI 桌面版中,转到文件菜单,点击选项和设置选项卡,然后点击选项:
![图 3.32 – 打开 Power BI 桌面版的选项和设置窗口]()
图 3.32 – 打开 Power BI 桌面版的选项和设置窗口
-
在选项窗口中,点击左侧的Python 脚本链接。右侧面板的内容将更新,您可以选择要引用的 Python 环境和用于 Python 可视化的 Python IDE。默认情况下,检测到的解释器是由 Miniconda 安装的默认解释器。为了选择特定的环境,您需要选择其他,然后点击浏览并提供对环境文件夹的引用:
![图 3.33 – 在 Power BI 中配置您的 Python 环境和 IDE]()
图 3.33 – 在 Power BI 中配置您的 Python 环境和 IDE
通常,您可以在
C:\Users\<your-username>\miniconda3\envs\中找到默认的环境文件夹。然后选择您的pbi_powerquery_env子文件夹。 -
默认情况下,Power BI 将 VSCode 识别为 Python IDE。保持原样并点击确定。
当我们在第十二章“探索性数据分析”中介绍 R 和 Python 脚本可视化时,您将看到如何从 Power BI 桌面版与 IDE 交互。
配置 Power BI 服务以与 R 协同工作
如您在第二章“配置 Power BI 中的 R”中所学,为了允许 Power BI 服务在数据转换步骤中使用 R 和 Power Query,您必须在安装了 R 引擎的外部机器上安装本地数据网关,并设置为个人模式。同样,Power BI 服务中的 Power Query 使用 Python 也是如此。因此,如果您尚未安装本地数据网关,请按照第二章中的步骤进行安装。
重要提示
Python 引擎和 R 引擎可以安装在同一外部机器上,并且可以通过单个数据网关引用。然而,您必须确保机器的资源足以处理来自 Power BI 服务的请求负载。
在 Power BI 服务中共享使用 Python 脚本的报告
关于如何在 Power BI 服务中使用 R 脚本进行数据转换的说明,在 第二章,配置 Power BI 中的 R 中提到,也适用于使用 Python 脚本的报告。以下是一些总结性的提示。
提示
你可以使用一个非官方架构,该架构利用一个与物理人无关,而是与一个 虚构的“服务”用户 关联的个人数据网关。此用户的凭证在所有使用 Python(以及 R)代码将数据转换到其报告中的分析师之间共享。此外,由于网关引用的 Python 引擎所在的机器是共享的,因此必须在计划活动期间保持开启。同一台机器通常也托管 R 引擎。在这个架构中,经常使用一个 Azure Windows 虚拟机,在该虚拟机上运行 R 和 Python 引擎以及数据网关。
作为提醒,图 3.34 总结了上述架构:

图 3.34 – 在数据转换中使用 Python 和 R 的企业架构
多亏了这种架构,才使得一组分析师能够使用 Python 脚本在他们的报告中,尽管个人模式下的本地数据网关施加了限制。
话虽如此,除了 Power Query 中 Python 脚本的限制之外,还有一些对于 Python 可视化也很重要的限制需要注意。
Python 可视化的限制
Python 可视化在处理数据方面有一些重要的限制,包括输入和输出:
-
Python 可视化可以处理 只有 150,000 行的数据框。如果行数超过 150,000,则只使用前 150,000 行。
-
Python 可视化有一个 输出大小限制为 2MB。
你还必须小心不要超过 Python 可视化的 5 分钟运行时计算,以避免超时错误。此外,为了避免性能问题,Python 可视化图表的分辨率固定为 72 DPI。
如你所想,Python 可视化的某些限制取决于你是在 Power BI Desktop 还是 Power BI 服务上运行该可视化。
在使用 Power BI Desktop 创建报告时,你可以执行以下操作:
-
在你的 Python 可视化引擎中安装任何类型的包(PyPI 和自定义)。
-
从 Python 可视化访问互联网。
在 Power BI 服务 中创建报告时,你可以执行以下操作:
-
你只能使用此链接中列出的 PyPI 包:
bit.ly/powerbi-python-limits。 -
你不能从 Python 可视化访问互联网。
重要提示
与 R 可视化的情况相反,你 没有 开发自定义 Python 可视化的选项。
一旦你的报告在 Power BI 服务上发布,你可以选择通过“发布到网络”选项将其分享到你的博客、你的网站之一或社交媒体上。不幸的是,根据微软的设计,Python 可视化(以及 R 可视化)在公开发布到网络上的报告中不可用。
提示
如果你绝对需要将特定的可视化发布到网络上,请记住,自定义 R 可视化可以克服发布到网络上的限制。因此,你需要从 Python 切换到 R。
摘要
在本章中,你了解了社区中最受欢迎的免费 Python 发行版及其使用最佳实践。
通过使用 Power BI Desktop 和 Power BI 服务的独特功能,你学习了如何正确创建特定的 Python 环境。
你还了解到在 R 社区中最受欢迎的 IDE(RStudio)也可以运行 Python 代码。此外,你已安装并配置了 VSCode,这是迄今为止最广泛使用的 Python 高级编辑器之一。
你还了解了在开发或企业环境中使用 Python 正确配置 Power BI Desktop 和 Power BI 服务的最佳实践。
最后,你了解了一些使用 Python 与 Power BI 相关的限制,这些知识对于避免在开发和部署报告时犯错误至关重要。
在下一章中,我们将最终开始使用 Power BI 中的 R 和 Python 脚本,进行数据摄取和数据转换。
第五章:4 导入未处理的数据对象
在本章中,您将探讨在报告创建通常的第一阶段使用 R 和 Python:数据摄取。从这一角度看,Power BI 是一个非常强大的工具,因为它自带了许多连接到各种数据源的工具。除了能够通过连接数据源直接导入数据外,您还可以使用 Power BI 轻松解决更复杂的数据加载场景。例如,您可以直接从 Power BI 中动态合并多个 CSV 文件或多个 Excel 工作簿的多个工作表,甚至可以使用M 语言在合并步骤中应用特殊逻辑。您还可以通过仅点击网页内容而不使用任何代码来抓取任何网页。所有这一切都归功于 Power BI 的标准功能,无需使用 R 或 Python。
然而,有些情况下,要导入并用于 Power BI 中的数据来自外部系统上进行的处理,这些系统以 Power BI 无法直接管理的数据格式持久化数据。想象一下,您是一名 Power BI 报告开发者,需要与一组数据科学家进行接口。他们可能对相当大的数据集进行的某些复杂处理可能需要非平凡的运行时间。这就是为什么数据科学家经常将此类处理的结果序列化到可接受大小的文件中,以便在需要时可以非常快速地进行反序列化。现在假设数据科学家团队向您提供了一个用 R 或 Python 序列化的这些文件之一,并要求您使用它来完成报告创建中所需的某些计算。您将如何做?
在本章中,您将了解如何在 Power BI 中处理来自 R(.rds)和 Python(.pkl)的序列化文件。本章将讨论以下主题:
-
在 R 中导入 RDS 文件
-
在 Python 中导入 PKL 文件
技术要求
本章要求您在计算机上已经安装了可用的互联网连接和Power BI Desktop。您必须已按照第二章,配置 Power BI 中的 R和第三章,配置 Power BI 中的 Python中概述的方式正确配置了 R 和 Python 引擎以及 IDE。
在 R 中导入 RDS 文件
在本节中,您将主要开发 R 代码,在各个示例中,我们将为您概述我们将要做什么。如果您对 R 的经验不多,您应该通过以下快速入门来熟悉 R 提供的数据结构:bit.ly/r-data-struct-quickstart。查看参考部分以获取更深入的信息。
Tidyverse 简介
使用 R 作为数据分析语言和数据科学分析的数据科学家必须了解名为 Tidyverse 的软件包集合(www.tidyverse.org)。它提供了数据整理和可视化的所有必需工具,为分析师提供了一个对其提供的整个软件包生态系统的统一方法。这样,它试图解决由未达成共同框架的开发者开发的软件包提供的 R 功能的“混乱”初始情况。
注意
如果你刚开始使用 R,你可能想从 Software Carpentry 的这个快速入门开始,熟悉主要概念:
bit.ly/tidy-quickstart。参考文献部分还包含有关 Tidyverse 的深入信息的链接。
在 Tidyverse 中,要能够处理表格数据,需要了解的基本数据类型是 tibble。Tibbles(新西兰人发音“tables”的方式)是 R 的 dataframes 的现代版本。从 tibble 开始,你可以使用 Tidyverse 包提供的简单函数执行所有想要的数据转换。
现在,你经常会在 Tidyverse 世界中进行的数据分析中看到 %>% 管道(R 语言允许用 % 包裹的符号定义为函数,> 表示链式操作)的使用。这个管道是从 Tidyverse 包中包含的 magrittr 包借用的,它具有将左侧对象作为右侧函数的第一个参数传递的功能。简而言之,如果你需要从 my_tbl tibble 中选择 my_col 列,而不是使用 select(my_tbl, my_col),你可以使用管道形式 my_tbl %>% select(my_col),使代码更易于阅读。
重要提示
目前,R Core 正在计划引入一个新的管道图形形式,即
|>。所以,当它们使其可用时,请准备好使用它。
为了简要理解本节中使用的代码,我们将逐块描述它,解释每个 R 对象的功能。
创建序列化的 R 对象
现在想象一下,你是一名数据科学家团队的一员,需要处理数据集的复杂处理,然后将获得的结果序列化到文件中以便按需重用。首先要做的事情是配置环境以安装 Tidyverse 的最新版本。
配置环境和安装 Tidyverse
打开 RStudio,按照以下步骤操作:
-
确保选择了最新的 R 引擎(在我们的例子中是 4.0.2),通过 工具 和 全局选项… 进行选择。
-
通过点击右上角的 项目 图标,然后选择 新建项目... 来创建一个新的项目:
![图 4.1 – 创建一个新的 RStudio 项目]()
图 4.1 – 创建一个新的 RStudio 项目
RStudio 项目使得将你的工作分成多个上下文变得简单,每个上下文都有自己的工作目录、工作空间、历史记录和源文档。
-
点击新建目录然后点击新建项目。
-
现在为项目文件夹输入一个名称,选择你想要放置它的文件夹,然后点击创建项目:
![图 4.2 – 创建新的项目文件夹]()
图 4.2 – 创建新的项目文件夹
你也可以在这里找到为你准备好的项目在 GitHub 仓库中:
Chapter04\importing-rds-files\importing-rds-files.Rproj。RStudio 将重新启动 R 会话,你刚刚创建的项目文件夹将成为你的项目工作目录。 -
如果你还记得,在第三章,配置 Power BI 中的 Python中,你已经禁用了使用较早日期创建的快照来下载包的 MRO 限制。这是为了安装包的最新版本。问题是,该操作的效果是临时的。为了看到这一点,现在在控制台中运行
getOption("repos")命令。你会看到默认仓库仍然是 MRO 设置的快照。 -
为了在项目级别永久覆盖仓库,你必须将之前使用的相同代码写入项目文件夹中的
.Rprofile文件。为此,前往控制台并输入file.edit(".Rprofile")。如果不存在,这将创建项目文件夹中的.Rprofile文件,并在 RStudio 的编辑器窗口中打开它。此时,输入以下代码:local({r <- getOption("repos") r["CRAN"] <- "https://cloud.r-project.org/" options(repos = r) }) message("Default repo replaced with 'https://cloud.r-project.org/'")现在通过按Ctrl + S(或文件 | 保存)保存
.Rprofile文件,然后通过点击右上角的项目图标(一个包含“R”的立方体)关闭项目,然后点击关闭项目:![图 4.3 – 关闭当前的 RStudio 项目]()
图 4.3 – 关闭当前的 RStudio 项目
-
重新打开你刚刚关闭的项目(你可以在右上角常规项目菜单中的列表中找到它,或者你可以在项目文件夹中找到
.Rproj文件)。你会在控制台中注意到消息:"默认仓库已替换为'https://cloud.r-project.org/'"。 -
再次在控制台中输入
getOption("repos")命令并按Enter。现在你会看到新的 CRAN 仓库作为默认仓库:![图 4.4 – 将新的 CRAN 仓库设置为默认]()
图 4.4 – 将新的 CRAN 仓库设置为默认
-
现在,只需在控制台中运行以下命令即可安装所有 Tidyverse 包:
install.packages("tidyverse")(它相当于通过 GUI 点击右下角的包选项卡然后点击安装来安装)。
太棒了!现在你确定你已经安装了 Tidyverse 的最新版本。
创建 RDS 文件
我们现在将指导你创建 R 对象的序列化到 RDS 文件中。假设创建此对象所需的计算时间非常长。我们还将让你创建一个不是简单 tibble 的对象,这个对象可以首先导出为 CSV 文件,然后导入到 Power BI 中。让我们开始:
-
通过按 Ctrl + Shift + N(或点击 + 新建文件 图标然后选择 R 脚本)在你的项目中打开一个新的 R 脚本。
-
现在,你将使用
library命令加载 Tidyverse 包,并使用data命令加载包含在 tidyr 包中的 growth population tibble(人口增长数据),这些数据是从 世界卫生组织全球结核病报告 中选取的子集:library(tidyverse) data("population") # Let’s have a look at the tibble population最后一条命令(与 tibble 的名称匹配)允许你观察 tibble 的前几行内容及其列的数据类型。突出显示所有命令并按 Ctrl + Enter(或在面板右上角点击 运行 图标)来运行它们。你会看到以下结果:
![图 4.5 – 加载“population” tibble]()
图 4.5 – 加载“population” tibble
所有以 # 开头的都是注释。
-
现在让我们检查 tibble 中有多少个不同的国家。你将使用
distinct函数,然后使用pull函数来提取 tibble 中的单个不同国家列,并以向量格式提取:population %>% distinct(country) %>% pull()你将看到所有不同的国家列表,如下所示:
![图 4.6 – 不同国家的列表]()
图 4.6 – 不同国家的列表
尝试突出显示代码直到包括
distinct(country)并运行突出显示的代码。你将始终看到不同的国家,但仍然作为 tibble 的一部分。 -
现在我们想要将年份和人口信息分组到每个国家的单独 tibble 中。简而言之,我们想要有一个包含国家的 tibble,并为每个国家创建另一个包含按年份的人口信息的 tibble。你可以使用
nest函数做到这一点:nested_population_tbl <- population %>% tidyr::nest( demographic_data = -country ) nested_population_tbl你刚刚将
nested_population_tbl变量分配给包含嵌套人口数据的新的 tibble。观察我们如何通过从tidyr源包调用::来显式调用nest函数。同时,观察将除国家列之外的所有内容“嵌套”到新demografic_data列中的 tibble 列表是多么容易。突出显示并运行前面的代码块,你会看到以下结果:![图 4.7 – 嵌套人口数据表]()
图 4.7 – 嵌套人口数据表
注意,新的
demographic_data列是一个 tibble 的列表。 -
现在,你可以最终使用
saveRDS函数将nested_population_tbl对象序列化到 RDS 文件中:saveRDS(nested_population_tbl, "nested_population_tbl.rds")
你可以在与本书相关的 GitHub 仓库中找到这里显示的 R 代码,在 Chapter04\importing-rds-files\01-create-object-to-serialize.R 文件中。为了正确执行文件中的代码,你应该首先打开 RStudio 项目,双击 Chapter04\importing-rds-files\importing-rds-files.Rproj 文件。然后你可以使用 RStudio 右下角的 文件 选项卡打开之前提到的 R 脚本。
太棒了!你可以通过查看相同的面板来验证文件是否已正确序列化:

图 4.8 – 正确创建的 RDS 文件
同样地,你将创建一个包含四个选定国家时间序列视图的 RDS 对象。时间序列数据与之前看到的人口增长数据相同。让我们看看你如何生成这个文件:
-
通过在 RStudio 控制台中输入
install.packages("timetk")命令安装由 Matt Dancho 开发的出色的 timetk 包。它使得可视化、整理和特征工程时间序列数据以进行预测和机器学习预测变得容易。更多详情请查看这里:business-science.github.io/timetk/。 -
在 RStudio 中打开
Chapter04\importing-rds-files\04-create-plots-object-to-serialize.R文件。文件的前一部分包含在上一节中已经看到的代码,用于生成人口嵌套 tibble。 -
在创建嵌套 tibble 立即之后,你将看到如何绘制与瑞典相关的时序数据。每个使用的 R 函数都在代码中有解释:
selected_country <- "Sweden" nested_population_tbl %>% # Get the row related to the selected country filter( country == selected_country ) %>% # Get the content of 'demografic_data' for # that row. Note that it is a list pull( demographic_data ) %>% # Extract the 'demografic_data' tibble from # the list (it has only 1 element) pluck(1) %>% # Now plot the time series declaring the date variable # and the value one. timetk::plot_time_series( .date_var = year, .value = population, .title = paste0("Global population of ", selected_country), .smooth = FALSE, # --> remove the smooth line .interactive = FALSE # --> generate a static plot )这是结果:
![图 4.9 – 瑞典人口增长时间序列图]()
图 4.9 – 瑞典人口增长时间序列图
-
现在,你将根据之前的示例为嵌套 tibble 中的每个国家创建一个时间序列图。好事是,多亏了
purrr包中 map 函数 提供的 函数式编程 的力量,你可以通过一个函数一次性完成这个操作。像往常一样,你将在代码中找到详细的解释:nested_population_plots_tbl <- nested_population_tbl %>% # Select a subset of countries filter( country %in% c("Italy", "Sweden", "France", "Germany") ) %>% # Add a new column called 'plot' applying the plot_time_series # function to the values of the demographic_data tibble (.x) # for each country (.y) in the 'country' field. # Do this thanks to the map2 function. mutate( plot = map2( demographic_data, country, ~ timetk::plot_time_series(.data = .x, .date_var = year, .value = population, .title = paste0("Global population of ", .y), .smooth = FALSE, .interactive = FALSE) ) ) %>% # Return just the 'country' and 'plot' columns. select( country, plot ) -
之后,只有由
nested_population_plots_tbltibble 提取的图表列表被序列化到一个 RDS 文件中:# The index of list items corresponds to the country_id values # into the selected countries tibble. plots_lst <- nested_population_plots_tbl$plot # Serialize the list of plots saveRDS(plots_lst, "plots_lst.rds")
干得好!你已经将你的 R 对象序列化到了 RDS 文件中。如果你想尝试反序列化它们,你可以按照 GitHub 仓库中的 02-deserialize-object-from-rds.R 和 05-deserialize-plots-object-from-rds.R 文件中的代码进行操作。
现在,你已经准备好直接在 Power BI 中使用文件中序列化的嵌套 tibble。
在 Power BI 中使用 RDS 文件
很明显,必须通过 R 脚本在 Power BI 中使用 RDS 文件。如您现在可能已经学到的,有两种 Power BI 对象可以通过它们使用 R 脚本:Power Query 编辑器和R 可视化。让我们从最简单的情况开始,即将 RDS 文件导入 Power Query 编辑器。
将 RDS 文件导入 Power Query 编辑器
当您知道可以从对象中提取表格信息并希望将其持久化到 Power BI 数据模型中时,您将导入一个序列化的 R 对象到 Power Query 编辑器中。让我们看看它是如何完成的:
-
打开 RStudio 并通过按 Ctrl + Shift + N 创建一个新的 R 脚本到项目中。您可以复制并粘贴
02-deserialize-object-from-rds.R文件的内容(或者如果您使用了 GitHub.Rproj文件打开项目,可以直接打开它)。 -
通过
readRDS函数加载 RDS 文件,并将其分配给新的deserialized_tbl变量,如下所示:library(tidyverse) project_folder <- "C:/<your>/<absolute>/<project_folder>/<path>" deserialized_tbl <- readRDS( file.path(project_folder, "nested_population_tbl.RDS") )注意,我们正在使用绝对路径来读取 RDS 文件,尽管我们处于 RStudio 项目中并且可以不使用路径引用该文件。这是因为Power BI 没有 RStudio 那样的“项目”概念,因此需要绝对路径来正确定位文件。另外,请注意,在 R 中,您可以使用双反斜杠(
\\)或简单斜杠(/)作为路径字符串中的分隔符。 -
现在尝试以下方式从嵌套的 tibble 中提取瑞典的人口统计数据:
sweden_population_tbl <- deserialized_tbl %>% filter( country == "Sweden" ) %>% pull( demographic_data ) %>% pluck(1) sweden_population_tbl在这段代码中,我们将
sweden_population_tbl变量分配给deserialized_tbl变量的内容,我们对它应用以下转换:a) 使用过滤函数过滤国家瑞典(从而获得与瑞典相关的行)。b) 从该行中,使用pull函数从原始 tibble 中分离出demographic_data字段的内容(您将得到一个列表)。c) 由于demographic_data列的内容是一个只包含一个 tibble 的列表,因此必须使用pluck函数取消列表。结果是组织在一个 tibble 中的瑞典人口数据,如图 4.11 所示:![图 4.10 – 以 tibble 格式组织的瑞典人口数据内容]()
图 4.10 – 以 tibble 格式组织的瑞典人口数据内容
-
现在,打开 Power BI Desktop 并确保它引用的是最早的 MRO。然后点击获取数据,然后点击更多…。在搜索框中开始键入
script,然后双击 R 脚本。R 脚本编辑器将会弹出。 -
将
03-deserialize-object-from-rds-in-power-bi.R文件的内容复制并粘贴到 R 脚本编辑器中,相应地更改 RDS 文件的绝对路径,然后点击确定。 -
将会打开导航器窗口,您可以选择要导入哪个数据框:
![图 4.11 – 将反序列化的数据框导入 Power BI]()
图 4.11 – 将反序列化的数据框导入 Power BI
你将在你的脚本中定义了多少个 dataframe(记住 tibbles 是 dataframe 的特殊化),就会看到多少个 dataframe。选择sweden_population_tbl dataframe 并点击Load。
-
当加载完成后,点击 Power BI Desktop 左侧菜单中的表格图标,以验证数据是否已正确以表格形式导入:

图 4.12 – dataframe 已正确导入
太棒了!你已经正确地将你的 RDS 文件导入 Power BI,以便以最合适的方式使用 Power Query 中的内容。
重要提示
如你所注意到的,Power BI 可以工作的唯一 R 数据结构是具有标准数据类型的 dataframe(或 datatable)。无法导入任何其他类型的 R 对象。如果你直接导入了deserialized_tbl dataframe,那么demographic_data列中的值将生成错误,并且将不可用。
有时候可能发生这样的情况,你无法在 Power Query 编辑器中反序列化 RDS 文件,以将其信息以表格格式提取出来,以便在 Power BI 数据模型中持久化。你可能需要在 R 视觉中动态地反序列化 RDS 文件的内容,以便在可视化中使用其信息。你将在下一节中看到如何解决这种情况。
在 R 视觉中导入 RDS 文件
现在假设你从数据科学家团队那里收到了包含每个国家时间序列图表的 RDS 文件。你的目标是允许报告用户通过选择一个国家来查看图表。
你面临的问题是:你知道,为了通过 R 脚本从 Power Query 编辑器导入任何信息,它必须以表格格式,并且必须使用标准数据类型。数据科学家提供的图表以ggplot格式分组在一个列表中(ggplot为 R 提供了创建优雅且复杂的图形的强大图形语言),它本身不是标准数据类型。你该如何将它们导入 Power BI?你需要一点横向思维。
重要提示
如你可能已经知道的,在 R 中可以将任何编程对象序列化为它的字节表示(R 中的原始向量)。字节表示可以进一步转换为它的字符串表示。一旦你有字符串(这是一个标准数据类型),你就可以将它们组织成一个 dataframe。完成之后,你可以将这个 dataframe 导入 Power BI。
当你需要“提供”R 视觉数据时,请记住以下考虑因素:
重要提示
当你在 Power BI 数据模型中选择多个表中的多个列(它们之间必须有关系)作为 R 视觉的值时,这些值将形成一个单一的 dataframe(去重后),在视觉的 R 脚本中引用。
此外,请记住以下事项:
提示
在某些情况下,你可能想 不删除重复行。在这种情况下,你可以在你的数据集中添加一个索引字段(行号),这将使所有行都被视为唯一,并防止分组。
如果你在 Power Query 编辑器中导入包含某种字符串表示的数据框,却无法将其转换回原始对象,那就没有意义。幸运的是,之前提到的直接转换操作都是可逆的,因此你可以在 R 可视化中使用逆转换来适当地提取和显示图表。此外,这个过程还包括了 R 可视化中处理数据的一个似乎未记录的限制。
重要提示
如果一个字符串长度超过 32,766 个字符,一旦传递到默认的数据框以在 R 可视化中引用,它将被截断。为了避免截断,有必要将字符串拆分成更小的块(我们选择了一个任意的长度为 10,000)并在使用数据到 R 可视化之前将这些块持久化到数据框的列中。
话虽如此,总的来说,在本节中你将执行以下操作:
-
在 Power Query 编辑器 中导入包含命名列表的图表的 RDS 文件。从中,提取包含国家名称的数据框和包含图表信息的数据框。
-
使用具有单选选项的筛选器中的国家数据框。
-
每次从筛选器中选择一个国家时,R 可视化将显示该国家人口增长的时间序列图。
让我们在一个包含你将在代码中找到的函数的图中总结处理图表数据的所有过程:

图 4.13 – 将 RDS 文件内容反序列化为 R 可视化
重要提示
由于 R 引擎返回了一个神秘的 ENVSXP 错误,无法直接导入包含国家名称和相应图表的单一数据框。一个命名列表工作得很好。
在以下步骤中,我们不会详细解释所有使用的函数,仅仅是因为我们将参考与本书相关的 GitHub 中共享的代码,其中每个细节都有注释。所以,让我们开始:
-
打开 Power BI Desktop,转到 获取数据,然后 更多…,然后 R 脚本 以导入 RDL 文件。
-
从 GitHub 仓库打开
06-deserialize-plots-object-from-rds-in-power-bi.R文件,复制内容,相应地更改 RDL 文件的绝对路径,将其粘贴到 R 脚本编辑器中,然后点击 确定。 -
Power Query 将检测到你在脚本中创建的三个数据框。仅选择 plots_df 数据框(包含图表字节的字符串表示),以及 selected_countries_df 数据框。然后点击 加载:
![图 4.14 – 选择包含有用数据的两个数据框]()
图 4.14 – 选择包含有用数据的两个数据框
-
点击左侧工具栏上的数据图标,然后点击管理关系按钮:
![图 4.15 – 管理关系按钮]()
图 4.15 – 管理关系按钮
-
引擎已自动创建了两个导入表之间的关系:
![图 4.16 – 自动检测到的表格之间的关系]()
图 4.16 – 自动检测到的表格之间的关系
点击关闭。
-
点击左侧工具栏上的报告图标返回报告画布:
![图 4.17 – 报告图标]()
图 4.17 – 报告图标
-
现在点击切片器图标:
![图 4.18 – 切片器图标]()
图 4.18 – 切片器图标
-
在画布中保持切片器视觉区域选中,点击字段面板上的selected_countries_df表,并选择country字段:
![图 4.19 – 为切片器视觉选择 country_name 列]()
图 4.19 – 为切片器视觉选择 country_name 列
-
然后点击切片器的格式图标并启用单选选项:
![图 4.20 – 只允许单选]()
图 4.20 – 只允许单选
设置单选非常重要,因为R 视觉内部的逻辑将管理单个图表的反序列化。
-
现在,切片器视觉将显示包含在selected_countries_tbl中的所有国家:
![图 4.21 – 这就是切片器视觉的样子]()
图 4.21 – 这就是切片器视觉的样子
-
点击报告画布以取消选择切片器视觉元素,然后点击R 脚本视觉图标:
![图 4.22 – R 脚本视觉图标]()
图 4.22 – R 脚本视觉图标
常规的启用脚本视觉窗口弹出。点击启用。
-
移动并拉伸 R 视觉的边框,使其几乎覆盖整个报告画布。保持选中状态,点击字段面板中的plots_df表,并选择chunk_id、country_id和plot_str字段:

图 4.23 – 选择要在 R 视觉中使用的字段
在格式选项卡中,你可以自由地关闭 R 视觉的标题。
-
从 GitHub 仓库打开
07-deserialize-plots-df-into-r-visual.R文件,复制内容,并将其粘贴到 R 视觉的脚本编辑器中。然后点击 R 脚本编辑器右上角的运行图标。 -
现在,你可以点击切片器中的每个国家,以查看其人口时间序列:

图 4.24 – 显示意大利的人口增长
太棒了!你刚刚创建了一个很少有人能做出来的报告。
重要提示
当你需要构建 R 中需要使用 Power BI 服务中 R 视觉未提供的包的复杂可视化时,这种技术非常有用。这些可视化可以制作成“离线”的,序列化到文件,然后在服务上的共享报告中使用。
你刚刚看到了如何在 Power BI 中导入 RDS 文件,尽管它不是 Power BI 的默认功能,以及如何直接在 R 视觉中使用它。
在下一节中,你将看到如何对使用 Python 序列化的文件执行相同操作。
在 Python 中导入 PKL 文件
让我们通过 GitHub 上的 Python 代码概述你要实现的内容。如果你不熟悉 Python,你应该通过这个教程熟悉基本结构:bit.ly/py-data-struct-quickstart。对于如何实现 Python 中的算法和数据结构的更详细研究,我们建议这本免费电子书:bit.ly/algo-py-ebook。
对 PyData 世界的简要介绍
PyData世界由对数据分析充满热情并喜欢使用开源数据工具的用户和开发者组成。PyData 社区还喜欢分享数据管理、处理、分析和可视化的最佳实践、新方法和新兴技术。Python 数据管理社区使用的重要且流行的包如下:
-
NumPy:这是 Python 中科学计算的主要库。它提供了一个高性能的多维数组对象以及用于处理数据的工具。
-
Pandas:Python 数据分析库(pandas 来自面板数据)建立在 NumPy 之上,以表格格式(如 CSV 文件、TSV 文件或 SQL 数据库表)接收数据,以便创建一个具有行和列的 Python 对象,称为DataFrame。此对象与统计软件中的表格非常相似,熟悉 R 概念的人将 pandas DataFrame 与 R 的 dataframe 数据类型概念上等同起来。
-
Matplotlib:这是一个用于生成图表的 Python 包。它始于 2000 年代初的一个项目,旨在使用 Python 可视化癫痫患者大脑中的电子信号。Matplotlib 的创建者是一位神经生物学家,他正在寻找一种方法来使用 Python 复制 MATLAB 的绘图功能。
-
Scikit-learn:也称为sklearn,其名称来源于两个词“SciPy”和“Toolkit”的结合。这是一个为 Python 设计的免费且强大的机器学习库,旨在与 NumPy、SciPy、pandas、Matplotlib 等库交互。
详细介绍这些库可以做什么不是本书的目的。
注意
如果您想通过利用这些库来开始学习如何在 Python 中处理数据,我们建议从这本免费书籍开始:bit.ly/data-science-py-ebook。在那之后,为了进一步学习,您绝对不能错过这本精彩的书:Sebastian Raschka 所著的Python Machine Learning: Machine Learning and Deep Learning with Python, Scikit-Learn, and TensorFlow 2, 第三版,由 Packt 出版。
为了简要了解本节中使用的代码,我们将尝试逐块描述其功能,并请您参考 GitHub 代码中的注释以获取更多详细信息。
创建一个序列化的 Python 对象
如前节所述,现在假设您是另一支需要使用 Python 进行一些复杂、耗时数据分析的数据科学家团队的一员。由于需要重复使用在其他过程中获得的结果,团队决定使用Pickle 文件(PKL)。这是通过使用pickle库将任何 Python 对象序列化然后写入文件来获得的。正如您在前一节中已经看到的,序列化意味着将内存中的对象转换为可以保存到磁盘或通过网络发送的字节流。显然,这是一个容易逆转的操作。事实上,还有可能反序列化一个序列化的对象。
重要提示
在开始之前,请确保您解压 GitHub 仓库 ZIP 文件的路径不包含任何包含在其中的文件夹名称中的空格,否则 Python 脚本执行将会出现错误。
那么,让我们开始安装我们环境中需要的东西,并适当地初始化 IDE。
配置环境和安装 PyData 包
打开您的Anaconda Prompt(从开始菜单),按照以下步骤操作:
-
确保使用
pbi_powerquery_env环境,输入以下代码:conda activate pbi_powerquery_env现在您将安装一些必要的缺失包,以便能够使用本节中的代码。
-
输入以下命令来安装matplotlib:
pip install matplotlib。
太好了!您的 Python 环境现在已准备好运行您的脚本。现在打开 Visual Studio Code,按照以下步骤操作:
-
前往文件,然后打开文件夹…。确保选择包含在您之前解压的 GitHub 仓库中的importing-pkl-files文件夹,位于Chapter04文件夹下。点击选择文件夹。
-
打开
01-create-object-to-serialize-in-pkl.py文件,在右侧的选定的文件夹中点击它。 -
记住您必须选择运行脚本的环境。因此,按Ctrl + Shift + P打开 Visual Studio Code 调色板,并开始输入文本“interpreter。”然后,选择Python: Select Interpreter,然后选择
pbi_powerquery_env环境。
极好!现在您已准备好序列化您的第一个 Python 对象。
创建 PKL 文件
Python 中最常用的两种数据结构是 列表 和 字典。到目前为止,您已经熟悉列表,您在 R 中之前已经见过,如果您从未在编程语言中开发过,那么字典可能对您来说可能是个新概念。字典是由一组 键值对 组成的数据结构。您可以使用花括号({…})来定义它们。具体来说,在本节中,您将创建一个包含国家名称和包含该国人口增长数据的 dataframe 的键值对字典。
您将使用的数据与上一节中使用的数据相同。这次,您将直接从 CSV 文件中加载它,而不是从内存中的包中加载。让我们开始吧:
-
由于
01-create-object-to-serialize-in-pkl.py文件已经在 Visual Studio Code 中打开,只需通过右上角的绿色箭头图标(在终端中运行 Python 文件)运行代码即可。这样,整个脚本将会执行。 -
在控制台中您不会看到任何特别的内容,只有运行
python.exe并将当前脚本路径作为参数的命令。但如果您在左侧的资源管理器中查看,您会看到nested_population_dict.pkl文件已正确创建:![图 4.25 – 您的第一个 PKL 文件已创建]()
图 4.25 – 您的第一个 PKL 文件已创建
-
就像在 Rstudio 中一样,您只能通过突出显示代码并按 Ctrl + Enter 来运行代码片段。您需要更改一个设置选项,以便允许使用 Python 的 交互窗口。转到 设置,按 Ctrl + ,(逗号),然后在搜索栏中开始输入
Send Selection To Interactive Window并检查选定的选项:![图 4.26 – 在 Jupyter 交互窗口中启用 Python 代码块的执行]()
图 4.26 – 在 Jupyter 交互窗口中启用 Python 代码块的执行
-
现在,您需要将 IPython 内核 (
ipykernel) 安装到您的pbi_powerquery_env环境中。通常,这个操作会由 Visual Studio Code 自动完成,但有时您可能会遇到错误。因此,手动操作会更好。打开您的 Anaconda Prompt 并输入以下命令:conda install --name pbi_powerquery_env ipykernel -y。 -
现在,选择从开始(
import pandas as pd)到定义countries的行(countries = population_df.country.unique()),然后按 Shift + Enter。您的代码块将会发送到交互窗口:![图 4.27 – 在 Visual Studio Code 中运行选定的脚本块]()
图 4.27 – 在 Visual Studio Code 中运行选定的脚本块
如 图 4.28 所示,在交互窗口中点击 变量 图标,您也可以检查每个变量的内容。
嘿,可能你没有注意到,但只需付出最小的努力,你就已经创建了你的第一个 PLK 文件!你可以通过运行 02-deserialize-object-from-pkl.py 文件中的代码来训练自己反序列化新创建的 PKL 文件。
现在,我们将指导你创建一个包含序列化字典的第二个 PKL 文件,该字典由国家和相应的人口增长时间序列的成对组成。这次,为了简单起见,你将只保留字典中的四个国家。让我们继续:
-
从左侧的资源管理器打开
04-create-plots-object-to-serialize-in-pkl.py文件。 -
你可以分步骤运行代码,以更好地理解其工作原理。在脚本大约一半的地方,你会找到以下代码:
# Let's try to plot the time series for Sweden selected_country = "Sweden" x = nested_population_dict[selected_country].year y = nested_population_dict[selected_country].population # Create a figure object fig_handle = plt.figure() # Plot a simple line for each (x,y) point plt.plot(x, y) # Add a title to the figure plt.title("Global population of " + selected_country) # Show the figure fig_handle.show() -
运行那部分代码后,将打开一个交互式窗口,显示瑞典的时间序列图:
![图 4.28 – 显示瑞典时间序列图的交互式窗口]()
图 4.28 – 显示瑞典时间序列图的交互式窗口
如果你想要创建新的图形,请保持打开状态,否则你可能会遇到奇怪的错误。
-
最后一段代码创建了一个包含每个国家图形的新字典,并将其序列化到文件中。一旦执行,你可以在左侧的资源管理器中看到
nested_population_plots_dict.pkl文件:

图 4.29 – 新的字典已正确序列化到 PKL 文件中
太棒了!你也序列化了第二个字典。你可以使用 05-deserialize-plots-object-from-pkl.py 脚本中的代码来练习反序列化它。
现在,你已准备好在 Power BI 中测试你的 PKL 文件,无论是在 Power Query 编辑器中还是在 Python 可视化中。
在 Power BI 中使用 PKL 文件
很明显,在 Power BI 中必须通过 Python 脚本来使用 PKL 文件。因此,有两个 Power BI 对象可以通过它们使用 Python 脚本:Power Query 编辑器和Python 可视化。让我们从最简单的情况开始,即将 PKL 文件导入到 Power Query 编辑器中。
在 Power Query 编辑器中导入 PKL 文件
当你知道如何从对象中提取表格信息并将其持久化到 Power BI 数据模型中后,你将把一个序列化的 Python 对象导入到 Power Query 编辑器中。让我们看看如何做:
-
打开 Power BI Desktop,并确保它引用的是
pbi_powerquery_env环境。然后点击获取数据,然后更多…。在搜索框中开始输入script,然后双击 Python 脚本。Python 脚本编辑器将弹出。 -
在 Visual Studio Code 中打开
03-deserialize-object-from-pkl-in-power-bi.py文件,并复制其内容。然后将其粘贴到 Power BI Desktop 中的 Python 脚本编辑器中,相应地更改 PKL 文件的绝对路径,并点击确定。 -
导航器窗口将打开,让你选择要导入哪个 dataframe:

图 4.30 – 将反序列化的数据框导入 Power BI
选择 sweden_population_tbl 数据框并点击 加载。
- 加载完成后,点击 Power BI Desktop 左侧菜单中的表格图标以验证数据是否已正确以表格形式导入:

图 4.31 – 数据框已正确导入
干得好!你已经正确地将你的 PKL 文件导入到 Power BI 中,以便以最合适的方式使用其内容 与 Power Query。
重要提示
与 R 脚本一样,Power BI 能够处理的数据结构只有具有标准数据类型的 pandas DataFrame。无法导入任何其他类型的 Python 对象。这就是为什么你无法直接导入字典的原因。
如前所述,可能发生 PKL 文件不包含可以在 Power Query 编辑器中提取的表格格式信息的情况。你可能需要在 Python 可视化中直接反序列化 PKL 文件的内容,以便使用这些信息创建图表。你将在下一节中看到如何解决这种情况。
在 Python 可视化中导入 PKL 文件
现在假设你收到了数据科学家团队发送的包含每个国家时间序列图的 PKL 文件。你的目标是允许报告用户通过选择一个国家来查看这些图表。
你面临的问题是这样的:你知道为了通过 Python 脚本将任何信息导入 Power Query 编辑器,它必须是表格格式,并且必须使用标准数据类型。数据科学家提供的图表以 Matplotlib 的 图像格式分组在字典中,而 Matplotlib 本身不是标准数据类型。那么,你如何将字典导入 Power BI 呢?与之前章节中用于 R 的相同“技巧”适用。
总结来说,在本节中你将执行以下操作:
-
在 Power Query 编辑器中导入包含图表字典的 PKL 文件。提取其键(国家)并在数据框中公开它们。使用其字节流表示形式填充另一个数据框。
-
使用包含国家的数据框作为单选切片器。
-
每次从切片器中选择一个国家时,Python 可视化将反序列化字节流到输入数据框中,并显示该国人口增长的时间序列图。
重要提示
此外,在这种情况下,当你从 Power BI 数据模型中的多个表(它们之间必须有关系)中选择多个列作为 Python 可视化的值时,这些值 将形成一个单一的数据框(去重后),在视觉的 Python 脚本中引用。
此外,适用于 R 数据框输入的相同建议也适用:
技巧
在某些情况下,你可能不想 删除重复行。在这种情况下,你可以在你的 Pandas 数据集中添加一个索引字段(行号),这将使所有行都被视为唯一,并防止分组。
再次,即使是 Python 可视化也对其导入的数据添加了一个似乎未记录的大小限制:
重要提示
如果字符串长度超过 32,766 个字符,它将被截断,一旦传递到 Python 可视化的输入数据框中。为了避免截断,我们需要将字符串分成每个 32,000 个字符的块(这是我们选择的任意值),并在使用 Python 可视化中的数据之前将这些块持久化到数据框的列中。
这里用一张图总结了整个过程,图中包含了你将在代码中找到的函数:

图 4.32 – 将 PKL 文件内容反序列化为 Python 可视化
在以下步骤中,我们不会详细解释所有使用的 Python 函数,仅仅因为我们将会参考与本书相关的 GitHub 仓库中共享的代码,其中每个细节都有注释。所以,让我们开始吧:
-
打开 Power BI Desktop,转到 获取数据,然后 更多…,然后 Python 脚本 以导入 PKL 文件。
-
从 GitHub 仓库打开
06-deserialize-plots-object-from-pkl-in-power-bi.py文件,复制内容,将其粘贴到 Python 脚本编辑器中,相应地更改 PKL 文件的绝对路径,然后点击 确定。 -
Power Query 将检测到你在脚本中创建的三个数据框。仅选择
plots_df(包含每个图表的字节字符串块的)和 selected_countries_df(包含国家名称的)数据框:![图 4.33 – 选择包含有用数据的两个数据框]()
图 4.33 – 选择包含有用数据的两个数据框
然后点击 加载。
-
在左侧工具栏中点击 数据 图标,然后点击 管理关系 按钮:
![图 4.34 – 管理关系按钮]()
图 4.34 – 管理关系按钮
-
引擎已自动创建了两个导入表之间的关系:
![图 4.35 – 表格之间自动检测到的关系]()
图 4.35 – 表格之间自动检测到的关系
点击 关闭 并使用左侧工具栏中的 报告 图标返回报告画布。
-
现在点击 Slicer 可视化图标:
![图 4.36 – Slicer 可视化图标]()
图 4.36 – Slicer 可视化图标
-
在画布上保持 Slicer 可视化区域被选中,点击 字段 面板上的 selected_countries_df 表,并选择 country_name 字段:
![图 4.37 – 为 Slicer 可视化选择 country_name 列]()
图 4.37 – 为 Slicer 可视化选择 country_name 列
-
然后,像上一节那样点击 Slicer 的格式图标,并启用单选选项。Slicer 可视化将显示包含在selected_countries_df表中的所有国家名称。设置单选非常重要,因为Python 可视化内部的逻辑将管理单个图表的反序列化。
-
点击报告画布以取消选择 Slicer 可视化区域,然后点击Python 可视化图标:
![图 4.39 – Python 可视化图标]()
图 4.39 – Python 可视化图标
常见的启用脚本可视化窗口弹出。点击启用。
-
移动并拉伸 Python 可视化边框,以便几乎覆盖整个报告画布。保持选中状态,点击字段面板中的plots_df表,并选择所有三个
chunk_id、country_id和plot_str字段:![图 4.40 – 选择在 Python 可视化中使用的字段]()
图 4.40 – 选择在 Python 可视化中使用的字段
在格式选项卡中,你可以自由地关闭 Python 可视化标题。
-
从 GitHub 仓库打开
07-deserialize-plots-df-into-python-visual.py文件,复制内容,并将其粘贴到 Python 可视化脚本编辑器中。然后,点击 Python 脚本编辑器右上角的运行图标。 -
现在,你可以点击滑块中的每个国家,以查看人口时间序列:

图 4.41 – 显示德国的人口增长
太棒了!你刚刚使用了一种世界上很少有人知道的方法创建了一份报告。
重要提示
当你需要使用 Power BI 服务中 Python 可视化未提供的包来构建复杂可视化时,这项技术非常有用。这些可视化可以离线制作,序列化到文件中,然后在该服务的共享报告中使用。
摘要
在本章中,你学习了 Tidyverse 方法在 R 开发中的应用,以及如何将 R 对象序列化到文件中。之后,你学习了如何在 Power Query 编辑器和 R 可视化中使用这些序列化文件。
你随后使用 Python 解决了相同的问题。具体来说,你学习了 PyData 社区最常用的包,学习了如何将 Python 对象序列化到文件中,以及如何在 Power BI 中(在 Power Query 编辑器和 Python 可视化中)使用它们。
在下一章中,你将有机会了解正则表达式和模糊字符串匹配有多么强大,以及它们可以为你的 Power BI 报告带来哪些好处。
参考文献
对于额外的阅读,请参阅以下书籍和文章:
-
《R 入门》 by R Core (
cran.r-project.org/doc/manuals/r-release/R-intro.html)。 -
《数据科学中的 R》 by Hadley Wickham (
r4ds.had.co.nz/index.html)。 -
《使用 R 进行机器学习:预测建模的高级技巧,第 3 版》 由 Brett Lantz 著,Packt Publishing 出版 (
www.packtpub.com/product/mastering-machine-learning-with-r-third-edition/9781789618006).
第六章:5 在 Power BI 中使用正则表达式
通常,许多数据清洗任务涉及在字符串之间执行复杂的搜索和替换。通常的搜索和替换工具有时不足以获得所需的结果。例如,假设你需要匹配字符串,不是通过精确的方式(例如,通过相等条件)而是使用它们之间的相似标准。了解如何使用正则表达式(别名为 regex)或模糊字符串搜索可以在需要高质量数据的项目中起到关键作用。多亏了 R 和 Python,你可以将这些工具添加到你的工具库中。
本章我们将涵盖以下主题:
-
正则表达式的简要介绍
-
在 Power BI 中使用正则表达式验证数据
-
在 Power BI 中使用正则表达式加载复杂的日志文件
-
在 Power BI 中使用正则表达式从文本中提取值
技术要求
本章要求你拥有一个正常工作的互联网连接,并且Power BI 桌面版已经安装在你的机器上。你需要按照第二章、配置 Power BI 中的 R和第三章、配置 Power BI 中的 Python中概述的方式正确配置 R 和 Python 引擎和 IDE。
正则表达式的简要介绍
正则表达式(通常简称为regex)由一系列字符定义,这些字符标识一个抽象的搜索模式。本质上,它是一种在 1951 年由形式语言和理论计算机科学专家开发的数学技术。它用于验证输入数据或从文本中搜索和提取信息。
如果你不知道正则表达式的语法,乍一看,它可能看起来非常复杂:

图 5.1 – 正则表达式模式的示例
幸运的是,有一些在线正则表达式可视化工具可以使理解模式变得更加容易(你可以在regexper.com找到其中之一)。例如,图 5.1中高亮显示的正则表达式可以如下可视化:

图 5.2 – 正则表达式的可视化
从图 5.2中,可以直观地推断出图 5.1中的正则表达式将识别文本中的电子邮件地址。
在本节中,我们当然不会深入讲解如何像专业人士一样使用正则表达式,这不是本节的目的。在这里,我们将解释一些基本规则,这将使你能够创建简单而有效的搜索模式。更多细节,请参阅本章末尾的参考文献部分。
正则表达式的基础知识
我们将通过使用示例来尝试解释正则表达式的基本原理,这可能是开始使用它们的直接方式。每个后续的小节将解释正则表达式的一个功能。为了测试我们的正则表达式,我们将使用在www.regexpal.com/提供的工具。让我们开始吧!
文本字符
要在正则表达式中包含一个或多个字面字符,必须使用“搜索”功能。让我们尝试在 May the power of extending Power BI with Python and R be with you! 文本中搜索 owe 字符串:

图 5.3 – 使用正则表达式搜索 "owe"
注意,该工具在默认的搜索中使用了 Global search 标志。特定的标志用字母(在我们的情况下,g)表示,紧随正则表达式分隔符 /.../ 之后。可用的标志如下:
-
g (global):这将匹配所有出现,并保留最后一个匹配的索引。
-
m (multiline):当启用时,字符串锚定符(你稍后会看到它们)将匹配行的开始和结束,而不是整个字符串。
-
i (ignore case):这将忽略字符串的大小写(大写或小写)来搜索模式。
请记住,并非所有编程语言都使用标志语法,如前所述。例如,Python 的 re 包(正则表达式的默认包)在 search、match 和 sub 函数中提供参数:
re.search('test', 'TeSt', re.IGNORECASE)
re.match('test', 'TeSt', re.IGNORECASE)
re.sub('test', 'xxxx', 'TesTing', flags=re.IGNORECASE)
这与 R 的 stringr 包中的 regex() 函数相同:
str_detect('tEsT this', regex('test', ignore_case=TRUE))
你还可以直接在你的正则表达式模式中使用 global modifiers。这些是 (?i) 用于不区分大小写和 (?m) 用于多行。例如,在 R 中,你也可以运行以下脚本:
str_detect('tEsT this', regex("(?i)test"))
注意,Python 不允许使用内联全局修饰符。
特殊字符
正则表达式使用 12 个特殊字符(也称为 元字符),每个都有特殊含义。它们是管道 |;反斜杠 \;美元符号 $;问号 ?;脱字符 ^;星号 *;加号 +;点 .;括号 ( 和 );开方括号 `` 和开花括号 {。
如果你需要搜索之前提到的一个字符,你必须使用反斜杠来转义它。所以,如果你想精确匹配 123$,你需要使用 123\$ 作为正则表达式模式。
接下来,你将学习元字符的含义和使用方法。
^ 和 $ 锚定符
锚定字符是特殊字符,因为它们用于将正则表达式匹配放置在字符串的特定位置。脱字符 ^ 用于指示 字符串的开始(或行),而美元符号 $ 用于指示 字符串的结束(或行)。一个示例可视化胜过千言万语:

图 5.5 – 使用脱字符 ^ 进行不区分大小写的全局搜索
在这种情况下,只有第一次出现(即字符串的开头)的匹配项会被匹配。
如果你也在正则表达式的末尾添加一个美元符号,则不会进行匹配,因为你正在请求匹配以may the power字符串开始并且也结束文本的匹配项。
OR 运算符
你可能需要匹配单个字符集或字符串集。例如,为了匹配ye字符串之后的任何s和t字符,你应该在ye[st]正则表达式内部使用字符类[st]。这样它将匹配yes和yet字符串。字符类还可以用来匹配字符范围内的一个出现,使用连字符-。例如,[0-9]匹配 0 到 9 之间的单个数字,而[A-Z]匹配从 A 到 Z 的单个大写字母。此外,你还可以将多个范围组合成一个字符类。例如,[A-Z0-9]只匹配数字或大写字母。
为了匹配两个字符串中的一个,你可以在括号内使用管道符|来分隔它们,例如(string1|string2)。以下是一个完整的示例:

图 5.6 – OR 运算符的完整示例
字符类还可以用来匹配任何不同于特定字符的字符。这是由于否定字符类的作用。
否定字符类
开方括号后出现的撇号会否定字符类的内容。例如,[^"]正则表达式匹配所有不是双引号的字符。
简写字符类
有些字符类使用得非常频繁。因此,我们定义了一些缩写,以便你能够快速地将它们包含在正则表达式中。以下是最常用的列表:
-
\w: 这会匹配一个字母数字字符,包括下划线。 -
\W: 这是\w的对立面,因此它匹配一个非字母数字字符,不包括下划线。例如,它可以匹配空格和标点符号。 -
\d: 这会匹配一个单个数字。 -
\D: 这是\d的对立面,因此它匹配一个非数字字符。 -
\s: 这会匹配“空白字符”,例如空格、制表符、换行符和回车符。
我们将在本章中相对频繁地使用简写字符类。
量词
量词表示一个字符或表达式必须匹配的次数。以下是最常用的列表:
-
+: 这会匹配其前面的内容一次或多次。例如,test\d+将匹配test字符串后跟一个或多个数字。test(-\d\d)+正则表达式将匹配test字符串后跟一个或多个由连字符和两个数字组成的序列:![图 5.7 – 使用重复一组字符]()
图 5.7 – 使用
+重复一组字符 -
{n}: 这将匹配其前面的内容重复 n 次。例如,\d{4}将匹配由 4 位数字组成的任何整数。 -
{n,m}: 这将匹配其前面的内容,重复 n 到 m 次。例如,prod-\d{2,6}将匹配prod-字符串后跟由 2 到 6 位数字组成的整数。 -
{n,}: 这将匹配 n 次或更多次其前面的内容。 -
?: 这将匹配其前面的一个或零次。例如,Mar(ch)?将匹配March和Mar。或者,colou?red正则表达式将匹配colored和coloured。 -
*: 这将匹配其前面的内容零次或多次。例如,code\d*将匹配code、code1或code173846。
点
点对应于单个字符,无论该字符是什么,除了换行符。这是一个非常强大的正则表达式元字符,并给你一个偷懒的机会。这正是为什么你必须小心不要滥用它,因为有时你可能会在匹配中包含意外结果。
贪婪和懒惰匹配
+、* 和 {…} 的重复是贪婪量词。贪婪意味着它们将消耗尽可能长的字符串。假设你只想匹配 <em>Power BI rocks</em> 字符串中使用的标签。初学者可能会尝试使用 <.+> 正则表达式,用文字表达就是“获取 <,然后获取一个或多个非换行符,最后,在末尾获取 >。”预期的结果是两个匹配项,<em> 和 </em>。让我们看看结果:

图 5.8 – .+ 的贪婪性
很明显,.+ 的组合捕获了从 < 的第一次出现到 > 的最后一次出现之间的所有内容,因此量词的贪婪性得到了定义。
那么,是否可以强制贪婪量词在检测到下一个字符的第一个出现时停止,防止它“吃掉”任何内容,直到相同字符的最后一个出现?换句话说,是否可以将贪婪量词转换为懒惰量词?答案是“可以”,可以通过在 + 后面添加 ? 元字符来实现。因此,<.+> 正则表达式变为 <.+?>。以下是结果:

图 5.9 – 利用 ? 元字符使贪婪量词变得懒惰
然而,请注意,懒惰量词性能较差。只要可能,总是更倾向于使用否定字符类。在我们的例子中,只需使用 <[^>]+> 正则表达式(即一个 < 字符,一个或多个非 > 字符,以及一个 > 字符)就可以达到相同的结果,而不会消耗计算资源:

图 5.9 – 使用否定字符类代替懒惰量词
到目前为止,你关于正则表达式所学的知识,是你理解我们将在接下来的几个例子中使用的更复杂正则表达式的最低基础。
检查电子邮件地址的有效性
如果你被要求使用你刚刚学到的概念来验证电子邮件地址,你的第一次尝试可能看起来像以下这样:^.+@.+\..+$。将这个正则表达式翻译成口语,我们得到以下内容:
-
^: 如果启用了多行标志,则匹配字符串的开始或一行。 -
.+: 匹配任何字符一次或多次,除了换行符。 -
@: 匹配一个 "@" 字符。 -
.+: 匹配任何字符一次或多次,除了换行符。 -
\.: 匹配一个 "." 字符。 -
.+: 匹配任何字符一次或多次,除了换行符。
当然,这个正则表达式将验证正确的电子邮件地址。但你确定它也能检测到明显错误的电子邮件地址的语法错误吗?让我们在 www.regexpal.com/ 上进行测试,使用错误的电子邮件地址 example@example.c(顶级域名,即点之后的部分,必须包含至少两个字符):

图 5.10 – 使用简单的正则表达式验证错误的电子邮件地址
嗯,这并不是什么结果:一个明显错误的电子邮件地址会被错误地认为是正确的。因此,通常需要使用更复杂的正则表达式,这些正则表达式可以尊重定义良好的语法规则。
在这个特定的情况下,我们将使用我们在生产中经常采用的特定正则表达式来验证电子邮件地址。它还考虑了是否使用了域名 IP。为了完整地显示它,正则表达式如下:
^(([^<>()[\]\\.,;:\s@\""]+(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))@((\[?[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\]?)|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
初看之下,这个正则表达式肯定会引起混淆。然而,如果我们尝试将其分解为其基本部分,它就变得容易阅读多了。
然而,如果你认为这个正则表达式真的很复杂,那么请看看考虑了所有(是的,所有)由 Arpa Internet 文本消息格式标准 提供的语法规则的那个正则表达式,你可以在 www.ex-parrot.com/pdw/Mail-RFC822-Address.html 找到它!相当令人印象深刻,不是吗?
电子邮件地址的格式定义为 本地部分@域名。你可以在维基百科上找到完整的规范 en.wikipedia.org/wiki/Email_address。我们将匹配允许我们验证大量不同电子邮件地址的最小规则(不是所有规则!)所以,考虑到我们将匹配域名或域名 IP,正则表达式的一般结构如下:

图 5.11 – 用于电子邮件验证的复杂正则表达式的结构
在 图 5.11 中,{0}、{1} 和 {2} 字符串只是占位符,不是要匹配的字符。话虽如此,让我们首先定义每个标记:
-
{0}符号匹配电子邮件的本地部分正则表达式。在这种情况下,使用图表比用文字解释本地部分正则表达式要容易得多。在 图 5.12 中,标签解释了每个子部分的每个细节:![图 5.12 – 详细解释的本地部分正则表达式]()
图 5.12 – 详细解释的本地部分正则表达式
请记住,括号将正则表达式模式组合在一起。括号允许你将正则表达式运算符应用于整个分组表达式,并将匹配的一部分作为结果数组中的单独项获取。与正则表达式表达式对应的文本被捕获在其中作为编号组,并且可以使用编号反向引用来重用。你将在以后学习如何使用此功能。记住,为了使元字符的匹配成为一个真实字符,必须在它之前放置转义反斜杠。所以,例如,
\]将是]字符。 -
{1}符号匹配电子邮件的域名。同样,我们将使用图表来解释域名正则表达式的作用:![图 5.13 – 详细解释的域名正则表达式]()
图 5.13 – 详细解释的域名正则表达式
-
{2}符号匹配电子邮件的域名 IP。这是较简单的子正则表达式,你可以通过查看 图 5.14 来了解它匹配的内容:

图 5.14 – 详细解释的域名 IP 正则表达式
如果你想要查看整个正则表达式的可视化,请参考以下链接,它来自 jex.im/regulex:

图 5.14 – 整个电子邮件正则表达式的可视化
现在,让我们继续验证另一种重要类型的信息的验证,这种信息经常受到输入错误的影响:日期。
检查日期的有效性
即使在日期的情况下,无知的正则表达式开发者可能会认为以下正则表达式足以验证日期(格式为 dd-mm-yyyy):^\d{1,2}([\-\/])\d{1,2}\1(?:\d{4}|\d{2})$。有两个新的表达式,以前从未遇到过,值得探索:
-
\1: 这是对组 1 的反向引用。正如我们在上一节中解释的那样,括号还可以帮助捕获字符串的一部分,你可以在正则表达式的其余部分中引用它。在这种情况下,\1语法表示,在确切的位置,你可以期望与第一对括号匹配的相同字符串部分。请注意,在 Python 中,你需要使用\g<1>语法而不是\1。 -
(?: … ):这是所谓的非捕获组。有时,你需要括号来正确应用量词,但你不想在结果中报告其内容。
将整个正则表达式翻译成口语,我们得到以下内容:
-
^:这匹配字符串的开始或一行,如果启用了多行标志。 -
\d{1,2}:这匹配 1 到 2 次重复的任何数字。 -
([\-\/]):这匹配位于-和/之间的任何字符,并将结果捕获为组 1。 -
\d{1,2}:这匹配 1 到 2 次重复的任何数字。 -
\1:这指的是捕获组 1。因此,它期望任何位于-和/之间的字符。 -
.+:这匹配一个或多个字符,除了换行符。 -
(?:\d{4}|\d{2}):这匹配以下两种选择之一:恰好 4 次重复的任何数字和恰好 2 次重复的任何数字。
你可以这样可视化整个正则表达式:

图 5.15 – 验证日期的正则表达式的第一次尝试的可视化
如你所猜,这个正则表达式验证以下格式的日期:dd-mm-yyyy、dd-mm-yy、d-m-yyyy 和 d-m-yy,使用 / 或 - 作为分隔符。然而,它不考虑由于无效日期(如 2 月 30 日或 9 月 31 日)引起的错误。
如果你想要一个也考虑这些错误的正则表达式,那么你必须使用以下:
^(?:(?:31\-\/|(1[02]))\-\/?\d\d)|(?:(?:29|30)\-\/|(?:1[0-2]))\-\/?\d\d)|(?:29[\-\/]0?2\-\/(?:(?:[02468][048])|(?:[13579][26])))|(?:(?:(?:0?[1-9])|(?:1\d)|(?:2[0-8]))\-\/|(?:1[0-2]))\-\/?\d\d))$
再次,以这种方式查看,这个正则表达式很难解释。然而,如果你稍微“从上面”看,你会意识到它由四个选择项组成:

图 5.16 – 复杂日期验证正则表达式的结构
此外,在这种情况下,{0}, {1}, {2}, 和 {3} 字符串只是占位符,不是用于匹配的字符。话虽如此,让我们首先定义 {0} 令牌。
{0} 令牌匹配具有 31 日的日期。与之前的案例一样,用可视化来解释这个正则表达式比用文字来说明要容易得多。在 图 5.17 中,标签解释了每个子部分的每一个细节:

图 5.17 – 详细解释具有 31 日的日期的正则表达式部分
用于其他占位符的正则表达式与我们刚才解释的非常相似。因此,我们将把其他正则表达式的解释留给读者作为练习。如果你想看到整个正则表达式的可视化,请参考以下图表,该图表来自 jex.im/regulex:

图 5.18 – 整个日期正则表达式的可视化
我们刚才检查的正则表达式允许验证 dd-mm-yyyy 格式及其所有变体。在代码中,我们将演示如何在 Power BI 中实现日期验证。此外,你还将找到允许你验证 mm-dd-yyyy 和 yyyy-mm-dd 格式及其所有变体的正则表达式(其中年份由两位数字组成,月份由一位数字组成,等等)。
现在你已经理解了之前展示的复杂正则表达式的原理,让我们继续在 Power BI 中实现它们以验证你的数据。
在 Power BI 中使用正则表达式验证数据
到目前为止,Power BI 在 Power Query 中没有原生的功能来执行正则表达式操作。有些情况下,你无法避免使用正则表达式从文本形式的数据中提取有用信息。能够使用正则表达式的唯一方法是使用 R 脚本或 Python 脚本。在这种情况下,你唯一的缺点是,如果你需要将报告发布到 Power BI 服务,为了让 Power Query 能够使用外部 R 或 Python 引擎,你必须也以个人模式安装本地数据网关。
然而,让我们直接进入现实世界的例子。
假设你在一个零售公司工作,公司里有一个团队专门负责识别欺诈客户。一旦团队成员识别出一名欺诈者,他们就会填写一个 Excel 表格,其中包含 Email 和 BannedDate 列以及其他列。你的任务是把这个 Excel 文件中的数据加载到 Power BI 中,并从其他数据源中选择欺诈者的信息,以便对这些购买进行特定分析。
在 Excel 文件中拥有正确的欺诈者电子邮件对于能够正确地与其他数据连接至关重要。拥有正确的禁用日期也很重要,以便知道在那之后该欺诈者是否有进一步的订单漏网。正如你所知,多个用户填写 Excel 文件时没有任何数据验证;因此,它容易受到人为错误的影响。因此,在填写某些字段时识别任何错误并突出显示它们,可以让欺诈团队能够纠正它们。正是在这种情况下,正则表达式才会对你有所帮助。
在 Power BI 中使用 Python 使用正则表达式验证电子邮件
在本书附带的重构库中,你可以在 Chapter05 文件夹中找到 Users.xlsx Excel 文件。其内容类似于 图 5.15:

图 5.19 – Users.xlsx 文件的内容
在本节中,我们将专注于电子邮件列。这包含欺诈团队手动输入的欺诈者的电子邮件地址,这在节的开头已经描述过。这些电子邮件地址并非都是语法正确的。此外,在 Excel 文件中,还有一个名为 IsEmailValidByDefinition 的列,其值(1=是;0=否)表示对应值的电子邮件实际上是否有效。
Python 有一个内置的包,称为 re,其中包含你处理正则表达式所需的所有函数。此外,在 pandas 中,有几个用于序列或数据框对象的方法,这些方法接受正则表达式以在字符串中查找模式。这些方法的工作方式与你在 Python 的 re 模块中找到的方法相同。我们很快将使用 match 方法。
你将学习如何使用 r'...' 语法来创建字符串。这是一个原始字符串,允许你将反斜杠(\)视为一个字面字符,而不是一个转义字符。
因此,打开你的 Power BI Desktop,确保你使用的 Python 环境是 pbi_powerquery_env,然后让我们开始吧:
-
从功能区点击Excel图标以从 Excel 导入数据:
![图 5.20 – 从 Excel 导入数据]()
图 5.20 – 从 Excel 导入数据
-
从打开对话框中,选择之前提到的
Users.xlsx文件。 -
从导航器窗口中,选择用户表,然后点击转换数据:
![图 5.21 – 选择用户表并点击转换数据]()
图 5.21 – 选择用户表并点击转换数据
-
点击转换菜单,然后点击运行 Python 脚本。
-
然后,将以下代码复制并粘贴到 Python 脚本编辑器中,并点击确定:
import pandas as pd import re df = dataset regex_local_part = r'([^<>()[\]\\.,;:\s@\""]+(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\"")' regex_domain_name = r'(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,})' regex_domain_ip_address = r'(\[?[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\]?)' pattern = r'^({0})@({1}|{2})$'.format(regex_local_part, regex_domain_name, regex_domain_ip_address) df['isEmailValidFromRegex'] = df['Email'].str.match(pattern).astype(int)你也可以在书附带仓库的
Chapter05\validating-data-using-regex文件夹中的01-validate-emails-with-regex-with-python.py文件中找到这个 Python 脚本。 -
Power BI Desktop 可能会显示一个如下所示的警告:需要提供有关数据隐私的信息。如果是这样,请点击继续,并遵循步骤 2;否则,你可以跳到步骤 3。
-
隐私级别窗口弹出。在这里,您将指定一个隔离级别,该级别定义了一个数据源将与其他数据源隔离的程度。您可以选择忽略隐私级别检查,但这可能会使机密数据暴露给未经授权的人员。您将被要求为 Python 脚本和从 Excel 加载的数据集选择隐私级别。如果您为两者都选择组织级别,Power BI 桌面上的所有操作都正常。然而,如果您计划将报告发布到Power BI 服务(或嵌入式),您必须使用“公共”级别。有关更多详细信息,请参阅
bit.ly/pbi-privacy-levels。目前,为这两个选项选择组织级别。 -
目前,我们只对
df数据集感兴趣。因此,点击其表值:![图 5.22 – 选择 Python 脚本转换后的 df 数据集]()
图 5.22 – 选择 Python 脚本转换后的 df 数据集
-
如您所见,新增了isEmailValidFromRegex列,它包含通过正则表达式验证电子邮件得到的布尔值。如果您进行检查,您会发现它们与IsEmailValidByDefinition列中定义的值一致:
![图 5.23 – 电子邮件正则表达式验证结果]()
图 5.23 – 电子邮件正则表达式验证结果
您的正则表达式做得很好!现在您可以回到主页菜单并点击关闭并应用。
多亏了isEmailValidFromRegex列,您现在可以适当地过滤正确的和错误的电子邮件地址,也许甚至可以将问题报告给专门的欺诈团队。
在 Power BI 中使用正则表达式和 R 验证电子邮件
如果您想使用 R 通过正则表达式进行电子邮件验证,过程基本上相同,只是有几处不同。
首先,从 4.0.0 版本开始,R 只允许使用原始字符串。此外,原始字符串的语法略有不同。您可以使用r'(...)'、r'[...]'或r'{...}'来无差别地使用,而不是r'...'。另外,与 Python 中在字符串内部的括号中使用数字占位符并通过format()函数分配不同,在 R 中,您可以直接在括号中使用变量名作为占位符。
话虽如此,您需要注意的第二件事是:在 R 中,不仅]被视为元字符,``也是。因此,当您想使用方括号作为普通字符时,您必须为两者都添加反斜杠转义字符(\)。因此,正则表达式中识别电子邮件本地部分正则表达式中的字符类的部分略有不同:
![图 5.24 – 在 R 中将开方括号作为普通字符使用时必须进行转义
图 5.24 – 在 R 中,当开方括号是字面字符时,必须对其进行转义
R:Base 提供了两个函数,使您能够使用正则表达式:grep() 和 grepl():
-
grepl()根据模式是否存在于字符字符串中返回一个布尔值。 -
grep()返回包含匹配或特定字符串匹配的字符向量中的索引。
由于我们想采用 Tidyverse 模式,我们将使用 stringr 包提供的包装函数,分别是 str_detect() 和 str_which()。
明确了这些差异后,使用 R 脚本在 Power BI 中验证 Users.xlsx Excel 文件中存在的电子邮件的过程实际上与上一节中我们使用 Python 讨论的过程相同:
-
重复上一节中的 步骤 1 到 3 以导入
Users.xlsx文件中的数据。 -
点击 变换 菜单,然后点击 运行 R 脚本。
-
然后,将以下代码复制并粘贴到 R 脚本编辑器中,并点击 确定:
library(dplyr) library(stringr) regex_local_part <- r'(([^<>()\[\]\\.,;:\s@\"]+(\.[^<>()\[\]\\.,;:\s@\"]+)*)|(\".+\"))' regex_domain_name <- r'((([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))' regex_domain_ip_address <- r'((\[?[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\]?))' pattern <- str_glue( '^({regex_local_part})@({regex_domain_name}|{regex_domain_ip_address})$' ) df <- df %>% mutate( isEmailValidFromRegex = as.integer(str_detect(Email, pattern)) )您还可以在随书提供的
02-validate-emails-with-regex-with-r.R文件中找到此 R 脚本,该文件位于Chapter05\validating-data-using-regex文件夹中。 -
Power BI Desktop 可能会显示一条通知,内容如下:需要提供关于数据隐私的信息。如果是这样,请点击 继续,并遵循此处说明。否则,您可以跳转到 步骤 5。此外,选择 R 脚本的 组织 级别。有时,您可能会发现数据集和分析脚本的兼容级别不兼容。在这种情况下,Power BI 可能会向您发出警报,例如 公式.防火墙:查询 'XXX'(步骤 'YYY')正在访问具有无法一起使用的隐私级别的数据源。请重新构建此数据组合。在这种情况下,只需按照以下方式打开 数据源设置 窗口:
![图 5.25 – 打开 Power Query 数据源设置窗口]()
图 5.25 – 打开 Power Query 数据源设置窗口
之后,您必须确保所有数据源具有相同的隐私级别(在我们的案例中为 组织),如果需要,通过 编辑权限… 选项为每个数据源更改它:
![图 5.26 – 编辑数据源的隐私权限]()
图 5.26 – 编辑数据源的隐私权限
到目前为止,您可以通过点击 刷新预览 来刷新预览数据。
-
此外,在这种情况下,我们只对
df数据集感兴趣。因此,点击其 表 值:![图 5.27 – 选择 R 脚本转换后的 df 数据集]()
图 5.27 – 选择 R 脚本转换后的 df 数据集
-
如您所见,已添加了
isEmailValidFromRegex列,它包含通过您的正则表达式验证电子邮件得到的布尔值(转换为 1 和 0)。如果您进行检查,它们将与IsEmailValidByDefinition列中定义的值相符。您正则表达式做得非常出色!现在您可以返回到主页菜单,然后点击关闭并应用。
多亏了isEmailValidFromRegex列,您现在可以适当地过滤报告中的正确和错误电子邮件地址。
现在,让我们看看如何在 Power BI 中使用 Python 验证日期。
使用 Python 在 Power BI 中验证日期的正则表达式
作为验证日期的示例,我们将使用之前使用的Users.xlsx Excel 文件。它包含一个名为BannedDate的列,该列包含表示mm/dd/yyyy格式的日期的字符串值,包括所有变体。此外,在 Excel 文件中,还有一个名为IsDateValidByDefinition的列,其值(1=是;0=否)指示匹配该值的日期是否有效。
到目前为止,您已经知道了使用正则表达式所需的 Python 函数。那么,让我们开始吧:
-
重复使用 Python 在 Power BI 中验证电子邮件的步骤 1 到 3部分,以导入
Users.xlsx文件中的数据。 -
点击转换菜单,然后点击运行 Python 脚本。
-
然后,将以下代码复制并粘贴到 Python 脚本编辑器中,然后点击确定:
import pandas as pd import re df = dataset regex_dates_having_day_31 = r'(?:(?:(?:0?[13578])|(?:1[02]))[\-\/]31\-\/?\d\d)' regex_non_leap_dates_having_days_29_30 = r'(?:(?:(?:0?[13-9])|(?:1[0-2]))\-\/\-\/?\d\d)' regex_leap_dates_having_day_29 = r'(?:0?2[\-\/]29\-\/?(?:(?:[02468][048])|(?:[13579][26])))' regex_remaining_dates = r'(?:(?:(?:0?[1-9])|(?:1[0-2]))\-\/|(?:0?[1-9])|(?:2[0-8]))\-\/?\d\d)' pattern = r'^(?:{0}|{1}|{2}|{3})$'.format(regex_dates_having_day_31, regex_non_leap_dates_having_days_29_30, regex_leap_dates_having_day_29, regex_remaining_dates) df['isValidDateFromRegex'] = df['BannedDate'].str.match(pattern).astype(int)您可以在与本书一起提供的存储库中的
Chapter05\validating-data-using-regex文件夹中找到的03-validate-dates-with-regex-with-python.py文件中找到一个更全面的 Python 脚本。该脚本处理mm-dd-yyyy、dd-mm-yyyy和yyyy-mm-dd格式的日期,包括所有变体,包括-和/作为分隔符。 -
如果 Power BI 要求您提供数据隐私信息,您已经知道如何根据我们之前讨论的内容进行操作。
-
如您所见,已添加了
isValidDateFromRegex列,它包含通过您的正则表达式验证电子邮件得到的布尔值。如果您进行检查,它们将与IsDateValidByDefinition列中定义的值相符:![图 5.28 – 日期的正则表达式验证结果]()
图 5.28 – 日期的正则表达式验证结果
您的正则表达式做得非常出色!现在您可以返回到主页菜单并点击关闭并应用。
多亏了isValidDateFromRegex列,您现在可以过滤正确和错误的电子邮件地址,并适当地处理它们。
使用 R 在 Power BI 中验证日期的正则表达式
如果你想要使用 R 语言通过正则表达式进行日期验证,在这种情况下,过程基本上与你在 使用 R 语言在 Power BI 中验证电子邮件的正则表达式 部分中学到的相同,只是有所不同。从我们在上一节中使用的相同的 Users.xlsx Excel 文件开始,以下是需要遵循的步骤:
-
重复 使用 Python 在 Power BI 中验证电子邮件的正则表达式 部分的 步骤 1 到 3 以导入
Users.xlsx文件中的数据。 -
点击 转换 菜单,然后点击 运行 R 脚本。
-
然后,将以下代码复制并粘贴到 R 脚本编辑器中,并点击 确定:
library(dplyr) library(stringr) df <- dataset regex_dates_having_day_31 <- r'((?:(?:(?:0?[13578])|(?:1[02]))[\-\/]31\-\/?\d\d))' regex_non_leap_dates_having_days_29_30 <- r'((?:(?:(?:0?[13-9])|(?:1[0-2]))\-\/\-\/?\d\d))' regex_leap_dates_having_day_29 <- r'((?:0?2[\-\/]29\-\/?(?:(?:[02468][048])|(?:[13579][26]))))' regex_remaining_dates <- r'((?:(?:(?:0?[1-9])|(?:1[0-2]))\-\/|(?:0?[1-9])|(?:2[0-8]))\-\/?\d\d))' pattern <- str_glue( '^(?:{regex_dates_having_day_31}|{regex_non_leap_dates_having_days_29_30}|{regex_leap_dates_having_day_29}|{regex_remaining_dates})$' ) df <- df %>% mutate( isDateValidFromRegex = as.integer(str_detect(BannedDate, pattern)) )你可以在与本书一起提供的存储库中的
Chapter05\validating-data-using-regex文件夹中找到更详尽的 R 脚本04-validate-dates-with-regex-with-r.R文件。该脚本处理mm-dd-yyyy、dd-mm-yyyy和yyyy-mm-dd格式的日期,包括所有变体,包括-和/作为分隔符。 -
如果 Power BI 需要你提供数据隐私信息,你根据我们在前几节中讨论的内容已经知道如何操作。
-
如你所见,已添加了 isValidDateFromRegex 列,它包含通过你的正则表达式验证电子邮件产生的布尔值。如果你进行检查,它们与 IsDateValidByDefinition 列中定义的值相一致。你的正则表达式做得很好!现在你可以回到 主页 菜单并点击 关闭并应用。
多亏了 isDateValidFromRegex 列,你现在可以适当地过滤报告中的正确和错误日期。
在下一节中,你将学习如何使用 Python 和 R 语言导入半结构化日志文件的内容。
使用正则表达式在 Power BI 中加载复杂日志文件
日志文件是开发人员和计算机系统管理员非常有用的工具。它们记录了系统发生的事情、何时发生以及哪个用户实际生成了事件。多亏了这些文件,你可以找到有关任何系统故障的信息,从而允许更快地诊断这些故障的原因。
日志通常是 半结构化数据,即不能以生成时的格式持久化在关系数据库中的信息。为了能够使用常规工具进行分析,首先,必须将此数据转换为更合适的格式。
由于它们不是结构化数据,直接导入 Power BI 是困难的,除非有人开发了专门的连接器来处理这种情况。在这些场景中,使用 Python 或 R 等语言中的正则表达式可以帮助我们获得所需的结果。
Apache 访问日志
假设你的公司通过 Apache 网络服务器发布了一个网站。你的经理要求你进行一项分析,以确定网站上哪些网页被点击得最多。获取这些信息的唯一方法是分析 访问日志文件。该文件记录了所有对网络服务器的请求。以下是一个 Apache 访问日志的示例:

图 5.29 – Apache 访问日志的一个示例
如你所见,乍一看,这个日志中的信息结构相当有组织。如果没有人定制 Apache 日志文件的输出,它默认使用 通用日志格式(CLF)。你可以在与本书一起提供的存储库中的 apache_logs.txt 文件中找到一个 Apache 访问日志的真实示例,该文件位于 Chapter05\loading-complex-log-files-using-regex 文件夹中。我们在 GitHub 存储库中找到了它,bit.ly/apache-access-log(点击 Download 查看它)。
如果你继续阅读那些日志文件的文档,你会推断出访问日志中记录的信息遵循 NCSA 扩展/组合日志格式。因此,记录的数据如下:
-
远程主机名(IP 地址)。
-
远程日志名(如果为空,你会看到一个破折号;在样本文件中未使用)。
-
如果请求被认证的远程用户(如果为空,你会看到一个破折号)。
-
请求接收的日期时间,格式为
[18/Sep/2011:19:18:28 -0400]。 -
请求服务器之间的双引号中的第一行请求。
-
请求的 HTTP 状态码。
-
响应的大小(以字节为单位),不包括 HTTP 头部(可能是一个破折号)。
-
RefererHTTP 请求头部,其中包含发起请求的页面的绝对或部分地址。 -
User-AgentHTTP 请求头部,其中包含一个字符串,用于标识请求用户代理的应用程序、操作系统、供应商和/或版本。
一旦你知道日志中写入信息的性质以及它的形式,你就可以利用正则表达式提供的强大工具来更好地结构化这些信息并将其导入 Power BI。
使用 Python 将 Apache 访问日志导入 Power BI
如前所述,你可以在与本书一起提供的存储库中的 apache_logs.txt 文件中找到一个 Apache 访问日志的真实示例,该文件位于 Chapter05\loading-complex-log-files-using-regex 文件夹中。你将使用 Python 脚本来加载这个文件中的信息,而不是使用 Power BI 连接器。
与你之前关于正则表达式和 Python 的学习相比,在 01-apache-access-log-parser-python.py Python 脚本(你可以在前面的文件夹中找到)中,你会遇到这些新的结构:
- 要在 Python 中逐行读取文本文件,你将使用
open(file, mode)函数和readlines()方法。具体来说,你将按顺序读取apache_logs.txt文件,将其作为只读文件('r')读取,并将每一行存储在列表中。
在正则表达式中,除了可以通过数字索引引用由圆括号标识的组之外,还可以通过名称来引用。这要归功于命名捕获组。通常,用于给组分配名称的正则表达式语法是 (?<group-name>...)。在 Python 中是 (?P<group-name>...):
-
在 Python 中,你可以定义一个可以合并在一起的正则表达式部分列表(使用分隔符
join),分隔符由正则表达式本身定义(\s+):regex_parts = [ r'(?P<hostName>\S+)', r'\S+', r'(?P<userName>\S+)', r'\[(?P<requestDateTime>[\w:/]+\s[+\-]\d{4})\]', r'"(?P<requestContent>\S+\s?\S+?\s?\S+?)"', r'(?P<requestStatus>\d{3}|-)', r'(?P<responseSizeBytes>\d+|-)', r'"(?P<requestReferrer>[^"]*)"', r'"(?P<requestAgent>[^"]*)?"', ] pattern = re.compile(r'\s+'.join(regex_parts) + r'$')注意,在这种情况下,使用了
re.compile()函数,因为必须在日志的所有行上多次进行匹配;因此,预编译正则表达式可能具有计算优势。 -
对日志中的每一行都进行模式匹配:
for line in access_log_lines: log_data.append(pattern.match(line).groupdict())groupdict()方法返回一个字典,其中以组名为键,以匹配字符串为值。每行的所有字典都附加到log_data列表中。
我们将如何解释每个正则表达式部分如何捕获所需字符串的细节留给读者去理解。
现在我们已经澄清了代码中的几个要点,让我们将日志导入 Power BI:
-
在 Power BI Desktop 中,务必使用
pbi_powerquery_env环境。 -
前往 获取数据 并选择 Python 脚本。
-
将
01-apache-access-log-parser-python.py文件中的脚本复制并粘贴到 Python 脚本编辑器中,然后点击 确定。 -
然后,从 导航器 窗口中选择 df 数据框,并点击 加载:
![图 5.30 – 选择由 Python 脚本返回的 df 数据框]()
图 5.30 – 选择由 Python 脚本返回的 df 数据框
-
如果你点击 数据 图标,你可以查看作为结构化表格加载的整个日志:

图 5.31 – 使用 Python 在 Power BI 中加载 Apache 访问日志
太棒了!多亏了正则表达式的强大功能,你刚刚轻松地将看似复杂的日志文件导入到 Power BI 中。
使用 R 将 Apache 访问日志导入 Power BI
在本节中,你将使用 R 脚本来加载 apache_logs.txt 文件的信息。
与你之前在 R 中学习的关于正则表达式的知识相比,在 02-apache-access-log-parser-r.R 脚本(你可以在同一前一个文件夹中找到)中,你会遇到这些新的结构:
-
要在 R 中逐行读取文本文件,你将使用
read_lines()函数从readr包中。具体来说,你将按顺序读取apache_logs.txt文件的每一行,以便将它们持久化到向量中。 -
为了充分利用 R 中的命名捕获组,你需要安装并使用一个名为 namedCapture 的新包的功能。多亏了这个包,命名组的正则表达式语法都允许:标准的
(?<group-name>…)正则表达式语法和(?P<group-name>…)正则表达式语法。 -
就像我们在 Python 脚本中所做的那样,在 R 中,你也将定义一个正则表达式部分的向量,然后使用
paste(..., collapse = '...')函数将其合并。这个函数的任务是通过\s+分隔符将正则表达式部分连接起来。合并所有部分后,使用paste0(…)函数将$字符添加到结果字符串的末尾。记住,R 中的原始字符串语法与 Python 不同。在这种情况下,我们将使用r'{...}'语法:regex_parts <- c( r'{(?P<hostName>\S+)}' , r'{\S+}' , r'{(?P<userName>\S+)}' , r'{\[(?P<requestDateTime>[\w:/]+\s[+\-]\d{4})\]}' , r'{"(?P<requestContent>\S+\s?\S+?\s?\S+?)"}' , r'{(?P<requestStatus>\d{3}|-)}' , r'{(?P<responseSizeBytes>\d+|-)}' , r'{"(?P<requestReferrer>[^"]*)"}' , r'{"(?P<requestAgent>[^"]*)?"}' ) pattern <- paste0( paste(regex_parts, collapse = r'{\s+}'), '$' ) -
使用
namedCapture包的str_match_named()函数在整个日志向量上执行模式匹配,通过一条单行命令:df <- as.data.frame( str_match_named( access_log_lines, pattern = pattern ) )
同样,我们将解释每个正则表达式部分如何捕获所需字符串的任务留给读者。
现在我们已经澄清了代码中的几个要点,让我们将日志导入 Power BI:
-
首先,你需要安装
namedCapture包。因此,打开 RStudio 并确保在 全局选项 中引用的是最新版本。然后,在新的脚本中运行以下代码以临时将 CRAN 设置为下载包的存储库:local({ r <- getOption("repos") r["CRAN"] <- "https://cloud.r-project.org/" options(repos = r) })所有这些都是为了下载
namedCapture包的最新版本。 -
现在,转到控制台并输入并运行以下代码:
install.packages("namedCapture") -
打开 Power BI Desktop,转到 获取数据,并选择 R 脚本。
-
将
02-apache-access-log-parser-r.R文件中的脚本复制并粘贴到 R 脚本编辑器中,然后点击 确定。 -
然后,从 导航器 窗口中选择 df 数据框,并点击 加载:
![图 5.32 – 选择由 R 脚本返回的 df 数据框]()
图 5.32 – 选择由 R 脚本返回的 df 数据框
-
如果你点击 数据 图标,你可以查看加载为结构化表的整个日志:

图 5.33 – 使用 R 在 Power BI 中加载的 Apache 访问日志
干得好!你甚至使用 R 成功地将半结构化日志文件导入 Power BI。
在 Power BI 中使用正则表达式从文本中提取值
我们想要展示的最后一种用例在处理向客户发货时非常常见。有时,会发生欺诈者设法窃取寄给客户的货物的情况;因此,公司必须向客户退款。被欺诈的客户随后联系客户服务部要求退款。如果提供给客户服务操作员的管理系统不允许以结构化的方式输入退款信息,操作员必须求助于唯一可能的方法:与订单关联的自由文本备注,其中指定了金额、原因和退款日期。
你已经知道,以自由文本形式输入的信息是每位分析师的噩梦,尤其是当你的老板要求你分析这些臭名昭著的备注中的信息时。
在本书附带的重构库中,你可以在Chapter05文件夹中找到OrderNotes.xlsx Excel 文件。其内容与图 5.34 所示的内容类似:

图 5.34 – 操作员为某些订单输入的自由文本备注
如你所见,通过查看 Excel 文件的内容,从备注中需要提取的相关信息如下:
-
退款金额
-
退款原因
-
退款日期
问题在于客户服务操作员在输入这些信息时使用了大量的想象力,而没有一丝预先设定的结构化规则。从这一点我们可以看出以下情况:
-
退款金额被输入为EUR xx.yy、EURxx.yy、xx.yy EUR、€ xx.yy、xx.yy€和xx.yy €。
-
所有信息片段之间的“分隔符”可以由一个或多个空格或由一个或多个空格包围的破折号组成。
-
退款日期始终以
dd/mm/yyyy格式(你很幸运!)。 -
退款原因可以包含任何文本。
考虑到输入备注的这种普遍性,你是否能够正确提取老板要求分析所需的信息?如果你知道如何最好地使用正则表达式,答案无疑是“是的”。
一个正则表达式统治一切
在前几节中积累的经验将使你立即理解我们将要提出的解决方案。考虑以下正则表达式部分:
-
货币:
(?:EUR|€) -
分隔符:
(?:\s+)?-?(?:\s+)? -
退款金额:
\d{1,}\.?\d{0,2} -
退款原因:
.*? -
退款日期:
\d{2}[\-\/]\d{2}[\-\/]\d{4}
记住非捕获组的语法 (?:…)?。使用这种语法,你明确告诉正则表达式引擎你不想捕获括号内的内容,因为这些内容不是提取的重要信息。话虽如此,最终的正则表达式不过是这些部分的多个替代组合,就像你在图 5.35 中看到的那样:

图 5.35 – 提取笔记信息的完整正则表达式结构
如果你想查看完整的正则表达式,最终的完整正则表达式如下:
^(?:(?:(?:EUR|€)(?:\s+)?-?(?:\s+)?(?P<RefundAmount>\d{1,}\.?\d{0,2})(?:\s+)?-?(?:\s+)?(?P<RefundReason>.*?)(?:\s+)?-?(?:\s+)?(?P<RefundDate>\d{2}[\-\/]\d{2}[\-\/]\d{4})(?:\s+)?-?(?:\s+)?)|(?:(?P<RefundAmount>\d{1,}\.?\d{0,2})(?:\s+)?-?(?:\s+)?(?:EUR|€)(?:\s+)?-?(?:\s+)?(?P<RefundReason>.*?)(?:\s+)?-?(?:\s+)?(?P<RefundDate>\d{2}[\-\/]\d{2}[\-\/]\d{4})(?:\s+)?-?(?:\s+)?)|(?:(?P<RefundDate>\d{2}[\-\/]\d{2}[\-\/]\d{4})(?:\s+)?-?(?:\s+)?(?:EUR|€)(?:\s+)?-?(?:\s+)?(?P<RefundAmount>\d{1,}\.?\d{0,2})(?:\s+)?-?(?:\s+)?(?P<RefundReason>.*?)(?:\s+)?-?(?:\s+)?)|(?:(?P<RefundDate>\d{2}[\-\/]\d{2}[\-\/]\d{4})(?:\s+)?-?(?:\s+)?(?P<RefundAmount>\d{1,}\.?\d{0,2})(?:\s+)?-?(?:\s+)?(?:EUR|€)(?:\s+)?-?(?:\s+)?(?P<RefundReason>.*?)(?:\s+)?-?(?:\s+)?))$
让我们在 Power BI 中使用 Python 实现它。
使用 Python 在 Power BI 中提取值
如您在图 5.35中看到的,我们的正则表达式包含被多次重复使用的命名组。不幸的是,Python re模块不支持在正则表达式中重复使用相同的命名组,顺便说一下,这个模块也是pandas背后使用的模块。为了使用更高级的正则表达式功能,例如之前提到的同名组或向后查找和向前查找语法(这些内容在本章中未探讨),您必须使用regex模块。因此,首先,您必须在您的pbi_powerquery_env环境中安装它。然后,您必须将位于Chapter05文件夹中的OrderNotes.xlsx Excel 文件加载到 Power BI Desktop 中。之后,您可以使用 Python 脚本转换该数据集。那么,让我们开始吧:
-
打开您的 Anaconda Prompt,使用
conda activate pbi_powerquery_env命令切换到您的pbi_powerquery_env环境,然后使用以下代码安装regex包:pip install regex。 -
打开您的 Power BI Desktop,确保在选项中引用的 Python 环境是
pbi_powerquery_env。 -
从功能区点击Excel图标,从 Excel 导入数据并打开
OrderNotes.xlsx文件。 -
从导航器窗口中选择Sheet1数据集,并点击转换数据:
![图 5.36 – 从 Excel 加载订单笔记并转换数据]()
图 5.36 – 从 Excel 加载订单笔记并转换数据
-
通过点击使用第一行作为标题,将加载的数据的第一行声明为列标题:
![图 5.37 – 使用第一行作为标题的按钮]()
图 5.37 – 使用第一行作为标题的按钮
-
然后,转到转换菜单并点击运行 Python 脚本。
-
打开位于
Chapter05\extracting-values-from-text-using-regex文件夹中的01-order-notes-parser-python.py文件中的 Python 脚本,将其复制并粘贴到运行 Python 脚本编辑器中,然后点击确定。 -
如果数据集的兼容性级别存在问题,只需打开数据源设置窗口,使用编辑权限…将每个数据集的权限设置为组织。然后,点击刷新预览。
-
我们只对
df数据集感兴趣。因此,点击其表值:![图 5.38 – 选择 Python 脚本转换后的 df 数据集]()
图 5.38 – 选择 Python 脚本转换后的 df 数据集
-
你可以看到,结果表多了三列,每一列都与一个命名组相关:
![图 5.39 – 使用 Python 和正则表达式从自由笔记中提取的值]()
图 5.39 – 使用 Python 和正则表达式从自由笔记中提取的值
最后,转到 主页 菜单并点击 关闭并应用。
太棒了!你刚刚成功地使用 Python 中的正则表达式重新组织了订单笔记中的数据。你的老板一定会非常满意!
使用 R 在 Power BI 中通过正则表达式提取值
在 R 中,我们仍然可以使用 namedCapture 包来管理在同一个正则表达式中多次重复使用的命名组。我们可以通过在其前面放置 (?J) 修饰符来实现这一点(这允许多个命名捕获组共享相同的名称)。与 Python 不同,在 R 中,namedCapture 包的 str_match_named() 函数不会返回由命名组捕获的一次性结果。它返回的列数与它被使用的次数一样多:

图 5.40 – 返回与命名组使用次数相同的列数
因此,我们不得不进一步处理结果;首先,通过将空字符替换为 NA 的空值,其次,通过应用 dplyr 的 coalesce() 函数,该函数通过保留非空值将多个列合并为一列。
重要提示
我们将这个限制指出了
namedCapture包的作者 Toby Dylan Hocking,他最近在包的新版本中实现了这个功能,名为nc。你可以在github.com/tdhock/namedCapture/issues/15找到实现的详细信息。在撰写本文时,nc包的新版本尚未发布到 CRAN。因此,我们认为在代码中保持使用namedCapture包是合适的。然而,你可以自由地在你未来的项目中采用新的nc包。
话虽如此,让我们开始使用 R 在 Power BI 中从订单笔记中提取值:
-
打开你的 Power BI Desktop,并确保引用的 R 引擎是最新版本(在我们的例子中,这是 MRO 4.0.2)。
-
从功能区点击 Excel 图标以从 Excel 导入数据,并打开位于
Chapter05文件夹中的OrderNotes.xlsx文件。 -
从 导航器 窗口中选择 Sheet1 数据集,并点击 转换数据:
![图 5.41 – 从 Excel 加载订单笔记并转换数据]()
图 5.41 – 从 Excel 加载订单笔记并转换数据
-
通过点击 使用第一行作为标题 将加载数据的第 一行声明为列标题。
-
然后,转到 转换 菜单并点击 运行 R 脚本。
-
打开位于
Chapter05\extracting-values-from-text-using-regex文件夹中的02-order-notes-parser-r.R文件中的 R 脚本,将其复制并粘贴到运行 R 脚本编辑器中,然后点击确定。 -
我们只对
df数据集感兴趣。因此,点击其表值:![图 5.42 – 选择 R 脚本转换后的 df 数据集]()
图 5.42 – 选择 R 脚本转换后的 df 数据集
-
你可以看到,结果表有另外三列,每一列都与一个命名组相关:
![图 5.43 – 使用 Python 正则表达式从自由笔记中提取的值]()
图 5.43 – 使用 Python 正则表达式从自由笔记中提取的值
最后,转到首页菜单并点击关闭并应用。
太棒了!你刚刚证明了你知道如何使用正则表达式重新排列订单笔记中的数据,即使使用 R。
摘要
在本章中,你被介绍了如何使用正则表达式的基础知识。使用最基本的方法,你能够有效地在 Power BI 中使用 Python 和 R 验证表示电子邮件地址和日期的字符串。
此外,你还学会了如何通过使用正则表达式从半结构化日志文件中提取信息,以及如何以结构化的方式将提取的信息导入 Power BI。
最后,你通过销售订单相关笔记的实际情况,学会了如何在 Python 和 R 中使用正则表达式从看似无法处理的自由文本中提取信息。
在下一章中,你将学习如何在 Power BI 中使用一些脱敏技术来匿名化或伪匿名化在导入 Power BI 之前以纯文本形式显示敏感个人数据的数据集。
参考文献
为了进一步阅读,请参考以下书籍和文章:
-
正则表达式:完整教程,作者:Jan Goyvaerts (
www.princeton.edu/~mlovett/reference/Regular-Expressions.pdf) -
Power BI/Power Query 中的数据隐私设置,第一部分:性能影响 (
blog.crossjoin.co.uk/2017/05/24/data-privacy-settings-in-power-bipower-query-part-1-performance-implications/)
第七章:6 在 Power BI 中匿名化和伪匿名化您的数据
对于为特定客户开发软件产品的开发者来说,经常发生的情况是他们想要重新包装该软件并将其出售给对类似功能感兴趣的另一个客户。然而,如果您想向新客户展示一些软件的演示截图,您应该避免展示任何可能敏感的数据。手动进入原始软件数据库的副本并尝试屏蔽数据,无疑是那些可怜的开发者在过去不得不做的任务之一,也许甚至在演示前几天。
描述的场景不需要将数据与第三方接收者共享,而是旨在通过显示类似真实的数据来成功向客户演示产品。因此,没有必要担心专业分析师可能进行的暴力破解攻击,其目的是在去标识化操作之前推导出原始数据。
当您需要与第三方接收者共享整个数据集时,情况肯定会发生变化。自 2018 年以来,这个问题变得更加敏感,尤其是在欧洲,因为公司必须更加关注数据隐私和个人可识别信息(PII),以符合通用数据保护条例(GDPR)的要求。
本章的目标是介绍使用 Python 或 R 脚本的去标识化技术,这些技术可以帮助 Power BI 开发者防止将个人的身份与报告上显示的信息联系起来。
在本章中,您将学习以下内容:
-
去标识化数据
-
在 Power BI 中匿名化数据
-
在 Power BI 中伪匿名化数据
技术要求
本章要求您拥有一个有效的互联网连接,并且Power BI Desktop已经安装在本机上。您必须已按照第二章、配置 Power BI 中的 R 和第三章、配置 Power BI 中的 Python 中概述的方式正确配置了 R 和 Python 引擎以及 IDE。
去标识化数据
PII(个人可识别信息),也称为个人信息或个人数据,是指与可识别个人相关的任何信息。PII 有两种类型——直接和间接。直接标识符的例子包括您的姓名、您的地址、您的照片或与您关联的 RFID(无线电频率识别)。另一方面,间接标识符是所有那些不明确指明您作为个人的信息,但以某种方式使识别您变得更容易的信息。间接标识符的例子包括您的车牌号码、您的银行账户号码、您在社交网络上的个人资料链接或您的工作地点。
去标识化数据的做法是操纵 PPI(个人可识别信息)以便无法再识别生成它们的人。
处理直接和间接个人标识符有两种选择——要么你决定完全销毁它们,要么你决定将它们与数据的其他部分分开,实施安全措施以防止任何人重新识别数据主体。但让我们首先探索一些最常见的去标识化技术。
去标识化技术
去标识化是一个对最终用户不可见的过程。经过与一组分析师的仔细研究后,通常是由数据管理员(或代表他们行事的人)决定哪些信息应该被去标识化。在接下来的章节中,我们将讨论最常用的去标识化技术。
信息删除
最简单的去标识化形式是从数据集中删除敏感信息:

图 6.1 – 匿名化,信息删除
很明显,这种简单方法的缺点之一可能是最终数据集不再符合必须消费它的应用程序预期的模式。
数据掩码
数据掩码隐藏了特定角色的用户不应看到的信息。这可能包括使用单词或字符替换修改数据。例如,你可以用一个符号,如*或x来替换一个值字符。以下是一个典型的数据掩码示例:

图 6.2 – 匿名化,数据掩码
请记住,如果电子邮件地址中可读的域名不是公开的,而是属于一个可识别的法律实体,那么应用的数据掩码不符合 GDPR 规则,因为工作场所变得可识别。
一些产品自带动态数据掩码解决方案。这些根据用户的角色、位置和权限屏蔽或阻止敏感信息。
注意
例如,Microsoft SQL Server和Azure SQL Database提供动态数据掩码作为避免向未经授权的用户暴露敏感数据的解决方案。数据库中的数据没有改变,因为掩码规则是在查询结果中应用的。
数据交换
数据交换是指在整个数据集中对包含敏感数据的列的值进行洗牌。例如,如果你有一个包含个人出生日期的列,这完全可以使用交换技术匿名化,并取得非常好的效果。
泛化
泛化是指用表示更广泛类别的其他值替换点值,该类别包含初始值。例如:
-
一个
25岁的年龄可以转换为>=18,或在 18 到 30 岁之间。 -
一个像
04/11/1989这样的出生日期可以被替换为出生年份1989。 -
一个邮政编码可以被更广泛的区域邮政编码所替代。
数据扰动
数据扰动是一种技术,通过添加一些随机噪声或创建合成数据来替换原始值。这种转换导致信息丢失,使得数据本身变得无用。
令牌化
令牌化是一种技术,用随机生成的字母数字值替换原始敏感值,称为令牌。完全随机的令牌提供最高的安全性,因为内容无法被重新工程。背后没有数学算法可以通过逆变换来获取原始值。因此,令牌与原始值之间的关联通常在安全的数据库中维护,并且令牌通常由特定的令牌服务器生成。只有令牌服务器与令牌数据库通信。
哈希
哈希是一种类似于令牌化的技术,区别在于生成的令牌,称为哈希值,是由数学算法生成的,具有固定长度,几乎不可能转换回原始值。如果你使用相同的哈希函数和相同的输入值,你总是会得到相同的哈希值作为输出。通常,还会添加一些额外的文本,称为盐,以使输入值更复杂,从而使得暴力攻击逆向工程哈希值更加困难。
加密
与哈希类似,加密使用数学算法来转换敏感数据。与哈希不同,它是一种双向转换,需要解密密钥来逆向工程加密值。使用加密映射表可以在解密数据时提高性能。
大多数生产力工具和数据库系统现在都内置了端到端加密功能。
注意
两个例子是 Microsoft SQL Server 或 Azure SQL 数据库,它们自带了始终加密功能。它通过在客户端加密数据并隐藏服务器上的加密密钥来实现。即使数据库管理员在没有明确权限的情况下也无法读取加密列中存储的信息。
现在你已经了解了用于去识别敏感信息最常见的转换方法,你将看到它们在匿名化和匿名化中的应用。
理解匿名化
匿名化是一种去识别过程,它将直接或间接的标识符与数据的其他部分分开,同时注意确保以下内容:
-
用假名(一个随机真实姓名,但更常见的是随机数字或字母数字标识符)替换一个或多个 PII,确保主体不被识别。由于假名对于相同的输入始终相同,因此保证了分析相关性。因此,对匿名化数据的分析不会失去价值。
-
不销毁原始 PII,确保整个数据集可以通过,例如,PII 和化名之间的查找表或数字密钥来重建(数据重新识别)。
-
采取适当的技术和组织措施,使从剩余数据中追踪个人的身份变得困难。
在此过程中,可以对一些你希望在可访问数据中保留的 PII 进行去标识化转换。例如,你可以用看起来相似的化名替换 PII 值,同时确保跟踪替换,以保证重新识别。
匿名化过程的示例显示在图 6.2中,其中使用查找表来保证逆转换的映射:

图 6.3 – 匿名化过程
这种类型的架构还保证了满足个人删除个人数据请求(如 GDPR 所要求的)的可能性,以下条件必须满足:
-
从那一刻起,仅通过从查找表中删除与它相关的关联,将无法识别该主体。
-
将避免完全丢失对数据分析有用的统计信息,因为可以使用不消除分析相关性的去标识化转换。
重要提示
请记住,当你永久失去一行可访问数据及其相应 PPI 之间的链接时,该行就变得完全匿名化,从而脱离 GDPR 的控制。
因此,我们引入了匿名化的概念。让我们看看这意味着什么。
什么是匿名化?
匿名化完全销毁直接和间接标识符,或者销毁与去标识化对应方的链接,因此不存在任何危险(或者至少是非常不可能的)任何攻击者能够重建数据所涉及主体的身份。它以过程的不可逆性为主要目标。因此,以下适用:
重要提示
匿名化数据超出了 GDPR 的范畴和控制,因为匿名化数据已不再是个人数据。
匿名化的最明显缺点是它从涉及的数据中移除了显著的价值。这是因为,在过程完成后,无法追踪生成这些数据的人的身份。因此,在匿名化任何数据集之前评估所有相关风险是明智的。
匿名化的第二个缺点是它通常使用随机生成的去标识化字符串,因此数据集的一些统计信息将永久丢失,使得数据科学家所做的任何工作都变得徒劳。
匿名化数据可能容易受到去匿名化攻击的影响。这些攻击包括通过可用外部信息丰富匿名化数据集,从而推断匿名化项。由于匿名化数据丰富、粒度细、随时间和上下文相对稳定,因此这些攻击更有可能成功。
通常,用于安全匿名化的最常用的去标识化技术如下:
-
令牌化
-
加密
现在我们来看看如何使用 Power BI 将这些概念应用于实际案例。
在 Power BI 中匿名化数据
在你作为 Power BI 报告开发者的职业生涯中可能会遇到以下一种可能的场景。想象一下,你被分配了一个Excel数据集,需要将其导入 Power BI 以创建一个报告展示给公司另一个部门。该 Excel 数据集包含敏感的个人数据,例如多次尝试用信用卡支付订单的人的姓名和电子邮件地址。以下是 Excel 文件内容的示例:

图 6.4 – 待匿名化的 Excel 数据
在匿名化敏感数据的同时,你会被要求创建报告。
第一眼就能注意到的是,你不仅需要匿名化姓名和电子邮件列,而且一些姓名或电子邮件地址可能包含在某些备注的文本中。虽然使用正则表达式定位电子邮件地址相对容易,但在自由文本中定位人名则不那么容易。为此,有必要采用一种名为自然语言处理(NLP)的技术,该技术被称为命名实体识别(NER)。多亏了 NER,我们可以在自由文本中识别和分类命名实体(如人名、地点等)。
基本思想是将全名和电子邮件地址都替换为随机令牌。根据所使用的分析语言,有不同解决方案,由不同包提供,但都导致相同的结果。
使用 Python 匿名化数据
Python 是在匿名化过程中执行去标识化转换最广泛使用的语言之一。有大量的包实现了这样的解决方案。特别是,微软发布了开源包 Presidio (microsoft.github.io/presidio/),迄今为止,它是数据保护和匿名化方面最好的解决方案之一。它为在自由文本和图像中发现的实体提供快速识别和匿名化形式,例如信用卡号码、姓名、位置、社会保险号码、电子邮件地址、财务数据等。PII 识别器 通过在多种语言中识别相关上下文,利用 NER、正则表达式、基于规则的逻辑和校验和。幕后,Presidio 采用 NLP 引擎来识别实体——它支持 SpaCy(默认)和 Stanza。Presidio 最有趣的功能之一是其 可扩展性。实际上,通过添加 自定义 PII 实体 可以非常容易地扩展 Presidio 分析器。
一旦识别出敏感实体,您需要用令牌替换它们。这些令牌是在 Python 中使用 secrets 模块生成的。
也就是说,您可以在随书提供的 GitHub 仓库的 Chapter06 文件夹中的 Python 文件 01-anonymize-data-in-power-bi-python.py 中找到这个实现的实现。它执行以下操作:
-
加载执行代码所需的库。您将使用
pandas模块,presidio_analyzer和presidio_anonymizer模块的一些函数,以及secrets模块。 -
定义两个自定义函数,一个用于匿名化电子邮件,另一个用于匿名化个人姓名。这两个函数都使用
analyzer.analyze()Presidio 函数来识别需要匿名化的实体,并使用secrets模块生成令牌到anonymizer.anonymize()函数中。 -
初始化 Presidio 分析器 和 Presidio 匿名化器 的主要对象。
-
对于之前在 Power BI Desktop 中加载的数据集的每一行(或者如果您想在 VSCode 中测试代码,则通过
pandas的read_excel()函数),将anonymizeEmail函数应用于Email和Notes列,并将anonymizeName函数应用于Name和Notes列。为了将函数应用于列中的每个单独值,我们采用了apply()函数,后面跟着一个名为 lambda 函数 的结构(由关键字lambda引入)。这是一个没有名称(匿名)的小函数,用于内联使用。以下是一个示例:df.Name = df.Name.apply(lambda x: anonymizeName(x))
为了继续进行,然而,有必要配置一个新的Python 环境。这是因为,截至目前,Presidio 仅支持 Python 3.6 到 3.8 版本。您的 pbi_powerquery_env 环境安装了更新的 Python 版本,因此您需要创建一个新的环境,并使用 Python 3.8。一旦创建,您必须安装运行代码所需的模块。
配置新环境所需的步骤如下:
-
打开您的 Anaconda Prompt。
-
输入并运行以下代码以创建新的
presidio_env环境并使用 Python 3.8:conda create --name presidio_env python=3.8 -
输入并运行以下代码以切换到新创建的环境:
conda activate presidio_env -
输入并运行以下代码以安装 Presidio 分析器:
pip install presidio_analyzer -
输入并运行以下代码以安装 Presidio 匿名化器:
pip install presidio_anonymizer -
分析器在幕后还安装了 SpaCy。因此,您还必须使用此代码安装 SpaCy 的 训练有素的英文文本管道(我们选择用于博客、新闻和评论的那个):
python -m spacy download en_core_web_lg这是 SpaCy 使用的最大管道,占用大约 788 MB。
-
输入并运行以下代码以安装 pandas:
pip install pandas -
如果您想使用 pandas 直接用 Python 加载 Excel 并在将其输入 Power BI 之前测试代码,您还需要
openpyxl模块:pip install openpyxl -
输入并运行以下代码以安装
matplotlib,这是与 Python 脚本一起使用的 Power BI 包装器所需的:pip install matplotlib
现在,您已经准备好在 Power BI 中对 Chapter06 文件夹中找到的 CustomersCreditCardAttempts.xlsx Excel 文件的内容应用匿名化。
那么,让我们开始吧:
-
打开您的 Power BI Desktop。确保引用的 Python 环境在选项中是
presidio_env(其主目录应为C:\Users\<your-username>\miniconda3\envs\presidio_env)。请记住,如果您找不到特定环境的路径,请在 Anaconda Prompt 中激活它(conda activate <your-env>),然后输入where python。 -
从功能区,点击 Excel 图标以从 Excel 导入数据:
![图 6.5 – 从 Excel 导入数据]()
图 6.5 – 从 Excel 导入数据
-
从 Open 对话框,选择上述
CustomersCreditCardAttempts.xlsx文件。 -
从 Navigator 窗口,选择
Customers工作表,然后点击 Transform Data:![图 6.6 – 选择 Customers 工作表并点击 Transform Data]()
图 6.6 – 选择 Customers 工作表并点击 Transform Data
-
点击 Transform 菜单,然后点击 Run Python Script。
-
将
01-anonymize-data-in-power-bi-python.py文件中的脚本复制到 Python 脚本编辑器中,然后点击 OK。 -
如果 Power BI 需要您提供数据隐私信息,您已经知道如何根据您在 第五章 中看到的,使用 Power BI 中的正则表达式来操作。
-
我们只对
df数据集感兴趣。因此,点击其 Table 值:![图 6.7 – Python 脚本转换结果选择数据集 df]()
图 6.7 – Python 脚本转换结果选择数据集 df
-
如您所见,
Name和Notes列中的人名以及Email和Notes列中的电子邮件地址已经正确进行了匿名化:

图 6.8 – Python 脚本转换结果的数据集
您现在可以点击 主页 选项卡中的 关闭并应用。
注意到 Notes 列中包含的人名也已经进行了匿名化。这是应用 SpaCy 引擎使用的 NER 算法的结果,该引擎在 Presidio 的底层运行。
此外,所使用的去标识化技术(分词)并不保留数据集的统计特性,因为将程序应用于相同的个人数据不会返回相同的去标识化字符串。
注意
当您发布已经对数据进行匿名化的报告时,相应的数据集也将被发布。只能访问数据集(而不能访问源 Excel 文件)的用户将只能看到匿名化数据,而没有能力了解被替换的原数据之间的关联。
多亏了匿名化,现在您可以在不担心暴露敏感数据风险的情况下开发您的报告。
让我们看看如何在 R 中做同样的事情。
使用 R 匿名化数据
您同样可以在 R 中实现数据匿名化过程。只要您在 R 中使用正则表达式识别字符串,处理速度就相当快。然而,当需要实现自然语言处理技术,如命名实体识别(NER)时,最广泛采用的 R 包通常是由其他语言开发的开源模块的包装。例如,openNLP R 包是 Apache OpenNLP 工具包的接口,基于用 Java 编写的机器学习算法。为了让 openNLP 包与 OpenNLP 软件接口,其安装还需要作为依赖项安装 rJava 包,该包使得 R 和 Java 世界之间能够进行对话。
为了在 R 中实现上一节中在 Python 中开发的相同匿名化功能,你将使用另一个广泛使用的 R 包,用于 NLP 操作,称为spacyr。这个库提供了一个方便的 R 包装器,围绕 Python 的spacy模块。在上一节中,你看到名为presidio的 Python 模块在幕后安装了与spacyr相同的spacy模块。如果你想知道如何从 R 模块中运行 Python 代码,请记住,在第三章,配置 Power BI 中的 Python,你通过名为reticulate的 R 包使用RStudio运行了 Python 代码。正如rJava负责处理 R 与 Java VM 的接口一样,reticulate允许 R 与 Python 环境接口并执行 Python 代码。简而言之,你将要开发的 R 代码所做的不过是执行你在上一节中使用的spacy Python 模块的功能。
注意
记住,你可以使用正则表达式将电子邮件地址替换为虚拟数据。相反,在自由文本中替换人名只能使用一个识别命名实体的 NLP 函数。因此,需要使用像
spacyr这样的包。
正如你在上一节中所做的那样,这里你也将通过分词来匿名化 Excel 文件CustomersCreditCardAttempts.xlsx的内容。在 R 中,将使用stringi包生成标记。
话虽如此,你可以在Chapter06文件夹中找到的02-anonymize-data-in-power-bi-r.R文件中的 R 代码执行以下操作:
-
加载执行代码所需的库。特别是,你将使用
Tidyverse中的stringr、dplyr和purrr进行数据处理;spacyr和stringi用于数据匿名化。 -
定义用于将电子邮件匿名化到自由文本的
anonymizeEmails函数。它使用spacyr函数的spacy_parse(),并附加like_email属性。由于它可以识别单个文本中的多个电子邮件地址,因此使用str_replace_all()函数将所有找到的实例替换为stringi包的stri_rand_strings函数生成的标记。 -
定义用于将人名匿名化到自由文本的
anonymizeNames函数。它包含比上一个函数更复杂的逻辑,因为一个人的名字可以由多个标记组成,这些标记不一定总是由空格分隔(例如,名字Roma Rodriguez-Bailey)。因此,为了识别指向单个个人的所有标记的集合,我们必须构建一个引用第一个和最后一个标记的正则表达式(例如,Roma.*?Bailey),它能够匹配整个名字。正如你所看到的,在上一节中不需要实现所有这些逻辑,因为 Python Presidio 模块处理了所有这些情况。 -
你必须初始化
spacyr,使其引用一个包含已安装spacy模块的 Python 环境。通常,如果你还没有在一个环境中安装spacy,你可以使用spacyr的spacy_install()函数,该函数设置一个新的 Python 环境,包含使它正常工作的所有必需内容。在我们的案例中,我们已经在上一节中创建了 Python 环境presidio_env,其中包含spacy模块和用于从网络中提取语言属性的已训练的en_core_web_lg模型。然后,只需在spacy_initialize()函数中引用环境presidio_env的路径,就可以正确配置spacyr。以下是代码:spacy_initialize( model = "en_core_web_lg", condaenv = r"{C:\Users\<your-username>\miniconda3\envs\presidio_env}", entity = TRUE )如果你在 RStudio 中运行它,如果一切正常,你会得到以下类似的消息:
successfully initialized (spaCy Version: 2.3.0, language model: en_core_web_lg) (python options: type = "condaenv", value = "C:\Users\<your-username>\miniconda3\envs\presidio_env") -
对于之前在 Power BI 中加载的(或使用
readxl包在 RStudio 中测试代码)数据集的每一行,将anonymizeEmail函数应用于Email和Notes列,将anonymizeName函数应用于Name和Notes列。为了将之前定义的两个函数应用于列中的每个元素,我们使用了purrr包的map()函数。更具体地说,map_chr()返回字符串向量的输出,以便它可以替换列内容。
简要解释了 R 脚本的用途后,让我们开始正事。为了使用spacyr R 包,它必须在最新的 R 引擎(在我们的案例中是 MRO 4.0.2)中安装。以下是必要的步骤:
-
打开Rstudio,并确保在全局选项中按照第二章,配置 Power BI 中的 R的步骤选择你已安装的最新 MRO 引擎。
-
由于 MRO 按照定义会从默认的CRAN(综合 R 档案网络)快照中下载新包,为了下载
CRAN中的最新版本包,你需要运行以下代码来覆盖引用的仓库:local({ r <- getOption("repos") r["CRAN"] <- "https://cloud.r-project.org/" options(repos = r) }) -
然后,在控制台中运行以下代码来安装
spacyr包:install.packages("spacyr")
你现在可以使用 R 在 Power BI 中对CustomersCreditCardAttempts.xlsx Excel 文件的内容进行匿名化。
那么,让我们开始吧:
-
打开你的 Power BI 桌面版,并确保在全局选项中引用的 R 引擎是最新版本的 MRO。
-
从功能区点击Excel图标以从 Excel 导入数据。
-
从打开对话框中,选择上述的
CustomersCreditCardAttempts.xlsx文件。 -
从导航器窗口中,选择客户表,然后点击转换数据:
![图 6.9 – 选择客户表并点击转换数据]()
图 6.9 – 选择客户表并点击转换数据
-
点击转换菜单,然后点击运行 R 脚本。
-
将
02-anonymize-data-in-power-bi-r.R文件中的脚本复制到 R 脚本编辑器中,并点击确定。请记住更改你机器的环境路径。 -
如果 Power BI 需要你提供数据隐私信息,你根据在第五章中看到的,在 Power BI 中使用正则表达式,已经知道如何操作。
-
我们只对
df数据集感兴趣。因此,点击其表值:![图 6.10 – R 脚本转换结果选择数据集 df]()
图 6.10 – R 脚本转换结果选择数据集 df
-
正如你所见,
Name和Notes列中的人名以及Email和Notes列中的电子邮件已经正确匿名化:

图 6.11 – R 脚本执行结果转换后的数据集
你可以在主页选项卡中点击关闭并应用。
正如你所见,执行 R 脚本的时间比 Python 脚本长。显然,通过reticulate传递信息产生的开销造成了差异。
重要提示
如果你需要匿名化一个不太大的数据集,建议直接使用 Python 脚本进行,以获得更好的性能。
再次强调,本报告发布的数据集将仅包含匿名化数据,不会赋予 Power BI 用户检索原始数据的能力。
现在我们来看看如何在 Power BI 中实现数据的匿名化。
Power BI 中的数据匿名化
与匿名化不同,匿名化通过将相同的输入字符串转换为相同的输出字符串来保持数据集的统计特性,并跟踪发生的替换,允许那些有权访问此映射信息的人重新获得原始数据集。
此外,匿名化通过将敏感数据替换为与原始数据具有相同形式的虚假字符串(假名),使得去标识化数据更加真实。
根据所使用的分析语言,有不同解决方案,由不同包提供,但最终达到相同的结果。让我们看看如何将 Power BI 中的匿名化应用于之前章节中使用的 Python 的同一 Excel 文件内容。
使用 Python 进行数据匿名化
你将使用的模块和代码结构与之前用于匿名化的那些非常相似。一个不同之处在于,一旦识别出敏感实体,它们就会被相同类型的虚假实体所替代。Python 中出类拔萃的虚假数据生成器有两个:Faker (faker.readthedocs.io/) 和 Mimesis (mimesis.readthedocs.io/)。在我们的例子中,我们将使用 Faker,它受到了之前为 PHP、Perl 和 Ruby 开发的同名库的启发。
此外,变化的是用于去识别实体的两个自定义函数的逻辑,以及添加了两个字典(emails_dict 和 names_dict)的管理,以维护个人数据与虚假数据之间的映射。
我们还在处理虚假数据方面添加了一些“盐” – 考虑到数据集中每个个体的国家,生成人员姓名和电子邮件地址,将其作为自定义函数中的参数传递。例如,如果个人是德国人,生成的姓名将是典型的德国姓名。
让我们详细看看这是怎么回事。相关的 Python 文件是 03-pseudonymize-data-in-power-bi-python.py,您可以在 Chapter06 文件夹中找到它:
-
在匿名化的情况下,使用了
anonymizer.anonymize()函数一次性替换了使用analyzer.analyze()函数识别的所有实体。现在,在相同的实体识别之后,我们必须 首先检查每个已识别的单个实体是否已经映射到一个虚假字符串。如果实体在其特定的字典中,您将检索相关的虚假字符串并使用它来匿名化文本。否则,您将生成一个新的虚假字符串并将其添加到字典中,将其与相关的实体关联。 -
当所有预期列的匿名化完成时,映射字典(包括名称和电子邮件)将持久化到
pkl文件中。这些字典在需要匿名化新的 Excel 数据以及数据集每次刷新时都会被解包并用作映射字典。这确保了相同的化名始终用于相同的个人数据,以及新的 Excel 行。
为了使用之前提到的 faker 模块,您需要以下方式在 presidio_env 环境中安装它:
-
打开您的 Anaconda Prompt。
-
输入并运行以下代码以切换到新创建的环境:
conda activate presidio_env -
输入并运行以下代码以安装
Faker:pip install Faker
完成此操作后,您可以在 Power BI 中开始实施匿名化:
-
打开您的 Power BI Desktop,确保引用的 Python 环境是
presidio_env在 选项 中(其主目录应为C:\Users\<your-username>\miniconda3\envs\presidio_env)。 -
从 Power BI 菜单栏中,点击 Excel 图标以从 Excel 导入数据。
-
从 打开 对话框中,选择上述
CustomersCreditCardAttempts.xlsx文件。 -
从 导航器 窗口中,选择 客户 工作表,然后点击 转换数据:
![图 6.12 – 选择客户工作表并点击转换数据]()
图 6.12 – 选择客户工作表并点击转换数据
-
点击 转换 菜单,然后点击 运行 Python 脚本。
-
将
03-pseudonymize-data-in-power-bi-python.py文件中的脚本复制到 Python 脚本编辑器中,然后点击 确定。请记住更改您机器的环境路径。 -
如果 Power BI 需要您提供数据隐私信息,您已经知道如何根据您在第五章,在 Power BI 中使用正则表达式中看到的内容进行操作。
-
我们只对
df数据集感兴趣。因此,点击其表值:![图 6.13 – 选择 Python 脚本转换后的数据集 df]()
图 6.13 – 选择 Python 脚本转换后的数据集 df
-
如您所见,
Name和Notes列中的人名和Email和Notes列中的电子邮件已经被正确地匿名化:

图 6.14 – Python 脚本转换后的数据集
然后,您可以在主页选项卡中点击关闭并应用。
就像匿名化一样,Name、Email和Notes列已经被正确地去标识化。除此之外,我们还有以下内容:
-
人的名字和电子邮件与个人国家的相一致性,尽管
Faker目前不允许您在生成的名字和相应的电子邮件之间保持一致性。例如,在图 6.14中,您可以看到第 11 行(名字Alfio Migliaccio未用于电子邮件)使用了一个不同的意大利名字的意大利名字和电子邮件。 -
使用
emails_dict和names_dict映射字典确保可以对匿名化数据集进行统计分析。 -
之所以能够追踪原始数据,归功于这些持久化到磁盘的映射字典。
在这种情况下,当您将报告发布以与 Power BI 用户共享时,您有以下内容:
注意
只能访问数据集的 Power BI 用户将只能看到去标识化的数据。通过向具有正确权限的人提供映射字典,您确保他们可以在任何法律需求下追踪原始数据。
你注意到吗,由于 Python 脚本,您能够将 Power BI 中执行的数据处理结果写入文件吗?
注意
与在 PKL 文件中使用序列化字典不同,例如,您可以将信息写入CSV或 Excel 文件。
简而言之,您有在 Power BI 之外记录信息的能力。您将在第七章,从 Power BI 到外部存储库记录数据中了解更多关于这种可能性。
现在我们来看看如何在 R 中实现匿名化。
使用 R 进行数据匿名化
我们将在 R 中用于数据匿名化的库与用于 R 中匿名化的库非常相似。为了完全模拟前一小节中开发的 Python 脚本的功能,我们还需要一个 R 包来生成假数据以替换敏感数据,例如人名和电子邮件地址。在 Python 中,我们使用了 Faker 模块,这是该目的最广泛使用的模块之一。在 R 中,也开发了一个具有相同功能的包,它受到了与 Faker 相同的代码的启发,并被称为charlatan (github.com/ropensci/charlatan)。此外,R 中匿名化的代码将遵循前一小节中 Python 脚本中已实现的相同逻辑,以下是一些细微的差异:
-
与字典不同,命名列表用于将化名映射到原始实体。
-
命名列表被序列化和持久化在RDS文件中,而不是PKL文件中。
为了使用charlatan R 包,它必须在最新的 R 引擎(在我们的案例中,MRO 4.0.2)中安装。以下是必须遵循的步骤:
-
打开 Rstudio,并确保在全局选项中选择你按照第二章,配置 R 与 Power BI中步骤安装的最新 MRO 引擎。
-
由于 MRO 按照定义会从默认的 CRAN 快照中下载新包,为了下载 CRAN 中包的最新版本,你需要通过运行以下代码来覆盖引用的存储库:
local({ r <- getOption("repos") r["CRAN"] <- "https://cloud.r-project.org/" options(repos = r) }) -
然后,在控制台中运行以下代码来安装
charlatan包:install.packages("charlatan")
现在,你已准备好使用 R 在 Power BI 中对CustomersCreditCardAttempts.xlsx Excel 文件的内容应用匿名化。让我们开始吧:
-
打开你的 Power BI 桌面版,并确保在全局选项中引用的 R 引擎是你的最新 MRO 版本。
-
从功能区点击Excel图标以从 Excel 导入数据。
-
从打开对话框中,选择上述
CustomersCreditCardAttempts.xlsx文件。 -
从导航器窗口中,选择客户表,然后点击转换数据:
![图 6.15 – 选择客户表并点击转换数据]()
图 6.15 – 选择客户表并点击转换数据
-
点击转换菜单,然后点击运行 R 脚本。
-
将
04-pseudonymize-data-in-power-bi-r.R文件中的脚本复制到 R 脚本编辑器中,并点击确定。请记住在代码中更改所需的路径。 -
如果 Power BI 需要你提供数据隐私信息,你已经知道如何根据你在第五章,在 Power BI 中使用正则表达式中看到的进行操作。
-
我们只对
df数据集感兴趣。因此,点击其表值:![图 6.16 – 选择 R 脚本转换后的数据集 df]()
图 6.16 – R 脚本转换的结果选择数据集 df
-
如你所见,
Name和Notes列中的人名以及Email和Notes列中的电子邮件已经正确匿名化:

图 6.17 – R 脚本转换的结果转换后的数据集
然后,你可以在主页选项卡中点击关闭并应用。
不幸的是,charlatan 目前还不支持it_IT区域设置。但是,由于它是一个开源项目,社区可能会很快实现它。然而,这并不会阻止我们获得非常好的数据集和将在 Power BI 服务上发布的报告的脱敏化,因为,在没有特定区域设置的情况下,始终使用默认的区域设置(en_US)。
据说,在 R 中实现去标识化程序的性能肯定不如 Python。然而,可以通过引入多任务操作的并行化来部分弥补这一差距。我们将在第八章中详细探讨这项技术,即调用外部 API 以丰富您的数据。
摘要
在本章中,你学习了匿名化和脱敏化之间的主要区别。你还学习了哪些技术最常用于采用这两种去标识化过程。
你还使用 Python 和 R 在 Power BI 中应用了通过标记化进行的匿名化过程,以及通过生成相似的假名进行的脱敏化过程。
在下一章中,你将学习如何将使用Power Query在 Power BI 中执行的操作生成的数据记录到外部存储库。
参考文献
对于额外的阅读,请参阅以下书籍和文章:
-
脱敏化、匿名化和 GDPR (
www.termsfeed.com/blog/gdpr-pseudonymization-anonymization/) -
对称和不对称加密 (
medium.com/hackernoon/symmetric-and-asymmetric-encryption-5122f9ec65b1) -
Python 密码学教程 (
www.tutorialspoint.com/cryptography_with_python/) -
使用 cyphr 在 R 中进行加密 (
docs.ropensci.org/cyphr/articles/cyphr.html)
第八章:7 从 Power BI 记录数据到外部源
如您从前几章所学,Power BI 使用 Power Query 作为 提取、转换、加载 (ETL)操作的工具。这个工具实际上非常强大——它允许您从各种数据源中提取数据,然后通过非常用户友好的选项轻松转换它,以便将其持久化到 Power BI 数据模型中。这是一个只能从外部读取信息的工具。实际上,Power Query 最严格的限制是其无法将信息写入 Power BI 之外。然而,由于与 Python 和 R 等分析语言的集成,您将能够将 Power Query 加载和转换过程的信息持久化到外部文件或系统中。在本章中,您将学习以下主题:
-
记录到 CSV 文件
-
记录到 Excel 文件
-
记录到 Azure SQL Server
技术要求
本章要求您拥有一个正常工作的互联网连接,并且您的机器上已经安装了 Power BI Desktop。您必须已按照 第二章、配置 Power BI 中的 R 和 第三章、配置 Power BI 中的 Python 中概述的方式正确配置了 R 和 Python 引擎和 IDE。
记录到 CSV 文件
将表格结构化信息记录到文件的格式中,最广泛使用的一种是 逗号分隔值 (CSV)。由于 CSV 文件仍然是平面文本文件,CSV 是在异构应用程序之间交换信息最流行的格式。
CSV 文件是矩形数据集(矩阵)的表示,包含数字或字符串列。矩阵的每一行由一系列值(每列一个)表示,这些值由逗号分隔,并且应该有相同数量的值。有时,可以使用其他值分隔符,如制表符(\t)、冒号(:)和分号(;)字符。第一行可能包含列标题名称。通常,使用由 CRLF (回车换行符)字符(通常输入为 \r\n)或简单地使用 Unix 系统中的 LF (\n)创建的 换行符 作为 行分隔符。因此,CSV 文件内容的示例可能如下所示:

图 7.1 – CSV 文件内容的示例
注意空格,因为它们成为字符串值的一部分!例如,V1, V2 行的第二个值将是 [空格]V2。
可能会发生字符串值包含换行符(CRLF)、双引号或逗号(如通常在“注释”等自由文本字段中发生的情况)。在这种情况下,值应该用双引号括起来,并且任何字面双引号必须使用另一个双引号进行转义。例如,对于 Col1 列的值 " C,D"、“E""F” 和 "G[CRLF]H" 在 CSV 文件中的格式如下所示:

图 7.2 – 包含逗号、双引号或 CRLF 的 CSV 值示例
重要提示
重要的是要记住,CSV 文件本身没有大小限制。
由于它是一个平面文本文件,其最大大小由文件系统施加的限制决定。例如,Windows 的默认文件系统是新技术文件系统(NTFS),其当前实现允许的最大文件大小为16 TB。然而,其设计的理论极限是16 EB(16 × 2⁶⁴字节)减去1 KB。
然而,旧的文件分配表文件系统,在其 32 位(FAT32)变体中,只能处理最大大小为4 GB。
重要提示
请记住,处理大型 CSV 文件会导致内存和/或 CPU 瓶颈。如果待加载的 CSV 文件大小超过您可用的 RAM,并且您的库不使用并行性/分布式/延迟加载机制,那么您很可能遇到
OutOfMemory错误。您将在第八章,在 Power BI 中加载超过可用 RAM 的大型数据集中学习如何处理这样的大型 CSV 文件。
让我们看看如何使用 Python 读取和写入 CSV 文件。
使用 Python 记录到 CSV 文件
Python 有一个内置的 CSV 模块,它提供了一些读取和写入 CSV 文件的功能。然而,非常常见的情况是,您需要导入一个 CSV 文件,其内容将通过 Pandas 函数进行转换,或者您必须将之前通过 Pandas 处理过的 DataFrame 以 CSV 格式导出。这就是为什么在这些情况下,直接使用内置的 pandas 函数与 DataFrame 对象一起使用要方便得多。您可以在Chapter07/Python文件夹中找到01-read-csv-file-in-python.py和02-write-csv-file-in-python.py文件,这些文件将向您展示如何使用 CSV 模块。在下一节中,我们将专注于 pandas 提供的功能。
让我们详细看看可用于处理 CSV 文件的 pandas 函数。
使用 pandas 模块
pandas模块提供的用于读取和写入 CSV 文件的功能非常简单直接。您可以使用read_csv()函数从 CSV 文件中读取数据。以下是可以将example.csv文件的内容加载到 DataFrame 中的代码:
Chapter07\Python\03-read-csv-file-with-pandas.py
import pandas as pd
data = pd.read_csv(r'D:\<your-path>\Chapter07\example.csv')
data.head(10)
这里是使用VS Code 交互窗口的输出:

图 7.3 – 使用示例 CSV 文件内容加载的 pandas DataFrame 的输出
read_csv函数还允许您传递sep参数来定义在读取文件时使用的值分隔符。
如果您需要从 DataFrame 的内容中写入 CSV 文件,则可以使用to_csv()函数。以下是一个您可能使用的代码示例:
Chapter07\Python\04-write-csv-file-with-pandas.py
import pandas as pd
data = {
'Col1' : ['A', 'B', 'C,D', 'E"F', 'G\r\nH'],
'Col2' : [23, 27, 18, 19, 21],
'Col3' : [3.5, 4.8, 2.1, 2.5, 3.1]
}
data_df = pd.DataFrame(data)
data_df.to_csv(r'D:\<your-path>\Chapter07\example-write.csv', index=False)
to_csv() 函数还允许您传递 sep 参数,以便定义您在文件中打算使用的值分隔符。
如您所见,在 Python 中处理 CSV 文件非常简单。在下一节中,您将在 Power BI 中将其付诸实践。
使用 Python 在 Power BI 中将电子邮件记录到 CSV 文件中
作为生成 CSV 文件的示例,我们将使用第五章,“使用正则表达式在 Power BI 中”中提供的相同场景,其中您需要验证电子邮件地址和禁用日期。目标是导出包含不正确电子邮件的行到 CSV 文件,并从数据集中过滤它们,以便在 Power BI 中只保留有效的电子邮件。我们将使用 pandas 提供的 to_csv() 函数。必要的步骤如下:
-
按照第五章,“使用正则表达式在 Power BI 中验证电子邮件”中“使用 regex 在 Power BI 中验证电子邮件的 Python”部分的步骤进行,但不要点击 关闭并应用。
-
然后,点击 运行 Python 脚本,输入以下脚本,然后点击 确定:
import pandas as pd filter = (dataset['isEmailValidFromRegex'] == 0) dataset[filter].to_csv(r'D:\<your-path>\Chapter07\Python\wrong-emails.csv', index=False) df = dataset[~filter]您也可以在
Python\05-log-wrong-emails-csv-in-power-bi.py文件中找到此 Python 脚本。注意,这里的~字符在这种情况下是filter变量定义的布尔条件的否定。 -
点击
df数据集的 表 值:![图 7.4 – 选择 Python 脚本转换后的 df 数据集]()
图 7.4 – 选择 Python 脚本转换后的 df 数据集
-
仅保留包含有效电子邮件的行:
![图 7.5 – 仅包含有效电子邮件的表格]()
图 7.5 – 仅包含有效电子邮件的表格
此外,
wrong-emails.csv文件已创建在您的Chapter07\Python文件夹中。 -
返回到 主页 菜单,然后点击 关闭并应用。
如果您检查创建的 CSV 文件的内容,它将与以下内容匹配:
UserId,Email,BannedDate,IsEmailValidByDefinition,IsDateValidByDefinition,isEmailValidFromRegex
2,example1@example.com/example2@example.com,06/07/2019,0,1,0
3,example33@example.com.,02/05/2018,0,1,0
5,example@example.com --> check,02/29/18,0,0,0
9,example,10/22/2018,0,1,0
13,.@example.com,04/24/018,0,0,0
16,example@example.c,6/7/2019,0,1,0
如您所见,CSV 文件中的电子邮件都是无效的。在此阶段,您可以与同事分享之前的文件,以便他们可以纠正无效的电子邮件。
干得好!您刚刚学会了如何使用 Python 从 Power BI 将信息记录到 CSV 文件中。现在,让我们看看您如何使用 R 做同样的事情。
使用 R 将日志记录到 CSV 文件中
基本的 R 语言提供了一些现成的函数用于处理 CSV 文件。然而,还有 readr 包,它是 Tidyverse 生态系统的一部分,提供了类似的功能,但在加载较大的 CSV 文件时速度更快。
让我们详细看看如何使用它们。
使用 Tidyverse 函数
readr 包提供了一些与使用 R 基础读取和写入 CSV 文件时看到的函数相似的函数。这些函数的优势在于,除了尊重 Tidyverse 世界中函数提供的通用接口外,它们的速度比标准函数快五倍,并且还有进度条。请确保你已经安装了至少版本 1.4.0 的包,否则请更新它。
像上一节中做的那样,始终使用 example.csv 文件,你可以通过以下方式使用 readr 包的 read_csv() 函数加载数据:
library(readr)
data_df <- read_csv(r'{D:\<your-path>\Chapter07\example.csv}')
作为输出,你可以看到以下规范:
-- Column specification -----------------------------------
cols(
Col1 = col_character(),
Col2 = col_double(),
Col3 = col_double()
)
除了 read_csv() 函数输出的是 tibble 而不是 DataFrame 之外,还有一个重要点需要注意:
重要提示
有趣的是,
read_csv()函数默认正确地导入换行符。
如果你检查新导入的 tibble,你将看到以下内容:

图 7.6– read_csv 正确导入字符 \r\n
就像 R 基础一样,readr 包也提供了相同的函数,read_csv2()、read_tsv() 和 read_delim(),以确保类似的接口,从而易于使用。
要将数据持久化到 CSV 文件,readr 包提供了 write_csv() 函数及其整个函数家族(类似于 R 基础的 write_csv2、write_tsv 和 write_delim)。与 write.csv() 不同,这些函数不包括行名作为文件中的列。此外,默认的行结束分隔符只是一个新行(\n)。因此,如果你想使用 \r\n 字符作为行分隔符来导出你的数据,你必须通过 eol 参数传递它们:
library(readr)
data_df <- data.frame(
Col1 = c('A', 'B', 'C,D', 'E"F', 'G\r\nH'),
Col2 = c(23, 27, 18, 19, 21),
Col3 = c(3.5, 4.8, 2.1, 2.5, 3.1)
)
write_csv(data_df, file = r'{D:\<your-path>\Chapter07\R\example-write.csv}', eol = '\r\n')
注意,在这种情况下,如果你想以完全相同的方式提取它们,你必须在你的数据中使用这两个字符(\r\n)。
正如你所见,在 R 中处理 CSV 文件与在 Python 中一样简单。在下一节中,你将使用 R 在 Power BI 中记录电子邮件和日期。
在 Power BI 中使用 R 将日期记录到 CSV 文件中
我们将始终使用 第五章 中介绍的情景,即 在 Power BI 中使用正则表达式,其中需要验证电子邮件地址和禁止日期。这次,我们将使用 R 将无效的禁止日期导出到 CSV 文件。必要的步骤如下:
-
按照第五章 在 Power BI 中使用正则表达式 中 使用 regex 在 Power BI 中用 R 验证日期 部分的所有步骤进行操作,但不要点击 关闭并应用。
-
然后点击 运行 R 脚本,输入以下脚本,然后点击 确定:
library(readr) library(dplyr) dataset %>% filter( isDateValidFromRegex == 0 ) %>% write_csv( r'{D:\<your-path>\Chapter07\R\wrong-dates.csv}', eol = '\r\n' ) df <- dataset %>% filter( isDateValidFromRegex == 1 )你也可以在
R\01-log-wrong-emails-in-r.R文件中找到这个 R 脚本。 -
点击
df数据集的 表 值:![图 7.7 – 选择 R 脚本转换后的 df 数据集]()
图 7.7 – 选择 R 脚本转换后的 df 数据集
-
只有包含有效电子邮件的行将被保留:
![图 7.8 – 仅包含有效电子邮件的表格]()
图 7.8 – 仅包含有效电子邮件的表格
此外,
wrong-dates.csv文件已创建在您的Chapter07\R文件夹中。 -
返回 主页 菜单,然后点击 关闭并应用。
在此阶段,您可以与同事分享刚刚创建的 wrong-emails.csv 文件,以便他们可以纠正无效的电子邮件。
太棒了!您刚刚学会了如何使用 R 从 Power BI 将信息记录到 CSV 文件中。现在让我们看看如何使用 Excel 文件来记录您的信息。
将日志记录到 Excel 文件中
如您可能已经知道,Microsoft Excel 是 电子表格 软件,可在 Microsoft 办公套件 中使用。它是世界上用于以表格格式存储和组织数据的工具之一,应用非常广泛。它在公司中非常受欢迎,因为它允许业务数据在部门之间共享,并使个人用户能够直接快速地进行数据分析,而无需 IT 部门帮助。
Excel 的早期版本以 Excel 工作表(XLS)格式存储信息。这是一个专有的 Microsoft 格式,基于 二进制交换文件格式(BIFF)。它一直是 v7.0(Excel 95)到 v11.0(Excel 2003)版本的标准格式。从 v8.0 到 v11.0,XLS 格式可以处理 64K(2¹⁶ = 65,536) 行 和 256 列(2⁸)。从 v12.0(Excel 2007)版本开始,默认格式已更改为 Excel Open XML 电子表格(XLSX)。这是基于 Office Open XML 格式,它基于使用 XML 定义所有参数的文本文件。
注意
您知道吗?XLSX 文件包含在 ZIP 格式压缩的多个 XML 文件中的数据?如果您想验证这一点,只需将您的 XLSX 文件重命名,例如,将
example.xlsx改名为添加.zip扩展名(例如,example.xlsx.zip)。然后,使用 文件资源管理器 或任何其他 Zip 客户端(如 7-Zip)提取其内容。
XLSX 格式可以处理 1024K(2²⁰ = 1,048,576) 行和 16,384(2¹⁴)列。
自从 Power Pivot(从 Excel 2013 开始)和 Power Query(从 Excel 2016 开始)被引入以来,大多数用于生成原型数据模型的数据摄取和分析活动通常由高级用户通过 Microsoft Excel 完成。Power Query 为您提供了一个丰富的工具集,用于在一个地方转换数据。Power Pivot 通过克服 Excel 的 1,048,576 行限制,使您能够处理大量数据。一旦导入,您可以使用数据透视表和 DAX 公式语言对它们进行操作,因为后台引擎与 分析服务表格 和 Power BI 相同。这就是为什么 Excel 和 Power BI 是 Microsoft 数据平台 上自助 BI 的顶级工具。
现在,让我们看看如何在 Python 中与 Excel 文件交互。从现在开始,我们将使用最新的 XLSX 格式。
使用 Python 将日志记录到 Excel 文件
在 Python 中与 Excel 文件交互最快的方法是使用 pandas 提供的函数。然而,你需要在 pbi_powerquery_env 环境中安装 openpyxl 包。如果你记得正确,你已经在 第六章 中安装了此包,即在 Power BI 中的数据匿名化和假名化 环境中。为了在 pbi_powerquery_env 环境中安装此包,只需按照以下步骤操作:
-
打开你的 Anaconda 提示符
-
将当前环境设置为
pbi_powerquery_env,输入以下命令并按Enter键:conda activate pbi_powerquery_env -
输入以下命令并按Enter键:
pip install openpyxl
例如,你将在 Chapter07 文件夹中找到 example.xlsx 文件。让我们看看如何使用 Python 导入其内容。
使用 pandas 模块
你可以使用此代码轻松地将数据导入到 pandas DataFrame 中:
Chapter07\Python\06-read-excel-file-with-pandas.py
import pandas as pd
data = pd.read_excel(r'D:\<your-path>\Chapter07\example.xlsx')
如果你在 VS Code 中可视化 DataFrame,你会看到如下内容:

图 7.9 – 加载 example.xlsx 文件内容的 pandas DataFrame 的输出
在这种情况下,如果 Excel 单元格包含带有 \r\n 字符的字符串,导入后换行符(\r)会丢失,正如你在 图 7.11 中可以看到的。
如你可能已经知道,Excel 文件(工作簿)可以包含一个或多个工作表(工作表),其中包含数据。如果你需要从特定工作表导入数据,你可以使用此代码:
data = pd.read_excel(r'D:\<your-path>\Chapter07\example.xlsx', sheet_name='<your-worksheet-name>')
同样,你可以使用此代码将 DataFrame 的内容写入 Excel 文件:
Chapter07\Python\07-write-excel-file-with-pandas.py
import pandas as pd
data = {
'Col1' : ['A', 'B', 'C,D', 'E"F', 'G\r\nH'],
'Col2' : [23, 27, 18, 19, 21],
'Col3' : [3.5, 4.8, 2.1, 2.5, 3.1]
}
data_df = pd.DataFrame(data)
data_df.to_excel(r'D:\<your-path>\Chapter07\Python\example-write.xlsx', index = False)
生成的 Excel 文件将有一个默认的 Sheet1 工作表,其内容如下所示:

图 7.10 – 使用 pandas 函数创建的 Excel 文件的内容
如果你将单元格 A6 的内容复制到一个高级编辑器中,你可以验证 \r\n 字符是否被保留。
如果你想要将数据集的内容写入到一个新的 Excel 文件中的特定命名工作表,你可以使用以下代码:
data_df.to_excel(r'D:\<your-path>\Chapter07\Python\example-write-named-sheet.xlsx', sheet_name='My data', index = False)
结果将是以下内容:

图 7.11 – 内容现在已写入到命名工作表中
如果你想要将数据集的内容写入到一个现有的 Excel 文件中的特定命名工作表,你必须使用 pandas 的 ExcelWriter 类,如下所示:
Chapter07\Python\08-write-excel-file-named-sheet-with-pandas.py
with pd.ExcelWriter(r'D:\<your-path>\Chapter07\Python\ example-write-named-sheet.xlsx', mode='a') as writer:
data_df.to_excel(writer, sheet_name='My data', index = False)
注意,mode='a'是用于“追加”。现在,让我们看看如何使用之前提到的 pandas 函数在 Power BI 中登录的一个例子。
使用 Python 在 Power BI 中记录电子邮件和日期到 Excel 文件
让我们回到前面章节中使用的相同场景,即我们在第五章,在 Power BI 中使用正则表达式中已经分析过的场景,其中你需要验证电子邮件地址和禁止日期。然而,这次的目标是将无效的电子邮件和无效的日期导出到 Excel 文件中的两个单独的工作表,然后与团队共享。
现在,打开你的 Power BI 桌面版,确保要使用的 Python 环境是pbi_powerquery_env,然后我们开始吧:
-
从功能区点击Excel图标以从 Excel 导入数据。
-
从打开对话框中选择你可以在
Chapter05文件夹中找到的Users.xlsx文件。 -
从导航器窗口中选择用户表单,然后点击转换数据。
-
点击转换菜单,然后点击运行 Python 脚本。
-
复制你可以在
Chapter07/Python文件夹中的09-validate-emails-dates-with-regex-in-power-bi.py文件中找到的代码,并将其粘贴到 Python 脚本编辑器中。这段代码只是你之前用来分别验证电子邮件和日期的脚本的合并。然后,点击确定。 -
仅选择与
df表名相关的表值,你将看到类似以下内容:![图 7.11 – 转换后的数据包含有效电子邮件和日期的标志]()
图 7.11 – 转换后的数据包含有效电子邮件和日期的标志
现在,你有了
isEmailValidFromRegex和isValidDateFromRegex这两个标志,它们允许你选择电子邮件和正确的日期。 -
再次点击运行 Python 脚本,输入你可以在
10-log-wrong-emails-dates-excel-in-power-bi.py文件中找到的脚本,然后点击确定。 -
仅选择与
df表名相关的表值,你将看到一个只保留包含有效电子邮件和日期的行的表格:![图 7.12 – 输出数据包含包含有效电子邮件和日期的行]()
图 7.12 – 输出数据包含包含有效电子邮件和日期的行
此外,
wrong-data.xlsx文件已经创建在你的Chapter07/Python文件夹中,它包含两个工作表:Wrong emails和Wrong dates。 -
返回到主页菜单,然后点击关闭并应用。
太棒了!你刚刚学会了如何使用 Python 从 Power BI 将信息记录到多个工作表的 Excel 文件中。现在,让我们看看如何使用 R 做同样的事情。
使用 R 记录到 Excel 文件
要在 R 中读取和写入 Excel 文件,我们建议使用两个独立的包:
-
readxl:这是一个属于 Tidyverse 世界的包,允许你以最简单和最灵活的方式读取 Excel 文件中的信息。
-
openxlsx:这是一个提供创建和编辑 Excel 文件高级界面的包。与其他执行相同任务的包相比,
openxlsx消除了背后对Java的依赖。
首先,你需要安装openxlsx包:
-
打开RStudio,确保你的最新MRO引擎在全局选项(在我们的案例中,MRO 4.0.2)中被选中。
-
在控制台中输入
install.packages("openxlsx")命令。请记住,它会安装与你的 MRO 安装定义的CRAN快照对应的版本。
现在你已经准备好学习如何读取和写入 Excel 文件中的数据了。
使用 readxl 和 openxlsx 包
readxl包提供了两个独立的函数——read_xls(),用于读取 XLS 格式的 Excel 文件,以及read_xlsx(),用于读取 XLSX 格式的文件。如果你想读取位于Chapter07文件夹中的example.xlsx文件的内容,你可以使用以下代码:
library(readxl)
data_tbl <- read_xlsx(r'{D:\<your-path>\Chapter07\example.xlsx}')
结果将是一个 tibble:

图 7.13 – 使用 read_xlsx 函数读取的 Excel 数据
如你所见,回车和换行字符(\r\n)被保留。如果你想从特定的工作表读取数据,你可以使用以下代码:
data_tbl <- read_xlsx(r'{D:\<your-path>\Chapter07\example.xlsx}', sheet = 'My sheet')
要将你的数据写入到 Excel 文件中,你可以使用openxlsx包的write.xlsx()函数,如下所示:
library(dplyr)
library(openxlsx)
data_df <- data.frame(
Col1 = c('A', 'B', 'C,D', 'E"F', 'G\nH'),
Col2 = c(23, 27, 18, 19, 21),
Col3 = c(3.5, 4.8, 2.1, 2.5, 3.1)
)
data_df %>%
write.xlsx(file = r'{D:\<your-path>\Chapter07\R\example-write.xlsx}', colNames = TRUE)
观察在这种情况下,你必须使用“Unix 约定”来处理换行,即只使用字符串中的\n字符,以便在 Excel 中拥有标准的 Windows 字符\r\n。
如果你想要将数据集的内容写入到一个新的 Excel 文件中的特定命名工作表中,你可以使用以下代码:
data_df %>%
write.xlsx(file = r'{D:\<your-path>\Chapter07\R\example-write.xlsx}', colNames = TRUE, sheetName = 'My data', append = FALSE)
如果,相反,你需要向一个现有的 Excel 文件中添加一个工作表,你必须使用命名列表的 DataFrame/tibbles 作为write.xlsx函数的输入。如果你可以手动为每个工作表分配一个字符串名称,以下是你可以使用的代码:
df_named_lst <- list("My data 1" = data_df, "My data 2" = data_df)
write.xlsx(df_named_lst, file = r'{D:\<your-path>\Chapter07\R\example-write.xlsx}')
请记住,如果你需要分别使用 DataFrame/tibbles 的列表(df_lst)和工作表名称的列表(names_lst),你可以使用以下代码将所有数据写入一个 Excel 工作簿中:
df_named_lst <- setNames(df_lst, names_lst)
write.xlsx(df_named_lst, file = r'{D:\<your-path>\<your-file>.xlsx}')
让我们现在看看使用之前 R 函数在 Power BI 中登录的示例。
在 Power BI 中使用 R 将电子邮件和日期记录到 Excel 中
我们将使用的示例仍然是来自第五章,在 Power BI 中使用正则表达式的示例,其中你需要验证电子邮件地址和禁止日期。最终目标始终是将无效的电子邮件和无效的日期导出到 Excel 文件中的两个单独的工作表中,然后与团队共享。
现在打开你的 Power BI Desktop,确保你的最新 MRO 在选项中被引用,然后我们开始吧:
-
从功能区点击Excel图标以从 Excel 导入数据。
-
从打开对话框中,选择你可以在
Chapter05文件夹中找到的Users.xlsx文件。 -
从导航器窗口中,选择用户工作表,然后点击转换数据。
-
点击转换菜单,然后点击运行 R 脚本。
-
将你可以在
Chapter07/R文件夹中的02-validate-emails-dates-with-regex-in-power-bi.R文件中找到的代码复制到 R 脚本编辑器中。这段代码只是将你之前用来分别验证电子邮件和日期的脚本合并在一起。然后点击确定。 -
仅选择与
df表名相关的表格值,你将看到类似以下内容:![图 7.14 – 转换后的数据包含有效电子邮件和日期的标志]()
图 7.14 – 转换后的数据包含有效电子邮件和日期的标志
现在,你有了
isEmailValidFromRegex和isDateValidFromRegex标志,允许你选择正确的电子邮件和日期。 -
再次点击运行 R 脚本,输入你可以在
03-log-wrong-emails-dates-excel-in-power-bi.R文件中找到的脚本,然后点击确定。请记住在代码中更改路径。 -
仅选择与
df表名相关的表格值,你将看到一个表格,其中只保留包含有效电子邮件和日期的行:![图 7.15 – 输出数据包含包含有效电子邮件和日期的行]()
图 7.15 – 输出数据包含包含有效电子邮件和日期的行
此外,
wrong-data.xlsx文件已创建在你的Chapter07/R文件夹中,它包含两个工作表:错误的电子邮件和错误的日期。 -
返回主页菜单,然后点击关闭并应用。
太棒了!你刚刚学会了如何使用 R 从 Power BI 将信息记录到多个工作表的 Excel 文件中。
在下一节中,你将学习如何将信息从 Power BI 记录到本地 SQL 服务器或Azure SQL 服务器。
记录到 Azure SQL 服务器
在绝大多数公司中,业务信息持久化在关系数据库管理系统(RDBMS)中。如果公司采用 Microsoft 数据平台,Microsoft 的典型关系数据库是其本地版本的 SQL Server。否则,它是Azure SQL Server,这是一个平台即服务(PaaS)的云托管数据库。
通常,将一个公司的所有关键信息集中在一个单一存储库中是一个好主意。这就是为什么了解如何将信息从 Power BI 流程中记录到 SQL 服务器数据库或 Azure SQL 数据库中可能很有用。
如果您已经可以访问本地或 Azure SQL 服务器上的 SQL Server 实例,您只需确保您的机器上已安装SQL Server ODBC 驱动程序。实际上,Python 和 R 将通过 ODBC 连接连接到(Azure) SQL Server。您可以选择直接在您的机器上安装驱动程序(通过链接bit.ly/ODBC-SQLServer),但更常见的情况是在安装管理任何 SQL 基础设施的最终客户端时间接安装此驱动程序,即SQL Server Management Studio(SSMS)。
另一方面,如果您无法访问本地 SQL Server 实例或 Azure SQL 数据库,那么您在本节中测试示例有两个选择:
-
安装一个免费的SQL Server Express Edition(或开发者版)实例。
-
使用您的账户从 Azure 门户创建一个 Azure SQL 数据库。
让我们详细看看如何为这些选项中的每一个进行操作。
安装 SQL Server Express
在本节中,我们将展示如何安装 SQL Server 的 Express 版。这是 Microsoft 数据库引擎的免费版本,也可以用于桌面和小型服务器数据驱动应用程序的生产。显然,Express 版有一些限制,使其与高端企业版区分开来。以下是一些例子:
-
数据库引擎实例使用的最大内存为 1,410 MB。
-
单个数据库的最大大小为 10 GB。
-
单个实例使用的计算能力限制在单个插槽或四个核心中的较小者。
尽管存在这些限制,SQL Server Express 仍然是一个用于生产小型应用程序的优秀解决方案。另一方面,如果您需要能够测试引擎的更高级功能,因为您知道您的应用程序在生产中将使用更完整的版本(标准版或企业版),您可以在您的机器上安装开发者版。这个版本允许您在不支付企业版许可证的情况下测试企业版的所有功能。当然,最严格的限制是开发者版不能用于生产。
话虽如此,目前有大量关于如何安装 SQL Server Express 最新版本(2019)的教程。我们建议您遵循以下教程进行安装:
www.sqlshack.com/how-to-install-sql-server-express-edition
只需将这些观察结果记在心里:
-
功能选择屏幕还建议安装SQL Server 复制。如果您还想单独测试 R、Python 和 SQL Server 中可用的全文搜索,我们建议选择以下选项,排除复制和 Java 选项:
![图 7.16 – 建议的实例特征]()
图 7.16 – 建议的实例特征
重要的是要保持共享功能默认选中,以便安装连接到实例所需的 ODBC 驱动程序。
-
记住将
sa(系统管理员)用户的密码保存在安全的地方,因为它提供了作为管理员安装实例的访问权限。 -
教程会在步骤 5中要求你安装 SSMS。如果你还没有安装,现在就安装它。
-
为了使用 SSMS 连接到你的 SQL Server Express 实例,而不是计算机名和实例名,你也可以使用
.\SQLExpress作为服务器名。话虽如此,教程建议使用SQL Server 身份验证和sa账户凭证测试你的连接。记住,你也可以直接使用Windows 身份验证连接到你的实例,因为你的用户会被自动添加到SQL Server 管理员组。正如教程所说,如果在点击连接后登录窗口没有任何问题地关闭,这意味着连接工作正常。
只需停在Windows 身份验证部分。以下适用:
重要提示
确保从 SSMS 到新的
SQLExpress实例的连接工作正常非常重要。这确认了实例安装正确。
安装完成后,你可以通过以下步骤验证 ODBC 驱动程序是否已安装:
-
点击 Windows开始按钮,并开始输入字符串
ODBC。你会看到类似以下的内容:![图 7.17 – Windows ODBC 数据源配置工具]()
图 7.17 – Windows ODBC 数据源配置工具
-
点击 32 位应用程序或 64 位应用程序,然后点击驱动程序选项卡。你会看到已安装的ODBC Driver 17 for SQL Server:

图 7.18 – 正确安装的 ODBC Driver 17 for SQL Server
太棒了!现在你的SQLExpress实例工作正常。
让我们看看如何通过 Azure 门户配置 Azure SQL 数据库。
创建 Azure SQL 数据库
为了创建 Azure SQL 数据库,你必须订阅 Azure 服务。这让你能够访问Azure 门户(portal.azure.com/)来管理你租户的所有 Azure 资源。一旦你访问了门户,请按照以下步骤操作:
-
在主搜索框中搜索
SQL 数据库并点击它:![图 7.19 – 在 Azure 门户中选择 SQL 数据库]()
图 7.19 – 在 Azure 门户中选择 SQL 数据库
-
点击页面中间的创建 SQL 数据库:
![图 7.20 – 点击创建 SQL 数据库]()
图 7.20 – 点击创建 SQL 数据库
-
你需要选择与你的账户关联的订阅(通常,如果你只有一个订阅,则选中的默认订阅即可)。然后选择一个资源组来收集你想要创建的所有资源(例如,一个虚拟文件夹)。如果你已经创建了一个,就选择它;如果需要,就创建一个新的。
-
你还必须提供数据库名称和服务器。作为数据库名称,使用
SystemsLogging。如果你是第一次创建 Azure SQL 数据库,你还需要创建一个新的服务器。因此,点击创建新服务器并提供一个服务器名称、登录名、相关的密码以及你偏好的位置。点击确定后,你会看到类似以下的内容:![图 7.21 – 输入数据库名称和新服务器]()
图 7.21 – 输入数据库名称和新服务器
-
由于我们正在创建一个用于测试目的的 Azure SQL 数据库,我们可以选择一个标准数据库事务单元(DTU)工作负载。因此,点击计算+存储下的配置数据库:
![图 7.22 – 配置计算]()
图 7.22 – 配置计算
默认选择通用用途选项。因此,点击左上角的寻找基本、标准、高级?链接,然后点击标准工作负载选项。然后,点击应用按钮。
-
在附加设置选项卡中,通过点击示例来选择安装示例数据(来自演示AdventureWorkLT数据库):
![图 7.2 – 选择安装示例数据库]()
图 7.2 – 选择安装示例数据库
-
点击审查+创建,然后点击创建以部署你全新的 Azure SQL 数据库。
-
部署完成后,点击转到资源以访问新创建资源的通用仪表板。在右上角,你会注意到服务器的名称采用以下形式:
<your-server-name>.database.windows.net -
为了从任何客户端访问你的 Azure SQL 数据库,你必须声明客户端的 IP 地址在服务器防火墙规则中。为此,点击仪表板顶部的设置服务器防火墙:
![图 7.24 – 在 Azure SQL 服务器防火墙上设置规则]()
图 7.24 – 在 Azure SQL 服务器防火墙上设置规则
除了其他选项外,你还会看到用于输入规则名称、起始 IP和结束 IP的文本框。此外,门户还会显示你当前连接的 IP 地址:
![图 7.25 – 复制当前 IP 地址并在规则中使用它]()
图 7.25 – 复制当前 IP 地址并在规则中使用它
如果您将从同一台机器连接到 Azure SQL 数据库,可以将您的客户端 IP 地址作为起始和结束 IP 地址输入。请注意,如果您的机器的公共 IP 地址不是静态的,它将在下一次重启时假设一个新的 IP 地址,与之前的地址不同。因此,如果您需要经常从您的机器连接到 Azure SQL 数据库,请确保创建一个 静态公共 IP 地址。如果您刚刚创建了一个新的 Azure 虚拟机,Azure 将在您第一次停止它时询问您是否想要将其当前公共 IP 地址设置为静态。如果您选择是,Azure 将自动完成所有操作。否则,您可以通过遵循以下教程轻松地在以后配置它:
bit.ly/AzureVM-assign-static-ip点击 保存 以保留防火墙设置中的更改。 -
到这一步,您可以使用 SSMS 测试到您新 Azure SQL 数据库的连接。如果您还没有安装它,请现在通过从链接
aka.ms/ssms下载安装程序进行安装。安装完成后,从 开始 菜单打开 Microsoft SQL Server Management Studio 应用程序(您可以在 Microsoft SQL Server 工具 XX 文件夹下找到它)。使用<your-server-name>.database.windows.net格式的服务器名称,选择 SQL Server 身份验证 作为身份验证方法,然后输入您在创建 Azure SQL 数据库时使用的登录名和密码。然后点击 连接:![图 7.26 – 使用 SSMS 连接到您的 Azure SQL 数据库]()
图 7.26 – 使用 SSMS 连接到您的 Azure SQL 数据库
-
如果 连接到服务器 窗口消失了,那么您已经连接到您的服务器。实际上,如果您在左侧的 对象资源管理器 中打开 数据库 节点,您可以看到包含
AdventureWorkLT表的SystemsLogging数据库:

图 7.27 – 您已连接到您的 Azure SQL 数据库
太棒了!您的 Azure SQL 数据库正在运行,并准备好使用。
现在我们尝试使用 Python 读取和写入 SQL Server 或 Azure SQL 数据库中的数据。
使用 Python 记录到 Azure SQL 服务器
pyodbc 是最广泛使用的 Python 模块,用于连接通过 ODBC 驱动程序可以连接的数据库。因此,首先您需要在 pbi_powerquery_env 环境中安装此包:
-
打开 Anaconda Prompt。
-
通过输入以下命令切换到
pbi_powerquery_env环境:conda activate pbi_powerquery_env -
通过输入以下命令安装新包:
pip install pyodbc
到这一步,您可以使用 Python 与您的数据库实例进行交互。
使用 pyodbc 模块
首先,你需要创建一个连接到你的数据库实例。你可以通过connect()函数来完成,该函数接受一个连接字符串作为参数。根据你是否需要连接到本地 SQL 服务器实例或 Azure SQL 数据库,连接字符串仅在服务器参数上有所不同。
你可以使用以下代码使用 Windows 认证建立与本地实例的连接:
import pyodbc
conn = pyodbc.connect(
'Driver={ODBC Driver 17 for SQL Server};'
r'Server=.\SQLExpress;'
'Database=master;'
'Trusted_Connection=yes;')
你也可以在Chapter07\Python文件夹中的11-read-write-on-azure-sql-server-with-python.py文件中找到这里使用的代码片段。与存储库中找到的其他 Python 脚本不同,在这个脚本中你可以找到像# %%这样的注释。它们是 VS Code 识别为Jupyter-like 代码单元的占位符。当 VS Code 识别到一个单元时,它会自动添加命令以在交互窗口中执行其内容,这使得用户与代码的交互更加容易。
如果你想使用 SQL 认证而不是 Windows 认证来连接到相同的实例,你可以使用以下代码:
import pyodbc
conn = pyodbc.connect(
'Driver={ODBC Driver 17 for SQL Server};'
r'Server=.\SQLExpress;'
'Database=master;'
'Uid=sa;'
'Pwd=<your-password>)
即使你想连接到 Azure SQL 数据库,之前的连接字符串格式仍然保持不变。你必须使用 <your-server-name>.database.windows.net 作为服务器名称。认证模式必须是 SQL 认证模式。因此,连接到你的 Azure SQL 数据库的代码如下:
import pyodbc
conn = pyodbc.connect(
'Driver={ODBC Driver 17 for SQL Server};'
'Server=<your-server-name>.database.windows.net;'
'Database=SystemsLogging;'
'Uid=<your-username>;'
'Pwd=<your-password>')
一旦你连接到了你选择的实例,你可以通过 pandas 的read_sql()函数从表或视图中读取数据,该函数接受一个 SQL 查询(在我们的案例中,是 SQL Server 的T-SQL)作为参数。例如,无论你连接到的是本地实例还是 Azure,你都可以运行以下代码来读取实例中可用的数据库信息:
import pandas as pd
data = pd.read_sql("SELECT database_id, name FROM sys.databases", conn)
data.head()
在 Azure SQL 的情况下,你会看到以下结果:

图 7.28 – Azure SQL 数据库中的查询结果
让我们尝试将一些内容写入数据库。首先,在你的本地实例的情况下,你需要创建一个新的数据库来写入你的数据。你可以在 SSMS 中按照以下步骤完成:
-
点击连接,然后点击数据库引擎…:
![图 7.29 – 在 SSMS 中连接到数据库引擎]()
图 7.29 – 在 SSMS 中连接到数据库引擎
-
使用 Windows 认证,并以字符串
.\SQLExpress作为服务器名称连接到你的SQLExpress实例。然后,点击连接:![图 7.30 – 连接到你的 SQLExpress 实例]()
图 7.30 – 连接到你的 SQLExpress 实例
-
点击工具栏顶部的新建查询:
![图 7.31– 打开新的查询编辑器]()
图 7.31– 打开新的查询编辑器
-
输入
CREATE DATABASE SystemsLogging脚本,然后点击带有绿色箭头的执行按钮(或者直接按F5键)。 -
在对象资源管理器中打开数据库节点,你现在可以看到全新的
SystemsLogging数据库:

图 7.32 – 你的 SQLExpress 实例中的新 SystemsLogging 数据库
现在你可以创建SystemsLogging数据库中的新WrongEmails表。通常,直接在 SSMS 中运行数据定义语言(DDL)(如CREATE)命令(如CREATE)更可取。在这种情况下,我们将通过 Python 来展示一些特殊的命令。你首先从conn连接创建一个cursor对象,然后调用它的execute()方法,传递给它一个CREATE TABLE查询:
conn = pyodbc.connect(
'Driver={ODBC Driver 17 for SQL Server};'
r'Server=.\SQLExpress;'
'Database=SystemsLogging;'
'Trusted_Connection=yes;')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE WrongEmails
(
UserId int,
Email nvarchar(200)
)
''')
conn.commit()
请记住,Azure SQL Server 数据库将其对象(表、视图等)收集在SQL 模式中。通常,当你创建数据库对象时,你也会在CREATE语句中指定模式。对于一个表,你通常使用CREATE TABLE <your-schema>.<table-name>脚本。WrongEmails表是在没有指定任何模式的情况下创建的。因此,它假定默认模式,即dbo。
确保在你的 Azure SQL 数据库中创建相同的表:
conn = pyodbc.connect(
'Driver={ODBC Driver 17 for SQL Server};'
'Server=<your-server-name>.database.windows.net;'
'Database=SystemsLogging;'
'Uid=<your-username>;'
'Pwd=<your-password>')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE WrongEmails
(
UserId int,
Email nvarchar(200)
)
''')
conn.commit()
在这个阶段,你可以使用cursor.execute()方法,逐行在WrongEmails表内写入pandas DataFrame 内容,传递给它一个INSERT INTO查询。在示例中,我们将使用 Azure SQL 数据库,因为那里也有SalesLT.Customer表(注意SalesLT模式),我们可以从中读取一些客户数据写入到WrongEmails表:
# Get data from sample Customers
data = pd.read_sql('SELECT TOP 10 CustomerID, EmailAddress FROM SalesLT.Customer', conn)
# Write Customers data into the WrongEmails table
cursor = conn.cursor()
# Write a dataframe into a SQL Server table row by row:
for index, row in data.iterrows():
cursor.execute("INSERT INTO WrongEmails (UserId, Email) values(?,?)", row.CustomerID, row.EmailAddress)
conn.commit()
cursor.close()
iterrows()函数遍历 DataFrame 的列,并返回一个包含列名和以 series 形式的内容的元组。请记住,如果你想要写入到本地 SQL 服务器,你只需要更改连接字符串,而你刚才看到的语法仍然有效。你无法直接在你的SQLExpress实例中运行代码,因为那里没有AdventureWorksLT示例数据,所以它会给你一个错误。
要显示WrongEmails表的前几行,你可以使用以下代码:
df = pd.read_sql('SELECT TOP 10 UserId, Email FROM WrongEmails', conn)
df.head()
你在 VS Code 中会看到类似的内容:

图 7.33 – WrongEmails 表的内容
现在,确保使用以下命令清空WrongEmails表,以便它可以准备用于后续使用:
cursor = conn.cursor()
cursor.execute('TRUNCATE TABLE WrongEmails')
conn.commit()
当你在数据库实例上完成所有读写操作后,记得按照以下方式关闭连接:
conn.close()
嘿!你刚刚学会了如何通过 Python 从 SQL Server 或 Azure SQL 数据库读取和写入数据。简单,不是吗?让我们将你学到的知识应用到 Power BI 中。
使用 Python 在 Power BI 中将电子邮件和日期记录到 Azure SQL 数据库
在本节中,我们将使用 第五章 中提供的相同场景,即 在 Power BI 中使用正则表达式,其中您验证了电子邮件地址和禁用日期。目标是记录包含不正确电子邮件的数据集的行到 Azure SQL 数据库,并从数据集中过滤它们,以便在 Power BI 中只保留有效的电子邮件。为了正确执行以下 Python 代码,您需要确保您已创建了前面部分讨论的 Azure SQL SystemsLogging 数据库和 WrongEmails 表。如果您愿意,您也可以使用您本地的 SQL Server 实例,只需在连接字符串中适当更改服务器名称。在这种情况下,请确保 SystemsLogging 数据库和 WrongEmails 表存在。
必要的步骤如下:
-
按照第五章 在 Power BI 中使用正则表达式 的 使用 regex 在 Power BI 中验证电子邮件的 Python 部分 中的所有步骤进行操作,但不要点击 关闭并应用。
-
然后点击 运行 Python 脚本,输入您可以在
Python\12-log-wrong-emails-azure-sql-in-power-bi.py文件中找到的脚本,然后点击 确定。 -
点击
df数据集的 表 值。 -
只会保留包含有效电子邮件的行:
![图 7.34 – 仅包含有效电子邮件的表]()
图 7.34 – 仅包含有效电子邮件的表
此外,无效的电子邮件已经写入您的
SystemsLoggingAzure SQL 数据库的WrongEmails表中。 -
返回到 主页 菜单,然后点击 关闭并应用。
为了验证无效的电子邮件确实已写入前面的表,您可以使用 SSMS 进行以下操作:
-
使用
<your-server-name>.database.windows.net字符串作为 服务器名称 和 SQL 身份验证连接到 SSMS 的 Azure SQL 数据库。 -
打开 数据库 节点,然后打开 表 节点,右键单击 dbo.WrongEmails 表,然后点击 选择前 1000 行:
![图 7.35 – 在 SSMS 中查询 WrongEmails 表]()
图 7.35 – 在 SSMS 中查询 WrongEmails 表
您将看到以下输出:

图 7.36 – SSMS 中 WrongEmails 表的内容
现在,第三方系统可以简单地访问您的 Azure SQL 数据库(甚至可以使用 Excel,参见 参考文献 部分)来读取无效的电子邮件并动员适当的团队进行纠正。
干得好!您刚刚学会了如何使用 Python 从 Power BI 将信息记录到 Azure SQL 数据库(您只需更改连接字符串即可将相同的内容写入本地 SQL Server 数据库)。现在,让我们看看如何使用 R 来完成同样的操作。
使用 R 记录到 Azure SQL 服务器
要通过 R 中的 ODBC 驱动程序连接到数据库,我们将使用两个包:DBI 和 odbc。DBI 包的任务是将数据库连接分为一个 前端 和一个 后端。您将要编写的 R 代码将仅使用公开的前端 API。后端将通过其他包安装提供的特殊驱动程序与特定的 DBMS 进行通信。在我们的例子中,odbc 包将使我们能够与 SQL Server 实例进行接口,无论是在本地还是在 Azure 上。因此,首先您需要在这些最新的 MRO 引擎中安装这些包:
-
打开 RStudio,并确保它在 全局选项 中引用您最新的 MRO。
-
将此命令输入到控制台:
install.packages(c('odbc', 'DBI'))
到目前为止,您可以使用 R 与您的数据库实例进行交互。
使用 DBI 和 odbc 包
此外,在这种情况下,您需要 创建一个连接 到您的数据库实例。您可以使用 DBI 包的 dbConnect() 函数来完成此操作,该函数接受一个 驱动对象(在我们的例子中是来自 odbc 包的 odbc())和一个 连接字符串 作为参数。
您可以使用以下代码使用 Windows 认证建立与本地 SQLExpress 实例的连接:
R\04-read-write-on-azure-sql-server-with-r.R
library(odbc)
library(DBI)
conn <- dbConnect(
odbc::odbc(), server = r'{.\SQLExpress}',
database = 'SystemsLogging', trusted_connection = 'yes',
driver = '{ODBC Driver 17 for SQL Server}'
)
如果您想使用 SQL 认证而不是使用相同的实例,可以使用以下代码:
conn <- dbConnect(
odbc::odbc(), server = r'{.\SQLExpress}',
database = 'SystemsLogging', uid = 'sa',
pwd = '<your-password>',
driver = '{ODBC Driver 17 for SQL Server}'
)
即使您想要连接到 Azure SQL 数据库,之前的连接字符串格式仍然保持不变。您只需使用 <your-server-name>.database.windows.net 格式作为服务器名称。在这种情况下,认证模式必须是 SQL 认证。因此,连接到您的 Azure SQL 数据库的代码如下:
conn <- dbConnect(
odbc::odbc(), server = '<your-server>.database.windows.net',
database = 'SystemsLogging', uid = '<your-username>',
pwd = '<your-password>',
driver = '{ODBC Driver 17 for SQL Server}'
)
一个有趣的事情是,一旦建立了连接,RStudio 允许您通过右上角的 连接 选项卡浏览服务器内的数据库。例如,图 7.37 显示了 SQLExpress 实例的 SystemLogging 数据库的内容,包括表中的各个列的详细信息:

图 7.37 – RStudio 的连接对象浏览器
记住,WrongEmails 表是在前面的部分创建的。
一旦您已连接到所选实例,您就可以轻松地通过 DBI 的 dbGetQuery() 函数从表或视图中读取数据,该函数接受一个 SQL 查询(在我们的例子中是 SQL Server 的 T-SQL)作为参数。例如,与 Python 一样,您可以运行以下代码来读取本地实例和 Azure SQL 服务器上可用的数据库信息:
data <- DBI::dbGetQuery(conn, "SELECT database_id, name FROM sys.databases")
head(data)
在 Azure SQL 的情况下,您将看到以下结果:

图 7.38 – Azure SQL 数据库查询的结果
让我们尝试将一些内容写入数据库。例如,你可以使用 dbAppendTable() 方法将 R DataFrame 内容写入 WrongEmails 表。它简单地接受连接对象、目标表名称以及包含源数据的 DataFrame。你只需在从数据库读取时,使用别名在 SQL 查询中(使用 AS 关键字)正确重命名源数据列,以确保名称与目标表列匹配。我们将使用 Azure SQL 数据库作为示例,因为还有一个 SalesLT.Customer 表,可以从其中获取源数据:
customers_df <- dbGetQuery(conn, "SELECT TOP 10 CustomerID AS UserId, EmailAddress AS Email FROM SalesLT.Customer")
dbAppendTable(conn, name = 'WrongEmails', value = customers_df)
请记住,如果你需要创建目标表并填充它,可以使用一次性的 dbCreateTable() 方法,该方法获取与 dbAppendTable() 方法相同的参数。要显示 WrongEmails 表的前几行,你可以使用以下代码:
df <- dbGetQuery(conn, "SELECT TOP 10 UserId, Email FROM WrongEmails")
head(df)
你在 VS Code 中会看到类似的内容:

图 7.39 – WrongEmails 表的内容
现在,请确保使用 dbSendQuery() 方法中的 TRUNCATE TABLE SQL 命令清空 WrongEmails 表(该方法仅执行查询而不检索任何数据),以便它可以准备使用:
dbSendQuery(conn, "TRUNCATE TABLE WrongEmails")
当你完成对数据库实例的所有读取和写入操作后,请记住按照以下方式关闭连接:
dbDisconnect(conn)
哇!你刚刚学会了如何使用 R 读取和写入 SQL 服务器或 Azure SQL 数据库中的数据!让我们将你所学应用到 Power BI 中。
使用 R 在 Power BI 中记录电子邮件和日期
在本节中,我们将使用之前用于展示如何从 Power BI 记录到 Azure SQL 的相同场景。为了正确执行以下 R 代码,你需要确保你已经创建了 Azure SQL 的 SystemsLogging 数据库和 WrongEmails 表,正如在 使用 Python 记录到 Azure SQL 服务器 部分中讨论的那样。如果你愿意,你也可以使用你的本地 SQL Server 实例,只需适当地更改连接字符串中的服务器名称。在这种情况下,请确保 SystemsLogging 数据库和 WrongEmails 表存在。必要的步骤如下:
-
按照第五章 在 Power BI 中使用正则表达式 的 使用正则表达式在 Power BI 中使用 regex 验证电子邮件 部分的所有步骤进行操作,直到结束,但不要点击 Close & Apply。
-
然后,点击 Run R Script,输入你可以在
R\05-log-wrong-emails-azure-sql-in-power-bi.R文件中找到的脚本,然后点击 OK。请记住在代码中编辑服务器的名称和你的凭据。 -
点击
df数据集的 Table 值。 -
只有包含有效电子邮件的行将被保留:
![图 7.40 – 仅包含有效电子邮件的表]()
图 7.40 – 仅包含有效电子邮件的表
此外,无效的电子邮件已经写入你的 Azure SQL 数据库中的
WrongEmails表。 -
返回主页菜单,然后点击关闭并应用。
如前所述,您可以使用 SSMS 验证无效的电子邮件是否已写入上一个表。您将看到以下输出:

图 7.41 – SSMS 中 WrongEmails 表的内容
太棒了!您已使用 R 从 Power BI 将错误的电子邮件记录到您的 (Azure) SQL Server 数据库中。
摘要
在本章中,您学习了如何使用非常简单直接命令,在 Python 和 R 中将 Power Query 处理的一些信息记录到 CSV 文件、Excel、本地 SQL 服务器和 Azure SQL 中。
在下一章中,您将了解如何处理由于您的机器 RAM 大小不足而无法从 Power BI Desktop 加载的非常大的 CSV 文件。
参考文献
如需进一步阅读,请查看以下书籍和文章:
-
逗号分隔值 (CSV) 文件的常见格式和 MIME 类型 (
tools.ietf.org/html/rfc4180) -
Excel (.xlsx) 扩展 Office Open XML 电子表格文件格式 (
docs.microsoft.com/en-us/openspecs/office_standards/ms-xlsx/) -
将 Excel 连接到 Azure SQL 数据库中的数据库 (
docs.microsoft.com/en-us/azure/azure-sql/database/connect-excel)
第九章:在 Power BI 中加载超过可用 RAM 的大型数据集
在上一章中,你学习了如何使用 Python 和 R 从 CSV 文件中读取和写入数据。当涉及到读取文件时,无论你使用 Power BI 的标准数据导入功能还是上一章中展示的技术,文件大小的主要限制是由于 Power BI Desktop 安装的机器上可用的 RAM 量。
在数据丰富阶段,可能需要从非常大的文件(以 TB 为单位的大小)中提取用于持续分析所需的信息。在这些情况下,几乎总是需要实施大数据解决方案来处理如此大量的数据。然而,非常常见的情况是需要导入比可用 RAM 稍大的文件,以便提取汇总信息,然后将其持久化到一个小表中,以便在处理过程中重复使用。在这种情况下,没有必要烦恼于要求苛刻的大数据平台,但可以利用 Python 和 R 中实现分布式计算系统的特定包提供的灵活性,而无需求助于基于 Apache Spark 的后端。
在本章中,你将学习以下主题:
-
使用大型数据集的典型分析场景
-
使用 Python 导入大型数据集
-
使用 R 导入大型数据集
技术要求
本章要求你有一个工作的互联网连接,并且Power BI Desktop已经安装在你的机器上。你必须已经按照第二章、配置 Power BI 中的 R和第三章、配置 Power BI 中的 Python中概述的方式正确配置了 R 和 Python 引擎和 IDE。
使用大型数据集的典型分析场景
数据科学家最频繁的活动之一是分析与业务场景相关的信息数据集。分析的目标是能够识别变量之间的关联和关系,这有助于以某种方式发现业务的新可衡量方面(见解),然后可以用来使业务更好地增长。可能的情况是,可用的数据可能不足以确定变量之间的强关联,因为任何额外的变量可能都没有被考虑。在这种情况下,尝试获取新的数据,这些数据不是由你的业务生成的,但可以丰富你的数据集的背景(一个数据增强过程),可以提高你变量之间统计关联的强度。例如,能够将天气预报数据与报告大坝水位测量的数据集联系起来,无疑引入了重要的变量,以更好地解释现象。
在这种情况下,你经常发现自己需要从外部数据源下载的 CSV 文件中提取信息。例如,假设你被分配了一个分析从 1987 年到 2012 年在美国主要机场的鞋店连锁店的盈利性受哪些因素影响的任务。首先出现在你脑海中的可能是,航班延误可能与人们留在机场有关。如果你必须花时间在机场,你肯定有更多的时间去参观那里的各种商店,因此你购买的机会也会增加。那么,你如何找到每个美国机场每年每一天的平均航空公司延误数据呢?幸运的是,研究和创新技术管理局 (RITA),运输统计局提供了包含美国所有商业航班到达和出发详细信息的汇总统计数据(bit.ly/airline-stats)和原始数据(bit.ly/airline-stats-data)。一套包含从 1987 年到 2012 年每月航空公司数据的 CSV 文件已经由微软收集并压缩,你可以直接从以下链接下载:bit.ly/AirOnTime87to12。如果你想了解更多关于文件字段的信息,请参阅 AirOnTime87to12.dataset.description.txt 文件。
有关的问题压缩文件大约有 4 GB 大小,一旦解压,包含从 1987 年到 2012 年间在美国完成的许多 CSV 文件,详细记录了航班数据,总大小为 30 GB!你的目标是计算每个出发机场的平均每日航班延误,并将结果数据集保存为 CSV 文件。你如何在 Power BI 中导入所有这些数据呢?让我们看看如何在 Python 中完成这项任务。
使用 Python 导入大型数据集
在 第三章,配置 Power BI 中的 Python 中,我们建议你在环境中安装一些最常用的数据管理包,包括 NumPy、pandas 和 scikit-learn。这些包的最大限制是它们无法处理大于它们所使用机器 RAM 的数据集,因此它们无法扩展到多台机器。为了遵守这一限制,通常使用基于 Spark 的分布式系统,Spark 已成为大数据分析领域的主导工具。然而,转向这些系统迫使开发者必须重新思考已经用 API 编写的代码,该 API 称为 PySpark,它旨在使用 Python 对 Spark 对象进行操作。这个过程通常被视为导致项目交付延迟,并给开发者带来挫败感,因为开发者对标准 Python 中可用的库有更大的信心。
针对前面提到的问题,社区开发了一个名为 Dask 的新库,用于 Python 的并行计算(dask.org/)。这个库为开发者提供了透明的方式来更原生地扩展 pandas、scikit-learn 和 NumPy 工作流程,而无需进行大量的重写。Dask API 几乎是那些模块 API 的复制品,这使得开发者的工作更加容易。
Dask 的一大优势是你不需要设置一个由多台机器组成的集群来操作 100+ GB 的数据集。你只需要一台配备多核 CPU 和 32 GB RAM 的笔记本电脑就可以轻松处理它们。因此,得益于 Dask,你可以在自己的笔记本电脑上对中等规模的数据集进行分析,而无需承担集群的典型开销,例如在各个节点上使用 Docker 镜像和复杂的调试。
重要提示
显然,即使是 Spark 团队也意识到了开发者使用 PySpark 时,习惯于以 pandas 作为数据处理模块进行开发所带来的不便。因此,他们推出了 Koalas(
koalas.readthedocs.io),它为 Apache Spark 提供了 pandas API。
事实仍然是,Dask 在仅使用笔记本电脑上的分布式系统方面比 Spark 具有许多优势。例如,Spark 基于一个 Java 虚拟机(JVM)基础设施,因此需要安装 Java 和其他组件,而 Dask 则是用纯 Python 编写的。此外,使用 Dask 可以更快地从笔记本电脑过渡到云端的集群,这可以通过 Azure 等平台轻松分配。这一切都得益于 Dask Cloud Provider 包(cloudprovider.dask.org/),它提供了在各个云平台上创建和管理临时 Dask 集群的类。如果你需要通过 Azure Spot 虚拟机或在 Azure 机器学习计算集群(例如使用 NVIDIA RAPIDS 进行 GPU 加速的数据科学)中利用 Azure 机器学习来创建 Dask 集群,请查看相关参考。
回到我们的话题,让我们看看如何在你的笔记本电脑上安装 Dask。
在你的笔记本电脑上安装 Dask
你将在已经安装了 pandas 和 NumPy 库的 pbi_powerquery_env 环境上安装 Dask。这次,仅仅运行 pip install dask 命令是不够的,因为这样你只会安装 Dask 的核心部分。对于 Dask 用户来说,正确的方式是安装所有组件。为了显示 Dask 操作执行计划的图形,还必须安装 Graphviz 模块。为了完成所有这些,请按照以下步骤操作:
-
打开你的 Anaconda 提示符。
-
切换到你的
pbi_powerquery_env环境,输入以下命令:conda activate pbi_powerquery_env -
输入以下命令来安装 Dask 的所有组件:
pip install "dask[complete]" -
输入以下命令来安装 Graphviz 的所有组件:
pip install graphviz
你还需要在 Windows 中安装 Graphviz 可执行文件:
-
访问
www.graphviz.org/download/,然后下载并安装稳定的 Windows 安装包。 -
在安装过程中,选择将 Graphviz 添加到当前用户的系统路径中。
在此,让我们探讨 Dask 提供的结构,这些结构允许你扩展常见的接口,例如 NumPy、pandas 和 Python 迭代器,以处理比可用内存更大的对象。
创建 Dask DataFrame
Dask DataFrame 是 Dask 大数据 集合的一部分,允许 pandas、NumPy 和 Python 迭代器轻松扩展。除了 Dask DataFrame,它是 pandas DataFrame 的对应物之外,Dask Array(模仿 NumPy)、Dask Bag(模仿迭代器)和Dask Delayed(模仿循环)也是集合的一部分。然而,我们将专注于 Dask DataFrame,这将使我们能够实现本章开头设定的分析目标。
Dask DataFrame 实际上不过是一组 pandas DataFrame,它们可以驻留在单个机器的磁盘上,或者驻留在集群的多个节点上,允许你管理比笔记本电脑 RAM 更大的数据集。我们假设你已经解压了本章开头提到的包含 1987 年至 2012 年美国航班数据的 CSV 文件,并将其放置在D:\<your-path>\AirOnTimeCSV文件夹中。
重要提示
如果你的笔记本电脑没有足够的硬件资源(至少 16 GB 的 RAM),你应该首先导入 CSV 文件的一个子集(例如 40-50 个文件)来测试脚本,以免等待过长的执行时间或发生内存错误。
然后,你可以非常容易地以下述方式创建你的 Dask DataFrame:
import os
import dask.dataframe as dd
main_path = os.path.join('D:\\', 'your-path', 'AirOnTimeCSV')
ddf = dd.read_csv(
os.path.join(main_path, 'airOT*.csv'),
encoding='latin-1',
usecols =['YEAR', 'MONTH', 'DAY_OF_MONTH', 'ORIGIN', 'DEP_DELAY']
)
注意到通配符*字符允许捕获文件夹中所有形式为airOTyyyymm.csv的 CSV 文件,其中yyyy表示年份,mm表示航班出发日期的月份。此外,CSV 文件的编码声明为latin-1。
重要提示
下载的 CSV 文件没有指示其编码。简单地尝试导入它们而不声明(因此默认假设为
utf-8),加载会返回以下奇怪的错误:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe4 in position 4: invalid continuation byte。在网上搜索,很容易发现这种错误与编码有关,而latin-1是正确的编码。
此外,通过usecols参数仅指定感兴趣的列是一个好主意,以限制要读取的列。这种做法还保证了只读取你确信不是完全空的列,从而避免由于与实际类型不同的推断数据类型而导致的读取错误。
重要提示
可能会发生某些列在开始时具有许多空值,因此 Dask 无法推断正确的数据类型,因为它使用样本来做到这一点。在这种情况下,你应该通过使用
dtype参数特别声明这些列的数据类型。
现在 Dask DataFrame 已经创建,让我们看看如何使用它来提取我们所需的信息。
从 Dask DataFrame 中提取信息
如果你已经运行了读取所有 CSV 文件的代码,你会注意到操作花费了非常少的时间。回想起来,这真的是读取 30 GB 数据所需的时间太少了。难道读取没有成功吗?大多数并行计算框架的秘密就与这个特性紧密相连:读取操作并没有真正执行,而是被添加到了一个可能的操作队列中,当你明确请求使用数据时,这些操作将被执行。这个概念被称为 延迟评估 或 延迟计算。
然后,你的 Dask DataFrame 可以在后续操作中使用,就像它已经包含数据一样。在我们的案例中,由于需要计算美国每个机场每年每一天的平均航空延误,考虑使用以下代码:
mean_dep_delay_ddf = ddf.groupby(['YEAR', 'MONTH', 'DAY_OF_MONTH', 'ORIGIN'])[['DEP_DELAY']].mean().reset_index()
如果你稍微熟悉 pandas DataFrame 的转换,你会注意到 Dask DataFrame 也使用了相同的方法。与 pandas 一样,你必须使用 双方括号 来输出 DataFrame;否则,你会得到一个单对括号的一维序列(查看这里:bit.ly/pandas-subset-df)。此外,为了将 groupby() 方法创建的索引作为 DataFrame 的列使用,你需要在 reset_index() 方法中重置它们(更多详情,请参阅:bit.ly/pandas-groupby)。
执行这段代码也花费了非常少的时间。正如你所想象的,平均操作已经在数据读取操作之后在转换队列中排队,在这个例子中,它被分配给了 mean_dep_delay_ddf DataFrame。如果你想更好地了解到目前为止转换队列的执行计划,你可以创建一个图来表示它。为了简单起见,我们将使用单个 CSV 文件作为输入来实现这个图。以下是必要的步骤:
-
创建一个名为
AirOnTimeCSVplot的文件夹。 -
只复制你之前解压缩的第一个 CSV 文件到之前的文件夹中。
-
在 Visual Studio Code 中打开一个新的 Python 脚本并运行以下代码:
import os import dask.dataframe as dd main_path = r'C:\<your-path>\AirOnTimeCSVplot' ddf_1_month = dd.read_csv( os.path.join(main_path, 'airOT*.csv'), encoding='latin-1', usecols =['YEAR', 'MONTH', 'DAY_OF_MONTH', 'ORIGIN', 'DEP_DELAY'] ) mean_dep_delay_1_month_ddf = ddf_1_month.groupby(['YEAR', 'MONTH', 'DAY_OF_MONTH', 'ORIGIN'])[['DEP_DELAY']].mean().reset_index() mean_dep_delay_1_month_ddf.visualize(filename='mean_dep_delay_1_month_dask.pdf')
visualize() 方法允许你可视化由引擎估计的任务图,以实现排队的转换,甚至在它们执行之前。具体来说,代码将在你运行的脚本所在的同一文件夹中生成一个 PDF 文件。
从图 8.1的底部开始,它表示新生成的 PDF 文件的内容,你可以看到单个源 CSV 文件被read-csv函数从由引擎分割的两个块中读取。dataframe-groupby-count-chunk和dataframe-groupby-sum-chunk函数应用于每个块,因为对于由分组操作的键定义的每个元组(YEAR,MONTH,DAY_OF_MONTH和ORIGIN),我们需要知道延迟总和(DEP_DELAY)和发生次数的总和来计算平均值。之后,dataframe-groupby-sum-agg函数对两个块上的两个dataframe-groupby-sum-chunk操作的结果进行聚合。同样,dataframe-groupby-count-agg函数聚合了两个dataframe-groupby-count-chunk操作的输出。一旦确定了总和和计数的数据框,就使用truediv函数计算每个分组之间的比率(即平均值)。最后,reset_index函数提供了所需的 DataFrame,这是分布式平均操作的结果。
如果你仔细思考,被称为分而治之(也称为Divide et Impera)的著名问题解决策略已经被采用。它包括将原始问题分解成更小且通常更简单的子问题,每个子问题递归解决。然后,将这些子问题的解决方案适当地组合起来,以获得原始问题的解决方案。如果你在 Hadoop 世界中有所经验,MapReduce范式遵循相同的哲学,后来 Spark 对其进行了维护和优化。

图 8.1 – 任务图背后的计算
话虽如此,让我们回到最初的脚本。我们定义了mean_dep_delay_ddf Dask DataFrame。获取所需结果所需的所有转换都已排队。你如何告诉 Dask 实际进行所有计算?你必须通过compute()方法显式请求结果。
重要提示
注意到
compute()返回的是内存中的结果。它将 Dask DataFrames 转换为 pandas DataFrames,Dask 数组转换为 NumPy 数组,Dask bags 转换为列表。这是一个只有在确定结果将舒适地适合你的机器的 RAM 时才应调用的方法。否则,你可以使用特定的方法,如to_textfiles()或to_parquet(),将你的数据写入磁盘。
如果你实例化了集群,你可以决定使用persist()方法将计算出的数据持久化到内存中,而不是磁盘上。结果仍然是一个分布式 Dask 对象,但它引用的是分布在集群内存中的预计算结果。
为了给你的脚本增添一些魔法,你可以使用ProgressBar()对象,它允许你监控计算进度。不幸的是,即使进度条达到 100%,工作者完成处理仍需要一些时间:

图 8.2 – Visual Studio Code 上的进度条
所以,不要放弃!在运行以下代码之前,请记住,在拥有 32GB RAM 和 6 核心处理器的机器上处理大约需要 10 分钟。脚本如下:
with ProgressBar():
mean_dep_delay_df = mean_dep_delay_ddf.compute()
你的笔记本电脑将把所有可用的逻辑处理器都用于并行计算,你将在任务管理器的逻辑处理器视图中看到这一点:

图 8.3 – 任务管理器中显示的并行计算
当处理完成后,你的 pandas DataFrame 将可用内存中,你可以如下查看它的几行:
mean_dep_delay_df.head(10)
输出将是以下内容:

图 8.4 – 输出 pandas DataFrame 的前 10 行
你可以在Chapter08\Python文件夹中的01-load-large-dataset-in-python.py文件中找到创建 Dask DataFrame 并从中提取信息的完整脚本。
最后,你能够通过处理高达 30GB 的 CSV 集来得到平均每家机场每天的航班延误数据集!
如果你想要将 Dask DataFrame 的内容写入 CSV 文件,而不经过生成 pandas DataFrame 的过程,你可以直接调用to_csv()方法,将生成文件的路径作为参数传递,如下例所示:
ddf.to_csv(r'D\<your-path>\mean_dep_delay_df.csv')
如你所想,调用to_csv()方法会触发所有排队转换的实际执行,就像调用compute()方法一样,因为你正在强制 Dask 读取 DataFrame 内容以便将其写入磁盘。因此,如果你需要生成 CSV 文件并创建稍后用于代码中的 pandas DataFrame,你不应该首先从 Dask DataFrame 调用to_csv(),然后调用compute()来获取 pandas DataFrame,因为你将强制转换管道的实际执行两次。在这种情况下,首先使用compute()生成 pandas DataFrame,然后从它生成 CSV 或 Excel 文件,就像你在第七章,从 Power BI 记录数据到外部源中学到的那样,会更方便。
现在我们来尝试应用你在 Power BI 中学到的知识。
使用 Python 在 Power BI 中导入大型数据集
你了解到 Power BI 可以直接使用 Python 脚本导入数据,并且数据必须以 pandas DataFrame 的形式组织,才能在数据模型中使用。因此,你将要开发一个脚本,使用前一小节中展示的对象来实例化一个包含美国航班延误平均数据的 pandas DataFrame。然后,从这个 DataFrame 中生成一个 CSV 文件。以下是实现它在 Power BI 中的步骤:
-
打开 Power BI 桌面版,确保它正在引用你的 Python
pbi_powerquery_env环境。 -
点击获取数据,开始输入
Python,然后双击Python 脚本。 -
将
02-load-large-dataset-in-power-bi.py文件的内容复制到Chapter08/Python文件夹中,并将其内容粘贴到 Python 脚本编辑器中。记得编辑 CSV 文件路径(main_path)和要生成的 CSV 文件的目标路径,然后点击确定。 -
大约 11 分钟后(使用一个 6 核心的笔记本电脑,32 GB 的 RAM),你将看到导航器窗口显示
mean_dep_delay_dfDataFrame 中的聚合数据:![图 8.5 – 在 Power BI 中加载的 mean_dep_delay_df DataFrame]()
图 8.5 – 在 Power BI 中加载的 mean_dep_delay_df DataFrame
选择 DataFrame,然后点击加载。之后,将出现一个加载弹出窗口,并保持活跃大约五分钟:
![图 8.6 – 将数据加载到 Power BI 的最终阶段]()
图 8.6 – 将数据加载到 Power BI 的最终阶段
当它消失时,你的数据已经加载到数据模型中。此外,脚本还生成了包含你的聚合数据的 CSV 文件。你将在文件夹中找到大约 60 MB 的
mean_dep_delay_df.csv文件:

图 8.7 – 从 Power BI 中的 Python 脚本创建的 CSV 文件内容
太棒了!你只用几行 Python 代码就成功将 30 GB 的数据导入到 Power BI 桌面版中,恭喜你!
你知道你还可以用 R 来做这件事吗?让我们看看怎么做。
使用 R 导入大型数据集
对于用于操作数据的 Python 包所展示的相同可伸缩性限制,在Tidyverse生态系统中的 R 包也存在。即使在 R 中,也无法使用大于机器可用 RAM 的数据集。在这些情况下采用的第一种解决方案也是切换到基于 Spark 的分布式系统,这些系统提供了SparkR语言。它提供了 R 中常用的 DataFrame 的分布式实现,支持过滤、聚合和选择操作,就像使用dplyr包一样。对于我们这些 Tidyverse 世界的粉丝来说,RStudio 积极开发sparklyr包,允许您使用dplyr的所有功能,即使是分布式 DataFrame。然而,由于运行所需的所有 Java 基础设施带来的开销,采用基于 Spark 的系统来处理总共占用略多于机器上可用 RAM 的 CSV 文件可能有些过度。
在这种情况下,采用disk.frame包(github.com/xiaodaigh/disk.frame)是获胜的方法。disk.frame允许您将大于 RAM 的数据集分成块,并将每个块存储在文件夹中的单独文件中。块文件是小于初始 DataFrame 的压缩fst格式的序列化,由fst包(github.com/fstpackage/fst)引入。它专门设计用来释放现代计算机中容易找到的固态硬盘的速度和并行性。存储在fst格式中的 DataFrame 具有完整的随机访问,包括列和行,因此能够从并行计算中受益。
重要提示
如果您想从
disk.frame包引入的并行性中受益,您必须必然将源 CSV 文件持久化存储在 SSD 磁盘上。如果您没有 SSD,并且使用 HDD 磁盘,您的处理将花费太长时间,而且不值得。
您可以通过指定您想要使用的并行工作进程的数量来定义disk.frame将使用的并行度。对于每个工作进程,它将使用future包提供的并行机制创建一个R 会话。
让我们详细看看如何使用disk.frame。
在您的笔记本电脑上安装 disk.frame
首先,您需要在您的 R 引擎中安装disk.frame包:
-
打开 RStudio 并确保它引用的是您最新的 CRAN(综合 R 档案网络)R 引擎(在我们的例子中,版本 4.0.2)。
-
点击控制台提示并输入此命令:
install.packages("disk.frame")。确保您安装了该包的最新版本(在我们的例子中,版本 0.5.0)。
到目前为止,您已经准备好在 RStudio 上测试它了。
创建 disk.frame 实例
为了创建一个 disk.frame 实例(基于磁盘的 DataFrame),您必须首先定义您想要初始化的并行工作器的数量。显然,工作器数量越多,disk.frame 实例创建得越快。但您不能初始化任何大量工作器,因为您可以使用并行线程的数量取决于您的机器的处理器。此外,您可以定义从工作器到另一个工作器可以交换的数据量的最大限制。因此,在开始使用 disk.frame 的脚本中,您会找到以下内容:
library(dplyr)
library(disk.frame)
n_cores <- future::availableCores() - 1
setup_disk.frame(workers = n_cores)
options(future.globals.maxSize = Inf)
availableCores() 函数计算您机器上可用的逻辑处理器数量。通常,对于计算密集型任务,最好留出一个,这样机器就不会变得无响应。setup_disk.frame() 函数创建用于计算的工人集群。此外,工作器之间可以交换的数据量没有限制(Inf 代表 无限)。
我们假设您已经解压了包含 1987 年至 2012 年美国航班数据的 CSV 文件,如本章开头所述,在 D:\<your-path>\AirOnTimeCSV 文件夹中。
重要提示
如果您没有足够的硬件资源的笔记本电脑,您应该首先导入 CSV 文件的一个子集(例如 40-50 个文件)来测试脚本,而无需等待过长的执行时间。
您必须使用 list.files() 函数定义每个 CSV 文件的路径列表,然后您可以使用 csv_to_disk.frame() 函数继续创建 disk.frame:
main_path <- 'D:/<your-path>/AirOnTime'
air_files <- list.files( paste0(main_path, '/AirOnTimeCSV'), full.names=TRUE )
start_time <- Sys.time()
dkf <- csv_to_disk.frame(
infile = air_files,
outdir = paste0(main_path, '/AirOnTime.df'),
select = c('YEAR', 'MONTH', 'DAY_OF_MONTH', 'ORIGIN', 'DEP_DELAY')
)
end_time <- Sys.time()
(create_dkf_exec_time <- end_time - start_time)
创建 disk.frame 实例包括两个步骤:
-
该过程在每个分配给临时文件夹的每个工作器中创建与 CSV 文件数量相同的块。
-
块在输出文件夹(由
outdir参数定义)中聚合并持久化,该文件夹与disk.frame关联。
简而言之,disk.frame 是一个文件夹,最好带有 .df 扩展名,其中包含从源文件生成的聚合块(.fst 文件)。正如您已经通过 Dask 看到的,通过 select 参数仅指定感兴趣的列是一个好主意,这样可以限制要读取的列。这种做法也保证了只读取您确信不是完全空的列,从而避免了由于与实际数据类型不同的推断数据类型而导致的读取错误。
重要提示
可能会发生某些列在开头有大量空值的情况,因此
disk.frame无法推断正确的数据类型,因为它使用样本来执行此操作。在这种情况下,您应该使用colClasses参数特别声明这些列的数据类型。
注意,一旦create_dkf_exec_time变量分配了值,圆括号就会将其打印在屏幕上,全部在一行中。在拥有 6 个核心和 32GB 的机器上,使用 11 个工作者的情况下,创建时间大约是 1 分 17 秒。
一旦disk.frame创建完成,构成集群的多个 R 会话可以保持活跃。为了强制断开连接,可以使用以下命令:
future:::ClusterRegistry("stop")
这样,机器资源就完全释放了。
重要提示
正确创建的
disk.frame可以通过使用disk.frame()函数并传入.df文件夹的路径来在以后引用,无需再次创建。
你可以在Chapter08\R文件夹中的01-create-diskframe-in-r.R文件中找到创建disk.frame的完整脚本。
现在让我们看看如何从新创建的disk.frame中获取信息。
从 disk.frame 提取信息
由于我们感兴趣的是获取美国每个机场每年每一天的平均航空公司延误,因此需要对整个disk.frame进行分组操作。为了优化执行时间,在这种情况下,还需要实例化一个工作者的集群,该集群将并行读取新创建的disk.frame中的信息。你可以使用从dplyr包中了解的语法来转换disk.frame实例。此外,为了优化机器资源,你将强制引擎只读取对请求的计算严格必要的列,使用disk.frame包中的srckeep()函数。在我们的案例中,选择列是不必要的,因为我们创建disk.frame实例时只保留了那些严格必要的列,但以下建议适用。
重要提示
在聚合数据提取脚本中始终使用
srckeep()函数是一个好习惯,因为如果disk.frame实例包含所有初始列,该操作会导致机器崩溃。
应使用以下代码来提取聚合数据:
library(dplyr)
library(disk.frame)
n_cores <- future::availableCores() - 1
setup_disk.frame(workers = n_cores)
options(future.globals.maxSize = Inf)
main_path <- ' D:/<your-path>/AirOnTime'
dkf <- disk.frame( paste0(main_path, '/AirOnTime.df') )
start_time <- Sys.time()
mean_dep_delay_df <- dkf %>%
srckeep(c("YEAR", "MONTH", "DAY_OF_MONTH", "ORIGIN", "DEP_DELAY")) %>%
group_by(YEAR, MONTH, DAY_OF_MONTH, ORIGIN) %>%
summarise(avg_delay = mean(DEP_DELAY, na.rm = TRUE)) %>%
collect()
end_time <- Sys.time()
(aggregate_exec_time <- end_time - start_time)
future:::ClusterRegistry("stop")
同样,正如在 Python 中的 Dask 所看到的,存在一些函数可以收集所有排队的转换并触发计算的执行。在disk.frame的情况下,该函数是collect()。这个操作的持续时间大约是 20 分钟,使用一个拥有 6 个核心的 32GB 机器,并使用 11 个工作者。
最终结果是包含所需空气延迟平均值的 tibble:

图 8.8 – 包含延迟平均值的 tibble 的前几行
在这个情况下,你也能够通过处理高达 30GB 的 CSV 集来获取平均飞行延误的几千行数据集!
你可以在Chapter08\R文件夹中的02-extract-info-from-diskframe-in-r.R文件中找到从disk.frame提取聚合数据的完整脚本。
以下常识性观察也适用于此。
重要提示
如果之前耗时较长的处理结果需要经常重用,最好是将其以可重用格式 持久化到磁盘上。这样,你就可以避免再次执行所有繁重的排队转换处理。
disk.frame 实例没有像 Dask DataFrame 那样的直接方法,允许其内容在所有排队转换之后写入磁盘。由于结果是 tibble,从所有意义上说它是一个 DataFrame,你可以按照你在 第七章,从 Power BI 记录数据到外部来源中学到的指示将其写入磁盘。
因此,让我们看看你如何在 Power BI 中应用到目前为止学到的知识。
使用 R 在 Power BI 中导入大型数据集
使用 R 通过 Power BI 加载大于可用 RAM 的数据集的最佳解决方案是能够在 Power BI 本身通过 R 脚本直接创建 disk.frame 实例。
重要提示
不幸的是,从各种 CSV 文件创建
disk.frame实例的操作在过程的第二阶段(将各种块聚合到disk.frame文件夹中)中在 Power BI 中会触发一个错误。
因此,为了从 Power BI 中的 disk.frame 实例提取信息,你必须首先在 RStudio(或任何其他 IDE 或任何自动化脚本)中创建它。一旦创建了 disk.frame 实例,就可以在 Power BI 中如下使用它:
-
确保你已使用 RStudio 正确创建了
disk.frame实例,方法是运行Chapter08/R文件夹中01-create-diskframe-in-r.R文件中的代码。 -
打开 Power BI Desktop 并确保它引用的是你最新的 CRAN R 引擎。
-
点击 获取数据,开始输入
script,然后双击 R 脚本。 -
将
03-extract-info-from-diskframe-in-power-bi.R文件的内容复制到Chapter08\R文件夹,并将其粘贴到 R 脚本编辑器中。请记住编辑要生成的 CSV 文件的目标路径,然后点击 确定。 -
大约 30 分钟后(使用具有 6 个核心和 32 GB RAM 的笔记本电脑),你将看到 导航器 窗口显示
mean_dep_delay_dfDataFrame 中的聚合数据:![图 8.9 – 在 Power BI 中加载的 mean_dep_delay_df DataFrame]()
图 8.9 – 在 Power BI 中加载的 mean_dep_delay_df DataFrame
选择 DataFrame 并点击 加载。之后,将出现一个 加载 弹出窗口,它将保持活跃约 10 分钟:
![图 8.10 – 将数据加载到 Power BI 的最后阶段]()
图 8.10 – 将数据加载到 Power BI 的最后阶段
当它消失时,你的数据已加载到数据模型中。此外,脚本还生成了包含你的聚合数据的 CSV 文件。你将在你的文件夹中找到大小约为 60 MB 的
mean_dep_delay_df.csv文件:

图 8.11 – 从 Power BI 中的 R 脚本创建的 CSV 文件内容
太棒了!你曾经想过只用几行代码就将 30 GB 的数据导入 Power BI Desktop 中,从中提取信息吗?好吧,你刚刚就做到了。
摘要
在本章中,你学习了如何将比你的笔记本电脑可用的 RAM 更大的数据集导入 Python 和 R。你还将这些知识应用于导入一组 CSV 文件,这些文件总共占用的空间超过了你机器上可用的 RAM。
在下一章中,你将学习如何使用用户可用的网络服务提取的外部信息来丰富你的数据。
参考文献
对于额外的阅读,请参阅以下书籍和文章:
-
使用 Azure(Spot)VMs 在 Azure 上创建 Dask 集群 (
cloudprovider.dask.org/en/latest/azure.html) -
在 Microsoft Azure ML 上使用 Dask 和 NVIDIA RAPIDS 进行大规模加速机器学习 (
github.com/drabastomek/GTC/tree/master/SJ_2020/workshop)
第十章:9 调用外部 API 以丰富您的数据
在上一章中,您看到了如何使用外部信息丰富您已有的数据的示例。在那个例子中,数据是通过 CSV 文件提供的,但这种情况并不总是如此。很多时候,用于丰富数据的有用数据是通过外部应用程序编程接口(API)暴露的,通常以网络服务端点形式存在。Power BI 允许您通过专用 UI 从网络服务中读取数据,但大多数情况下它不可用。因此,您必须求助于编写M 代码来完成这项工作。编写 M 代码并不太难,但也不是那么直接。您还必须小心不要编写在将报告发布到 Power BI 服务时会导致刷新问题的代码。此外,在 Power BI 中,无法并行调用同一网络服务以减少检索数据时的等待时间。采用 Python 或 R 从网络服务中获取数据可以非常容易地解决所有这些问题。
在本章中,您将学习以下主题:
-
什么是网络服务
-
注册 Bing Maps 网络服务
-
使用 Python 进行地址地理编码
-
使用 R 进行地址地理编码
-
使用 Power BI 访问网络服务
技术要求
本章要求您在计算机上已安装并配置好工作状态的互联网连接和Power BI 桌面版。您必须已按照第二章、配置 Power BI 中的 R和第三章、配置 Power BI 中的 Python中概述的方式正确配置了 R 和 Python 引擎以及 IDE。
什么是网络服务
在您作为分析师的工作过程中,您可能需要通过您网络内的系统公开的 API 检索数据,例如。然而,这种情况很少见,因为如今几乎所有的外部数据源都作为网络服务公开,即使在公司内部也是如此。
网络服务是异构信息系统之间通信信息最常见和最受欢迎的方式。网络服务基本上是一个托管在服务器上的软件模块,通过互联网向客户端提供数据以响应特定的请求。
网络服务的设计模型主要有两种:简单对象访问协议(SOAP)和表示状态转移(REST)。
-
SOAP 严重依赖于XML,并通过模式定义了一个高度类型化的消息结构。服务与客户端之间交换的所有消息都是通过网络服务定义语言(WSDL)编码的,而 WSDL 是基于 XML 格式的。WSDL 最重要的方面之一是它定义了服务提供者和每个服务消费者之间的绑定合同。因此,任何对 API 的更改都涉及到对客户端的更改。如今,几乎所有重要的事情都在 HTTP 上运行。但请记住,除了 HTTP 之外,SOAP 还可以利用任何其他传输协议(如 SMTP 和 TCP)。
-
REST 正在成为所有公共 API 的默认设计模型。它是一种完全依赖于 HTTP 协议(与 SOAP 相对)的架构。它不使用 WSDL 合约,因此更加灵活且易于实现。REST 可以处理任何格式的数据,如 XML 或 YAML,但最常用的无疑是JSON。与以功能驱动的 SOAP 不同,REST 非常数据驱动。正因为如此,所有用于数据丰富化的 Web 服务都是RESTful(更多详情请参阅参考资料)并且可以生成任何格式的输出——不仅限于 JSON,还有 CSV,或者例如真正简单的聚合(RSS)等!基本上,REST 提供了一种更轻量级的方法与服务交互,在大多数情况下使用 URL 来接收或发送信息。公开的基本方法有
GET、POST、PUT和DELETE。
很可能您已经听说过Web 服务端点。一般来说,Web 服务通过接受客户端的GET请求并提供响应来工作。考虑到 REST API,端点是一个客户端应用程序可以访问 Web 服务的 URL。一个 Web 服务可以提供多个端点。例如,如果您考虑Bing Maps REST 服务,用于地理编码的端点如下:dev.virtualearth.net/REST/v1/Locations。用于提供两个航点之间路线的端点是这个:dev.virtualearth.net/REST/v1/Routes。
现在我们已经清楚什么是 Web 服务以及经常与之相关的技术术语的含义,我们可以继续向您展示如何使用 RESTful 的 Web 服务。
注册 Bing Maps Web 服务
在本章中,我们将以 Bing Maps Web 服务为例。因此,您需要通过您的 Microsoft 账户创建一个免费的 Bing Maps Basic 密钥。完成此操作的必要步骤如下:
-
前往
www.microsoft.com/en-us/maps/create-a-bing-maps-key,在主横幅之后选择基本密钥选项,然后点击获取免费基本密钥按钮。 -
在下一个欢迎页面,点击登录并使用您的 Microsoft 账户进行登录。
-
在下一页,您将被告知这是您第一次使用电子邮件在Bing Maps 开发者中心进行身份验证。点击是的,让我们创建一个新账户以创建一个新账户。
-
在下一页输入您的账户详情并点击创建。之后,您将登录到开发者中心,页面上会显示一些声明,如公告和重要提醒。
-
在该页面上,在我的账户菜单下,点击我的密钥。
-
您将看到一个表单,要求输入新的密钥。只需填写所需的字段,在应用程序名称字段中输入
geocoding-test,并将密钥类型保留为基本,将应用程序类型保留为开发/测试。然后,点击创建。 -
将出现一个页面来确认密钥已创建,如下所示:
![图 9.1 – Bing Maps 密钥确认]()
图 9.1 – Bing Maps 密钥确认
-
点击显示密钥来查看你将在下一个示例中使用的密钥,并将其复制。
-
在你的浏览器 URL 栏中输入以下 URL,将
<your-bing-maps-api-key>字符串替换为你的密钥:dev.virtualearth.net/REST/v1/Locations/1%20Microsoft%20Way%20Redmond%20WA%2098052?key=<your-bing-maps-api-key>。然后,按Enter键。 -
一些浏览器,如 Firefox,会美化 Web 服务返回的 JSON 响应。在你的情况下,如果一切顺利,你应该看到如下结果:

图 9.2 – 使用 Bing Maps Locations API 通过浏览器进行的第一次地理编码
太好了!你刚刚使用 Bing Maps Locations API 将作为查询参数传递的地址进行了地理编码!现在让我们看看如何使用 Python 自动化这个过程。
使用 Python 进行地址地理编码
在本节中,我们将向你展示如何使用直接通过GET方法调用 URL(这最终等同于你之前通过浏览器进行的示例调用)以及一个专门的 Python 软件开发工具包(SDK),它简化了查询过程。
使用显式的 GET 请求
如果我们想要从 Bing API 接收地址的地理编码数据,我们需要通过传递感兴趣的地址作为参数来向 Web 服务发起请求。参数通过将参数与端点 URL 进行适当的连接传递。在我们的例子中,用于地理编码地址的端点 URL 的完整格式如下:

图 9.3 – 使用 Bing Maps Locations API 的 GET 请求的 URL 格式
以下是在图9.3中可以看到的每个字符串标记的定义:
-
base_url:端点 URL 字符串,即http://dev.virtualearth.net/REST/v1/Locations/ -
address:你想要进行地理编码的地址字符串,使用百分编码技术转换,以避免在最终的 URL 中使用特殊字符 -
AUTH_KEY:你的 Bing 密钥
以这种方式构建了 URL 后,可以使用requests模块的get()方法来发起一个GET请求。在请求成功后,你会捕获包含 JSON 的 Web 服务响应内容,并从中提取感兴趣的值。例如,为了提取formattedAddress值,你需要导航到数据结构中,如图9.4所示:

图 9.4 – Bing Maps Locations API 响应的视觉结构
因此,你可以像这样导航你的 JSON 变量 data,以获取该值:
data['resourceSets'][0]['resources'][0]['address']['formattedAddress']
从 图 9.4 中,你可以看到资源节点可以包含多个子节点,这些子节点用整数标识。这是因为有时地理编码引擎无法精确地从传递的地址中识别地理点,因此返回多个结果。estimatedTotal 属性指示已识别的可能地理编码的数量。为了简单起见,我们从 JSON 中提取了最后识别的资源,即具有最高标识符的资源。
除了 JSON 格式的答案外,你还可以从请求中提取其他值,例如答案状态的理由(有助于了解 GET 操作是否成功),文本格式的答案的完整内容,以及用于 GET 请求的 URL。你需要 requests、urllib、json、pandas 和 time 模块。后者用于测量数据集中所有地址的地理编码执行时间。所有这些都可以封装在一个方便的函数中,称为 bing_geocode_via_address():
def bing_geocode_via_address(address):
# trim the string from leading and trailing spaces using strip
full_url = f"{base_url}query={urllib.parse.quote(address.strip(), safe='')}?key={AUTH_KEY}"
r = requests.get(full_url)
try:
data = r.json()
# number of resources found, used as index to get
# the latest resource
num_resources = data['resourceSets'][0]['estimatedTotal']
formattedAddress = data['resourceSets'][0]['resources'][num_resources-1]['address']['formattedAddress']
lat = data['resourceSets'][0]['resources'][num_resources-1]['point']['coordinates'][0]
lng = data['resourceSets'][0]['resources'][num_resources-1]['point']['coordinates'][1]
except:
num_resources = 0
formattedAddress = None
lat = None
lng = None
text = r.text
status = r.reason
url = r.url
return num_resources, formattedAddress, lat, lng, text, status, url
本节包含的代码块可在 Chapter09\Python 文件夹中的 01-geocoding-with-python.py 文件中找到。
假设我们有一个包含要地理编码地址的 pandas DataFrame 的列表,编写一个方便的函数,该函数接受 DataFrame 行和存储地址的列名作为输入是有意义的。它将调用前面的函数以获取地理编码值以添加到当前行,并将返回它:
def enrich_with_geocoding(passed_row, col_name):
# Fixed waiting time to avoid the "Too many requests"
# error as basic accounts are limited to 5 queries per second
time.sleep(3)
address_value = str(passed_row[col_name])
num_resources, address_formatted, address_lat, address_lng, text, status, url = bing_geocode_via_address(address_value)
passed_row['numResources'] = num_resources
passed_row['formattedAddress'] = address_formatted
passed_row['latitude'] = address_lat
passed_row['longitude'] = address_lng
passed_row['text'] = text
passed_row['status'] = status
passed_row['url'] = url
return passed_row
你可以使用此链接中的测试数据集测试这些函数:bit.ly/geocoding-test-addresses。它也位于 Chapter09 文件夹中。
你只需将前面的函数作为参数传递给地址的 DataFrame apply() 方法,以将其应用于其每一行,如下所示:
base_url= "http://dev.virtualearth.net/REST/v1/Locations/"
AUTH_KEY = os.environ.get('BINGMAPS_API_KEY')
df_orig = pd.read_csv(r'D:\<your-path>\Chapter09\geocoding_test_data.csv', encoding='latin-1')
df = df_orig[['full_address','lat_true','lon_true']]
tic = time.perf_counter()
enriched_df = df.apply(enrich_with_geocoding, col_name='full_address', axis=1)
toc = time.perf_counter()
print(f"{df.shape[0]} addresses geocoded in {toc - tic:0.4f} seconds")
注意,Bing Maps 服务密钥是通过 os.environ.get('BINGMAPS_API_KEY') 函数调用来设置的。这种方式访问敏感数据避免了在代码中以纯文本形式写入它。因此,它必须已经预先写入到 BINGMAPS_API_KEY 环境变量中。你也可以通过以下脚本使用 Python 来完成此操作:
os.environ['BINGMAPS_API_KEY'] = '<your-bing-api-key>'
然而,每次你重新启动 Visual Studio Code 时,该变量都会丢失。为了使其永久可用,你必须通过你的操作系统直接设置用户变量环境。在 Windows 上,你可以按照以下指南操作:phoenixnap.com/kb/windows-set-environment-variable。一旦添加,你必须重新启动 Visual Studio Code 以使其对代码可见。
重要提示
我们更倾向于直接将 CSV 文件加载到 Python 脚本中。这相当于首先通过 Power BI 文本/CSV 连接器加载 CSV 文件,然后添加一个转换步骤,该步骤将运行我们刚刚分析的 Python 代码。
地理编码操作对于 120 个地址大约需要 34 秒。最终 DataFrame 的一部分内容如下:

图 9.5 – 地理编码 DataFrame 的内容
壮观!您仅用几行 Python 代码在 34 秒内地理编码了多达 120 个地址。但您知道您还可以通过地理编码所有地址来并行化 GET 请求,从而在更短的时间内完成吗?让我们看看您如何实现这一点。
使用并行显式 GET 请求
正如您在 第八章 中学到的,在 Power BI 中加载超过可用 RAM 的大型数据集,瑞士军刀允许您在 Python 中并行化您的计算是 Dask。好事是 Dask DataFrame 提供了 apply() 方法,它具有与 pandas DataFrame 的 apply() 函数相同的功能,不同之处在于它是并行的。因此,上一节中使用的代码经过一些小的修改就可以实现显著减少执行时间。您可以在 Chapter09\Python 文件夹中的 02-geocoding-parallel-with-python.py 文件中找到完整的脚本。
重要提示
当 API 需要时,最好使用 批量模式 提供多个输入,而不是并行进行多次调用。然而,如果批量模式不受 API 支持,那么多次并行调用是提高执行时间的唯一方法。Bing Maps Locations API 支持批量模式,但为了演示目的,我们更喜欢采用多次调用技术。
enrich_with_geocoding() 和 bing_geocode_via_address() 函数保持不变。相反,Dask DataFrame 已经在数据读取阶段引入,如下所示:
import dask.dataframe as dd
ddf_orig = dd.read_csv(r'D:\<your-path>\Chapter09\geocoding_test_data.csv', encoding='latin-1')
ddf = ddf_orig[['full_address','lat_true','lon_true']]
内部,Dask DataFrame 被划分为许多分区,其中每个分区都是一个 pandas DataFrame。现在,当数据量不大时,它可能通过 read_csv() 在单个分区中加载。实际上,如果您运行以下代码:ddf.npartitions,您将看到它返回 1。正因为如此,在这种情况下,有必要将 Dask DataFrame 重新分区到适当的分区数量,以利用并行性,如下所示:
ddf = ddf.repartition(npartitions=os.cpu_count()*2)
以下适用于 DataFrame 中的分区数量:
重要提示
在 DataFrame 中确定理想分区数量的精确规则并不存在。之前使用的公式对我们正在考虑的数据集是有效的,并且是通过经验确定的。
在这个阶段,你只需通过传递enrich_with_geocoding()函数来调用 Dask DataFrame 的apply()方法,就像你在上一节中使用 pandas DataFrame 的apply()方法时做的那样:
enriched_ddf = ddf.apply(enrich_with_geocoding, axis=1, col_name='full_address', meta={'full_address': 'string', 'lat_true': 'float64', 'lon_true': 'float64', 'numResources': 'int32', 'formattedAddress': 'string', 'latitude': 'float64', 'longitude': 'float64', 'text': 'string', 'status': 'string', 'url': 'string'})
在这种情况下,我们必须指定预期的输出元数据。这是因为许多 DataFrame 上的操作依赖于知道列的名称和类型。内部,Dask DataFrame 尽其所能通过所有操作传播此信息。通常,这是通过在小型虚拟数据集上评估操作来完成的。有时,然而,这种操作可能会在用户定义的函数(如apply)中失败。在这些情况下,许多函数支持一个可选的meta关键字,它允许你直接指定元数据,从而避免推断步骤。
重要提示
Dask DataFrame 的
apply()方法现在只支持axis=1模式(按行)。
你刚刚定义了一个懒转换。要实际执行它,你必须使用compute()函数,因此在这种情况下,你可以测量地理编码所有地址所需的时间:
tic = time.perf_counter()
enriched_df = enriched_ddf.compute()
toc = time.perf_counter()
print(f'{enriched_df.shape[0]} addresses geocoded in {toc - tic:0.4f} seconds')
最终结果是惊人的:120 个地址在仅仅3.5 秒内就被地理编码了,而顺序代码需要 35 秒!多亏了 Dask,你实现了运行时间的 10 倍提升!
正如你在本节和上一节中看到的,代码中最不舒服的部分可能是从网络服务返回的 JSON 中检索每个值(bing_geocode_via_address()函数中的逻辑),因为你必须事先知道结果的架构。然而,有一些 Python 模块包含用于与特定网络服务交互的函数。让我们看看如何使用Geocoder库,它简化了最广泛使用的地理编码提供商(如 Google 和 Bing)的采用。
并行使用 Geocoder 库
Geocoder模块(geocoder.readthedocs.io/)是一个用 Python 编写的简单且一致的地理编码库。它使用独特的 JSON 模式从预期的地理编码提供商那里获得一致的响应。一些可用的提供商包括 Google、Bing、Mapbox 和 TomTom。
首先,你需要在pbi_powerquery_env环境中安装 Geocoder 库。你可以通过以下步骤完成:
-
打开 Anaconda 提示符。
-
通过输入以下命令切换到
pbi_powerquery_env环境:conda activate pbi_powerquery_env。 -
通过输入以下命令安装 Geocoder 库:
pip install geocoder。
多亏了 Geocoder,对地址的GET请求被简单地转换成了命令r = geocoder.bing(address, key = AUTH_KEY)。而且,返回的对象r已经包含了用于地理编码的有用属性,例如r.address、r.lat或r.lng。因此,你在上一节中遇到的bing_geocode_via_address()函数被尴尬地简化了,如下所示:
def bing_geocode_via_address(address):
r = geocoder.bing(address, key = AUTH_KEY)
return r.address, r.lat, r.lng, r.json, r.status, r.url
在这种情况下,如果地理编码操作返回多个匹配项,Geocoder 会选择最合适的匹配项,从而节省你进一步的麻烦。返回多个结果的功能目前还在开发中。在包的较新版本中,你可以通过将大于一值的 MaxRows 参数传递给地理编码函数,以获取尽可能多的可能结果。这样,分析师就可以选择满足他们需求的结果。
你可以在 Chapter09\Python 文件夹中的 03-geocoding-parallel-using-sdk-with-python.py 文件中找到使用 Geocoder 库并行地理编码地址的完整脚本。
现在,你可以理解拥有一个旨在简化你使用特定 API 生活的 SDK 是一件大好事。
重要提示
我们始终建议的一个主要原则是,每次你需要它时都 避免重新发明轮子。如果你需要完成一个目标(在我们的案例中,地理编码一个地址),很可能在你之前有人已经想到了它,并且可能已经与社区分享了一个简化你生活的库。始终花半小时在网络上搜索可能的现有解决方案,这些解决方案可以智能地解决你的问题。除了节省宝贵的时间外,你还避免了陷入别人已经克服的复杂性的风险。
虽然我们已知 Geocoder 库的存在,但我们仍然想在前面几节中向你展示如何从头开始向网络服务发送 GET 请求。这是因为并不能确定所有你未来可能需要的网络服务都有 SDK。
现在我们来看看如何使用 R 获取相同的结果。
使用 R 进行地址地理编码
你刚刚学习了如何通过原始 GET 请求到端点以及通过方便的 SDK 使用 Python 查询网络服务。正如你所能猜到的,你也可以使用 R 来做这两件事。让我们看看如何操作。
使用显式的 GET 请求
允许在 R 中调用 URL 的包是 httr(默认与引擎一起安装)。通过它,简单的 GET 请求直接转换为 GET(your_url)。正如你在前面的章节中已经看到的,百分编码必须应用于要作为网络参数传递给 Bing Maps Locations API 端点的地址。允许你对字符串应用此类编码的函数在 RCurl 包中,命名为 curlPercentEncode()。此外,方便的 tictoc 包将被用来测量运行时间。RCurl 和 tictoc 包都需要安装,如下面的步骤所示:
-
打开 RStudio 并确保它在 全局选项 中引用你最新的 CRAN R 引擎。
-
点击 控制台 窗口,并输入以下命令:
install.packages("RCurl")。然后按 Enter 键。 -
输入以下命令:
install.packages("tictoc")。然后按 Enter。
在这一点上,一旦你定义了针对查询的特定于 Web 服务的 base_url 和 AUTH_KEY 变量,执行以下代码就足以获得 GET 请求的结果:
encoded_address <- RCurl::curlPercentEncode(address)
full_url <- stringr::str_glue('{base_url}query={encoded_address}?key={AUTH_KEY}')
r <- httr::GET(full_url)
stringr 包的 str_glue() 函数已被用于连接字符串。请注意,在这种情况下,AUTH_KEY 变量也是通过使用 AUTH_KEY = Sys.getenv('BINGMAPS_API_KEY') 从环境变量设置的。这意味着必须在之前设置环境变量。你可以在 R 会话中使用以下 R 脚本直接设置它:
Sys.setenv(BINGMAPS_API_KEY = '<your-bing-api-key>')
然而,每次你重新启动 RStudio 时,该变量都会丢失。为了使其永久可用,你必须通过你的操作系统直接设置用户变量环境。在 Windows 中,你可以遵循以下指南:phoenixnap.com/kb/windows-set-environment-variable。一旦添加,你必须重新启动 RStudio 以使其对代码可见。
一旦你获得了请求的结果,你就可以继续解析 JSON 内容以获取所需的地理编码值。在前面章节中解析结果时所考虑的一切仍然适用。因此,你将不得不处理一些地理编码操作可能返回的多个结果。正如之前用于 Python 脚本所做的那样,所有这些逻辑都可以封装到 R 中的 bing_geocode_via_address() 函数中,该函数返回通过地理编码传递的地址所获得的一系列值。你可以在 R\Chapter09 文件夹中的 01-geocoding-with-r.R 文件中找到代码。
一旦你将 geocoding_test_data.csv 文件的内容加载到 tbl_orig tibble 中,并在 tbl 中选择了感兴趣的列,你将利用 purrr 包的 map() 函数的便利性来为从 tibble 中提取的每个地址执行之前定义的 bing_geocode_via_address() 函数:
tic()
tbl_enriched <- tbl %>%
pull( full_address ) %>%
map_dfr( ~ bing_geocode_via_address(.x) ) %>%
bind_cols( tbl, . )
toc()
注意我们如何使用 pull() 函数将 full_address tibble 列转换为向量,然后将它作为参数传递给 map_dfr() 函数,该函数只接受列表或向量作为输入。你可能想知道 map_dfr() 函数是做什么用的。它是 map() 函数家族的一部分。虽然 map() 返回一个结果列表(每个结果都是通过将输入函数应用于输入向量的每个元素获得的)作为输出,但 map_dfr() 直接将行绑定,当 map() 的每个输出元素都是一个命名 DataFrame、列表或向量时。因此,map_dfr() 的最终结果是一个由输入函数返回的元素按行排列的 DataFrame/tibble。整个逻辑被 tic() 和 toc() 函数对封装起来。通过运行整个代码块(从 tic() 到 toc() 包括),你可以非常方便地获得代码内部的执行时间。
你可能已经注意到,在这种情况下不需要像enrich_with_geocoding()这样的中间函数,该函数接受 tibble 的单独行作为参数。由于bing_geocode_via_address()函数返回一个命名列表,map_dfr()函数能够正确地解释它并将其绑定到一个单一的 tibble 中。
重要提示
我们更喜欢直接将 CSV 文件加载到 R 脚本中。这相当于首先通过 Power BI 的文本/CSV连接器加载 CSV 文件,然后添加一个运行我们刚刚分析的 R 代码的转换步骤。
由于map_dfr()只返回一个地理编码值的 tibble,你必须将这个 tibble 绑定到初始的tbl上,以便得到一个单一的增强型 tibble。因此,我们使用了函数bind_cols(tbl, .),其中参数.表示通过管道操作传递的地理编码值 tibble。话虽如此,整个地理编码操作大约需要 30 秒(这与使用 Python 得到的结果相当),最终的 tibble 将如下所示:

图 9.6 – 地理编码 DataFrame 的内容
干得好!你甚至使用 R 也能通过网络服务进行地址地理编码。简单,对吧?你现在是否好奇想了解如何利用机器提供的并行性来完成这项工作?让我们看看如何进行。
使用并行显式 GET 请求
就像在 Python 中一样,我们不得不使用 Dask 模块来并行化计算;在 R 中,我们需要引入一个新的包来实现相同的功能。这个新包叫做furrr(furrr.futureverse.org/),旨在将purrr家族映射函数的表达能力与future包提供的并行处理能力结合起来(future.futureverse.org/)。furrr和future包都是称为Futureverse(www.futureverse.org/)的有趣框架的一部分,该框架旨在以最简单的方式并行化现有的 R 代码。在实践中,furrr允许你用最小的努力将purrr的map()和map_*()函数替换为furrr的future_map()和future_map_*()函数,你的代码将神奇地并行运行。请注意,未来引擎也是你在第八章“在 Power BI 中加载超过可用 RAM 的大型数据集”中了解到的disk.frame包的后端。
首先,你需要安装furrr包。只需在 RStudio 的控制台窗口中运行命令install.packages("furrr")即可。
要将刚刚所说的应用到前一个章节中分析的代码,只需修改其最后一部分如下即可获得相同的结果(并且显然加载furrr库而不是purrr),但在并行化计算的同时:
n_cores <- availableCores() - 1
plan(cluster, workers = n_cores)
tic()
tbl_enriched <- tbl %>%
pull( full_address ) %>%
future_map_dfr( ~ bing_geocode_via_address(.x) ) %>%
bind_cols( tbl, . )
toc()
多亏了availableCores()函数,您可以识别出机器上存在的虚拟处理器的数量。不使用所有这些是一个好的做法,因为它可以使机器变得无响应。未来实例的plan()函数允许您定义未来引擎执行计算的策略(同步或异步)。它还允许您定义将并行工作的工人数。
重要提示
通常,Windows 机器上使用的默认策略是多会话,并且它在 RStudio 中运行代码时表现良好。我们发现,使用这种策略,Power BI 无法处理为并行计算生成的多个会话。相反,我们发现选择集群策略,尽管机器是唯一的,但允许 Power BI 完成计算。
另一个最小的改动是将与网络服务相关的base_url和AUTH变量直接声明在由future_map_dfr()调用的函数中,而不是在主代码中,以简化操作。通过furrr调用函数传递变量与标准实践略有不同(请参阅此链接:furrr.futureverse.org/articles/articles/gotchas.html),我们希望避免增加最小复杂性,以免分散对主要概念的注意力。
您可以在Chapter09\R文件夹中的02-geocoding-parallel-with-r.R文件中找到完整的脚本。
如果您运行代码,您将得到令人印象深刻的结果:120 个地址在仅仅3 秒内就被地理编码了,而顺序代码需要 30 秒!此外,在这种情况下,您通过furrr实现了运行时间的 10 倍提升!就这么简单,对吧?
您可以通过采用一个为您调用网络服务的大部分工作的地理编码包来进一步简化您刚刚运行的代码。让我们看看这是怎么回事。
使用 tidygeocoder 包并行处理
tidygeocoder (jessecambon.github.io/tidygeocoder/) 包提供了一组受支持的地理编码服务的统一高级接口,并以 tibble 格式返回结果。一些可用的提供者包括 Google、Mapbox、TomTom 和 Bing(从版本 1.0.3 开始)。
首先,您需要安装它。如果您想安装最新版本,请记住使用以下代码重置快照存储库:
local({
r <- getOption("repos")
r["CRAN"] <- "https://cloud.r-project.org/"
options(repos = r)
})
然后,您只需在控制台窗口中运行install.packages("tidygeocoder")命令即可。
多亏了 Tidygeocoder,对地址的 GET 请求被简单地转换为 details_tbl <- geo(address, method = 'bing', full_results = TRUE) 命令,而且令人高兴的是,返回的对象 r 已经包含了用于地理编码的有用属性,例如 details_tbl$bing_address.formattedAddress。因此,您在前几节中遇到的 bing_geocode_via_address() 函数显得过于简单,如下面的代码块所示:
bing_geocode_via_address <- function(address) {
details_tbl <- geo(address, method = 'bing', full_results = TRUE)
details_lst <- list(
formattedAddress = details_tbl$bing_address.formattedAddress,
lat = details_tbl$point.coordinates[[1]][1],
lng = details_tbl$point.coordinates[[1]][2],
details_tbl = details_tbl
)
return( details_lst )
}
此外,在这种情况下,tidygeocoder 包会在地理编码操作返回多个匹配项时选择最合适的匹配项,从而让您免受更多头痛的困扰。
重要提示
注意,Tidygeocoder 假设已经设置了
BINGMAPS_API_KEY环境变量,并使用它来登录到网络服务。
您可以在 Chapter09\R 文件夹中的 03-geocoding-parallel-using-sdk-with-r.R 文件中找到使用 Geocoder 库并行地理编码地址的完整脚本。
如您现在可能已经猜到的,使用社区提供的 SDK 可以使您的生活更加轻松,是一个明智的选择。如果某个网络服务的 SDK 不可用,您仍然学习了如何使用 R 进行原始 GET 请求。
现在,让我们通过在 Power BI 中使用 R 和 Python 实现网络服务数据丰富解决方案来查看我们迄今为止所学内容的益处。
使用 Power BI 访问网络服务
Power BI 已经具有允许您将网络服务公开的数据访问到 Power Query 的默认功能。有两种主要模式:
-
通过 GUI(点击 获取数据,然后 网络,如果需要,可以设置高级选项)
-
通过 M 语言,使用
Web.Contents()函数
使用 GUI 非常繁琐,几乎总是无法达到预期的结果。要有效地使用 Power BI 的原生功能连接到网络服务,唯一的方法是编写 M 代码。在 M 中编写代码并不太难。然而,当将使用 Web.Contents() 函数的报告发布到 Power BI 服务时,会出现一些复杂情况。简而言之,当您需要以动态方式构建用于 GET 请求的 URL 时,必须小心使用 相对路径 和 查询选项。如果您不使用这种特定的结构,服务将无法刷新数据。此外,Power BI 服务不允许您安全地存储敏感数据,例如 API 密钥,迫使您将此信息嵌入到代码中。此外,使用 M 无法并行地对端点进行多次调用。
正是因为上述原因,我们建议使用 R 或 Python 来访问网络服务,尤其是如果 SDK 可用以简化其使用。
请记住,以下限制适用:
重要提示
如果使用网络服务数据的报告需要发布到 Power BI 服务,您只能通过 Power Query 查询网络服务,而不能在 R 可视化中查询,因为它们在服务上使用的环境没有暴露在互联网上。
话虽如此,为了能够使用我们提供的脚本在 Power BI 中通过 Bing Maps Locations API 进行地址地理定位,以下条款适用:
重要提示
您必须在您的操作系统上定义
BINGMAPS_API_KEY环境变量作为用户环境变量,才能使用我们为 Power BI 提供的地理编码脚本。如果使用从网络服务中提取的数据的报表需要发布到 Power BI 服务,请确保在数据网关机器上也创建相同的环境变量。
现在,让我们看看如何使用 Python 在 Power BI 中从网络服务中提取数据。
使用 Python 在 Power BI 中进行地址地理编码
在 Power BI 中,我们将使用通过地理编码 SDK 调用网络服务的 Python 代码,并利用 Dask 实现并行处理。要使用的代码实际上与之前分析过的代码相同。因此,只需遵循以下步骤:
-
打开 Power BI 桌面并确保它引用了
pbi_powerquery_env环境。 -
点击 获取数据,搜索并选择 Python 脚本,然后将您在
04-geocoding-parallel-using-sdk-in-power-bi-with-python.py文件中找到的脚本复制到Chapter09\Python文件夹中。请确保编辑geocoding_test_data.csv文件的路径。然后点击 确定。 -
几秒钟后,您将看到 导航器 窗口出现,您可以在其中选择
enriched_df表:![图 9.7 – 在 Power BI 中加载的 enriched_df 数据框]()
图 9.7 – 在 Power BI 中加载的 enriched_df 数据框
然后,点击 加载。
您刚刚使用 Python 在 Power BI 中并行查询了 Bing Maps Locations API!简单,对吧?
同样可以使用 R 来做同样的事情。让我们看看如何进行。
使用 R 在 Power BI 中进行地址地理编码
在 Power BI 中,我们将使用通过 Tidygeocoder SDK 调用网络服务的 R 代码,并利用 furrr 包实现并行处理。要使用的代码实际上与之前分析过的代码相同。因此,只需遵循以下步骤:
-
打开 Power BI 桌面并确保它引用了您最新的 CRAN R。
-
点击 获取数据,搜索并选择 Python 脚本,然后将您在
04-geocoding-parallel-using-sdk-in-power-bi-with-r.R文件中找到的脚本复制到Chapter09\R文件夹中。请确保编辑geocoding_test_data.csv文件的路径。然后点击 确定。 -
几秒钟后,您将看到 导航器 窗口出现,您可以在其中选择
tbl_enriched表:![图 9.8 – 在 Power BI 中加载的 tbl_enriched 数据框]()
图 9.8 – 在 Power BI 中加载的 tbl_enriched 数据框
然后,点击 加载。
您还从 Power BI 内部并行查询了 Bing Maps Locations API,并使用了 R 语言!
摘要
在本章中,您学习了如何使用 Python 和 R 查询 RESTful 网络服务。除了学习如何使用这两种语言执行原始的 GET 请求外,您还学习了如何利用机器的多线程能力并行调用同一端点的多个调用。此外,您还遇到了 Bing Maps Locations API 的 Python 和 R SDK,这些 SDK 使得访问数据变得更加容易。最后,您看到了如何在 Power BI 中轻松实现所有这些。
在下一章中,您将了解到如何通过应用复杂算法到您已有的数据来丰富您的数据。这样,您将创建出新的变量,为您的数据带来新的视角,使其更有助于实现您的目标。
参考文献
为了进一步阅读,请查看以下书籍和文章:
-
RESTful 实际上意味着什么 (
codewords.recurse.com/issues/five/what-restful-actually-means) -
Bing Maps REST 服务 (
docs.microsoft.com/en-us/bingmaps/rest-services) -
R 的未来:全面概述 (
cran.r-project.org/web/packages/future/vignettes/future-1-overview.html) -
[视频] 使用 Power Query 访问 API 和网络服务数据 (
www.youtube.com/watch?v=SoJ52o7ni2A) -
使用 List.ParallelInvoke() 并行调用 M 函数 (
blog.crossjoin.co.uk/2018/09/20/invoking-m-functions-in-parallel-using-list-parallelinvoke/)
第十一章:使用复杂算法计算 10 个列
数据摄入阶段允许你从任何数据源收集你分析所需的所有信息。一旦各种数据集被导入,可能的情况是,这些信息中的一些,未经处理,在从分析的角度描述现象时可能并不有用。通常需要应用非平凡算法来处理你拥有的数据,以便得到能够解决问题的度量或指标,而 Power BI 通常没有计算它们的工具。幸运的是,多亏了 R 和 Python,我们拥有了计算度量所需的一切。
在本章中,你将学习以下主题:
-
两个地理位置之间的距离
-
使用 Python 实现距离计算
-
使用 R 实现距离计算
-
线性规划的基本原理
-
要解决的 LP 问题的定义
-
使用 Python 处理优化问题
-
使用 R 解决 LP 问题
技术要求
本章要求你拥有一个工作的互联网连接,并且Power BI 桌面版已经安装在你的机器上。你必须已经按照第二章、配置 Power BI 中的 R和第三章、配置 Power BI 中的 Python中概述的方式正确配置了 R 和 Python 引擎以及 IDE。
两个地理位置之间的距离
通常情况下,你的数据集中会有用经纬度表示的坐标,这些坐标标识了地球上的点。根据你需要完成的分析的用途,你可以利用这些坐标来计算最能帮助你描述你想要处理的场景的度量。例如,假设你有一个数据集中包含一些酒店的地理坐标,如果你想要给游客提供额外的有价值的信息,那么计算每个酒店到最近机场的距离可能是有意义的。让我们先确定我们案例中需要考虑的距离类型。
球面三角学
如何测量三角形(三角学)的研究在过去一直非常有趣。古埃及人和巴比伦人已经处理了边之间的关系问题,尽管他们还没有角度的概念。多亏了希腊数学,我们现在所知的三角函数的概念开始在世界范围内传播,甚至达到了印度和中国。
是古希腊人,在探索了在平面上绘制的三角形的所有相关属性之后,提出了在球面上绘制三角形的想法。测量球面上点之间距离的重要性很快就被证明在后来的几个世纪中对于导航和天文学具有兴趣。因此,几个头脑热切地致力于发现今天可以收集在球面三角学名称下的重要属性。
如果你画一个球面三角形,你将立即注意到它与平面三角形的区别:

图 10.1 – 球面三角形
与平面三角形的主要区别在于,球面三角形的边是大圆或测地线(总是将球体分成两半的圆周),由于中心角(靠近中心,O)与它们各自弧的长度成比例(长度 = π x 角度),因此边a、b和c是以角度单位而不是线性单位来测量的。如果你想可视化球面三角形的边属于三个大圆的事实,图 10.2可以帮到你:

图 10.2 – 生成球面三角形的球大圆
描述球面三角学的数学使得能够定义过去被伟大的数学家所强调的球面上两点之间的所有距离。
至于两个地理点之间的距离,参考坐标系将是使用纬度和经度的那个。显然,我们不会深入探讨我们即将提出的距离证明的数学细节(也因为我们中的一些确实非常复杂)。然而,我们确实想提供一个介绍,为地理距离最常用的概念奠定基础。
现在我们来探讨两点之间最直接的距离,即余弦定理距离。
余弦定理距离
余弦定理距离(也称为大圆距离)是在球面上两点之间的最短距离,沿着球面测量。给定两个点,P和Q,一个唯一的大圆穿过它们。这两个点将大圆分成两个不同的弧。较短弧的长度是两点之间大圆的距离:

图 10.3 – 点 P 和点 Q 之间的大圆距离
注意,如果计算距离的两个点位于球体的对径点(对径点 u 和 v),那么穿过它们的大圆是无限的,这两点之间的距离很容易计算,因为它正好测量了球体周长的一半。
另一方面,当点不是对称点时,可以通过控制球面三角学的 球面余弦定律(见本章末尾的 参考文献)推导出前面的距离。不经过繁琐的数学步骤,计算球面上两点之间余弦(或大圆)距离的公式如下:

图 10.4 – 两点之间余弦距离定律的公式
看起来,这并不是你见过的最简单、最干净的公式,对吧?尽管如此,今天有了方便的计算器,前面的计算仍然可行。现在想象一下古代的航海家,他们曾经使用地球上的各个点之间的大圆距离。即使他们有正弦和余弦表可以简化一些计算,他们又如何能使用前面的公式呢?这将是一项非常复杂的活动,可能会出现导致水手丧生的错误。
重要提示
如果两个点很近(例如,在球面上相隔几公里)并且你没有精确度高的计算器,你可能会得到不准确的结果。
由于这些原因,那个时代的数学家引入了新的三角函数 haversin(或 hav),它允许你转换和平滑大圆距离公式,同时也避免了之前提到的小距离误差。让我们看看它是如何做到这一点的。
Haversine 距离定律
被称为 haversine(来自 half-versed sine)的函数定义如下:

图 10.5 – haversine 函数的定义
多亏了这个新函数,可以将余弦距离定律重写如下:

图 10.6 – Haversine 距离定律的定义
这种新的距离公式被称为 Haversine 距离定律。当你考虑到,除了正弦和余弦表,还出版了 Haversine 表时,它对当时航海者的无疑有用性就变得明显了。因此,两点之间的距离计算变得即时。
重要提示
即使这个公式也受到特殊(并且有些不寻常)的对称点情况下的舍入误差的影响。
要得到比 Haversine 公式更精确的距离公式,你必须使用 Vincenty 公式。让我们看看这是怎么回事。
Vincenty 的距离
地球测量学家 Vincenty 得出两点之间距离更精确公式的获胜假设是将地球视为不是球体,而是一个在两极略微扁平的椭球体(差异仅为约 21 公里):

图 10.7 – 椭球体表示
与计算球面上距离的 Haversine 方法不同,Vincenty 的公式描述了一种需要通过多次迭代收敛到解的方法。具体来说,计算一系列方程,其输出被反馈到同一序列的方程中,目的是在经过一定次数的迭代后最小化计算值。因此,Vincenty 的公式在计算上要求更高。
Vincenty 的公式与两个问题相关:
-
直接问题:给定初始点,(Φ1, L1),初始方位角,α1,和距离,s,找到终点,(Φ2, L2),和方位角,α2。
-
反向问题:给定两点的坐标,(Φ1, L1)和(Φ2, L2),找到方位角α1,α2和椭球体距离,s。
我们感兴趣的反向问题是计算距离。为了了解公式的复杂性,请查看本章末尾的参考文献部分。
Vincenty 的公式在需要高精度测量的项目中广泛使用,因为它们在地球椭球体上的精度在 0.5 毫米(0.020 英寸)以内。
重要提示
如果点几乎是对抗的,算法无法收敛,误差会很大或收敛缓慢。
2013 年,Karney 使用牛顿法为所有输入点对提供了快速收敛,没有任何误差。
在这一点上,出现的问题是使用哪种距离最好以及何时使用。让我们在下一节中尝试理解这一点。
使用哪种距离以及何时使用
考虑到余弦定理距离法在两点之间短距离的强烈限制,今天在常见应用中最常用的方法是 Haversine 定律(简称 Haversine)和 Vincenty 的公式(简称 Vincenty)。以下是我们在特定场景下关于使用哪种距离的建议:
-
对于彼此靠近的点(想想短程飞行),地球近似为一个球体的可能性非常大。因此,基于球体地球模型的方法,如 Haversine,由于计算上更简单(因此更快),将非常合适。
-
对于 远离地点(例如长途飞行,尤其是连接相反半球的情况),球形地球模型开始变得不那么精确。在这些情况下,Vincenty 的椭球体逆公式,它计算上更为复杂(因此通常更慢),将给出更好的结果。如果您有计算限制,您需要考虑更快的模型是否足够精确以满足您的需求。
既然您已经对理论有了深入的了解,让我们继续在 Python 和 R 中实现这些算法。
使用 Python 实现距离
我们将要实现的距离算法的场景涉及一个包含美国酒店经纬度的数据集。目标是增加距离最近机场的距离。
酒店数据在 Back4App 上公开可用(bit.ly/data-hotels-usa)。为了方便,我们只从纽约市提取了 100 家酒店,我们将使用 Haversine(球形模型)和 Karney(椭球模型)方法计算每家酒店与拉瓜迪亚机场和约翰·肯尼迪机场的距离(您可以在以下位置找到机场数据:datahub.io/core/airport-codes)。您可以在 GitHub 仓库的 Chapter10 文件夹中找到方便的已提取数据集。具体来说,您将在 hotels-ny.xlsx 文件中找到酒店数据,在 airport-codes.csv 文件中找到机场数据。
使用 Python 计算距离
如我们之前提到的,我们并不是喜欢重新发明轮子的人,尤其是在解决特定问题领域可能遇到复杂性的风险时。幸运的是,Python 拥有一个非常活跃的程序员社区,他们在特定的科学领域拥有专业知识,并且公开分享他们的成果。这就是 Jean M. Brouwers 创建的 PyGeodesy 包(github.com/mrJean1/PyGeodesy)的例子,它由 Jean M. Brouwers 创建,纯 Python 实现了各种用于地球的球形和椭球模型的各种计算工具,这些工具由 Chris Veness 为 Java 制作,Charles Karney 本人为 C++ 制作。
要使用此模块,您显然必须在您的环境中安装它,并且由于我们打算使用 Karney 优化的距离公式,我们还必须安装 geographiclib 包,该包由他直接维护。因此,按照以下步骤操作:
-
打开 Anaconda Prompt。
-
切换到
pbi_powerquery_env环境,输入conda activate pbi_powerquery_env命令,然后按 Enter。 -
安装
PyGeodesy包,输入pip install PyGeodesy,然后按 Enter。 -
安装
geographiclib包,输入pip install geographiclib,然后按 Enter。 -
如果你还没有这样做,也可以通过输入
pip install openpyxl来安装openpyxl包,然后按Enter键。
到这一点,你可以继续进行 Python 代码。首先,请注意PyGeodesy包包含一个具有基本大地测量函数的表单,称为formy。在这个模块中,有直接根据 Haversine 和 Vincenty 公式计算距离的函数,但它不包含 Karney 公式的变体。因此,除了标准的 pandas 和 NumPy 模块外,还必须导入以下内容:
from pygeodesy import formy as frm
from pygeodesy.ellipsoidalKarney import LatLon as kLatLon
要根据 Karney 计算距离,必须使用ellipsoidalKarney模块提供的对象。基本上,你必须使用该模型的LatLon方法在这两个椭球体上创建两个点,然后计算距离。这总结在以下karney用户定义函数中:
def karney(lat1, lng1, lat2, lng2):
return kLatLon(lat1, lng1).distanceTo(kLatLon(lat2, lng2))
之后,为了方便起见,创建了一个用户定义的第二个函数作为调用各种距离计算的包装器:
def geodistance(lat1, lng1, lat2, lng2, func):
return func(lat1, lng1, lat2, lng2)
然后,将酒店数据导入到hotel_df数据框中,将机场数据导入到airports_df数据框中。由于机场数据框包含一个coordinates列,其中包含用逗号分隔的经纬度字符串,因此这两个值使用split()函数分割成两个单独的列,然后追加到没有coordinates列的相同源数据框中,现在这个列已经没有用了:
airports_df = pd.concat([
airports_df.drop([’coordinates’], axis=1),
airports_df[’coordinates’].str.split(‘, ‘, expand=True).rename(columns={0:’longitude’, 1:’latitude’}).astype(float)], axis=1)
为了方便地访问特定机场的纬度和经度值,创建了一个用户定义的airportLatLongList()函数,它接受一个包含机场数据的数据框作为参数,该数据框具有iata_code、latitude和longitude列,以及感兴趣的机场的特定IATA 代码。记住,IATA 机场代码是一个三字母代码,用于识别世界上许多机场和都市地区,由国际航空运输协会(IATA)定义。因此,约翰·F·肯尼迪国际机场由 IATA 代码JFK识别,拉瓜迪亚机场由代码LGA识别。因此,为了获取这些机场的坐标,你可以使用以下代码:
jfk_lat, jfk_long = airportLatLongList(airports_df, ‘JFK’)
lga_lat, lga_long = airportLatLongList(airports_df, ‘LGA’)
话虽如此,多亏了geodistance()函数,只需要两个点的地理坐标就可以计算它们之间的距离。例如,如果你想计算点A(lat1,lng1)和点B(lat2,lng2)之间的 haversine 距离,你只需使用以下代码:
geodistance(lat1, lng1, lat2, lng2, func=frm.harvesine)
要能够计算它们之间的 Karney 距离,可以利用karney()函数并使用以下代码:
geodistance(lat1, lng1, lat2, lng2, func=karney)
然而,如果您不再想将geodistance()函数应用于两个单独的点,而是应用于包含在数据框列中的一个点序列和第二个固定点,您应该如何操作?由于前面的函数需要五个输入参数,我们可以使用 pandas 数据框的apply()方法(如此处所示:bit.ly/pandas-apply-lambda)。相反,我们引入了一种方便的方法来评估输入 Series 的连续元组的函数。为了向量化一个函数,您必须调用np.vectorize()方法,并将要应用于地理坐标 Series 的函数作为参数传递。然后,您还必须按照以下方式传递输入函数的参数:
hotels_df[’haversineDistanceFromJFK’] = np.vectorize(geodistance)(
hotels_df[’latitude’],
hotels_df[’longitude’],
jfk_lat,
jfk_long,
func=frm.haversine)
上次计算得出的距离(以米为单位)存储在hotels_df数据框的新haversineDistanceFromJFK列中。同样,可以通过简单地引用代码块中的karney函数来计算 Karney 距离。
重要提示
向量函数与使用
np.vectorize()的函数不同。向量函数是 NumPy 内置的函数,在底层编译代码(C 或 Fortran)中执行,以便使用特殊处理器寄存器同时操作多个项目。正如您所想象的,向量化比for循环更高效且更可取。有关更多详细信息,请参阅参考文献部分。
如果您在Python文件夹中的01-distances-from-airports-in-python.py文件中运行代码,您将得到类似以下内容:

图 10.8 – 从酒店到 JFK 和 LGA 机场的 Haversine 和 Karney 距离已添加
太棒了!您已经能够使用 Python 计算出所有酒店和两个机场之间的 Haversine 和 Karney 距离(以米为单位)。在这个阶段,使用类似的代码在 Power BI 中计算距离是直接的。让我们看看如何做。
使用 Python 在 Power BI 中计算距离
是时候在 Power BI 中实现您所学的知识了。因此,启动 Power BI 桌面,让我们开始吧:
-
确保 Power BI 桌面在选项中引用了您的最新环境。之后,点击Excel导入
hotels-ny.xlsx文件,您可以在Chapter10文件夹中找到它。选择它并点击打开。 -
从导航器窗口中选择Sheet 1表:
![图 10.9 – 选择 Sheet 1 表]()
图 10.9 – 选择 Sheet 1 表
然后,点击变换数据。
-
点击变换菜单,然后点击运行 Python 脚本。
-
将
Chapter10\Python文件夹中的02-distances-from-airports-in-power-bi-with-python.py文件中的脚本复制到 Python 脚本编辑器中,然后点击确定。 -
你可能会被提示将 Excel 文件中的权限与为脚本最初选择的权限(在我们的案例中,组织)对齐。在这种情况下,你根据在第五章中看到的,在 Power BI 中使用正则表达式,已经知道如何进行操作。
-
我们只对
dataset中的数据感兴趣。因此,点击其表值。 -
Power Query 将通过添加 Haversines 和 Karney 两种方法计算每个酒店到两个机场(JFK 和 LGA)的距离来转换你的数据:
![图 10.10 – Python 脚本转换的结果]()
图 10.10 – Python 脚本转换的结果
-
然后,你可以在主页选项卡中点击关闭并应用。
太好了!你刚刚通过在 Power BI 中使用 Python 添加了两个地理点之间的距离,丰富了你的数据。让我们看看如何在 R 中做到这一点。
使用 R 实现距离
情景将与前一个章节中描述的相同。因此,我们将用将纽约市的一些酒店与纽约的两个主要机场——约翰·肯尼迪机场和拉瓜迪亚机场——之间的距离来丰富与这些酒店相关的数据。
要处理的数据文件可以在 GitHub 仓库的Chapter10文件夹中找到。具体来说,你将在hotels-ny.xlsx文件中找到酒店数据,在airport-codes.csv文件中找到机场数据。
使用 R 计算距离
R 社区也很幸运,有一个免费提供的包,该包实现了地理应用中的球面三角函数。这个包叫做geosphere (cran.r-project.org/web/packages/geosphere/),就像 Python 的PyGeodesy包一样,它受到了 Chris Veness 和 Charles Karney 公开提供的代码的启发。
首先,你需要安装这个新包:
-
打开 RStudio 并确保它引用的是你最新的 CRAN R(在我们的案例中是版本 4.0.2)。
-
点击控制台窗口,输入此命令:
install.packages('geosphere')。然后,按Enter键。
现在,你已准备好在 R 中编写代码。除了允许你读取 CSV 和 Excel 文件(readr和readxl)并简化数据转换操作(dplyr和purrr)的常用包之外,你当然必须加载你刚刚安装的包。
你可以使用read_xlsx()函数轻松地将酒店数据导入hotels_tbl tibble,使用read_csv()函数将机场数据导入airport_tbl tibble。在此阶段,要做的第一个操作是将airports_tbl中的coordinates列的内容拆分到两个新列,longitude和latitude:
airports_tbl <- airports_tbl %>%
tidyr::separate(
col = coordinates,
into = c(‘longitude’, ‘latitude’),
sep = ‘, ‘,
remove = TRUE,
convert = TRUE )
注意使用tidyr包中的separate函数的简单性:
-
管道将
airports_tbltibble 作为函数的第一个参数传递。 -
声明要拆分的列(
col = coordinates)。 -
声明两个新的目标列(
into = c('longitude', 'latitude'))。 -
声明要拆分的列值中找到的分隔符(
sep = ', ')。 -
转换完成后,删除要拆分的列(
remove = TRUE)。 -
如果目标列是数值列,则自动转换数据类型(
convert = TRUE)。
所有这些操作都在一个操作中完成,并且非常清晰。这也是数据分析员喜欢 R 的原因之一!
再次强调,我们使用一个函数来方便地访问特定机场的经纬度值。这个函数是airportLongLatVec(),它接受两个参数:一个包含机场数据的 dataframe,其中包含iata_code、latitude和longitude列,以及感兴趣的机场的特定IATA 代码:
airportLongLatVec <- function(df, iata) {
ret_vec <- df %>%
filter( iata_code == iata ) %>%
select( longitude, latitude ) %>%
unlist()
return(ret_vec)
}
输出是一个命名向量。因此,可以通过这种方式轻松找到两个机场的坐标:
jfk_coordinates <- airportLongLatVec(airports_tbl, ‘JFK’)
lga_coordinates <- airportLongLatVec(airports_tbl, ‘LGA’)
你基本上已经准备好转换数据了。从geosphere包中,你将使用distHaversine()和distGeo()函数。前者的名称本身就说明了其功能。distGeo()函数根据 Karney 的公式计算椭球面上两点之间的最短距离。这两个函数都接受两对坐标(按经度和纬度的顺序)以向量形式。为了得到与 Python 相同的结果,distHaversine()函数必须接受与 PyGeodesy 默认使用的地球球面模型相同的平均半径作为参数。这个半径是国际大地测量和地球物理学联合会(IUGG)定义的R1(平均半径),其值为 6,371,008.771415 米。
在这一点上,可以使用purrr包中已经看到的map()函数族来对hotels_tbl tibble 进行增强操作。在第一步中,我们创建一个新列p1,使用map2()函数包含经纬度对向量。在第二步中,我们将distHaversine()和distGeo()函数应用于新创建的点p1以及标识机场的固定点(jfk_coordinates和lga_coordinates),以创建包含距离的新列。这是所需的代码:
hotels_tbl <- hotels_tbl %>%
mutate(
p1 = map2(longitude, latitude, ~ c(.x, .y))
) %>%
mutate(
haversineDistanceFromJFK = map_dbl(p1, ~ distHaversine(p1 = .x, p2 = jfk_coordinates, r = 6371008.771415)),
karneyDistanceFromJFK = map_dbl(p1, ~ distGeo(p1 = .x, p2 = jfk_coordinates)),
haversineDistanceFromLGA = map_dbl(p1, ~ distHaversine(p1 = .x, p2 = lga_coordinates, r = 6371008.771415)),
karneyDistanceFromLGA = map_dbl(p1, ~ distGeo(p1 = .x, p2 = lga_coordinates))
) %>%
select( -p1 )
回想一下,map2()函数接受两个向量作为输入,并将它们并行运行,将它们的值传递给~符号后面的函数(在我们的例子中是声明向量的c()函数)。而map_dbl()函数接受包含地理坐标(以向量格式)的列p1作为输入,并将它的元素传递给~后面的函数(在我们的例子中是带有其他固定参数的distGeo()),将输出转换为双精度数值数据类型的向量。
如果你运行R文件夹中01-distances-from-airports-in-r.R文件的代码,你会得到类似以下内容:

图 10.11 – 增强后的包含距离的 hotels tibble
哇!你能够使用 R 计算所有酒店和两个机场之间的 Haversine 和 Karney 距离。在这个阶段,使用类似的代码在 Power BI 中计算距离是直接的。让我们看看如何做。
使用 R 在 Power BI 中计算距离
是时候将你在 Power BI 中学到的知识付诸实践了。因此,启动 Power BI Desktop,让我们开始吧:
-
确保 Power BI Desktop 在选项中引用你的最新环境。之后,点击Excel导入
hotels-ny.xlsx文件,该文件位于Chapter10文件夹中。选择它并点击打开。 -
从导航器窗口中选择Sheet 1表:
![图 10.12 – 选择 Sheet 1 表]()
图 10.12 – 选择 Sheet 1 表
然后,点击 转换数据。
-
点击 转换 菜单,然后点击 运行 R 脚本。
-
将
Chapter10\R文件夹中的02-distances-from-airports-in-power-bi-with-r.R文件中的脚本复制到 R 脚本编辑器中,然后点击确定。 -
我们只对
hotels_df中的数据进行关注。因此,点击其 表 值。 -
Power Query 将通过添加每个酒店到两个机场(
JFK和LGA)的距离来转换你的数据,使用 Haversines 和 Karney 的两种方法:![图 10.13 – Python 脚本转换的结果]()
图 10.13 – Python 脚本转换的结果
-
你可以点击 关闭并应用 在 主页 选项卡。
太好了!你刚刚通过在 Power BI 中使用 R 添加两个地理点之间的距离来丰富了你的数据。
在下一节中,你将看到如何使用线性优化算法丰富你的数据集。
线性规划的基础
线性规划(LP)算法被应用于所有那些优化和因此资源经济对于活动持续至关重要的领域。为了理解这一切,你需要一些数学知识。这样,让我们回顾一下我们在年轻时学习过程中遇到的几何概念。
线性方程和不等式
我们在生活中至少遇到过一次线性方程这个术语。在 simplest sense 中,线性方程由两个变量 x 和 y 之间的数学关系组成,形式为 ax + by + c = 0,在笛卡尔平面上标识一条 直线:

图 10.14 – 线性方程 2x + y = 2 的表示
显然,线性方程中涉及的变量可以超过两个。只要我们有三个变量(我们能够看到的著名的三维),线性方程的表示就是可能的。在这种情况下,形式为 ax + by + cz + d = 0 的三个变量的线性方程表示空间中的一个 平面:

图 10.15 – 通用线性方程 ax + by + cz + d = 0 的表示
当线性方程中有超过三个变量时,我们通常称其表示不再是平面,而是一个超平面。
还有线性不等式,它们是涉及不等式的线性函数(由符号 <、>、≤ 和 ≥ 标识)。就像线性方程一样,同样地,你可以用两个变量或三个变量来绘制线性不等式。在两个变量的情况下,它们表示线的一侧的所有点(即平面的一个区域),在三个变量的情况下,它们表示直平面的两侧的点(即一个体积):

图 10.16 – 两个或三个变量的通用线性不等式的表示
注意到由不等式确定的区域是无限的,但有一个边缘是由来自相同不等式的线性方程构成的,考虑的是 = 符号而不是不等式符号。
嗯,当你思考这些概念时,你通常会联想到它们与理论数学相关,但事实并非如此。与运输、制造、运输等相关的一些简单概念可以追溯到线性方程或不等式。让我们看看一个例子。
公式化线性优化问题
想象一下,你在一个制造公司工作,需要生产两种产品,P1 和 P2。为了生产它们,你需要一台机器。具体来说,生产一个单位的 P1 产品需要机器上 30 分钟的加工时间,而生产一个单位的 P2 产品需要同样的机器上 25 分钟。此外,机器 M 可以连续运行的小时数是 40 小时(= 40*60 分钟)。也就是说,30 分钟乘以 P1 产品数量的总和加上 25 分钟乘以 P2 产品数量的总和不能超过 40 小时的加工时间。因此,在加工结束时,生产 x 个 P1 产品和 y 个 P2 产品的机器小时 约束可以总结如下:
哇!你刚刚使用了一个简单的线性不等式来描述一个制造过程的约束。
现在想象一下,你已经收到了客户对 P1 和 P2 产品的需求。具体来说,将所有请求加起来,你需要至少生产 45 个单位的 P1 和 25 个单位的 P2 来满足你的客户。这些需求约束可以总结如下:
太棒了!你已经为你的问题添加了两个更多的线性约束。如果你给这些业务约束添加一个目标,例如,你想要最大化 P1 和 P2 的总单位数 z,那么约束集和目标就构成了一个线性优化问题:
简单来说,对吧?如果我们想更正式一点,线性规划(也称为线性优化)包括一套用于解决线性方程组和不等式系统的技术,目的是最大化或最小化线性目标函数。特别是,变量x和y被称为决策变量,设定的目标被称为目标函数或成本函数。在这个例子中,业务案例只需要不等式约束,但也可能有等式约束。
太棒了!但是,我们现在已经从数学角度设定了问题,那么我们该如何解决它呢?首先,我们需要将所有不等式表示在轴上(在这种情况下可以,因为它们只包含两个变量)。如开头所见,线性不等式表示一个平面的一部分,这部分由直线界定,考虑不等式本身的等号符号。相交所有这些平面,我们就能确定一个所有不等式共有的区域,称为可行区域。基本上,所有在这个区域内的点都满足由不等式确定的约束。
如果你想绘制与我们所展示的示例约束相关的可行区域,无需复习所有在中学学习的几何知识,只需使用WolframAlpha (www.wolframalpha.com/)即可。在搜索引擎中输入以下字符串:plot 30x+25y<=2400 and x>=45 and y>=25。所有约束必须同时满足,因此使用了and运算符。按下Enter键,你将看到以下结果:

图 10.17 – WolframAlpha 绘制的可行区域
我们在 WolframAlpha 的结果中添加了可行区域顶点的值,这是一个三角形。
重要提示
已证明,如果优化问题是可解的,那么最大化或最小化目标函数的解恰好位于可行区域的某个顶点上。
正如你在图 10.17中看到的,顶点 C 的x值为59.1。显然,由于无法只生产一个单位的分数部分,因此不可能有分数部分的产品 P1。因此,应该考虑最近的整数值,即 59。如果业务问题要求决策变量必须为整数,那么问题就变成了混合整数线性规划(MILP)的问题。因此,在我们的例子中,我们考虑顶点A、B和C**=(59,25)作为我们问题的可能解,并将它们的坐标代入目标函数z = x + y*:
-
A:z = 45 + 25 = 70
-
B:z = 45 + 42 = 87
-
C:z = 59 + 25 = 84
我们推断出我们问题的解是顶点B,最大值为 87 单位,即P1 = 45和P2 = 42。
注意
总结来说,生产 45 个产品 P1 和 42 个产品 P2 满足机器的每小时生产约束和客户需求约束,同时最大化总生产量。
你以前想过你今天之前能解决一个线性优化问题吗?好吧,你已经做到了!显然,当这些问题如此简单时,用手解决这些问题是可能的。但是当决策变量的数量增加时,就不再可能绘制可行区域,因此也就无法通过肉眼在多维空间中定位顶点。在这些情况下,是数学,尤其是社区为 Python 和 R 提供的包,使我们能够找到问题的解决方案。让我们首先看看一个稍微复杂一点的线性规划问题案例。
要解决的线性规划问题的定义
现在想象一下,你正在为一家公司工作,该公司需要从全球不同仓库向不同国家运输产品。你必须提供以下内容:
-
仓库中可用的产品数量:
![图 10.18 – 仓库中可用的产品数量]()
图 10.18 – 仓库中可用的产品数量
-
国家所需的产品数量:
![图 10.19 – 国家所需的产品数量]()
图 10.19 – 国家所需的产品数量
-
从每个仓库到所有请求国家的运输成本:

图 10.20 – 从仓库到国家的成本
你的目标是通过满足来自不同国家的所有客户需求来最小化你公司的成本。
表述线性规划问题
如前所述,你必须首先用数学方法表述问题。让我们使用几个数值索引,i 和 j,来识别发送的数量和成本。具体来说,考虑从 Warehouse i 发送到 Country j 的产品数量 x[ij],根据这个定义决策变量和成本的矩阵:

图 10.21 – 发送产品和成本的数量定义矩阵
量 x[ij] 是一个整数且非负()。
根据前面的定义,问题的目标是使目标函数最小化,该函数可以写成从 Warehouse i 到 Country j 的运输成本 (Cij) 与从 Warehouse i 到 Country j 发送的产品数量 (xij) 之间的乘积之和:
将其完整写出,并按仓库组织以方便阅读,从 图 10.21 中的成本金额来看,前面的目标函数可以重写如下:
到目前为止,你必须正式化约束条件,它们有两种类型:仓库供应约束和客户需求约束:
-
仓库供应约束:一旦确定了仓库(例如,Warehouse ITA,其中 i = 1),从这个仓库向所有国家(x[1j]的总和)发送的产品总和不能超过该仓库包含的产品最大数量(对于Warehouse ITA,50,000 个产品;见 图 10.18)。也就是说,对于所有六个国家,我们有以下结果:因此,您将为每个仓库有一个类似的约束(总共四个约束)。
-
客户需求约束:无论货物来自哪个仓库,您都必须满足每个国家产品的需求。因此,从所有仓库向给定国家(例如,法国,其中 j = 2)发送的产品总和必须至少等于该国家的需求(法国至少需要 15,000 个产品;见 图 10.19)。因此,考虑到所有四个仓库,我们得到以下结果:因此,您将为每个国家有一个类似的约束(总共六个约束)。
因此,最终的线性优化问题可以表述如下:
太棒了!您成功地用数学术语表述了一个非平凡的业务问题。现在让我们看看如何用 Python 来解决它。
使用 Python 处理优化问题
如您可能已经猜到的,开发 Python 包的大型社区永远不会停滞不前。在这种情况下,它提供了一个帮助我们解决线性优化问题的模块。它的名字是PuLP (github.com/coin-or/pulp),它是一个用 Python 编写的 LP 模型器。它与解决 LP、混合整数规划(MIP)和其他相关问题的最常见免费和非免费引擎进行接口,例如GNU 线性规划工具包(GLPK)、Coin-or 分支和剪枝(CBC),这是默认的,以及IBM ILOG CPLEX。它的使用非常直接。让我们立即用上一节的问题来实践一下。
在 Python 中解决 LP 问题
本节将解释的代码可以在存储库的Chapter10\Python文件夹中的03-linear-optimizaiont-in-python.py文件中找到。
首先,您必须在您的环境中安装PuLP模块:
-
打开 Anaconda Prompt。
-
输入
conda activate pbi_powerquery_env命令。 -
输入
pip install pulp命令。
之后,您可以使用 NumPy 向量和矩阵定义将构成约束和成本的值:
import pandas as pd
import numpy as np
import pulp as plp
warehouse_supply_df = pd.read_excel(r’D:\<your-path>\Chapter10\RetailData.xlsx’, sheet_name=’Warehouse Supply’, engine=’openpyxl’)
warehouse_supply = warehouse_supply_df[’product_qty’].to_numpy()
country_demands_df = pd.read_excel(r’D:\<your-path>\Chapter10\RetailData.xlsx’, sheet_name=’Country Demand’, engine=’openpyxl’)
country_demands = country_demands_df[’product_qty’].to_numpy()
cost_matrix_df = pd.read_excel(r’D:\<your-path>\Chapter10\RetailData.xlsx’, sheet_name=’Shipping Cost’, engine=’openpyxl’)
n_warehouses = cost_matrix_df.nunique()[’warehouse_name’]
n_countries = cost_matrix_df.nunique()[’country_name’]
cost_matrix = cost_matrix_df[’shipping_cost’].to_numpy().reshape(n_warehouses,n_countries)
在脚本文件中,您还会找到直接从Chapter10文件夹中的RetailData.xlsx文件导入值的代码。
然后,您可以定义一个LpProblem对象,给它一个名称,并指定要应用于目标函数的优化类型(最小化或最大化):
model = plp.LpProblem(“supply-demand-minimize-costs-problem”, plp.LpMinimize)
您可以稍后向这个空对象添加目标函数和约束。
为了构建目标函数,我们首先必须通过LpVariable函数定义决策变量(x[ij]),该函数接受变量名称、表示变量索引的字符串完整列表、变量的类别(连续、整数或二进制)以及任何上界或下界值。索引列表只是通过嵌套列表推导(bit.ly/nested-list-comprehensions)构建的:
var_indexes = [str(i)+str(j) for i in range(1, n_warehouses+1) for j in range(1, n_countries+1)]
print(“Variable indexes:”, var_indexes)
这是一个示例输出:
Variable Indices: [’11’, ‘12’, ‘13’, ‘14’, ‘15’, ‘16’, ‘21’, ‘22’, ‘23’, ‘24’, ‘25’, ‘26’, ‘31’, ‘32’, ‘33’, ‘34’, ‘35’, ‘36’, ‘41’, ‘42’, ‘43’, ‘44’, ‘45’, ‘46’]
现在可以轻松地定义决策变量如下:
decision_vars = plp.LpVariable.matrix(
name=”x”,
indexs=var_indexes,
cat=”Integer”,
lowBound=0 )
由于决策变量需要乘以之前在cost_matrix中定义的C[ij]成本,因此将decision_vars列表格式化为与成本矩阵相同的形状,以便能够执行元素级乘法,也称为Hadamard 乘积:
shipping_mtx = np.array(decision_vars).reshape(n_warehouses,n_countries)
print(“Shipping quantities matrix:”)
print(shipping_mtx)
它返回以下输出:
Shipping quantities matrix:
[[x_11 x_12 x_13 x_14 x_15 x_16]
[x_21 x_22 x_23 x_24 x_25 x_26]
[x_31 x_32 x_33 x_34 x_35 x_36]
[x_41 x_42 x_43 x_44 x_45 x_46]]
目标函数随后定义为成本矩阵和运输矩阵元素级乘积的总和:
objective_func = plp.lpSum(cost_matrix * shipping_mtx)
print(objective_func)
输出如下:
8*x_11 + 18*x_12 + 14*x_13 + 40*x_14 + 40*x_15 + 25*x_16 + 12*x_21 + 10*x_22 + 8*x_23 + 18*x_24 + 40*x_25 + 18*x_26 + 34*x_31 + 32*x_32 + 30*x_33 + 10*x_34 + 33*x_35 + 35*x_36 + 25*x_41 + 20*x_42 + 18*x_43 + 35*x_44 + 30*x_45 + 10*x_46
如果你记得正确,这个表达式与你在上一节中看到的完整目标函数是一致的。
然后,你可以将目标函数添加到模型中,如下所示:
model += objective_func
约束不等式也是以相同的方式添加的:
for i in range(n_warehouses):
model += plp.lpSum(shipping_mtx[i][j] for j in range(n_countries)) <= warehouse_supply[i], “Warehouse supply constraints “ + str(i)
for j in range(n_countries):
model += plp.lpSum(shipping_mtx[i][j] for i in range(n_warehouses)) >= country_demands[j] , “Country demand constraints “ + str(j)
最后,我们可以通过运行这个简单的脚本来解决问题:
model.solve()
首先要做的事情是检查解的状态,它可以取Optimal、Not Solved、Infeasible、Unbounded和Undefined等值:
status = plp.LpStatus[model.status]
print(status)
在我们的情况下,状态是Optimal,因此已经找到了问题的最优解。那么,让我们看看目标函数在找到的解上的值:
print(“Total Cost:”, model.objective.value())
该值等于 2,270,000 欧元,这对应于满足所有施加约束条件下的最低可能成本。如果你想以非常可读的方式查看组成运输矩阵的变量的解值,你最好将它们转换成 pandas 数据框:
decision_var_results = np.empty(shape=(n_warehouses * n_countries))
z = 0
for v in model.variables():
try:
decision_var_results[z] = v.value()
z += 1
except:
print(“error couldn’t find value”)
decision_var_results = decision_var_results.reshape(n_warehouses,n_countries)
col_idxs = [’Italy’,’France’,’Germany’,’Japan’,’China’,’USA’]
row_idxs = [’Warehouse ITA’,’Warehouse DEU’,’Warehouse JPN’,’Warehouse USA’]
dv_res_df = pd.DataFrame(decision_var_results, columns=col_idxs, index=row_idxs)
dv_res_df
出现的结果如下:

图 10.22 – 根据找到的解决方案发送的数量
例如,很容易看出,法国客户必须从德国仓库接收 10,000 个单位,从美国仓库接收 5,000 个单位以满足他们的需求。
如果你想要检查每个仓库发送的总数量,你可以运行以下代码:
warehouse_shipped_qty = np.zeros(shape=(n_warehouses))
z = 0
for i in range(n_warehouses):
warehouse_shipped_qty[z] = plp.lpSum(shipping_mtx[i][j].value() for j in range(n_countries)).value()
z += 1
w_shipped_df = pd.DataFrame(warehouse_shipped_qty, columns=[’qty’], index=row_idxs)
w_shipped_df
你将得到以下结果:

图 10.23 – 从每个仓库发送的总数量
太棒了!你只用几行 Python 代码就解决了非简单线性优化问题。你能猜到吗?现在让我们看看如何将你在 Power BI 中学到的知识应用到实践中。
使用 Python 在 Power BI 中解决 LP 问题
在 Power BI 中应用我们刚刚看到的方法并不像在上一章中遇到的其它情况那样直接。首先,我们必须已经在数据模型中加载了国家需求、仓库供应和运输成本的数据,这些数据可能来自任何数据源。在我们的案例中,它们已经在 Excel 中准备好了,所以我们将继续在 Power BI Desktop 中加载它们:
-
点击功能区上的Excel 工作簿(或在主画布上的从 Excel 导入数据),在
Chapter10文件夹中选择RetailData.xlsx文件,然后点击打开。 -
选择CountryDemand、ShippingCost和WarehouseSupply表(带有蓝色标题的表),然后点击转换数据:

图 10.24 – 从 Excel 选择三个表
现在,你处于之前描述的情况。在此阶段,Power Query 中的每个查询都有自己的步骤堆栈,这些步骤生成了它:

图 10.25 – 带有其应用步骤堆栈的 CountryDemand 查询
在上一节中,你看到为了计算最优分配,你需要能够在单个 Python 脚本中使用所有三个数据集。如果你将 Python 脚本作为之前列出的三个查询之一中的步骤添加,你只能在脚本中与该单个查询的数据进行交互。那么你如何创建一个可以使用所有可用数据的脚本呢?你必须求助于以下策略:
为了在 Python 脚本中使用属于多个查询的数据,你必须首先将每个查询导出的每个数据结构序列化到一个 pickle 文件中。然后,通过连接创建一个新的查询,该查询由三个查询合并而成(连接后的数据集是否有意义并不重要,因为它将不会被使用)。此时,为这个合并查询添加一个 Python 脚本步骤,在这个步骤中反序列化每个查询中之前序列化的所有三个对象。你最终将在一个脚本中拥有来自三个不同数据源的对象。
这样,你可以确保首先执行序列化三个数据集对象的 Python 脚本,然后按照顺序执行反序列化脚本来解决线性优化问题。
让我们详细看看如何完成上一段中描述的任务:
-
在左侧选择CountryDemand查询,点击功能区上的转换选项卡,然后点击运行 Python 脚本。
-
在脚本编辑器中输入以下 Python 代码,然后点击确定:
import pickle country_demands = dataset[’product_qty’].to_numpy() pickle.dump( country_demands, open(r”D:\<your-path>\Chapter10\Python\country_demands.pkl”, “wb”) ) -
点击值下对应的数据集的表。你将始终看到国家需求数据,但在幕后,一个名为
country_demands.pkl的 NumPy 向量已经被序列化,存储在Chapter10/Python文件夹中。 -
在左侧选择ShippingCost查询,点击功能区上的转换选项卡,然后点击运行 Python 脚本。
-
在脚本编辑器中输入以下 Python 代码,然后点击确定:
import pickle n_warehouses = dataset.nunique()[’warehouse_name’] n_countries = dataset.nunique()[’country_name’] cost_matrix = dataset[’shipping_cost’].to_numpy().reshape(n_warehouses, n_countries) pickle.dump( cost_matrix, open(r”D:\<your-path>\Chapter10\Python\cost_matrix.pkl”, “wb”) ) -
点击值下对应的dataset的表。你将始终看到成本矩阵数据,但幕后,一个名为
cost_matrix.pkl的 NumPy 向量已在Chapter10/Python文件夹中序列化。 -
在左侧选择WarehouseSupply查询,点击功能区上的转换选项卡,然后点击运行 Python 脚本。
-
在脚本编辑器中输入以下 Python 代码,然后点击确定:
import pickle warehouse_supply = dataset[’product_qty’].to_numpy() pickle.dump( warehouse_supply, open(r”D:\<your-path>\Chapter10\Python\warehouse_supply.pkl”, “wb”) ) -
点击值下对应的dataset的表。你将始终看到仓库供应数据,但幕后,一个名为
warehouse_supply.pkl的 NumPy 向量已在Chapter10/Python文件夹中序列化。 -
在左侧的查询面板上右键单击,选择新建查询,然后选择组合,然后选择合并查询为新。
-
在合并窗口中,选择CountryDemand和ShippingCost表,点击两个表中的
country_name列,然后点击确定。 -
将出现新的Merge1查询。点击它,展开ShippingCost列的内容,然后点击确定。
-
切换到主页选项卡,确保已选择Merge1查询,然后点击合并查询以合并WarehouseSupply:
![图 10.26 – 将另一个查询合并到 Merge1]()
图 10.26 – 将另一个查询合并到 Merge1
-
在合并窗口上选择WarehouseSupply表,然后点击两个表中的
ShippingCost.warehouse_name和warehouse_name列,然后点击确定。 -
展开WarehouseSupply列,保留所有选中项,然后点击确定。
-
点击功能区上的转换选项卡,点击运行 Python 脚本,并将脚本输入到
Chapter10\Python文件夹中的04-linear-optimization-in-power-bi-with-python.py文件中。 -
你将得到以下错误:Formula.Firewall: 查询‘Merge1’(步骤‘Run Python script’)引用了其他查询或步骤,因此它可能无法直接访问数据源。请重新构建此数据组合。
-
前往文件,点击选项和设置,然后点击选项。在选项窗口中,选择隐私选项卡,然后点击始终忽略隐私级别设置:
![图 10.27 – 隐私级别设置选项]()
图 10.27 – 隐私级别设置选项
你可以安全地这样做,因为你确信你已经将所有数据源的权限设置为组织。
-
切换到主页选项卡,点击刷新预览。几秒钟后,点击
result_df数据框的表:![图 10.28 – 选择 result_df 表]()
图 10.28 – 选择 result_df 表
-
你将看到以下表格的内容,该表格显示了针对每个仓库和国家的组合,线性优化问题的解提供的运输数量以及相应的成本:
![图 10.29 – 选择 result_df 表]()
图 10.29 – 选择 result_df 表
-
点击 关闭并应用。
记住,对源隐私级别的控制已被禁用。你应该在项目结束时将其重新启用,以便根据每个文件的隐私级别设置组合数据,以防因查询折叠机制而导致可能的数据泄露(更多详情,请参阅 参考文献 部分)。
重要提示
还要记住,如果你想将报告发布到 Power BI 服务,所有数据源的隐私级别必须设置为 公共。
你知道吗?你看到的所有关于 LP 问题的内容也可以在 R 中实现?让我们看看如何实现它。
使用 R 解决 LP 问题
如果 Python 社区非常活跃,那么 R 社区当然也不会停滞不前!实际上,优化建模包(OMPR)是可用的(dirkschumacher.github.io/ompr/),这是一个为在 R 中建模和解决 LP 问题而创建的领域特定语言。
通常,所有其他在 R 中开发的、服务于相同目的的包大多是矩阵导向的,迫使你在将对象传递给求解器之前将所有对象转换为矩阵和向量。这项任务乍一看可能足够简单,但当要解决的问题变得复杂时,编写解决这些问题的 R 代码可能会变得困难。
另一方面,ompr 包提供了足够的表达能力,允许你增量地建模你的 LP 问题,这也要归功于 %>% 管道的使用。因此,你会感觉就像在使用 dplyr 函数编写代码一样,忘记了矩阵和向量。
此外,ompr 包依赖于另一个名为 ompr.roi 的包,以便能够选择解决 LP 问题的首选引擎。该包在幕后使用一个复杂的框架来处理 R 中的线性和非线性优化问题,这个框架被称为 R 优化基础设施(ROI),由另一个名为 ROI 的包提供(roi.r-forge.r-project.org/)。
在我们的示例中,我们将使用由 ROI 添加的插件 GLPK 求解器。
因此,让我们看看如何使用 ompr 包在 R 中设置我们在上一节中描述的 LP 问题。
在 R 中解决 LP 问题
首先,你需要安装 ompr 正确运行所需的包。因此,按照以下步骤操作:
-
打开 RStudio 并确保它引用的是你最新的 CRAN R 版本(在我们的例子中是版本 4.0.2)。
-
点击 控制台 窗口,输入以下命令:
install.packages('ompr')。然后,按 Enter。 -
输入以下命令:
install.packages(‘ompr.roi’)。然后,按Enter键。 -
输入以下命令:
install.packages(‘ROI.plugin.glpk’)。然后,按Enter键。
你可以在Chapter10\R文件夹中的03-linear-optimization-in-r.R文件中找到所有将展示的代码。
首先,你需要导入所需的包和来自Chapter10文件夹中的RetailData.xlsx文件的数据:
library(dplyr)
library(tidyr)
library(readxl)
library(ompr)
library(ompr.roi)
library(ROI.plugin.glpk)
warehouse_supply_tbl = read_xlsx(r’{D:\<your-path>\Chapter10\RetailData.xlsx}’, sheet = ‘Warehouse Supply’)
country_demands_tbl = read_xlsx(r’{D:\<your-path>\Chapter10\RetailData.xlsx}’, sheet = ‘Country Demand’)
cost_matrix_tbl = read_xlsx(r’{D:\<your-path>\Chapter10\RetailData.xlsx}’, sheet = ‘Shipping Cost’)
之后,你可以从 tibbles 中计算数组和成本矩阵,这些是随后使用ompr设置模型所需的:
n_warehouses <- cost_matrix_tbl %>%
distinct(warehouse_name) %>%
count() %>%
pull(n)
n_countries <- cost_matrix_tbl %>%
distinct(country_name) %>%
count() %>%
pull(n)
warehouse_supply <- warehouse_supply_tbl %>%
pull(product_qty)
country_demands <- country_demands_tbl %>%
pull(product_qty)
cost_matrix <- data.matrix(
cost_matrix_tbl %>%
pivot_wider( names_from = country_name, values_from = shipping_cost ) %>%
select( -warehouse_name )
)
rownames(cost_matrix) <- warehouse_supply_tbl %>% pull(warehouse_name)
为了将cost_matrix_tbl tibble 中成本数据的垂直形式转换为水平形式,我们使用了tidyr包提供的非常方便的pivot_wider()函数。
在此阶段,如果我们遵循公式化 LP 问题部分中展示的数学模型,使用ompr暴露的函数定义模型几乎是直截了当的:
model <- MIPModel() %>%
# define the x integer variables, paying attention to define also the lower bound of 0
add_variable( x[i, j], i = 1:n_warehouses, j = 1:n_countries, type = “integer”, lb = 0 ) %>%
# define the objective function, declaring also the “sense” that is the type of problem (minimize)
set_objective( sum_expr(cost_matrix[i, j] * x[i, j], i = 1:n_warehouses, j = 1:n_countries), sense = ‘min’ ) %>%
# add warehouse supply constraints
add_constraint( sum_expr(x[i, j], j = 1:n_countries) <= warehouse_supply[i], i = 1:n_warehouses ) %>%
# add customer demand constraints
add_constraint( sum_expr(x[i, j], i = 1:n_warehouses) >= country_demands[j], j = 1:n_countries )
sum_expr()函数乍一看可能难以理解。让我们以这段代码为例:
sum_expr(x[i, j], j = 1:n_countries)
可以完整阅读如下:将决策变量 x[i,j]替换为从 1 到 n_countries(即 6)的值,然后对结果变量求和。总之,你要求计算如下:
x[i,1] + x[i,2] + x[i,3] + x[i,4] + x[i,5] + x[i,6]
在此阶段,一旦定义了模型,你可以使用以下代码使用glpk求解器来解决问题:
result <- model %>%
solve_model(with_ROI(solver = ‘glpk’))
获得的结果(显然,因为解决方案是最优的)与之前使用 Python 的PuLP模块看到的结果一致:
decision_var_results <- matrix(result$solution, nrow = n_warehouses, ncol = n_countries, )
rownames(decision_var_results) <- warehouse_supply_tbl %>% pull(warehouse_name)
colnames(decision_var_results) <- country_demands_tbl %>% pull(country_name)
decision_var_results
你一定会认出之前已经看到的解决方案:
Italy France Germany Japan China USA
Warehouse ITA 40000 0 10000 0 0 0
Warehouse DEU 0 10000 15000 5000 0 0
Warehouse JPN 0 0 0 40000 0 0
Warehouse USA 0 5000 0 0 25000 25000
你看到你也能在 R 中解决 LP 问题了吗?并没有什么特别复杂的地方,对吧?非常好!
让我们现在应用在 Power BI 中看到的内容。
使用 R 在 Power BI 中解决 LP 问题
在使用 Python 解决 Power BI 中的 LP 问题部分中已经暴露了解决我们的 LP 问题在 Power BI 中的实现复杂性。因此,我们将在这里使用 R 进行单独的步骤,而不深入细节:
-
打开 Power BI Desktop,确保在选项中将隐私设置为始终忽略隐私级别设置。
-
点击功能区上的Excel Workbook(或在主画布上的从 Excel 导入数据),选择
Chapter10文件夹中的RetailData.xlsx文件,然后点击打开。 -
选择CountryDemand、ShippingCost和WarehouseSupply表格(带有蓝色标题的表格),然后点击Transform Data:
![图 10.30 – 从 Excel 中选择三个表格]()
图 10.30 – 从 Excel 中选择三个表格
-
在左侧选择CountryDemand查询,点击功能区上的Transform选项卡,然后点击Run R Script。
-
在脚本编辑器中输入以下 R 代码(正确更改路径),然后点击OK:
library(dplyr) country_demands <- dataset %>% pull(product_qty) saveRDS(country_demands, r’{D:\<your-path>\Chapter10\R\country_demands.rds}’) country_demand_df <- dataset你将看到国家需求数据,但幕后,一个名为
country_demands.rds的向量已经被序列化,存放在Chapter10/R文件夹中。 -
在左侧选择ShippingCost查询,点击功能区上的转换选项卡,然后点击运行 R 脚本。
-
在脚本编辑器中输入以下 R 代码,然后点击确定:
library(dplyr) library(tidyr) n_warehouses <- dataset %>% distinct(warehouse_name) %>% count() %>% pull(n) n_countries <- dataset %>% distinct(country_name) %>% count() %>% pull(n) cost_matrix <- data.matrix( dataset %>% pivot_wider( names_from = country_name, values_from = shipping_cost ) %>% select( -warehouse_name ) ) rownames(cost_matrix) <- dataset %>% distinct(warehouse_name) %>% pull(warehouse_name) saveRDS(cost_matrix, r’{D:\<your-path>\Chapter10\R\cost_matrix.rds}’) cost_matrix <- dataset你将看到成本矩阵数据,但幕后,一个名为
cost_matrix.rds的矩阵已经在Chapter10/R文件夹中序列化。 -
在左侧选择WarehouseSupply查询,点击功能区上的转换选项卡,然后点击运行 R 脚本。
-
在脚本编辑器中输入以下 R 代码,然后点击确定:
library(dplyr) warehouse_supply <- dataset %>% pull(product_qty) saveRDS(warehouse_supply, r’{D:\<your-path>\Chapter10\R\warehouse_supply.rds}’) warehouse_supply_df <- dataset你将看到仓库供应数据,但幕后,一个名为
warehouse_supply.rds的向量已经在Chapter10/R文件夹中序列化。 -
在左侧的查询面板上右键单击,选择新建查询,然后合并,然后合并查询为新建。
-
在合并窗口中,选择CountryDemand和ShippingCost表,然后选择两个表中的
country_name列并点击确定。 -
将出现一个新的Merge1查询。点击它,展开ShippingCost表的内容,然后点击确定。
-
切换到主页选项卡,确保已选择Merge1查询,然后点击功能区上的合并查询以合并WarehouseSupply。
-
在合并窗口中选择WarehouseSupply表,然后点击两个表中的
ShippingCost.warehouse_name和warehouse_name列,然后点击确定。 -
展开WarehouseSupply列并点击确定。
-
点击功能区上的转换选项卡,点击运行 R 脚本,并输入脚本,你可以在
Chapter10\R文件夹中的04-linear-optimization-in-power-bi-with-r.R文件中找到该脚本。 -
你将看到以下表格的内容,该表格显示了每个仓库和国家的组合提供的线性优化问题的解的发货量值以及相应的成本:
![图 10.31 – **WarehouseSupply**表的内容]()
图 10.31 – WarehouseSupply表的内容
-
点击关闭并应用。
记住,源隐私级别控制已被禁用。你应该在项目结束时将其重新启用,以便根据每个文件的隐私级别设置合并数据,以防因查询折叠机制可能导致的潜在数据泄露(更多详情,请参阅参考文献部分)。
摘要
在本章中,你学习了如何使用 Python 和 R 根据球面三角学中最常用的定义来计算两个地理点之间的距离。然后你将这一知识应用于 Power BI 中的实际案例。
你还学习了如何通过一些 Python 和 R 包解决最简单的线性规划问题。同样,你将所学知识应用于 Power BI 中的实际案例。
在下一章中,你将看到如何通过统计学为你的商业分析添加一些盐分。
参考文献
对于额外的阅读,请参阅以下书籍和文章:
-
关于球面三角学 (
www.robingilbert.com/blog/2017-10-01-on-spherical-trigonometry/) -
使用 Python 计算两个 GPS 点之间的距离(Vincenty 逆公式) (
nathanrooy.github.io/posts/2016-12-18/vincenty-formula-with-python/) -
使用 NumPy 和 pandas 在 Python 中进行向量化和平行化 (
datascience.blog.wzb.eu/2018/02/02/vectorization-and-parallelization-in-python-with-numpy-and-pandas/) -
数据隐私防火墙背后的场景 (
docs.microsoft.com/en-us/power-query/dataprivacyfirewall)
第十二章:11 添加统计洞察:关联
如你在上一章所见,通过应用复杂算法可以丰富你的数据。除了距离和线性规划之外,统计学通常可以成为数据分析的终极武器。我们将解释一些旨在从你的数据中提取关于变量之间关联的相关洞察的统计程序的基本概念。
本章,你将学习以下主题:
-
探索变量之间的关联
-
数值变量之间的相关性
-
类别变量和数值变量之间的相关性
技术要求
本章要求你拥有一个正常工作的互联网连接,并且Power BI Desktop已经安装在你的机器上。你必须已经按照第二章、配置 Power BI 中的 R和第三章、配置 Power BI 中的 Python中概述的方式正确配置了 R 和 Python 引擎和 IDE。
探索变量之间的关联
初看之下,你可能会想知道寻找变量之间关系的目的何在。能够理解一对变量的行为并识别它们行为中的模式,有助于企业主识别关键因素,将公司健康指标转向对其有利的方向。了解绑定两个变量趋势的模式,让你能够通过了解其中一个来以某种确定性预测另一个。因此,了解揭示这些模式的方法,赋予你一种对商业主总是有吸引力的分析超级能力。
通常情况下,当其中一个变量的值以某种方式与另一个变量的值相关时,两个变量是相关的。当你能够以某种方式测量两个变量关联的程度时,这被称为相关性。当两个变量是数值时,相关性概念立即适用。让我们看看如何。
数值变量之间的相关性
我们通常做的第一件事是理解两个数值变量之间是否存在关联,就是将它们表示在两个笛卡尔轴上,以获得一个散点图:

图 11.1 – 一个简单的散点图
使用散点图,可以识别可能的关联的三个重要特征:
-
方向:它可以是正的(增加),负的(减少),或者未定义的(未找到关联或增加和减少)。如果一个变量的增量与另一个变量的增量相对应,则方向为正;否则,为负:
![图 11.2 – 关联的方向类型]()
图 11.2 – 关联的方向类型
-
形式:它描述了关联在其最简单意义上的一般形式。显然,有许多可能的形式,但有一些更常见,例如线性和曲线(非线性)形式:
![图 11.3 – 关联的形状]()
图 11.3 – 关联的形状
-
强度:关联的强度取决于散点图中点如何接近描绘关联通用形状的线。

图 11.4 – 关联的强度
随着这些视觉模式通过应用数学概念变得可测量,我们可以定义数值变量之间不同类型的关联。
卡尔·皮尔逊的相关系数
皮尔逊相关系数(r)衡量分析变量之间关联的线性程度。这意味着如果 r = 1 或 r = -1,那么两个变量处于完美的线性关系中。系数的符号确定关联的方向。如果 r 在负或正方向上接近 0,则意味着变量之间的关联非常弱。系数的值不能超出 [-1, 1] 的范围。
皮尔逊相关系数的平方(R²)被称为确定系数,它衡量一个变量的方差(衡量观测值与其平均值之间的分散程度)中有多少百分比是由于另一个变量的方差,假设关联是线性的。例如,如果一个人的体重和身高变量之间的相关系数 r 为 0.45,那么可以说大约 20%(0.45 x 0.45)的体重变化(方差)是由于身高变化引起的。
计算相关系数 r 很少用手动完成,因为任何数据管理平台都提供了一个易于计算它的函数。如果你好奇,给定一个包含 n 个实体的数据集,确定你想要计算相关系数的变量 x 和 y – 这是计算它的公式:

图 11.5 – 皮尔逊相关系数的公式
x̄ 和 ȳ 的值对应于数据集中同义变量的平均值。请注意,皮尔逊相关函数是对称的,这意味着计算时列的顺序并不重要。
图 11.6 中展示了为一些特定关联(Boigelot 分布)计算的相关系数 r 的示例:

图 11.6 – 在 Boigelot 分布上计算的皮尔逊相关系数
图 11.6 中散点图的第一行给出了关联强度如何通过相关性的大小来衡量,趋向于线性关系。
第二行显示,无论线性关系的角度如何,相关系数 r 的绝对值总是等于 1,也就是说,它总是正确地识别线性关系及其方向。唯一的例外是中心水平线性关系的情况,对于这种情况,系数 r 是未定义的,因为对于所有 x 的值,都有一个唯一的 y 值。
第三行显示了相关系数的一个最重要的局限性:
重要提示
皮尔逊相关系数在检测两个变量之间的非线性关联模式方面不如后者。
这些是其他局限性:
-
在一个非常小的数据集大小(比如说,3-6 个观测值)的情况下,可能会出现关联存在的情况,尽管实际上并不存在。
-
相关系数对异常值(位于大多数其他值较远处的观测值)非常敏感。也可能发生异常值给出关于存在关联的虚假印象:
![图 11.7 – 由于异常值导致的错误相关值]()
图 11.7 – 由于异常值导致的错误相关值
我们将在第十二章中更详细地介绍异常值,添加统计洞察、异常值和缺失值。
-
如果数据集中的观测值被分为不同的聚类,这可能会产生虚假的关联感:
![图 11.8 – 由于聚类导致的错误相关值]()
图 11.8 – 由于聚类导致的错误相关值
-
用于计算相关系数的变量必须在连续尺度上定义。对于基于离散尺度的变量,例如服务的 1-10 级评分,您必须使用斯皮尔曼秩相关系数,我们将在下一节中探讨。
-
如果在一个关联中存在一个变量与第二个变量的值域具有不等的变异性,我们就面临了异方差性的情况。散点图假设典型的锥形形状,就像图 11.1中的那样。在这种情况下,相关系数可能会识别出错误线性关系(多个线性关系都可能满足散点图的锥形形式):
![图 11.9 – 由于异方差性导致的错误相关值]()
图 11.9 – 由于异方差性导致的错误相关值
-
计算皮尔逊相关系数涉及的变量不应高度偏斜,也就是说,变量的分布必须不相对于对称的钟形曲线(正态分布,其中均值、中位数和众数相等)扭曲或不对称,否则可能会发生相关程度真实大小的减少:

图 11.10 – 分布偏态类型
在明确了这些限制之后,首先出现的问题是:当关联是非线性的,变量基于顺序尺度,或者它们具有偏斜分布时,我该如何计算两个数值变量之间的相关系数?这个问题的一个可能解决方案是由查尔斯·斯皮尔曼提供的。
查尔斯·斯皮尔曼的相关系数
查尔斯·斯皮尔曼引入了皮尔逊相关系数的非参数替代方案。非参数统计方法指的是它不假设其计算的数据遵循由少量参数描述的特定模型。这种方法总结在一个新的相关系数中,称为斯皮尔曼等级相关系数(用ρ,rho表示)。斯皮尔曼系数衡量两个变量之间关联的强度和方向,一旦它们的观测值根据其值进行排序。计算它的公式如下:

图 11.11 – 斯皮尔曼等级相关系数公式
值D是每个观测值的两个等级之间的差值。要了解更多计算细节,请查看参考文献。请注意,斯皮尔曼的相关函数也是对称的。
斯皮尔曼相关系数的范围从-1 到+1。系数的符号表示它是正单调关联还是负单调关联。
斯皮尔曼相关系数的重要特征
因为斯皮尔曼相关系数适用于等级,它提供了两个连续随机变量之间单调关联的度量,并且是顺序变量的最佳拟合相关系数。由于其计算方式与皮尔逊相关系数不同,斯皮尔曼相关系数对异常值稳健。
记住,如果一个关联在其整个定义域内是增加的或减少的(而不是两者的组合),则称其为单调关联:

图 11.12 – 单调和非单调关联
重要提示
检查关联的单调性很重要,因为一般来说,
相关系数无法准确描述非单调关系。
计算 Boigelot 分布的斯皮尔曼相关系数,我们得到以下结果:

图 11.13 – 在 Boigelot 分布上计算的斯皮尔曼相关系数
即使斯皮尔曼相关系数也无法捕捉到你在图 11.13第三行分布中观察到的非线性模式。这并不是由于上述关联的非线性,而是由于它们的非单调性。相比之下,对于单调非线性关联,斯皮尔曼相关系数比皮尔逊相关系数更适合:

图 11.14 – 非线性单调关联中的皮尔逊和斯皮尔曼相关系数
斯皮尔曼相关系数并不是唯一使用数据排序进行计算的。肯德尔也使用这种策略。让我们来看看它的特点。
莫里斯·肯德尔的相关系数
肯德尔秩相关系数(τ,tau)也是一种检测两个变量之间关联的非参数方法。其计算基于一致对和不一致对的概念(更多细节请参阅参考文献部分)。用于计算肯德尔相关系数的公式如下:

图 11.15 – 肯德尔相关系数公式
n[c]值表示一致对的数量,而n[d]值表示不一致对的数量。与其他相关函数一样,肯德尔的相关性也是对称的。
为斯皮尔曼相关系数所做的所有假设也适用于肯德尔。以下是两种相关系数的比较:
-
这两种相关系数都能很好地处理有序数据和非线性连续单调数据。
-
这两种相关系数对异常值都具有鲁棒性。
-
当样本量小且存在许多相同等级时,肯德尔相关系数比斯皮尔曼相关系数更受欢迎。
-
肯德尔的系数通常比斯皮尔曼的小。
-
肯德尔相关系数的计算复杂度比斯皮尔曼的相关系数更高。
话虽如此,让我们在一个实际案例中使用这三个相关系数。
实际案例的描述
您的老板要求您执行一个客户要求的全球人口分析。具体来说,您需要了解在过去的年份中,预期寿命和人均国内生产总值(GDP)之间是否存在关系以及关系的程度。
在网上寻找有用的数据时,您意识到有一个名为 Gapminder 的网站可以帮助您(www.gapminder.org/)。它对抗毁灭性的误解,并推广一个基于事实的世界观,每个人都可以理解,通过将来自多个来源的数据结合成独特的时间序列,这些时间序列在其他地方找不到。该网站精确地揭示了关于预期寿命的数据(bit.ly/life-expectancy-data)和关于人均 GDP 的数据(bit.ly/gdp-per-capita-data)。此外,还有一个人已经将数据转换成更适合我们目的的形式,并在 CSV 文件中与社区分享了他的工作(bit.ly/gdp-life-expect-data)。
太棒了!您已经拥有了开始 Python 分析所需的一切。
在 Python 中实现相关系数
要运行本节中的代码,您需要在您的 pbi_powerquery_env 环境中安装 Seaborn 模块。如您现在可能已经学到的,请按以下步骤操作:
-
打开 Anaconda 提示符。
-
输入命令
conda activate pbi_powerquery_env。 -
输入命令
pip install seaborn。
本节中提供的代码可在 Chapter11\Python 文件夹中的 01-gdp-life-expectancy-analysis-in-python.py 文件中找到。
在这一点上,让我们快速查看上述 CSV 文件中的数据,同时导入后续操作所需的表单:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
dataset_url = 'http://bit.ly/gdp-life-expect-data'
df = pd.read_csv(dataset_url)
df.head()
# If you’re not using VS Code run this instead
# print(df.head())
您会看到类似这样的:

图 11.16 – GDP 和预期寿命数据集的样本
我们感兴趣的变量是 lifeExp 和 gdpPercap。在绘制这两个变量的散点图之前,让我们也看一下每个变量的分布。因此,让我们定义我们将用于绘图的函数并绘制分布图:
def distPlot(data, var, title, xlab, ylab, bins=100):
hplot = sb.distplot(data[var], kde=False, bins=bins)
plt.title(title, fontsize=18)
plt.xlabel(xlab, fontsize=16)
plt.ylabel(ylab, fontsize=16)
return hplot
def scatterPlot(data, varx, vary, title, xlab, ylab):
hplot = sb.scatterplot(varx, vary, data=data)
plt.title(title, fontsize=18)
plt.xlabel(xlab, fontsize=16)
plt.ylabel(ylab, fontsize=16)
return hplot
distPlot(data=df, var='lifeExp', title='Life Expectancy', xlab='Life Expectancy years', ylab='Frequency')
# In case you're not using a Jupyter notebook run also the following:
# plt.show()
distPlot(data=df, var='gdpPercap', title='GDP / capita', xlab='GDP / capita ($)', ylab='Frequency')
# In case you're not using a Jupyter notebook run also the following:
# plt.show()
创建的图表如下:

图 11.17 – 预期寿命和 GDP 变量的分布
如您所见,预期寿命的分布大致符合正态分布,而 GDP 的分布则是完全正偏斜。如果这两个变量之间存在显著的关联,那么您应该预期散点图可能是非线性的。让我们来看看:
scatterPlot(data=df, varx='lifeExp', vary='gdpPercap', title='Life Expectancy vs GDP/Capita', xlab='lifeExp', ylab='gdpPercap')
# In case you're not using a Jupyter notebook run also the following:
# plt.show()
您会得到这个:

图 11.18 – 预期寿命与人均 GDP 的散点图
从图 11.18中,您可以清楚地看到两个变量之间的关联性,并且正如预期的那样,是非线性的(背景箭头)。更重要的是,存在一些异常值,它们在顶部被突出显示。因此,您面临两个使皮尔逊相关系数无效的假设。幸运的是,这种关联是单调递增的,因此斯皮尔曼和肯德尔相关系数应该能更准确地检测到模式。
pandas 已经提供了三种相关系数的 Python 实现。因此,相关性分析非常直接:
df[['lifeExp','gdpPercap']].corr(method='pearson')
df[['lifeExp','gdpPercap']].corr(method='spearman')
df[['lifeExp','gdpPercap']].corr(method='kendall')
pandas 数据框的corr()函数返回其数值列的每一对的相关计算。由于相关函数是对称的(即,计算时列的顺序无关紧要),这三个调用返回三个数据框,每个数据框都将所有数值特征作为行和列,并将特征之间的相关系数作为值:

图 11.19 – 人均寿命与人均 GDP 的相关性
如您所见,所有三个相关系数都表明两个变量之间存在正相关关系。正如预期的那样,皮尔逊相关系数检测到的强度最弱(r = 0.58)。相比之下,斯皮尔曼和肯德尔相关系数的幅度更高,分别为ρ = 0.83和τ = 0.64。特别是从斯皮尔曼相关系数来看,两个变量之间存在强烈的关联性。
在数据科学项目中,通常需要选择最具有预测性的变量来预测目标变量,尤其是在有大量列的情况下。相关性技术当然可以帮助我们做到这一点:假设目标变量是人均 GDP,我们可以决定保留所有与目标变量相关系数大于 0.7 的变量作为预测变量。您理解在这种情况下,考虑正确的相关性方法非常重要,否则,您可能会拒绝与目标变量强相关的变量。
太好了!让我们看看如何使用 R 复制这个分析。
在 R 中实现相关系数
我们在 R 中推荐使用的用于计算相关系数的包之一是corrr (github.com/tidymodels/corrr)。它允许你根据 Tidyverse 建议的实践非常容易地探索和重新排列correlate()函数返回的 tibbles。
-
打开 RStudio 并确保它引用的是您最新的 CRAN R(在我们的例子中是版本 4.0.2)。
-
点击控制台窗口,输入以下命令:
install.packages('corrr')。然后按Enter键。
您将在本节中找到的代码可在Chapter11\R文件夹中的01-gdp-life-expectancy-analysis-in-r.R文件中找到。
因此,让我们继续导入必要的库,从网络上的 CSV 文件中加载数据到 tibble 中,并显示前几行:
library(readr)
library(dplyr)
library(corrr)
library(ggplot2)
dataset_url <- 'http://bit.ly/gdp-life-expect-data'
tbl <- read_csv(dataset_url)
tbl
您将在控制台中看到以下内容:

图 11.20 – 人口 tibble 的前几行
此外,在这种情况下,我们定义了绘制分布图和散点图所需的函数,并使用它们生成变量lifeExp和gdpPercap的分布图:
distPlot <- function(data, var, title, xlab, ylab, bins=100) {
p <- ggplot( data=data, aes_string(x=var) ) +
geom_histogram( bins=bins, fill="royalblue3", color="steelblue1", alpha=0.9) +
ggtitle(title) + xlab(xlab) + ylab(ylab) +
theme( plot.title = element_text(size=20), axis.title = element_text(size=16) )
return(p)
}
scatterPlot <- function(data, varx, vary, title, xlab, ylab) {
p <- ggplot( data=data, aes_string(x=varx, y=vary)) +
geom_point(
color='steelblue1', fill='royalblue3',
shape=21, alpha=0.8, size=3
) +
ggtitle(title) + xlab(xlab) + ylab(ylab) +
theme( plot.title = element_text(size=20), axis.title = element_text(size=16) )
return(p)
}
distPlot(data = tbl, var = 'lifeExp', title = 'Life Expectancy', xlab = 'Life Expectancy (years)', ylab = 'Frequency')
distPlot(data = tbl, var = 'gdpPercap', title = 'GDP / capita', xlab = 'GDP / capita ($)', ylab = 'Frequency')
这是您得到的图表:

图 11.21 – 生命预期和人均 GDP 的分布图
您可以通过以下方式获取两个变量之间的散点图:
scatterPlot(data = tbl, varx = 'lifeExp', vary = 'gdpPercap', title = 'Life Expectancy vs GDP/Capita', xlab = 'lifeExp', ylab = 'gdpPercap')
这是您得到的图形:

图 11.22 – 生命预期与人均 GDP 之间的散点图
通过corrr包的correlate()函数,可以简单地获得(持久化在 tibbles 中的)相关矩阵,如下所示:
tbl %>% select( lifeExp, gdpPercap ) %>% correlate( method = 'pearson' )
tbl %>% select( lifeExp, gdpPercap ) %>% correlate( method = 'spearman' )
tbl %>% select( lifeExp, gdpPercap ) %>% correlate( method = 'kendall' )
这是控制台的结果:

图 11.23 – tibbles 中的相关系数
简单易懂,对吧?现在您知道了如何在 Python 和 R 中获取相关系数,让我们在 Power BI 中实现您所学的知识。
使用 Python 和 R 在 Power BI 中实现相关系数
Power BI 凭借 DAX 功能,能够为已加载到数据模型中的数据引入最少量的统计分析。您可以在以下链接中找到您可以使用的一系列统计函数:http://bit.ly/dax-stats-func。至于简单的皮尔逊相关系数,您可以通过预定义的快速度量来使用它,这些度量在幕后为您添加了一些有时非平凡的 DAX 代码。更多详情,您可以点击此链接:bit.ly/power-bi-corr-coef。然而,没有简单的方法来实现斯皮尔曼和肯德尔相关系数。
此外,您可能已经听说过在 Microsoft 的 AppSource 中可用的相关系数图(bit.ly/power-bi-corr-plot)。您可能会认为可以使用它来获取相关系数。首先,该图仅显示皮尔逊相关系数,而不显示斯皮尔曼和肯德尔系数。此外,您无法从视觉或自定义视觉中提取系数以持久化它们,以便在表中使用它们进行后续计算(例如,特征选择)。因此,在这种情况下,相关系数图的想法完全偏离了目标。
唯一的方法是使用 Python 或 R。让我们看看如何做到这一点。
在这种情况下,由于代码的简单性,我们将在一个项目中实现 Python 和 R 中的相关系数。
首先,请确保 Power BI Desktop 在选项中引用了正确的 Python 和 R 版本。然后按照以下步骤操作:
-
点击获取数据,选择网络,然后点击连接:
![图 11.24 – 从网络获取数据]()
图 11.24 – 从网络获取数据
-
将
http://bit.ly/gdp-life-expect-data字符串输入到URL文本框中,然后点击确定。 -
您将看到数据预览。然后点击转换数据。
-
在功能区点击转换,然后点击运行 Python 脚本。
-
输入以下 Python 脚本,然后点击确定:
import pandas as pd corr_df = dataset.corr(method='pearson') # You need to convert row names into a column # in order to make it visible in Power BI corr_df.index.name = 'rowname' corr_df.reset_index(inplace=True)您可以在
Chapter11\Python文件夹中的文件02-gdp-life-expectancy-analysis-in-power-bi-with-python.py中找到此代码。 -
我们只对
corr_df中的数据感兴趣。因此,点击其表值。 -
您将看到数据集中所有数值列的皮尔逊相关系数预览。
-
在功能区点击主页,然后点击关闭并应用。
-
重复步骤 1 到 3。
-
在功能区点击转换,然后点击运行 R 脚本。
-
输入以下 R 脚本,然后点击确定:
library(dplyr) library(corrr) # You need to select only numeric columns # in order to make correlate() work corr_tbl <- tbl %>% select( where(is.numeric) ) %>% correlate( method = 'spearman' )您可以在
Chapter11\R文件夹中的文件02-gdp-life-expectancy-analysis-in-power-bi-with-r.R中找到此代码。 -
我们只对
corr_tbl中的数据感兴趣。因此,点击其表值。 -
您将看到数据集中所有数值列的斯皮尔曼相关系数预览。
-
在功能区点击主页,然后点击关闭并应用。
太棒了!您刚刚使用 Python 和 R 在 Power BI 中计算了源数据集数值列的皮尔逊和斯皮尔曼相关系数。简单,不是吗?
您可能想知道,“好吧,关于数值变量我都很清楚。如果我有分类(非数值)变量怎么办?我如何计算它们之间的相关性?那么数值变量和分类变量之间的相关性又是如何呢?”让我们看看如何进行这类分析。
分类变量与数值变量之间的相关性
我们已经展示了,在两个数值变量的情况下,通过查看它们的散点图,您可以了解它们之间的关联。显然,当其中一个或两个变量是分类变量时,这种策略不能使用。请注意,当变量取值为名称或标签时,该变量是分类的(或定性的,或名义的)。例如,智能手机操作系统(iOS、Android、Linux 等)。
让我们看看如何分析两个分类变量的情况。
考虑两个变量都是分类的
那么,是否存在一种图形表示法可以帮助我们理解两个分类变量之间是否存在显著的关联?答案是肯定的,它的名字就是 mosaic plot。在本节中,我们将以泰坦尼克号灾难数据集作为参考数据集。为了了解 mosaic plot 的外观,让我们考虑变量 Survived(取值为 1 和 0)和 Pclass(乘客舱位,取值为 1、2 和 3)。由于我们想要研究这两个变量之间的关联,我们考虑由它们生成的以下 mosaic plot:

图 11.25 – Survived 和 Pclass 变量的 mosaic plot
简而言之,mosaic plot 的目标是,通过表示所讨论元素对的瓷砖颜色,一眼就能看出每个变量各个元素之间的关联强度。
重要提示
基本上,当瓷砖的颜色趋向于 深蓝色 时,与变量相互独立时预期的数量相比,该瓷砖所代表的观察值就越多。
另一方面,当瓷砖的颜色趋向于 深红色 时,与变量相互独立时预期的数量相比,该瓷砖所代表的观察值就越少。
查看 图 11.25 中与 Survived = 0 相关的顶部一行瓷砖,我们可以说,在非幸存者中,超过 50%的人属于三等舱,并且这个数字比没有幸存者与舱位之间关联时预期的数字要大。因此,总的来说,我们可以这样说,非幸存者与三等舱之间存在中等强度的 正相关。换句话说,在非幸存者中找到三等舱的人的可能性相当高。相比之下,非幸存者中头等舱的人数远低于没有幸存者与舱位之间关联时预期的数字。因此,非幸存者与头等舱之间存在强烈的负相关。因此,头等舱的人很可能不在非幸存者中。
相反,关注 图 11.25 中瓷砖的底部一行,我们证实了我们之前所说的:在幸存者中找到头等舱的人的可能性非常高,而在三等舱的人中找到的可能性非常低。
图 11.25 中图表后面的数字基于条件概率,可以通过计算由分析中每个分类变量的各个元素生成的 列联表(也称为 交叉表)来找到:

图 11.26 – Survived 和 Pclass 变量的 mosaic plot
请参阅参考部分以获取关于列联表和马赛克图的更多详细信息。
但总的来说,我们如何数值地确定两个分类变量之间关联的全局强度?答案在于图 11.26中突出显示的系数,即Cramér 的 V。让我们看看这是怎么回事。
Harald Cramér 的相关系数
Cramér 的相关系数(V)衡量两个分类变量之间关联的强度,其范围从 0 到+1(与之前看到的系数不同,它不接受负值)。这个系数基于Pearson 的卡方(χ2)统计量,用于测试两个分类变量是否独立。Cramér 的 V 公式如下:

图 11.27 – Cramér 的 V 公式
在图 11.27的公式中,值N是样本大小,k是关联中每个分类变量中不同元素的最小数量。
Cramér 的 V 系数是一个对称函数,可以使用图 11.28的指南来确定其效应大小的范围:

图 11.28 – Cramér 的 V 效应大小范围
V是一个对称函数的事实导致重要信息丢失。让我们看看为什么。
Henri Theil 的不确定性系数
假设你在以下数据集中有两个分类变量IsFraudster和Hobby:

图 11.29 – 分类变量的样本数据集
Hobby变量的每个值都可以与IsFraudster变量的唯一值相关联(例如,Chess → Fraudster)。然而,反之则不成立(例如,Fraudster → [Chess, Body building])。因此,Hobby → Fraudster关联的强度(我知道Hobby并需要确定IsFraudster)比Fraudster → Hobby关联的强度(我知道IsFraudster并需要确定Hobby)更大。不幸的是,使用 Cramér 的系数V会导致这种区别消失,因为它是一个对称函数。为了保持关系的非对称性,我们必须引入 Theil 的不确定性系数。
Theil 的不确定性系数,也称为熵系数,在两个变量X和Y之间,其范围为[0, 1],定义为以下:

图 11.30 – Theil 的不确定性系数公式
它基于熵的概念,它提供了关于一个变量中包含的信息的变异或多样性(因此是不确定性)的信息,由H(X)给出。然后它还基于条件熵(或联合熵)H(X|Y),它衡量与两个变量X和Y相关的数据多样性。
例如,始终考虑泰坦尼克号灾难数据集,系数U(Survived|Pclass)是 0.06;相反,U(Pclass|Survived)是 0.09。
让我们看看在处理一个变量是数值的另一个变量是分类的关联时应该考虑哪个系数。
考虑一个数值变量和一个分类变量
如果你想要图形化地表示一个数值变量和一个分类变量之间的关联,箱线图或小提琴图就是适合的图形表示类型。如果你已经遇到过需要通过突出显示关键统计量来表示变量分布的问题,那么你应该熟悉箱线图:

图 11.31 – 箱线图的图形解释
小提琴图不过是同一变量的直方图/分布图和箱线图的组合:

图 11.32 – 小提琴图的图形解释
你可以在参考文献中找到更多关于箱线图和小提琴图的信息。
当你需要将一个数值变量与一个分类变量相关联时,你可以为分类变量的每个元素构建一个小提琴图。回到泰坦尼克号灾难数据集的例子,考虑到Pclass(分类)和Age(数值)变量,你得到这个多小提琴图:

图 11.33 – 小提琴图的图形解释
如果你仔细观察每个小提琴图内部,你会看到每个黑色箱线图内部都有一个白色点。这些点代表Pclass变量每个元素中Age变量分布的平均值。由于它们彼此之间的高度相当不同,Pclass变量可能是Age变量的良好预测因子。但我们如何衡量数值和分类变量之间关联的强度呢?答案是相关比给出的。
卡尔·皮尔逊的相关比
再次感谢卡尔·皮尔逊,我们有了计算分类变量和数值变量之间非线性关联程度的工具。这就是相关比(η,eta),它在方差分析(ANOVA)的研究中引入,其范围从 0 到+1。
正如r²可以解释为另一个变量线性解释的一个变量的方差百分比,η²(也称为类内相关系数)表示由独立(预测)变量线性或非线性解释的因变量(目标)变量的方差百分比。为了使这种解释有效,需要因变量是数值的,而独立变量是分类的。
当分类变量是序数变量(即星期几可以转换为整数)时,相关比才是一个非对称函数;否则,它只有一个方向上的意义。从类内相关系数公式(通过应用平方根我们可以立即得到相关比)如下:

图 11.34 – 相关比公式
该值是按类别 x 分解的 y 的平均值,也是整个 y(跨类别)的平均值。由于 σ 代表变量的 方差,我们可以将 η² 读作变量 y 的分散度与每个 x 类别加权的比率的比值,与 y 的总分散度。
重要提示
当 η² 趋近于 1 时,每个个体类别围绕其平均值分散的观察值就越少。因此,数值变量的总分散度完全归因于类别的分解,而不是每个类别的个体分散度。这就是为什么在这种情况下我们可以说数值变量和分类变量之间存在强烈的关联。
为了更好地理解上述概念,考虑分析三个科目所取得的分数。如果每个科目的分数都分散,你有一个确定的 eta。另一方面,如果每个科目的分数都一致,那么 eta 等于 1。这个陈述在 图 11.35 中表示,其中每个观察值都作为小提琴图中的一个点来绘制:

图 11.35 – 改变每个主题的等级分布后 η 的差异
因此,现在让我们在 Python 中实现本节中描述的相关系数。
在 Python 中实现相关系数
Python 社区非常幸运,因为 Shaked Zychlinski 开发了一个包含许多数据分析工具的库,其中包括一个考虑 pandas dataframe 列的数据类型并生成具有适当相关性的 dataframe 的函数。这个库是 Dython (shakedzy.xyz/dython/),可以通过 pip 安装。此外,如果你想要创建马赛克图,还必须在你的 pbi_powerquery_env 环境中安装 statsmodels (www.statsmodels.org/)。正如你可能已经学到的,按照以下步骤操作:
-
打开 Anaconda 提示符。
-
输入命令
conda activate pbi_powerquery_env。 -
输入命令
pip install dython。 -
输入命令
pip install statsmodels。
本节中提供的代码可在 Chapter11\Python 文件夹中的 03-titanic-disaster-analysis-in-python.py 文件中找到。
在 Dython 的所有实用工具中,我们需要 nominal 模块中的那些。在加载主要库之后,我们还创建了一个辅助函数来绘制小提琴图。
到目前为止,你可以从网络上公开的 CSV 文件中加载泰坦尼克号灾难数据,并将数值列Survived和Pclass(乘客等级)转换为字符串,因为它们是分类变量:
dataset_url = 'http://bit.ly/titanic-data-csv'
df = pd.read_csv(dataset_url)
categ_cols = ['Survived', 'Pclass']
df[categ_cols] = df[categ_cols].astype(str)
然后可以计算上述两个分类变量之间的Cramér's V系数:
cramers_v(df['Survived'], df['Pclass'])
它返回一个值为0.34,表示中等的相关强度。
你也可以计算给定Pclass变量的Survived变量的Theil's U不确定性系数:
theils_u(df['Survived'], df['Pclass'])
返回值是0.087。反过来,你可以计算给定Survived变量的Pclass变量的相同系数,以展示函数的不对称性:
theils_u(df['Pclass'], df['Survived'])
这个函数返回0.058。所以很明显,关联Pclass → Survived比相反的关联更强。
我们来计算变量Age(乘客年龄)和Pclass之间的相关性比率η如何?让我们来试试:
correlation_ratio(categories=df['Pclass'], measurements=df['Age'])
结果是0.366。现在,如果你想要得到每个列对的相关值,而不考虑它们的数据类型,那么associations()函数就是我们的好朋友。你只需指定是否想要使用 Theil 的 U 系数(nom_nom_assoc = 'theil')或 Cramér 的 V 系数(nom_nom_assoc = 'cramer')来处理分类变量,然后就可以了:
ass = associations(df, nom_nom_assoc = 'theil',
num_num_assoc = 'pearson',
figsize=(10,10), clustering=True)
结果,你会得到一个美丽的热图,帮助你一眼看出哪些列具有最强的相关性:

图 11.36 – 相关性热图
如你所注意到的,你也可以通过num_num_assoc参数选择在数值变量之间使用哪种类型的相关性(皮尔逊、斯皮尔曼、肯德尔)。此外,你可以使用代码ass['corr']访问系数数据框。
现在我们来看看如何在 R 中实现相同的事情。
在 R 中实现相关性系数
在 CRAN 上没有与 Dython 类似的 R 包允许你在不考虑数据类型的情况下计算 tibble 中列之间的相关性。也没有一个包定义了之前定义的相关性比率。因此,基于 Dython 包的源代码,我们从头开始创建了它们。
相反,我们使用了 CRAN 包,这些包公开了之前章节中介绍的一些相关性函数。特别是,这些包如下:
-
rstatix (
github.com/kassambara/rstatix),一个直观且易于管道操作的框架,用于基本统计测试。我们用它来使用cramer_v()函数。 -
DescTools (
andrisignorell.github.io/DescTools/),一个包含各种基本统计函数的集合。我们用它来使用UncertCoef()函数。 -
vcd (
cran.r-project.org/web/packages/vcd/index.html),一个用于分类数据的可视化技术和工具集合。 -
sjPlot (
strengejacke.github.io/sjPlot/),一组用于数据可视化的绘图和表格输出函数。
因此,你需要在你拥有的最新版本的 CRAN R 中安装这些包:
-
打开 RStudio 并确保它引用的是你最新的 CRAN R(在我们的例子中是版本 4.0.2)。
-
点击 控制台 窗口并输入以下命令:
install.packages('rstatix')。然后按 Enter。 -
点击 控制台 窗口并输入以下命令:
install.packages('DescTools')。然后按 Enter。 -
点击 控制台 窗口并输入以下命令:
install.packages('vcd')。然后按 Enter。 -
点击 控制台 窗口并输入以下命令:
install.packages('sjPlot')。然后按 Enter。
本节中提供的代码可在 Chapter11\R 文件夹中的文件 03-titanic-survive-class-analysis-in-r.R 中找到。
在加载最重要的库之后,我们再次创建了一个辅助函数来绘制小提琴图。之后,我们定义了 correlation_ratio() 函数来计算 η 和 calc_corr() 函数来计算 tibble 的关联度,无论给定列的数据类型如何。
在这一点上,你可以从网络上公开的 CSV 文件中加载泰坦尼克号灾难数据,并将数值列 Survived 和 Pclass(乘客等级)转换为因子,因为它们是分类变量:
dataset_url <- 'http://bit.ly/titanic-data-csv'
tbl <- read_csv(dataset_url)
tbl <- tbl %>%
mutate( across(c('Survived', 'Pclass'), as.factor) )
然后可以计算上述两个分类变量之间的 Cramér 的 V 系数:
rstatix::cramer_v(x=tbl$Survived, y=tbl$Pclass)
它返回一个值为 0.34,表示中等关联强度。
你还可以计算给定 Pclass 变量的 Survived 变量的 Theil 的 U 不确定性系数:
DescTools::UncertCoef(tbl$Survived, tbl$Pclass, direction = 'row')
返回值是 0.087。在这种情况下,我们还可以计算数值变量 Age 和分类变量 Pclass 之间的 关联比 η:
correlation_ratio( categories = tbl$Pclass, measurements = tbl$Age, numeric_replace_value = 0)
结果是 0.366。现在你可以获取每对列的关联值,无论它们的数据类型如何。我们实现了 calc_corr() 函数来达到这个目的。你只需指定是否想使用 Theil 的 U 系数(theil_uncert=True)或 Cramér 的 V 系数(theil_uncert=False)来处理分类变量即可:
# Create two data frames having the only column containing the tibble column names as values
row <- data.frame(row=names(tbl))
col <- data.frame(col=names(tbl))
# Create the cross join data frame from the previous two ones
ass <- tidyr::crossing(row, col)
# Add the corr column containing correlation values
corr_tbl <- ass %>%
mutate( corr = map2_dbl(row, col, ~ calc_corr(data = tbl, row_name = .x, col_name = .y, theil_uncert = T)) )
corr_tbl
你将看到如下结果:

图 11.37 – 从泰坦尼克号数据中得到的关联 tibble
你还可以使用以下代码绘制刚刚创建的关联 tibble 的热图:
corr_tbl %>%
ggplot( aes(x=row, y=col, fill=corr) ) +
geom_tile() +
geom_text(aes(row, col, label = round(corr, 2)), color = "white", size = 4)
这里是结果:

图 11.38 – 关联 tibble 的热图
太棒了!你已经在 R 中实现了本章研究的所有相关系数。现在让我们看看如何在 Power BI 中实现它们,无论是使用 Python 还是 R。
在 Power BI 中使用 Python 和 R 实现相关系数
显然,Power BI 并非作为高级统计分析工具而诞生,因此你不会找到例如实现前几节中看到的所有相关系数所需的所有功能。在这种情况下,Python 和 R 提供的支持对于此目的至关重要。
此外,在这种情况下,由于代码的简单性,我们将在一个项目中实现 Python 和 R 中的相关系数。
首先,确保 Power BI Desktop 在选项中引用了 Python 和 R 的正确版本。然后按照以下步骤操作:
-
点击获取数据,选择网页,然后点击连接。
-
将
http://bit.ly/titanic-dataset-csvURL 输入到URL文本框中,然后点击确定。 -
你将看到数据的预览。然后点击转换数据。
-
在功能区点击转换,然后运行 Python 脚本。
-
将你可以在文件
04-correlation-analysis-in-power-bi-with-python.py中找到的脚本输入到Chapter11\Python文件夹中。 -
我们只对
result_df中的数据感兴趣。因此,点击其表格值。 -
你将看到数据集中所有列之间的所有相关系数的预览。
-
在功能区点击主页,然后点击关闭并应用。
-
重复步骤 1 到 3。
-
在功能区点击转换,然后运行 R 脚本。
-
将你可以在
Chapter11\R文件夹中找到的文件04-correlation-analysis-in-power-bi-with-r.R中的脚本输入。 -
我们只对
corr_tbl中的数据感兴趣。因此,点击其表格值。 -
你将看到数据集中所有列之间的所有相关系数的预览。
-
在功能区点击主页,然后点击关闭并应用。
太棒了!你刚刚使用 Python 和 R 在 Power BI 中计算了源数据集中所有数值列的相关系数!
摘要
在本章中,你学习了如何根据皮尔逊、斯皮尔曼和肯德尔计算两个数值变量的相关系数。然后你还学习了如何通过卡方和泰尔不确定性系数计算两个分类变量的相关系数。最后,你还学习了如何通过相关比计算一个数值变量和一个分类变量的相关系数。
在下一章中,你将看到统计在确定数据集中的异常值和填补缺失值中的真正重要性。
参考文献
对于进一步阅读,请参阅以下书籍和文章:
-
斯皮尔曼秩相关系数 (
statistics.laerd.com/statistical-guides/spearmans-rank-order-correlation-statistical-guide.php) -
一致对和矛盾对 (
www.statisticshowto.com/concordant-pairs-discordant-pairs/) -
马赛克图和卡方检验 (
towardsdatascience.com/mosaic-plot-and-chi-square-test-c41b1a527ce4) -
理解箱线图 (
towardsdatascience.com/understanding-boxplots-5e2df7bcbd51) -
小提琴图解析 (
towardsdatascience.com/violin-plots-explained-fb1d115e023d) -
寻找分类相关性 (
towardsdatascience.com/the-search-for-categorical-correlation-a1cf7f1888c9)
第十三章:12 添加统计洞察:异常值和缺失值
为了通过统计函数扩展 Power BI 中的数据丰富可能性,我们将探讨一些在您的数据集中检测单变量和多变量异常值的方法。此外,还将探讨在数据集和时间序列中插补可能缺失值的高级方法。对于经验丰富的分析师来说,了解这些技术至关重要,因为 Power BI 默认不提供此类目的的有用工具。
在本章中,我们将涵盖以下主题:
-
异常值是什么以及如何处理它们
-
识别异常值
-
实施异常值检测算法
-
缺失值是什么以及如何处理它们
-
诊断缺失值
-
实施缺失值插补算法
技术要求
本章要求您拥有有效的互联网连接,并在您的机器上安装Power BI 桌面版。您必须已按照第二章,配置 Power BI 中的 R和第三章,配置 Power BI 中的 Python中概述的配置了 R 和 Python 引擎以及 IDE。
异常值是什么以及如何处理它们
通常,异常值被定义为那些与其他数据样本中的观察值距离异常远的观察值。换句话说,它们是数据集中的不常见值。我们所说的异常距离显然没有固定的测量标准,而是严格依赖于您正在分析的数据集。简单来说,这将由分析师根据他们的经验和业务现实的功能知识来决定,即基于数据集的异常距离,超过这个距离将考虑其他异常距离。
重要提示
对于数值变量或按分类变量的元素分组的数值变量来说,讨论异常值是有意义的。仅对分类变量讨论异常值是没有意义的。
但为什么会有这么多关注于管理异常值?答案是,它们往往会对某些统计操作产生不希望看到的宏观影响。最显著的例子是在“不舒服”位置存在异常值时的线性相关,以及消除异常值后计算出的相同结果:

图 12.1 – 一个简单的散点图
如图 12.1所示,以及从第十一章,添加统计洞察:关联中学到的,皮尔逊相关系数r很容易受到异常值的影响。
但然后,是否总是足够通过事先移除异常值来解决您在分析中可能遇到的问题?正如您所想象的那样,答案是“不”,因为这完全取决于您正在处理的异常值类型。
异常值产生的原因
在考虑对变量的异常值采取任何行动之前,有必要考虑可能产生它们的原因。一旦确定了原因,可能立即就可以修复异常值。以下是可能的原因分类:
-
数据录入错误:可能存在一个分析师在收集数据时犯了错误。例如,如果一个分析师正在收集一组人的出生日期,他们可能会错误地写成 177 而不是 1977。如果他们收集的日期属于 1900 到 2100 的范围,那么纠正由于录入错误而造成的异常值相当容易。在其他情况下,可能无法恢复正确的值。
-
有意异常值:非常常见的是,受测量影响的个人有意引入“错误”。例如,青少年通常不会准确报告他们消费的酒精量。
-
数据处理错误:通常应用于分析解决方案的数据转换过程可能会引入意外的错误,这反过来又可能导致可能的异常值。
-
抽样误差:有时,你进行分析的数据必须是从一个更大的数据集中抽取的样本。在这种情况下,分析师可能没有选择代表整个数据集总体的数据子集。例如,你需要测量运动员的身高,但错误地包括了某些篮球运动员在你的数据集中。
-
自然异常值:所谓的“自然”异常值存在,因为它们是商业性质的一部分,而不是任何错误的结果。例如,圣诞节期间购物中心销售更多产品几乎是肯定的。
一旦确定了特定异常值的性质,当然更容易尝试尽可能多地纠正它们。我们如何进行?有一些常见的纠正异常值的方法可以考虑。
处理异常值
处理异常值的最常用方法如下:
-
删除它们:分析师得出结论,完全消除异常值可以保证最终分析结果更好。
-
对它们进行上限处理:当确定所有极端观测值的行为与具有上限值的观测值相同的情况下,通常会对超过该值(绝对值)的所有观测值分配一个固定的极端值(上限)。
-
分配新值:在这种情况下,通过用空值替换异常值来消除异常值,并使用最简单的一种技术对这些空值进行估计:用固定值替换空值,例如,可以是所讨论变量的均值或中位数。你将在下一节中看到更复杂的估计策略。
-
转换数据:当分析师处理自然异常值时,变量分布的直方图往往呈现出偏斜的形状。右偏分布非常常见,如果直接使用这些分布,许多假设正态分布的统计测试将给出错误的结果。在这种情况下,通常通过应用单调函数来转换变量,这种方式在某种程度上“校正”了不平衡(例如,
log()函数就是这样)。一旦转换,新的变量满足测试的要求,因此可以无错误地进行分析。一旦从转换后的变量中获得结果,必须使用最初使用的函数的逆函数再次转换(如果使用了log(),则逆函数是exp()),以便得到与所分析的业务变量一致的价值。
现在你已经了解了处理异常值最常见的方法,你需要弄清楚如何识别它们。
识别异常值
根据你是逐个分析变量(单变量分析)还是同时分析多个变量(多元分析),有不同的方法用于检测异常值。在单变量情况下,分析相对简单。然而,多元情况则更为复杂。让我们详细考察它们。
单变量异常值
识别单个变量的异常值最直接且广泛使用的方法之一是利用箱形图,这是你在第十一章,添加统计洞察:关联中学到的。箱形图的一些关键点是四分位距(IQR),定义为从第一四分位数(Q1)到第三四分位数(Q3)的距离,下须(Q1 - 1.5 x IQR),和上须(Q3 + 1.5 x IQR):

图 12.2 – 盒形图的主要特征
具体来说,所有在下须之前和上须之后的观测值都被识别为异常值。这也被称为图基方法。
当处理多个变量时,识别变得更加复杂。
多元异常值
当你处理多个变量(多元异常值)时,识别异常值并不总是直接的。这取决于涉及的变量数量和数据类型。
数值变量和分类变量
只要你需要分析数值变量如何在分类变量的不同元素之间分布,使用到目前为止看到的工具仍然是可行的。事实上,只需为按分类变量的每个元素分组的数值变量的值绘制箱形图即可:

图 12.3 – 数值变量与分类变量
实际上,可能数值变量本身并没有显示出任何异常值,但当它被按分类变量的元素分解时,却揭示了一些异常值。
所有数值变量
通常,没有经验的分析师在只有数值变量的情况下,倾向于简化多维情况中异常值的确定。
重要提示
一个人可能会假设在任何变量中极端的观测值也是多变量异常值,这通常是正确的。然而,情况并非总是如此:当变量相关时,可以有一个在任一变量中都不是单变量异常值的多变量异常值。
当只处理数值变量时,仍然可以使用测量分布中心距离的算法。让我们以两个数值变量的情况为例,这使我们能够通过散点图可视化异常值:

图 12.4 – 两个数值变量的散点图
正如你在图 12.4中可以看到的,我们在边缘也添加了每个分析变量的两个箱线图,以验证对于每一个,都没有异常值,除了底部检测到的那个。你还可以看到有一个异常值,它显然与其他所有观测值不同,但并未被两个箱线图检测到。
想象一下固定一个假设的分布中心,并定义一个距离(欧几里得距离),超过这个距离的观测值将被认为是异常值:

图 12.5 – 分布中心的欧几里得距离
上述规则定义了一个以分布中心为中心的圆。考虑到图 12.5中看到的半径,你将识别出一些(可能是假阳性?)异常值,这些异常值仅通过查看箱线图是无法识别的,但之前未能识别的异常值在此情况下仍然未被识别。正如你可以很好地理解的那样,问题是分布具有沿笛卡尔平面主对角线分布的椭圆形形状。使用圆形来拟合不同形状的分布显然是不合适的。
如果有一种距离同时考虑了分布的形状呢?这正是马氏距离的情况。这种新的距离与其他距离的不同之处在于它考虑了两个变量之间的协方差。协方差和皮尔逊相关系数是与非常相似的概念相关的两个量,因此在某些情况下它们可以互换(参考相关文献)。马氏距离考虑两个变量之间的相关性的事实在图 12.6中显而易见:

图 12.6 – 分布中心的马氏距离
对于好奇者,这是计算它的公式:

图 12.7 – 马氏距离公式
是一个多变量观察值,是所有观察值的多变量均值,S 是协方差矩阵。马氏距离依赖于所有观察值的均值(一个非常不稳定的度量,对异常值非常敏感)和协方差矩阵,这让你意识到对于皮尔逊系数遇到的相同限制也适用,如下所示:
-
在不便利的位置出现的可能异常值可能会极大地影响由所有观察值的均值定义的多变量中心。如果中心计算不当,那么使用马氏距离的结果很可能识别出错误的异常值。这个问题可以通过通过基于中位数的方法计算中心来解决,这种方法对极端观察值的出现更加稳健。
-
如果存在极端异常值,协方差矩阵也可能受到负面影响。这个问题也可以通过采用协方差矩阵的稳健版本(最小协方差行列式,MCD)来解决。除了提供稳健的协方差矩阵外,这种方法还返回观察值中心的稳健估计。
-
在存在偏斜、非线性或异方差分布的情况下,使用马氏距离很可能返回错误的异常值。在这些情况下,有必要在应用距离计算之前尽可能地对涉及的变量进行变换。变换的目标是获得尽可能正常的分布,并使各种变量之间的关联尽可能线性。在这些情况下,使用Box-Cox 变换或Yeo-Johnson 变换。
当处理大于两个的混合变量(数值和分类变量)时,识别异常值变得更加复杂。有必要使用不同的数据科学技术(用于分类变量的特征工程技术、处理不平衡数据集等)并应用特定的机器学习异常检测算法。因此,这类情况超出了范围。
一旦识别出异常值(单变量和多变量),分析师就有责任决定采用哪种方法来尝试修复它,如果可能的话。
现在我们来看看如何根据前面章节中学到的知识来实现异常检测算法。
实现异常检测算法
你首先需要做的是在 Python 中实现你刚刚学到的内容。
在 Python 中实现异常检测
在本节中,我们将使用由 Paulo Cortez 等人创建的 Wine Quality 数据集(archive.ics.uci.edu/ml/datasets/wine+quality)来展示如何在 Python 中检测异常值。该数据集包含与不同类型的红葡萄酒一样多的观测值,每种红葡萄酒都由变量测量的感官特性描述,除了 quality 变量,它使用从 1 到 10 的离散等级尺度来衡量产品的质量。
您可以在 Chapter12\Python 文件夹中的 01-detect-outliers-in-python.py 文件中找到本节使用的代码。
一旦您将来自 winequality-red.csv 文件的数据直接加载到 df 变量中,让我们首先检查 sulphates 变量。让我们通过显示其箱线图来检查它是否包含任何异常值,该箱线图是通过我们定义在代码中的包装函数获得的:

图 12.8 – 硫酸盐箱线图
显然,在 1.0 之后有很多值。为了能够在数据集中定位它们,我们创建了一个函数,该函数接受一个数据框作为输入以及要考虑的数值列的名称,作为输出返回添加了布尔值列的数据框,当列的值为异常值时包含 True,否则为 False:
def add_is_outlier_IQR(data, col_name):
col_values = data[col_name]
Q1=col_values.quantile(0.25)
Q3=col_values.quantile(0.75)
IQR=Q3-Q1
outliers_col_name = f'is_{col_name.replace(" ", "_")}_outlier'
data[outliers_col_name] = ((col_values < (Q1 - 1.5 * IQR)) | (col_values > (Q3 + 1.5 * IQR)))
return data
add_is_outlier_IQR(df, 'sulphates')
一旦我们确定了 sulphates 变量的初始分布的异常值,我们可以通过移除异常值来绘制其箱线图,以查看发生了什么变化:
df_no_outliers = df.loc[~df['is_sulphates_outlier']]
结果箱线图如下:

图 12.9 – 移除异常值后的硫酸盐箱线图
如您所见,一些异常值仍然可见。这是由于从初始分布中移除异常值导致分布发生变化(其统计特性发生了变化)。因此,图 12.9 中显示的是新创建的分布的异常值。
如前所述,分析师需要确定识别出的异常值是否可以以某种方式纠正、消除或保留在原处。假设在这种情况下,第二个分布中的异常值是自然异常值。让我们尝试将新的分布分解为 quality 变量的单个值,并为每个值绘制一个箱线图:

图 12.10 – 每个质量值的硫酸盐箱线图
如 图 12.10 所示,获得 5 等级的葡萄酒的硫酸盐分布有几个异常值。这可能会促使分析师试图了解硫酸盐的存在对用户给予葡萄酒的最终评分影响有多大,特别关注到被认为是平均质量的葡萄酒的情况。
另一方面,如果我们想识别数据集中所有数值变量的多元异常值,排除质量变量,我们需要改变方法,尝试应用马氏距离,正如你在上一节中学到的。我们假设每个变量的异常值消除已经得到验证。因此,现在让我们尝试确定df_no_outliers数据框中的数值变量是否存在多元异常值。首先,然而,有必要检查分析变量的分布是否偏斜。因此,我们尝试为每个变量绘制直方图:
df_no_outliers.drop('quality', axis=1).hist(figsize=(10,10))
plt.tight_layout()
plt.show()
结果的图表如下:

图 12.11 – 无异常值的所有葡萄酒质量变量的直方图
很明显,其中一些变量(如残留糖分、氯化物、总二氧化硫等)极端右偏斜,因此有必要尝试应用尝试“标准化”单个分布的变换。通常,应用Box-Cox 变换。但由于在这种情况下,分布的一些值不是正数,因此无法应用它们。因此,有必要使用其他具有相同目标的变换,称为Yeo-Johnson。有关这些变换的更多详细信息,请参阅参考文献。
为了方便起见,我们创建了一个包装函数,该函数通过应用 Yeo-Johnson 变换将仅包含数值变量的 pandas 数据框进行转换,并返回相应的 lambda 值:
from sklearn.preprocessing import PowerTransformer
def yeo_johnson_transf(data):
pt = PowerTransformer(method='yeo-johnson', standardize=True)
pt.fit(data)
lambdas = pt.lambdas_
df_yeojohnson = pd.DataFrame( pt.transform(data), columns=data.columns.values )
return df_yeojohnson, lambdas
然后,一旦将数据框转换成对象,你可以尝试绘制变换后变量的分布直方图,以查看偏斜是否已经平滑:
df_transf, lambda_arr = yeo_johnson_transf(df_no_outliers[numeric_col_names])
df_transf.hist(figsize=(10,10))
plt.tight_layout()
plt.show()
这是你得到的图表:

图 12.12 – 所有葡萄酒质量变量变换后的直方图
很明显,现在分布看起来更像是正态分布的“钟形”。现在你可以计算马氏距离,有把握它会以更少的错误检测到异常值。
异常值的识别使用的是协方差的鲁棒估计器,即MCD。由于平方马氏距离的行为类似于卡方分布(见参考文献),我们可以通过传递给其百分位数函数(ppf())所需的截止值来计算高于该值的阈值,将其视为异常值:
from sklearn.covariance import MinCovDet
robust_cov = MinCovDet(support_fraction=0.7).fit(df_transf)
center = robust_cov.location_
D = robust_cov.mahalanobis(df_transf - center)
cutoff = 0.98
degrees_of_freedom = df_transf.shape[1]
cut = chi2.ppf(cutoff, degrees_of_freedom)
一旦确定了阈值值,你可以在数据框中添加两列:一列用于标识观察值(行)是否为马氏距离的异常值,另一列报告观察值非异常值的概率:
is_outlier_arr = (D > cut)
outliers_stat_proba = np.zeros(len(is_outlier_arr))
for i in range(len(is_outlier_arr)):
outliers_stat_proba[i] = chi2.cdf(D[i], degrees_of_freedom)
df['is_mahalanobis_outlier'] = is_outlier_arr
df['mahalanobis_outlier_stat_sign'] = outliers_stat_proba
df[df['is_mahalanobis_outlier']]
你将看到一个类似以下的数据框块:

图 12.13 – 数据框中显示的异常值信息
哇!仅凭最少的统计学知识,您就能在 Python 中识别出数值变量的多元异常值。
使用 R 也可以得到相同的结果。让我们看看如何做到。
在 R 中实现异常值检测
您可以在Chapter12\R文件夹中的01-detect-outliers-in-r.R文件中找到本节使用的代码。为了正确运行,您需要安装新包:
-
打开 RStudio 并确保它引用的是您最新的 CRAN R(在我们的案例中是版本 4.0.2)。
-
点击控制台窗口并输入以下命令:
install.packages('robust')。然后按Enter键。 -
输入以下命令:
install.packages('recipes')。然后按Enter键。
一旦您已将winequality-red.csv文件中的数据直接加载到df变量中,您将使用boxPlot()包装函数绘制sulphates变量的箱线图:

图 12.14 – 硫酸盐变量的箱线图
由于图 12.14中存在许多异常值,它们使用add_is_outlier_IQR()函数被识别,该函数向数据框添加一个标识列。正如其名称所示,该函数基于四分位数范围确定异常值。在此阶段,再次绘制相同变量的箱线图,这次消除了之前识别出的异常值:

图 12.15 – 移除异常值后的硫酸盐变量箱线图
假设您现在想识别多元异常值,首先查看各个变量的直方图以查看是否存在显著的偏度是有意义的。直方图使用以下dataframeHist()函数绘制:
dataframeHist <- function(data, bins = 10) {
data %>%
tidyr::pivot_longer( cols = everything() ) %>%
ggplot( aes(value) ) +
geom_histogram( fill='orange', na.rm = TRUE, bins = bins )+
theme( ... ) +
facet_wrap(~ name, scales = "free")
}
该函数的一个特殊功能是使用tidyr包的pivot_longer()函数对所有列进行操作,以将它们的名称垂直化到新的name列中,对应于新value列中的初始值。结果如下:

图 12.16 – 每个数值变量的多个直方图
由于偏度明显,您可以使用我们为您创建的yeo_johnson_transf()包装函数应用 Yeo-Johnson 变换。这个函数的特殊之处在于它利用了recipes包中的一个现成步骤,这简化了整个预处理阶段。要了解更多关于recipes的使用,请查看参考文献。
如前节所述,Yeo-Johnson 变换在此情况下很好地解决了偏度问题。因此,可以尝试通过以下代码应用马氏距离来检测异常值:
data <- df_transf %>%
select( numeric_col_names )
cov_obj <- data %>%
covRob( estim="mcd", alpha=0.7 )
center <- cov_obj$center
cov <- cov_obj$cov
distances <- data %>%
mahalanobis( center=center, cov=cov )
在此阶段,给定一个与我们要用来确定异常值的统计显著性相关的截止值,您可以获得相应的阈值,超过该阈值即可认为观察值是异常值。一旦计算了阈值,创建一个用于异常值的指示列就变得非常简单。还可以通过pchisq()函数添加一个列,表示观察值可能不是偶然地被认为是异常值的概率:
cutoff <- 0.98
degrees_of_freedom <- ncol(data)
outliers_value_cutoff <- qchisq(cutoff, degrees_of_freedom)
data <- data %>%
mutate(
is_mahalanobis_outlier = distances > outliers_value_cutoff,
mahalanobis_outlier_proba = pchisq(distances, ncol(data)) )
data %>% filter( is_mahalanobis_outlier == TRUE )
最终结果部分展示了以下输出:

图 12.17 – 包含多变量异常值信息的最终 tibble
干得好!您能够在 R 中识别出多变量异常值。
到目前为止,将 Python 和 R 代码应用于 Power BI 是显而易见的。
在 Power BI 中实现异常值检测
Power BI 有工具允许您通过悬停来图形化地查看异常值并进行分析。其中之一是在 2020 年 11 月发布的,是异常检测功能。另一个是异常值检测自定义可视化。让我们看看它们的主要区别:
-
异常检测功能一旦作为预览功能启用(目前处于预览状态),即可直接在 Power BI 中使用。它仅支持包含轴字段中时间序列数据的折线图可视化。
-
异常值检测是一个开源的 R 自定义可视化工具,需要单独安装(
bit.ly/power-bi-outliers-detection)。它实现了五种不同的异常值检测方法,并且对于单变量和双变量数据集表现良好。应避免使用多变量数据集。
如您所注意到的,这两个工具都是 Power BI 可视化工具。
重要提示
在 Python 或 R 可视化中执行的所有转换都会修改将来的可视化对象的数据框,但这些更改不能以任何方式持久保存在数据模型中。
正是因为这个原因,我们决定展示一些可以在 Power Query 中使用 Python 或 R 实现的检测异常值的方法。这样,您只需通过适当过滤数据模型表即可识别出异常值。由于代码的简单性,在这种情况下,我们还将在一个项目中实现 Python 和 R 中的相关系数。
首先,请确保 Power BI 桌面版在选项中引用了正确的 Python 和 R 版本。然后按照以下步骤操作:
-
点击获取数据,搜索
web,选择Web,然后点击连接: -
将
archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv输入到 URL 文本框中,然后点击确定。 -
您将看到数据的预览。然后点击转换数据。
-
在功能区点击转换,然后运行 Python 脚本。
-
在
Chapter12\Python文件夹中的02-detect-outliers-in-power-bi-with-python copy.py文件中输入脚本。 -
我们只对
dataset中的数据感兴趣。因此,点击其表格值。 -
你将看到数据集的预览,其中也包含用于识别异常值的新列。
-
在功能区点击主页,然后点击关闭并应用。
-
重复步骤 1 到 3。
-
在功能区点击转换,然后运行 R 脚本。
-
在
Chapter12\R文件夹中的02-detect-outliers-in-power-bi-with-r.R文件中输入脚本。 -
我们只对
datatibble 感兴趣。因此,点击其表格值。 -
你将看到数据集的预览,其中也包含用于识别异常值的新列。
-
在功能区点击主页,然后点击关闭并应用。
太棒了!你刚刚使用 Python 和 R 在 Power BI 中识别了一个数值数据集的异常值!
缺失值是什么以及如何处理它们
描述现实世界现象的数据通常有很多缺失数据。数据不足是一个不容忽视的事实,尤其是如果分析师想要对数据集进行高级研究,以了解其中变量之间的相关性程度。
处理缺失值不当的后果可能很多:
-
具有缺失值的变量的统计功效会降低,尤其是在单个变量缺失大量值时。
-
缺失值的数据集的代表性也可能降低,因此,所讨论的数据集可能无法正确代表现象所有观察值的实质性特征。
-
任何统计估计可能不会收敛到整个总体的值,从而产生偏差。
-
进行分析的结果可能不正确。
但让我们看看可能导致数据集中缺失值的原因可能是什么。
缺失值的原因
缺失值可能有许多原因,由故意或无意的行为决定。以下是一个非详尽的列表:
-
由于书写、读取或传输过程中的错误导致的数据损坏
-
用空值替换过度扭曲数据集的异常值
-
拒绝回答调查中的问题
-
对调查问题中提出的问题缺乏了解
然而,所有这些原因都可以总结为四种情况:
-
完全随机缺失(MCAR):产生空值的成因完全独立于如果赋值(Y)的假设值,以及数据集中其他变量的值(X)。它们仅依赖于外部变量(Z)。从统计角度来看,MCAR 数据的优势在于,仅包含变量 X 和 Y 完整值的整个数据集是整个总体的无偏样本。不幸的是,MCAR 案例在现实世界数据中很少见。
-
随机缺失(MAR):部分不完整变量(Y)的缺失数据与数据集中某些其他变量(X)有关,这些变量没有空值,但与不完整变量本身的值(Y)无关。在 MAR 情况下,仅包含 X 和 Y 两个变量完整值的样本数据集构成了整个总体的有偏样本,因为它肯定会错过所有那些 Y 的空值所依赖的 X 的值,从而导致一个不能代表整个现象的数据集。MAR 比 MCAR 是一个更现实的假设。
-
由于外部变量 Z 导致的非随机缺失(MNAR Z):部分不完整变量(Y)的缺失数据依赖于数据集中未包含的变量(外部变量)。例如,给定一个没有“性别”变量的数据集,可能会有年龄值为零的观测值。可能的情况是,不提供此信息的受访者大多是女性,因为她们典型地不愿意透露自己的年龄。因此,消除“年龄”变量非空值的观测值将生成一个有偏的数据集。
-
由于缺失值 Y 导致的非随机缺失(MNAR Y):部分不完整变量(Y)的缺失数据依赖于它们如果被赋值时的假设值。例如,众所周知,青少年倾向于从不透露他们饮酒的事实。因此,如果我们从数据集中移除那些酒精消费值为空的观测值,我们隐含地冒着从数据集中移除大多数与青少年相关的观测值的风险,从而获得一个有偏的数据集。
也有一些统计测试可以帮助你理解缺失数据的分布是否为 MCAR(参见参考文献)。但是,如前所述,MCAR 的情况非常罕见,因此最好假设考虑中的数据集缺失值的分布要么是 MAR,要么是 MNAR。
根据缺失数据分布的类型,可以采用特定的策略来清理缺失值。
处理缺失值
当可能时,首先要做的是与数据的相关方一起了解数据集中缺失值的原因,以及是否有可能恢复它们。不幸的是,大多数时候无法从源数据中恢复缺失数据,因此必须根据具体情况采用不同的策略。
简单的手动推断
可能存在一些变量,它们的手动推断是显而易见的。例如,在“颜色”变量的“蓝色”值对应的情况下,你会注意到“重量”变量总是取值为 2.4,除了少数几个值为空的情况。在这些情况下,根据“蓝色”颜色值为 2.4,很容易推断出“重量”变量的缺失值。
丢弃数据
分析师首先想到的缺失值解决方案无疑是消除问题的根源,即消除缺失值。消除它们有几种方法:
-
完全案例分析(CCA)删除:这种方法涉及删除任何包含至少一个缺失数据元素的观测值(行)。当缺失值的数量较少,观测值数量足够大时,通常应用这种方法。正如你在四种缺失数据类型的分类中所看到的,唯一不会导致数据集有偏的情况是 MCAR,这在描述现实世界现象的数据集中是非常罕见的。因此,当数据集中观测值的数量足够高时,面对 MCAR 的情况,列表删除并不是一个好的策略。
-
成对或可用案例分析(ACA)删除:根据统计分析中考虑的变量,这种方法仅删除那些对于涉及的唯一变量具有空值的观测值(行)。不涉及分析的变量中的空值不是删除观测值的原因。同样,只有当分析中的案例是 MCAR 时,采用这种方法才不会生成有偏的数据集。这种方法最明显的缺点是,如果你需要比较不同的分析,你不能应用它,因为样本中观测值的数量会随着不同分析中涉及的变量的变化而变化。
-
变量删除:这种方法考虑在研究分析中从分析中删除整个变量(而不是从先验数据集中删除!)当缺失值的比例在 60%及以上时。如果在仔细研究后得出结论,该变量不包含当前分析的重要信息,那么删除变量是有意义的。否则,始终更倾向于尝试插补方法。一般来说,删除变量总是最后的选项,并且只有在最终分析确实从中受益时才应考虑。
即使在尝试应用这些消除技术之后,分析师仍然必须解决缺失值的问题,他们必须求助于插补技术。让我们看看最常用的方法。
均值、中位数和众数插补
这是一个直观吸引人的方法,也称为单值插补,其中你用预定义的值填充缺失值。不幸的是,简单性被一些不可忽视的问题所抵消。
可能最常用的缺失值替代方法是使用变量的分布均值,这是在忽略缺失值的情况下得到的。这种选择的动机在于均值是随机从正态分布中抽取的观察值的合理估计。然而,如果所讨论的分布是偏斜的,即使数据集的缺失值分布是 MCAR,分析师也有可能做出严重偏颇的估计。
可以通过使用变量的中位数来解决偏斜问题。然而,事实仍然是,单次插补的常见问题是用一个单一值来替代缺失值,然后将其视为真实值。因此,单次插补忽略了不确定性,并且几乎总是低估方差(记住方差与信息同义;方差为 0 的变量是常数值变量,通常不会丰富统计分析)。
众数(出现频率最高的值)插补通常用于表示为数字的分类数据。即使这种方法在没有强有力的理论依据的情况下使用,也会引入偏差,以至于有时分析师更愿意为缺失值创建一个新的类别。
多重插补通常比单次插补更可取,因为它通过考虑插补内和插补间方差来克服低估方差的问题。让我们看看这是怎么回事。
多重插补
多亏了 Donald B. Rubin,1987 年公开了一种处理单次插补情况下方差低估问题的方法。这种方法被称为多重插补,包括以下步骤:
-
插补:这一步骤与单次插补步骤非常相似,只是这次,对于每个缺失值,从分布中提取m次值。这一操作的结果是一组m个插补数据集,其中所有观测值始终相同,而插补值则根据各自分布的不确定性而不同。
-
分析:您使用所有m个插补数据集进行所需的统计分析。这一步骤的结果是通过对每个m个插补数据集应用相关分析而获得的m个结果(或分析)。
-
合并:将m个结果合并,以获得具有正确统计特性的无偏估计。将每个缺失值的m个估计值合并,以便得到一个估计方差,该方差结合了通常的抽样方差(插补内方差)和由缺失数据引起的额外方差(插补间方差)。
整个过程可以用图 12.18来概括:

图 12.18 – 多重插补过程
在数据是 MCAR(完全随机缺失)、MAR(条件随机缺失)甚至当数据是 MNAR(非随机缺失)且有足够辅助变量的情况下,可以使用多重插补。
多重插补最常见的实现方式如下:
-
链式方程多重插补(MICE):这种方法专注于一次填补一个变量的缺失值。一旦专注于一个变量,MICE 将使用数据集中的所有其他变量(或适当选择的这些变量的子集)来预测该变量的缺失值。缺失值的预测基于数值变量的线性回归模型和分类变量的逻辑回归模型。
-
Amelia II:这个名字是为了纪念阿梅莉亚·埃尔哈特,一位美国航空先驱,她在 1937 年尝试成为首位完成全球环球飞行第一人时,在太平洋中央失踪。Amelia II 结合了基于自助法的算法和期望最大化(EM)算法,使其快速且可靠。它也非常适合时间序列数据。
最近,已经使用深度学习算法实现了多重插补。特别是,去噪自编码器多重插补(MIDAS)算法在准确性和效率方面相较于其他多重插补策略具有显著优势,尤其是在应用于具有复杂特征的大型数据集时。
单变量时间序列插补
缺失数据的问题不仅困扰着多元表格数据集,也影响着时间序列数据集。例如,持续收集现象数据的传感器可能在任何时候停止工作,从而在序列中产生空缺。通常,分析师面对的是含有缺失值的时间序列,必须以某种方式对这些值进行插补,因为提交序列的流程无法处理空值。
时间维度给出的事件后果约束迫使分析师使用特定的插补方法来处理时间序列。让我们看看最常用的方法:
-
最后观测值前推(LOCF)和下一观测值后推(NOCB):在 LOCF 方法中,使用变量最后观测到的(即非空)测量值来填补所有后续的缺失值。LOCF 无偏的唯一条件是缺失数据完全随机,且用作 LOCF 插补基础的数据与未知缺失数据的分布完全相同。由于无法证明这些分布完全相同,所有利用 LOCF 的分析都存在嫌疑,并且几乎肯定会生成有偏的结果。NOCB 方法与 LOCF 类似,但方向相反,它使用缺失值之后的第一个(非空)观测值来替换缺失值。显然,它具有与 LOCF 相同的局限性。
-
指数加权移动平均(EMWA):一般来说,移动平均在时间序列中常用于平滑短期效应引起的波动,并突出长期趋势或周期。EWMA 的设计使得较老的观测值赋予较低的权重。随着观测值的变老,权重呈指数下降(因此得名“指数加权”)。缺失值通过使用结果“平滑”时间序列的值进行插补。
-
插值:插值技术是从时间序列中插补缺失数据最广泛使用的技术之一。基本思想是使用一个简单的函数(例如线性函数、多项式函数或样条函数)来拟合缺失值附近的非零点,然后对缺失观测值进行插值。
-
季节分解插补:如果分析的时间序列具有季节性,这种方法可能会给出非常好的结果。采用的程序是从时间序列中移除季节性成分,对季节调整后的序列进行插补,然后将季节性成分加回。
对于多变量时间序列,也有用于插补缺失值的算法。
多变量时间序列插补
这个主题超出了本章的范围,但我们只是想说明,我们之前讨论的Amelia II算法也用于多变量时间序列的缺失值插补,而它不适用于单变量时间序列的插补。
为了确定是否要插补缺失值,我们必须首先在数据集中识别它们。让我们看看如何做到这一点。
在 R 和 Python 中诊断缺失值
在考虑在数据集中插补缺失值之前,我们必须首先了解缺失值对每个变量影响的程度。
您可以在本节的Chapter12\R\03-diagnose-missing-values-in-r.R和Chapter12\Python\03-diagnose-missing-values-in-python.py文件中找到使用的代码。为了正确运行代码以及后续章节的代码,您需要按照以下方式安装所需的 R 和 Python 包:
-
打开 Anaconda 提示符。
-
输入
conda activate pbi_powerquery_env命令。 -
输入
pip install missingno命令。 -
输入
pip install upsetplot命令。 -
然后,打开 RStudio 并确保它正在引用您最新的 CRAN R(在我们的案例中是版本 4.0.2)。
-
点击控制台窗口并输入
install.packages('naniar')。然后按Enter。 -
输入
install.packages('imputeTS')。然后按Enter。 -
输入
install.packages('forecast')。然后按Enter。 -
输入
install.packages('ggpubr')。然后按Enter。 -
输入
install.packages('missForest')。然后按Enter。 -
输入
install.packages('mice')。然后按Enter。 -
输入
install.packages('miceadds')。然后按Enter。
让我们看看在您面对数据集中缺失值分析时,哪些功能会派上用场。
R 包naniar提供了vis_miss()函数,该函数可以在单个图像中显示整个数据框的缺失值:

图 12.19 – 整个数据集中缺失值的图
您可以利用missingno库在 Python 中绘制类似的图表(github.com/ResidentMario/missingno)。
仅知道与考虑的变量的总值相比的缺失值百分比可能有限。这就是为什么通常还很有用通过miss_var_summary()函数了解每个列的详细信息:

图 12.20 – 缺失值摘要
我们在代码库中开发了一个类似的功能。
能够可视化缺失值组合以及变量之间的缺失交集将很有趣。R 包naniar(github.com/njtierney/naniar)通过gg_miss_upset()函数允许您进行此类分析:

图 12.21 – 数据集缺失值的 UpSet 图
要在 Python 中实现相同的图,过程要复杂一些。您必须首先使用upsetplot模块(github.com/jnothman/UpSetPlot)。问题在于提供该包暴露的UpSet()函数所需的输入数据框格式。因此,我们创建了辅助函数upsetplot_miss(),您可以在代码中找到它,以便在 Python 中轻松创建缺失值的 upset 图。
如果您需要了解时间序列中的缺失值,imputeTS R 包提供的ggplot_na_distribution()函数可以非常清楚地显示时间序列中的空洞:

图 12.22 – 时间序列中检测缺失值
如果您需要获取关于时间序列中缺失值统计的更完整细节,statsNA()函数就是您所需要的:

图 12.23 – 时间序列中缺失值的统计
一旦您仔细研究了每个变量的缺失值分布及其交集,您就可以决定在数据集中保留哪些变量,以及将哪些变量提交给各种插补策略。让我们看看如何在 R 和 Python 中实现插补。
实现缺失值插补算法
从现在开始,所有缺失值分析都将使用 R 来完成,因为为这种语言开发了非常统计专业且易于使用的包,而在 Python 生态系统中不存在这些包。
假设我们需要计算泰坦尼克号灾难数据集中两个数值变量Age和Fare之间的皮尔逊相关系数。让我们首先考虑删除缺失值的情况。
删除缺失值
应用列表和成对删除技术对泰坦尼克号数据集中数值变量之间的皮尔逊相关系数计算的影响是明显的。让我们加载数据并仅选择数值特征:
library(dplyr)
dataset_url <- 'http://bit.ly/titanic-data-csv'
tbl <- readr::read_csv(dataset_url)
tbl_num <- tbl %>%
select( where(is.numeric) )
如果你现在分别计算两种技术的相关矩阵,你会注意到差异:
# Listwise deletion
cor( tbl_num, method = 'pearson', use = 'complete.obs' )
# Pairwise deletion
cor( tbl_num, method = 'pearson', use = 'pairwise.complete.obs' )
你将看到以下结果:

图 12.24 – 使用列表和成对删除计算的相关矩阵
让我们看看如何在表格数据集的情况下插补缺失值。
插补表格数据
你可以在Chapter12\R\04-handle-tabular-missing-values-in-r.R文件中找到本节使用的代码。
再次,从泰坦尼克号灾难数据集开始,你需要做的第一件事是删除Name和Ticket列,因为它们有大量的不同值。
重要提示
重要的是要消除具有大量不同值的分类变量,否则 MICE 算法会因为所需的 RAM 过多而失败。通常,高基数变量对于其他变量的缺失值插补没有用。在某些情况下,这些变量中包含的信息对于插补可能是基本的(例如,邮政编码)。在这种情况下,需要使用变换来降低基数,同时不丢失变量中包含的信息。有关更多信息,请参阅参考文献。
由于Cabin列中的缺失值占所有值的 70%以上,我们决定也删除它。之后,分类变量Survived、Sex和Embarked被转换为因子:
tbl_cleaned <- tbl %>%
select( -Cabin, -Name, -Ticket ) %>%
mutate(
Survived = as.factor(Survived),
Sex = as.factor(Sex),
Embarked = as.factor(Embarked)
)
在这一点上,可以通过应用 Rubin 在多次插补中提供的池化技术来计算每对数值变量的皮尔逊相关系数。miceadds包暴露了包装函数,简化了以mice()函数的结果作为参数的最常见统计分析操作。在我们的情况下,感兴趣的函数是micombine.cor(),我们在corr_impute_missing_values()函数中使用它:
corr_impute_missing_values <- function(df, m = 5, variables, method = c('pearson', 'spearman')) {
method <- method[1]
df_imp_lst <- mice(df, m = m, printFlag = FALSE)
corr_tbl <- miceadds::micombine.cor(df_imp_lst, variables = variables, method = method) %>%
as_tibble() %>%
arrange( variable1, variable2 )
return( corr_tbl )
}
因此,很容易获得上述相关系数:
# Get the indexes of numeric columns
numeric_col_idxs <- which(sapply(tbl_cleaned, is.numeric))
corr_tbl <- corr_impute_missing_values(tbl_cleaned, variables = numeric_col_idxs, method = 'pearson')
corr_tbl
这里是结果:

图 12.25 – 多次插补数据集的相关性统计推断
不深入其他领域的细节,变量之间的相关系数由r列给出。由于r系数是推断过程的结果,lower95和upper95列定义了上下 95%置信区间的界限。
重要提示
如果你遇到像Error in matchindex(yhatobs, yhatmis, donors) : function 'Rcpp_precious_remove' not provided by package 'Rcpp'这样的错误,那么很可能是你正在运行一个使用较早版本的
Rcpp编译的包的最新版本。使用install.packages('Rcpp')命令更新Rcpp应该可以解决这个问题。
有时,分析的目标不是从统计函数中获得结果,而是简单地填补缺失值留下的空白,因为相关的数据集必须用于训练一个不允许空值的机器学习算法。scikit-learn 的最新版本(目前处于实验阶段)公开了带有SimpleImputer、KNNImputer和IterativeImputer方法的插补模块。这样,就可以通过机器学习算法(k-最近邻;线性回归)以及其他更简单的方法(使用固定值、均值、中位数或众数替换)来插补数据集的缺失值,并且还可以获得算法整体表现的平均分数(交叉验证均方误差)。你将在第十三章,使用无高级或嵌入式容量的机器学习中看到一个这些方法的例子。
如果,另一方面,你需要从一元时间序列中插补缺失值,你会怎么做?让我们看看。
插补时间序列数据
你可以在Chapter12\R\05-handle-time-series-missing-values-in-r.R文件中找到本节使用的代码。
我们考虑一个每月平均飞机乘客数量的时间序列。让我们复制它,并从中随机删除 10%的值,此外,我们手动删除几个重复的值。然后,我们将两个时间序列合并成一个单一的 tibble:
air_df <- read.csv('https://bit.ly/airpassengers')
# Create 10% of missing values in the vector
set.seed(57934)
value_missing <- missForest::prodNA(air_df['value'], noNA = 0.1)
# Force a larger gap in the vector
value_missing[67:68,] <- NA
# Add the vector with missing values to the dataframe
air_missing_df <- air_df %>%
mutate( date = ymd(date) ) %>%
rename( complete = value ) %>%
bind_cols( value = value_missing )
结果可以在图 12.22中看到。imputeTS包公开了方便的函数,实现了在一元时间序列插补部分已描述的缺失值插补。一旦使用不同的算法和参数插补了值,就可以计算准确性,因为你也知道完整的时间序列。我们使用forecast包公开的accuracy()函数,使用各种指标(如平均绝对误差和均方根误差)来计算最终的准确性:

图 12.24 – 时间序列中插补值的误差指标
季节分解插补(seadec)策略似乎是最好的。以下是根据此策略绘制缺失值的图表:

图 12.25 – 时间序列中插补值的表示
现在我们来看看如何在 Power BI 中应用我们迄今为止关于缺失值的所学知识。
在 Power BI 中插补缺失值
我们深入探讨了缺失值插补的理论和技术,无论是处理表格数据集还是时间序列数据,这正是因为在 Power BI 中,除了用默认值(如固定值、平均值或中位数)替换它们的简单解决方案外,没有其他原生工具可以采用它们。实际上,当业务分析师发现自己需要填补数据中的空白时,他们通常会寻求数据科学家或具有统计知识的人的帮助来解决该问题。现在你已经学习了这一章,你能够自己解决这个问题了!
让我们应用我们在前几节中为 Power BI 中的表格和时间序列数据所做的操作:
-
打开 Power BI 桌面版,并确保它引用的是最新引擎。
-
点击获取数据,搜索
web,然后双击Web连接器。 -
输入以下 URL 并点击确定:
bit.ly/titanic-dataset-csv。 -
在下一个导入屏幕上,点击转换数据。
-
切换到转换选项卡,点击运行 R 脚本,复制
Chapter12\R文件夹中的06-impute-tabular-missing-values-in-power-bi-with-r.R文件中的脚本,将其粘贴到编辑器中,然后点击确定。 -
你可能需要配置 R 脚本和 CSV 文件的隐私级别。在这种情况下,选择组织和公共级别。
-
我们只对
corr_tbl中的数据感兴趣。因此,点击其表值。 -
因此,你会看到包含使用 MICE 和多元插补技术提供的池化方法计算的相关系数的表格:
![图 12.26 – 使用多元插补技术计算的相关表]()
图 12.26 – 使用多元插补技术计算的相关表
-
切换到主页标签页,然后点击关闭并应用。
-
点击获取数据,然后双击文本/CSV连接器。
-
在Chapter12文件夹中找到
air.csv文件,然后点击打开。 -
在下一个导入屏幕上,点击转换数据。
-
Power BI 自动将
date文本字段解释为日期字段,因此应用了一个从文本到日期的更改类型操作。为了正确处理使用lubridate包的 R 脚本中的日期,你必须删除更改类型步骤,在插入 R 脚本之前点击红色的叉号:![图 12.27 – 删除更改类型步骤]()
图 12.27 – 删除更改类型步骤
-
转到转换选项卡,点击运行 R 脚本,复制
Chapter12\R文件夹中07-impute-time-series-missing-values-in-power-bi-with-r.R文件中的脚本,将其粘贴到编辑器中,然后点击确定。 -
可能需要您配置 R 脚本和 CSV 文件的隐私级别。在这种情况下,请选择组织和公共级别。
-
因此,您将看到包含原始时间序列(
value列)和通过不同插补算法获得的其他时间序列(每个都在不同的列中)的表格:![图 12.28 – 填充时间序列的表格]()
图 12.28 – 填充时间序列的表格
-
转到主页选项卡,然后点击关闭并应用。
太棒了!您成功地将最复杂的缺失值插补算法应用于 Power BI 中的表格数据集和时间序列,而且几乎不费吹灰之力。恭喜您!
摘要
在本章中,您学习了什么是异常值,它们通常由什么引起,以及如何处理它们。您还学习了如何根据涉及的变量数量和它们的给定类型,在 Python 和 R 中识别它们。
您还涉及的一个重要主题是如何在表格和时间序列数据集中填充缺失值。您学习了如何诊断它们并使用 R 进行插补。
之后,您在 Power BI 中实现了值插补算法。
在下一章中,您将了解如何在 Power BI 中使用机器学习算法,而无需使用高级版或嵌入式功能。
参考文献
对于额外的阅读,请参阅以下书籍和文章:
-
在 R 中使用 ggExtra 包将边际图添加到 ggplot2 散点图 (
statisticsglobe.com/ggplot2-graphic-with-marginal-plot-in-r) -
关于协方差你应该知道的 5 件事 (
towardsdatascience.com/5-things-you-should-know-about-covariance-26b12a0516f1) -
马氏距离及其局限性 (
rpubs.com/jjsuarestra99/mahalanobis) -
Box-Cox 转换解释 (
towardsdatascience.com/box-cox-transformation-explained-51d745e34203) -
如何在 R 中使用 Power Transforms 进行机器学习 (
machinelearningmastery.com/power-transforms-with-scikit-learn/) -
马氏距离与卡方分布的关系 (
markusthill.github.io/mahalanbis-chi-squared/) -
使用 recipes 包进行简单的预处理 (
www.rebeccabarter.com/blog/2019-06-06_pre_processing/) -
异常检测 (
docs.microsoft.com/en-us/power-bi/visuals/power-bi-visualization-anomaly-detection) -
缺失数据:机制、方法和信息 (
www.i-deel.org/uploads/5/2/4/1/52416001/chapter_4.pdf) -
多重插补通过链式方程:它是怎样的,以及它是如何工作的? (
www.ncbi.nlm.nih.gov/pmc/articles/PMC3074241/) -
Amelia II:缺失数据处理程序 (
www.jstatsoft.org/article/view/v045i07) -
关于分类变量编码的所有内容 (
towardsdatascience.com/all-about-categorical-variable-encoding-305f3361fd02)
第十四章:13 使用高级或嵌入式容量之外的机器学习
多亏了现在通过强大的笔记本电脑或通过云提供的计算能力,您可以通过机器学习模型轻松快速地丰富您的分析。Power BI 提供了集成工具(与数据流密切相关),允许您使用在 Azure Machine Learning 上开发的数据科学家机器学习模型、通过 Azure AutoML 训练和部署的模型,或者通过便捷的图形界面直接通过认知服务公开的服务。唯一的缺点是,这些工具(称为 高级 AI)只有在您使用 嵌入式 容量、高级 容量或 高级按用户(PPU)许可证时才启用。这意味着使用 Power BI Desktop 或仅使用带有 Pro 许可证的 Power BI 服务的用户不能从机器学习中受益吗?绝对不是,我们将通过 Python 和 R 展示您如何做到这一点。
在本章中,您将涵盖以下主题:
-
使用数据流与 Power BI 中的机器学习交互
-
使用 AutoML 解决方案
-
在 Power Query 中嵌入训练代码
-
在 Power Query 中使用训练好的模型
-
在 Script Visuals 中使用训练好的模型
-
在 Power Query 中调用 Web 服务
技术要求
本章要求您拥有一个正常工作的互联网连接,并且您的机器上已经安装了 Power BI Desktop。您必须已按照 第二章、配置 Power BI 中的 R 和 第三章、配置 Power BI 中的 Python 中概述的方式正确配置了 R 和 Python 引擎和 IDE。
使用数据流与 Power BI 中的机器学习交互
您可以直接通过 Power BI Desktop 访问 高级 AI 功能,或者您可以通过数据流访问 数据流的高级 AI 功能,这些是易于使用的大数据处理工具,可以将大数据转换为仪表板中显示的洞察。但是,正如您所想象的,这两种模式都需要介绍中提到的上述许可证。
这些功能可通过 Power BI Desktop 访问,在 Power Query 主页标签上:

图 13.1 – Power BI Desktop 中的 AI 洞察
在 图 13.1 中您可以看到的前两个选项(文本分析和视觉)在幕后使用 Azure 认知服务,具体是文本分析服务和计算机视觉服务。基本上,多亏了 Power BI 中的这些功能,您现在可以使用 四个功能 通过机器学习的力量丰富您的数据。

图 13.2 – Power BI 中的认知服务功能
这些功能如下:
-
TagImages. 分析图像,根据其内容生成标签
-
ExtractKeyPhrases. 评估非结构化文本,并为每个文本列返回一组关键短语
-
DetectLanguage. 评估文本输入,并为每一列返回语言名称和 ISO 标识符
-
ScoreSentiment. 评估文本输入并为每个文档返回一个情感分数,范围从 0(负面)到 1(正面)
AI Insights 的另一种选择是能够在 Power Query 中将托管在Azure 机器学习中的模型用作评分函数。

图 13.3 – Power BI 中的 Azure 机器学习功能
更重要的是,高级 AI 功能还包括通过数据流自动机器学习在 GUI 上即时创建机器学习模型的能力。
自动机器学习解决方案非常方便,特别是对于没有太多机器学习经验的分析师来说。您将在下一节中详细了解这一点。现在您只需要知道,在 Power BI 中,您可以生成三种类型的模型:分类(二元或多标签)、回归和时间序列预测(即将推出)。

图 13.4 – Power BI 中的数据流自动机器学习
在幕后,有 Azure AutoML 服务允许您进行模型训练,但通过利用数据流,您不需要实例化机器学习工作区来运行自动机器学习实验。
只有 Power BI Pro 许可证的用户无法直接从 Power BI GUI 访问这些出色的功能。然而,多亏了 Python 和 R 在 Power BI 中的引入,现在可以使用机器学习算法或外部服务,只需几行代码就可以实现它们的实现。
真的可能只有几行代码就足够训练一个机器学习模型吗?其中的技巧在哪里?!让我们来解释这个谜团。
使用自动机器学习解决方案
从头编写代码进行机器学习需要特定的知识,而通常使用 Power BI 的通用分析师并不具备这些知识。因此,我们建议从现在开始,对于没有数据科学背景的分析师,推荐使用自动机器学习(AutoML)过程。这难道意味着任何人都可以仅通过使用 AutoML 算法,而不了解这一科学背后的理论,就能创建一个准确的机器学习模型吗?绝对不是!以下适用:
重要提示
自动机器学习工具减轻了分析师在机器学习过程中典型的重复性任务(超参数调整、模型选择等)。通常,那些需要分析师具备特定理论知识(例如,缺失值填充、数据集平衡策略、特征选择和特征工程)的步骤被排除在自动化步骤之外。因此,在开始自动机器学习过程之前,没有应用只有专家才知道的适当转换到数据集,会导致生成一个可能足够准确但无法确保产品性能的基线模型。
你可能会认为 AutoML 工具受到数据科学家的厌恶。这也是一个神话。他们中的许多人将其用作快速原型设计和重复步骤的执行工具,同时他们专注于更关键的任务。
在本章中,我们将满足于获得离散的性能模型(如果我们足够幸运,拥有一个正确转换的训练数据集,有时会非常好),因此 AutoML 解决方案提供的输出已经足够好了。
此外,我们将专门使用 Python 中的 AutoML 解决方案,因为它是大多数第三方机器学习平台中最广泛使用的语言。R 语言的使用范围略小于 Python,但这并不意味着你在 R 中得到的结果有任何不准确性。相反,正如你可能在之前的章节中注意到的,一些仅存在于 R 而不存在于 Python 中的专门统计函数包,允许对数据进行高度灵活的操作。
简而言之,到目前为止,使用 Python 进行机器学习工作允许模型在流行的平台之间轻松共享。因此,我们建议它适用于 Power BI 分析师,他们可能更愿意将模型创建委托给更专业的平台,然后再将其导入 Power BI。
让我们看看本章代码中将使用哪些 AutoML 工具。
PyCaret
PyCaret (pycaret.org/) 是一个开源、低代码的 Python 机器学习库,它自动化了机器学习实验周期,使商业分析师和领域专家能够访问这些高级技术,同时也帮助数据科学家变得更加高效和高效。
PyCaret 可以解决的问题类型如下:
-
分类(预测一个分类目标变量)
-
回归(预测一个数值目标变量)
-
聚类(将观测值分组到具有特定属性的特定集合中)
-
异常检测(在数据集中寻找异常值的过程,这些异常值的数量远少于常规观测值)
-
自然语言处理(用于分类和回归的有用特征中的文本转换)
-
关联规则(一种基于概率理论的基于规则的技巧,用于根据概率理论在特征之间找到重要关系)
对于更有经验的用户,PyCaret 还提供了方便的模型集成和模型解释功能。
Azure AutoML
Azure AutoML 是一种基于云的服务,可用于自动化构建用于分类、回归和预测任务的机器学习管道。这些管道涉及数据集的预处理阶段,以便更好地适应下一阶段使用的训练算法。在调整和训练多个模型后,Azure AutoML 将选择其中最准确的模型,同时还会考虑由先前训练模型集成产生的另外两个模型。
可用的任务如下:
-
分类
-
回归
-
时间序列预测
要了解更多关于这个平台的详细信息,请参阅参考资料。
RemixAutoML for R
为了完整起见,我们还建议 R 的 AutoML 解决方案之一,即RemixAutoML(github.com/AdrianAntico/RemixAutoML)。这是一个函数集,它简化了 R 中许多 AutoML 包的使用(CatBoost、LightGBM、XGBoost 和 H2O)。除了让没有经验的分析师能够通过 AutoML 用几行代码创建机器学习模型之外,这个库还包含非常高级的功能(例如,用于特征工程和时间序列预测的功能),这些功能通常被更有经验的分析师使用。
现在让我们看看在 Power BI 中使用机器学习模型的多种方法。
在 Power Query 中嵌入训练代码
训练机器学习模型的最简单解决方案之一是在导入您将构建模型的数据集后,直接在 Power Query 中编写所需的代码。
在相当大的数据集上训练模型通常需要相当长的时间来完成。当您在 Power Query 中嵌入代码时,它将在每次数据刷新时运行,这可能会导致在线获取数据时出现不可忽视的延迟。因此,以下适用:
重要提示
当您确信完成模型训练所需的时间是可以接受的时,建议使用此解决方案。
现在让我们看看如何使用 PyCaret 编写一些训练代码的示例。
使用 PyCaret 训练和使用 ML 模型
让我们以泰坦尼克号灾难数据集为例来训练一个机器学习模型。具体来说,我们希望创建一个模型,根据数据集中其他特征描述的乘客属性来预测乘客是否幸存(Survived列)。显然,这是一个二元分类(是否幸存?是或否),我们可以很容易地使用 PyCaret 实现。
由于 PyCaret 和其他所有依赖库都在不断进化,因此您还需要安装Visual C++ Build tools来构建必要的轮子并避免诸如Failed building wheel for
-
从
visualstudio.microsoft.com/visual-cpp-build-tools/下载安装程序并运行它。 -
在下一个窗口中,仅选择使用 C++进行桌面开发选项。
-
您将收到提示重新启动计算机。请这样做。
-
一旦您的笔记本电脑重新启动,运行您的 Anaconda Prompt 并输入以下命令以创建新的
pycaret_env环境:conda create --name pycaret_env python=3.7 -
输入以下命令以切换到新环境:
conda activate pycaret_env -
输入以下命令以安装 PyCaret 的完整版本:
pip install pycaret
完成这些步骤后,您可以继续查看模型的训练代码。唯一的小问题是处理数据集中的缺失值(您已经在第十二章,添加统计洞察、异常值和缺失值中分析过它们)。不幸的是,PyCaret 目前仅支持使用最简单的方法处理缺失值,即使用均值或中位数对数值进行推断,以及使用众数或固定字符串对分类值进行推断。由于我们想要向您展示如何使用K-Nearest Neighbors (KNN)算法推断缺失值,正如第十二章,添加统计洞察、异常值和缺失值中预期的那样,您将需要编写比平时更多的代码。
用于通过 KNN 算法推断缺失值的代码将在 Power BI 中导入 Titanic 数据集后的第一个转换步骤中使用。您可以在Chapter13/Python文件夹中的01-impute-dataset-with-knn.py文件中找到代码。首先,代码将进行简单的特征选择,消除在模型训练过程中可能引起噪声的字段。之后,由于上述由 scikit-learn 通过KNNImputer模块公开的推断算法不处理数据集中的分类变量,代码还负责使用ordinal encoding技术(使用类别到整数的映射)对分类变量进行编码,这得益于 scikit-learn 的OrdinalEncoder模块。在此阶段,代码使用默认的距离度量来推断缺失值,即欧几里得距离度量,在计算训练数据集成员之间的距离时不会包括 NaN 值。
一旦获得推断数据集,您可以使用该数据集来训练模型,然后对测试数据集进行评分。您可以在Chapter13/Python文件夹中的02-train-model-with-pycaret.py文件中找到代码。为了方便起见,您将使用推断数据集的 95%来训练模型,而剩余的 5%将用作测试数据集。所有这些步骤都将放在 Power BI 中用于缺失值推断的前一个转换步骤之后。
你将利用 scikit-learn 的 train_test_split() 方法将数据集分为训练集和测试集。之后,通过调用 PyCaret 的 setup() 和 compare_models() 函数,模型训练将非常简单。在 setup() 函数中,你需要定义用于训练的数据框,目标变量(Survived),以及哪些是分类变量,哪些是顺序变量。此外,必须使用静默模式,否则需要用户干预来验证其他变量的推断类型。compare_models() 函数使用交叉验证训练并评估 PyCaret 为分类提供的所有模型的性能。除了返回表现最佳的模型外,此函数还返回交叉验证返回的每个模型的性能值。

图 13.5 – 所有模型的性能
图 13.5 展示了每个模型的几个典型分类指标。其中最常用的是当数据集平衡时(换句话说,当目标变量的一个类别与另一个类别的观测数之间略有不成比例时)的 ROC 曲线下面积(AUC 或 AUC-ROC)。以下说明适用:
重要提示
compare_models()函数在 Power BI 中无法正常工作,除非你禁用并行处理,通过将n_jobs=1传递给setup()函数。如果你没有将1分配给n_job,则 PyCaret 默认将其设置为 -1(最大并行性),在幕后,最佳模型会使用多个线程正确计算,但 Power BI 无法追踪到主进程,因此会卡住。
在 AUC 大约为 0.85(由于过程是随机的,因此可能会有所变化)的情况下,随机森林分类器似乎是通过训练 95% 的补全数据集获得的最佳模型。然后,你将使用新训练的模型(best_model)通过 PyCaret 的 predict_model() 函数来获取数据集剩余 5% 的预测。你将得到类似以下的结果:

图 13.6 – 测试数据集的预测
如你所见,对数据集进行评分生成的结果包括两个新的分类列:Score 列代表一个度量值的估计,例如预测类别在 Label 列中报告的概率。如果你对获得真正的概率估计感兴趣,你必须校准模型(更多细节请参阅参考文献)。训练的模型也将保存为 PKL 文件以供将来重用。
让我们看看如何在 Power BI 中实现这里所解释的内容。
在 Power BI 中使用 PyCaret
首先,确保 Power BI Desktop 在 选项 中引用了新的 pycaret_env Python 环境。然后,按照以下步骤操作:
-
点击 获取数据,搜索
web,选择 Web,然后点击 连接。 -
将
http://bit.ly/titanic-dataset-csvURL 输入到 URL 文本框中,然后点击 确定。 -
你将看到数据预览。然后,点击 转换数据。
-
在功能区点击 转换,然后点击 运行 Python 脚本。
-
在
Chapter13\Python文件夹中的01-impute-dataset-with-knn.py文件中找到脚本并输入。 -
我们只对
df_imputed数据框中的数据进行关注。因此,点击其 表 值。 -
你将看到所有缺失值已填充的数据集预览。
-
在功能区点击 转换,然后点击 运行 Python 脚本。
-
在
Chapter13\Python文件夹中的02-train-model-with-pycaret.py文件中找到脚本并输入。 -
我们只对
predictions数据框中的数据进行关注。因此,点击其 表 值。 -
你将看到由模型生成的预测和输入数据集的预览。
-
在功能区点击 主页,然后点击 关闭并应用。
太棒了!你刚刚使用几行 Python 代码训练了一个机器学习模型,并使用 PyCaret 对测试数据集进行了评分!
现在我们来看看当模型在 Power BI 之外训练时该如何操作。
在 Power Query 中使用训练好的模型
正如你在 第四章 中所看到的,导入未处理的数据对象,你过去通常会将复杂、耗时处理的结果(因此也是一个机器学习模型)以你使用的语言特定的序列化格式共享。在那个阶段,反序列化文件并准备好在 Power Query 中使用以预测新观察的目标变量是非常简单的。然而,了解评分函数(它接收新观察作为输入并返回预测)所需的依赖关系很重要,因为它们与模型的训练方式密切相关。因此,我们建议以下做法:
重要提示
当你需要使用第三方提供的序列化机器学习模型时,确保该模型的开发者也为你提供了一个可工作的评分函数,以避免在预测未知观察的目标值时出现不必要的麻烦。
如果你这么想,序列化和反序列化机器学习模型的能力可以解决在上一节中直接在 Power Query 中训练模型时提出的延迟问题。假设你第一次运行嵌入式训练代码。紧接着,将模型序列化并保存到磁盘上。在下一次刷新时,你不必再次运行训练代码,而是检查预期的路径中是否存在序列化模型文件。如果存在,则加载该文件,反序列化它,并使用该模型进行下一步;如果不存在,则再次运行训练代码。
显然,上述过程涉及到专家的干预,当模型表现不佳时,专家会决定删除序列化文件,因为在此期间,业务数据可能已经发生了重大变化,以至于之前的模型不再准确,就像使用过去数据进行的训练之后那样(这个过程被称为模型漂移;更多细节请参阅参考资料)。
我们不会深入探讨此解决方案的实现细节,但我们想提供一个针对上一个部分提出的问题的可能解决方案的提示。
现在我们将使用已经训练好的 PyCaret 模型在 Power BI 中实现一个未见数据集的评分。
使用训练好的 PyCaret 模型在 Power Query 中进行评分
如果您记得正确的话,在上一个部分中,您将 Power BI 中训练好的模型保存到了磁盘上的 PKL 文件中。您还导出了相同代码中计算的测试数据集到 CSV 文件。在本会话中,您将直接使用序列化模型,使用load_model()函数加载它,以及要评分的测试 CSV 数据集。由于模型是使用 PyCaret 训练的,所以评分函数简单给出为predict_model()函数。请记住,当不使用像 PyCaret 这样的简化框架时,评分函数可能更复杂。
在 Power BI 中遵循以下步骤:
-
点击获取数据,选择文本/CSV,然后点击连接:
-
在
Chapter13文件夹中选择titanic-test.csv文件,然后点击打开。 -
您将看到测试数据的预览。然后,点击转换数据。
-
在功能区点击转换,然后点击运行 Python 脚本。
-
在
Chapter13\Python文件夹中的03-score-dataset-using-pycaret-model.py文件中输入脚本。 -
我们只对
predictions数据框感兴趣。因此,点击其表值。 -
您将看到测试数据集的预览,其中增加了两个额外的列——
标签和评分。 -
在功能区点击主页,然后点击关闭并应用。
如您所见,这是在 Power BI 中使用自定义机器学习模型进行评分的最直接和最常见的方式。事实上,我们建议以下做法:
重要提示
在 Power BI 之外的平台进行训练和通常管理机器学习模型是很方便的,这样可以解耦模型开发/调整干预措施与报告的其他部分。
现在我们来看看如何在脚本可视化中直接使用序列化的机器学习模型。
在脚本可视化中使用训练好的模型
如你在 第四章 中所学,导入未处理的数据对象,由于对象序列化和其字符串表示,你可以将任何对象以字符串数据框的形式导入到 Python 或 R 视觉中。一旦该数据框在脚本视觉中可用,你可以通过逆序列化转换将其还原为原始对象。由于你可以对任何对象执行我们描述的操作,显然你也可以对已在 Power BI 外部训练的机器学习模型执行此操作。
当在 Script Visual 会话中可用适当反序列化的模型时,可以使用上一节中描述的评分函数立即预测新的观测值。
你可能会问自己,当数据必须首先在 Power BI 数据模型中可用才能在视觉中使用时,在脚本视觉中评分数据集有什么意义。事实上,如果用作模型输入的观测数据已经在 Power BI 的数据模型中找到,那么直接在 Power Query 中应用批量评分可能更好,这样就可以将预测作为数据集的新列使用。所有这些都是绝对正确的。然而,在某些情况下,使用脚本视觉是方便的:
重要提示
当你需要实现一些允许你探索模型输出并动态改变变量的模拟报告时,在脚本视觉中使用机器学习模型是方便的,而不必刷新整个报告。
在这种情况下,我们建议使用 Power BI 中的 假设参数 (bit.ly/power-bi-what-if) 来处理数值特征,它们是动态的,并为用户提供了一个非常实用的报告。对于分类变量,你可以使用 Power BI 中的 输入数据 功能手动输入它们的内容,这将创建一个断开连接的表。假设参数默认在数据模型中创建断开连接的表。
为了正确理解这段话,请确保你理解了 第四章,导入未处理的数据对象 的内容。假设你必须向一个期望两个变量作为输入的机器学习模型提供观察结果 – 一个数值变量和一个分类变量。当将信息传递给脚本视觉的数据框时,除了来自 Power Query 的序列化模型数据框的字段(model_id、chunk_id 和 model_str)之外,你还需要将相关值分配给两个输入变量相关的参数筛选器。由于在筛选时每个参数每次只能选择一个值,因此所有参数的集合形成一个元组,在我们的情况下是(numeric_value、category_id)。这个元组将根据字符串块数据框(由 model_id、chunk_id 和 model_str 列组成)的行数进行复制,并将其连接到它,以提供最终的数据框,该数据框将在脚本视觉会话中名为 dataset 的变量中可用。

图 13.7 – 将 PKL 文件内容反序列化到 Python 视觉中
一旦在脚本视觉中有了数据集数据框,你可以仅对列(model_id、chunk_id 和 model_str)应用反序列化转换,从而获得可用于评分的机器学习模型。如果你选择仅对列(number、category)应用,并对结果数据框的所有行应用 distinct 函数,你将获得作为反序列化模型输入提供的参数元组。因此,你可以从模型中计算预测值,向其提供参数元组作为输入。然后,你可以在脚本视觉中显示的图中使用这个预测。
让我们实际看看如何在 Python 脚本视觉中动态预测来自机器学习模型的值。
在脚本视觉中使用训练好的 PyCaret 模型评分观察结果
你将要做的事情是正确序列化 Power Query 中字典中包含的机器学习模型(在我们的情况下,只有一个)。这样,就获得了一个包含上述字典中每个序列化模型字符串表示的数据框。因此,可以通过报告中的筛选器选择感兴趣的模型,并因此使用 Python 脚本视觉中的相应数据框部分,在其中可以反序列化数据框的内容,从而获得用于评分的模型。
因此,让我们继续在 Power BI 中开发我们的报告。确保 Power BI 在 选项 中正确引用 pycaret_env 环境。以下是需要遵循的步骤:
-
点击 获取数据,然后 更多…。在搜索框中开始输入
script,双击 Python 脚本。Python 脚本编辑器将弹出。 -
将
04-serialize-ml-models-in-power-query.py文件的内容复制到Chapter13\Python文件夹中。然后,将其粘贴到 Python 脚本编辑器中,相应地更改 PKL 文件的绝对路径,然后点击确定。 -
导航器窗口将打开,为您提供选择要导入哪个 dataframe 的选项。选择包含模型 ID 的
model_ids_dfdataframe 和包含序列化模型字符串表示的models_dfdataframe,然后点击加载。在幕后,通过model_id字段在模型 ID 和序列化模型 dataframe 之间自动创建 1:1 关系。![图 13.8 – 模型表之间自动创建的关系]()
图 13.8 – 模型表之间自动创建的关系
这种关系允许您筛选models_df表中的行集,以便在 Python 可视化中使用,对应于您通过下一步创建的切片器选择的模型 ID。
-
点击切片器视觉图标。
![图 13.9 – 选择切片器视觉]()
图 13.9 – 选择切片器视觉
然后,点击model_ids_df表中的model_id度量值。
![图 13.10 – 点击 model_id 度量值以在切片器中显示]()
图 13.10 – 点击 model_id 度量值以在切片器中显示
-
点击切片器右上角的向下箭头以选择下拉切片器类型。
![图 13.11 – 选择下拉切片器类型]()
图 13.11 – 选择下拉切片器类型
-
调整切片器的底部边缘,点击其格式选项,开启单选选项,关闭切片器标题,然后添加标题
模型 ID。![图 13.12 – 设置切片器选项]()
图 13.12 – 设置切片器选项
然后,将其移动到报告的顶部中央。
-
您现在将为每个变量添加一组“假设”参数及其关联的切片器,并将它们作为输入传递给模型。点击功能区上的建模选项卡,然后点击新建参数。
-
在下一个对话框中,在名称字段中输入
Pclass 参数,将数据类型保留为整数,在最小值字段中输入1,在最大值字段中输入3,将增量保留为 1,并在默认值字段中输入2。![图 13.13 – 为 Pclass 添加“假设”参数]()
图 13.13 – 为 Pclass 添加“假设”参数
保持将切片器添加到本页选中,然后点击确定。
-
调整 Pclass 切片器的底部边缘。然后,点击其格式选项,关闭切片器标题,开启标题,并将文本输入为
乘客等级。然后将它移动到报告的左上角。 -
确保将Pclass 参数的Pclass值重命名为在模型中表示它的变量的相同名称,即
Pclass。![图 13.14 – 重命名 Pclass 参数值]()
图 13.14 – 重命名 Pclass 参数值
-
由于变量Sex是分类的(F 或 M),您需要手动为其创建一个不连续的表格。因此,在功能区中点击首页选项卡,然后点击输入数据。
-
创建表格的第一列,Sex,并向其中添加值 0 和 1。然后,创建新的列,SexLabel,当Sex为
0时输入Female,当Sex为1时输入Male。![图 13.15 – 手动输入 Sex 的数据]()
图 13.15 – 手动输入 Sex 的数据
将
Sex作为表名输入,然后点击加载。 -
让我们为
Sex变量添加一个筛选器。首先在报告画布上的一个空白处点击。然后,点击筛选器视觉元素以将筛选器添加到报告中。接着,点击SexLabel字段,然后点击Sex字段(顺序很重要)。然后,点击筛选器右上角的向下箭头以选择下拉筛选器类型。同时,点击其格式选项,在选择控制组中开启单选选项,关闭筛选器标题选项,开启标题,并将文本输入为Sex。调整其底部边缘并将其移动到乘客等级筛选器下方。![图 13.16 – 为 Sex 创建的新下拉筛选器]()
图 13.16 – 为 Sex 创建的新下拉筛选器
-
让我们为
Age变量创建一个新的What-if参数。在功能区上点击建模选项卡,然后点击新建参数。在下一个对话框中,在名称字段中输入Age param,保留数据类型为整数,在最小值字段中输入1,在最大值字段中输入80,保留增量为1,并在默认值字段中输入30。保持添加筛选器到本页选中,然后点击确定。 -
调整年龄筛选器的底部边缘。然后,点击其格式选项,关闭筛选器标题,开启标题,并将文本输入为
Age。然后,将其移动到报告的顶部左侧,位于 Sex 筛选器下方。 -
请确保将年龄参数的年龄值重命名为模型中代表它的变量的相同名称,即
Age。![图 13.17 – 重命名年龄参数值]()
图 13.17 – 重命名年龄参数值
-
让我们为
SibSp变量创建一个新的What-if参数。在功能区上点击建模选项卡,然后点击新建参数。在下一个对话框中,在名称字段中输入SibSp param,保留数据类型为整数,在最小值字段中输入0,在最大值字段中输入8,保留增量为1,并在默认值字段中输入0。保持添加筛选器到本页选中,然后点击确定。 -
调整 SibSp 筛选器的底部边缘。然后,点击其格式选项,关闭筛选器标题,开启标题,并将文本输入为
Siblings/spouse aboard。然后,将其移动到报告的顶部左侧,位于年龄筛选器下方。 -
确保将SibSp 参数的SibSp 值重命名为它在模型中代表的变量的相同名称,即
SibSp。![图 13.18 – 重命名 SibSp 参数值]()
图 13.18 – 重命名 SibSp 参数值
-
让我们为
Parch变量创建一个新的 What-if 参数。在功能区点击建模选项卡,然后点击新建参数。在下一个对话框中,在名称字段中输入Parch param,保留整数作为数据类型,在最小值字段中输入0,在最大值字段中输入6,保留增量为1,并在默认值字段中输入0。保持添加切片器到本页选中,然后点击确定。 -
调整 Parch 切片器的底部边缘。然后,点击其格式选项,关闭切片器标题,开启标题,并将文本输入为
Parents/children aboard。然后,将其移动到报告的顶部左侧,位于 SibSp 切片器下方。 -
确保将Parch 参数的Parch 值重命名为它在模型中代表的变量的相同名称,即 Parch:
![图 13.19 – 重命名 Parch 参数值]()
图 13.19 – 重命名 Parch 参数值
-
让我们为
Fare变量创建一个新的 What-if 参数。在功能区点击建模选项卡,然后点击新建参数。在下一个对话框中,在名称字段中输入Fare param,选择十进制数作为数据类型,在最小值字段中输入0,在最大值字段中输入515,在增量字段中输入1,然后在默认值字段中输入250。保持添加切片器到本页选中,然后点击确定。 -
调整 Fare 切片器的底部边缘。然后,点击其格式选项,关闭切片器标题,开启标题,并将文本输入为
Fare。然后,将其移动到报告的顶部左侧,位于 Parch 切片器下方。 -
确保将Fare 参数的Fare 值重命名为它在模型中代表的变量的相同名称,即
Fare。![图 13.20 – 重命名 Fare 参数值]()
图 13.20 – 重命名 Fare 参数值
-
让我们为
Embarked变量添加一个切片器。由于Embarked变量是分类的(0、1或2),你需要手动为其创建一个不连续的表格。因此,在功能区点击主页选项卡,然后点击输入数据。 -
创建表格的第一列,Embarked(此名称必须与模型变量的名称相对应),然后向其中添加值 0、1 和 2。然后,创建一个新的列,EmbarkedLabel,并输入
Cherbourg、Queenstown和Southampton,分别对应0、1和2。![图 13.21 – 手动输入 Embarked 数据]()
图 13.21 – 手动输入 Embarked 数据
将表格名称输入为
PortEmbarkation,然后点击加载。 -
现在,为
Embarked变量添加一个切片器。首先在报告画布上的一个空白处点击。然后,点击切片器可视化以将切片器添加到报告中。首先点击 EmbarkedLabel 字段,然后点击 Embarked 字段(顺序很重要)。然后,点击切片器右上角的向下箭头以选择 下拉 切片器类型。还要点击其格式选项。在 选择控制 组中开启 单选,关闭 切片器标题,开启 标题,并将文本输入为启航港。调整其底部边缘并将其移至乘客类别切片器下方。 -
现在,在报告画布上的一个空白处点击,然后在 可视化 字段中点击 Python 可视化,并在提示时启用它。之后,将其移动到报告的中心。
-
保持选中状态,点击
models_df表的所有三个字段(chunk_id、model_id 和 model_str)。 -
在保持 Python 可视化选中的同时,也点击所有参数的所有度量(带有计算器图标的那些),以及分类变量的识别字段(Embarked 和 Sex 字段)。记住,度量的名称必须与模型提供的变量名称相对应,以便报告能够正常工作。在选择度量后,你可能需要再次启用 Python 可视化。你可以通过简单地点击 Python 可视化中标记为 选择以启用 的黄色按钮来完成此操作。选择完成后,你应该在 Python 可视化中看到所有度量的名称以及
models_df表的字段名称。![图 13.22 – 在 Python 可视化中可见的选定度量名称]()
图 13.22 – 在 Python 可视化中可见的选定度量名称
-
现在,点击 Python 可视化的 格式 选项卡,展开 标题 区域,使用 预测字符串 编辑文本,并将字体大小增加到 28 点。
-
将
05-deserialize-ml-models-in-python-visual.py文件的代码复制到Chapter13\Python文件夹中,并将其粘贴到 Python 可视化脚本编辑器中。然后,点击 Python 脚本编辑器右上角的 运行脚本 箭头图标。你将得到一个预测(标签和分数),预测你选择的参数描述的人是否会存活。

图 13.23 – 泰坦尼克号模型的完整预测模拟报告
请记住,使用这里遵循的相同方法,可以使用 R 训练的模型制作相同的报告。实际上,为了完整性,我们在存储库中添加了 Chapter13\R 文件夹,其中包含本节中使用的脚本,这对于获得这里得到的结果非常有用。在这些脚本中,我们使用预定义的算法(随机森林)训练了模型,并使用了最近引入的 Tidymodels 框架,该框架利用了 Tidyverse 原则。有关更多详细信息,请参阅参考文献。
哇!你已经成功在 Power BI 中创建了一个动态预测报告,这是少数开发者能够做到的!
现在我们来看看即使没有 Premium 功能、嵌入式功能或 PPU 许可证,如何调用 Microsoft 在 Power BI 中公开的 AI 和机器学习服务。
在 Power Query 中调用 Web 服务
在 Power Query 中与机器学习模型交互的另一种方式是调用 Web 服务。正如你可能已经知道的,机器学习模型可以用来批量处理多个观察值的评分,使用一个训练好的模型(之前描述的过程)。与机器学习模型交互的另一种选择是将它部署到 Web 服务中,以便可以通过 REST API 调用它。你已经学习了如何在 第九章 中与外部 API 一起工作,调用外部 API 以丰富您的数据。以下内容适用于外部 API:
重要提示
记住,由于出于安全原因阻止了互联网访问,你不能从 Python 或 R 可视化中通过 REST API 调用消耗外部服务。因此,你只能在 Power Query 中消耗这些服务。
作为示例,在本节中,你将了解如何通过 Azure Machine Learning 调用已发布端点的预测,以及如何使用认知服务中 Azure Text Analytics 提供的服务。你可以在 Power Query 中使用一些 M 代码来访问这些服务,尽管这并不完全直接。幸运的是,有可用的 SDK,这使得访问公开的服务变得容易得多。这些 SDK 是为 Python 开发的,因此我们的示例将仅限于 Python。
让我们先看看如何与使用 Azure AutoML 训练的模型交互。
在 Power Query 中使用 Azure AutoML 模型
在本节中,你将首先了解如何使用 Azure AutoML GUI 训练机器学习模型。之后,你将使用发布在 Azure 容器实例上的模型作为 Power BI 中的 Web 服务。
使用 Azure AutoML UI 训练模型
为了使用 Azure AutoML,您必须首先有权访问 Azure 订阅(记住您可以创建一个免费账户,如链接所示:bit.ly/azure-free-account)。之后,您需要创建一个 Azure 机器学习工作区,通过 Azure 提供的不同技术来训练模型。您可以通过简单地遵循链接中的步骤来完成此操作:bit.ly/create-azureml-workspace。一旦工作区被分配,您就可以登录到 Azure 机器学习工作室,这是一个所有您将与之工作的机器学习资产都组织得最好的环境。执行以下步骤以登录到 Azure ML Studio 并开始一个 AutoML 实验:
-
前往
ml.azure.com/。 -
您将被提示选择您的 Azure 订阅和要工作的 Azure ML 工作区。点击 开始。您将看到如下内容:
![图 13.24 – Azure ML Studio 门户]()
图 13.24 – Azure ML Studio 门户
-
首先,您需要导入用于训练模型的数据库集。您将使用上一节中完成的缺失值插补所获得的相同数据集。点击左侧菜单上的 数据集,然后点击 创建数据集。
![图 13.25 – 在 Azure ML 中创建新的数据集]()
图 13.25 – 在 Azure ML 中创建新的数据集
-
您将被提示输入数据集名称和类型。将名称输入为
titanic-imputed,并将类型保留为 表格。![图 13.26 – 选择数据集名称和类型]()
图 13.26 – 选择数据集名称和类型
然后,点击 下一步。
-
您必须上传包含泰坦尼克号灾难插补数据的 CSV 文件。因此,点击 上传,然后点击 上传文件,最后通过 打开文件 对话框在
Chapter13文件夹中选择titanic-imputed.csv文件。文件将被上传到在实例化新的 Azure ML 工作区时在幕后创建的默认 Azure Blob 存储中(workspaceblobstore)。点击 下一步。 -
在下一页,您将预览您正在导入的数据集。引擎会自动为您选择最佳导入选项。但如果您想更改某些内容,您可以在这一页进行更改。在这种情况下,一切都已经就绪,所以点击 下一步。
-
在下一页,您可以更改您正在读取的数据的插补模式。在这种情况下,保留每个字段的推断类型,因为导出的 CSV 文件包含整数和小数数值。然后,点击 下一步。
-
将显示一个摘要页面。因此,只需点击 创建,您的数据集就会被添加到 Azure ML。
-
现在,您需要创建一个用于模型训练的计算集群。点击左侧菜单上的 计算 选项卡,然后点击 计算集群,最后点击 新建。
![图 13.27 – 创建新的计算集群]()
图 13.27 – 创建新的计算集群
-
然后,您可以选择集群的首选位置以及每个集群节点要使用的虚拟机类型和大小。您可以保留默认选择并点击下一步。
-
为您的集群选择一个名称(在我们的案例中,是
cluster),最小节点数(保持为 0 以使其在未使用时自动关闭),以及最大节点数(将其设置为 2)。然后,点击创建以分配您的计算集群。 -
现在,点击左侧菜单上的自动机器学习选项卡,然后点击新建自动机器学习运行。
![图 13.28 – 创建新的 AutoML 实验]()
图 13.28 – 创建新的 AutoML 实验
-
在下一页,选择titanic-imputed数据集并点击下一步。
-
现在,您可以通过输入新实验(虚拟文件夹)的名称来配置运行,该实验将包含所有 AutoML 运行(我们使用了
titanic作为名称),机器学习目标列(Survived,预测的目标),以及用于执行 AutoML 运行的计算集群(之前创建的cluster)。![图 13.29 – 配置您的 AutoML 运行]()
图 13.29 – 配置您的 AutoML 运行
-
您可以声明您想要运行的机器学习实验类型。在我们的案例中,它是一个分类。
![图 13.30 – 设置 AutoML 任务类型]()
图 13.30 – 设置 AutoML 任务类型
-
点击查看更多配置设置,您可以选择在实验中使用的首要指标。选择AUC 加权选项,然后点击保存。
-
通过点击查看特征化设置,您可以启用 AutoML 提供的自动特征化选项。默认情况下,它是开启的。您还可以为每一列选择特征类型以及为每个缺失值填充策略(策略是简单的)。保持一切在自动,然后点击保存。
-
现在您可以点击完成以开始您的 AutoML 实验。您将被重定向到运行页面,过一会儿,您将看到实验正在运行。
![图 13.31 – 您的 AutoML 实验正在运行]()
图 13.31 – 您的 AutoML 实验正在运行
-
大约 30 分钟后,实验应该结束。点击AutoML 运行页面上的模型选项卡,您将看到根据最佳性能排序的训练管道。
![图 13.32 – AutoML 找到的最佳性能管道]()
图 13.32 – AutoML 找到的最佳性能管道
-
对于最佳性能模型(VotingEnsemble),还自动生成了可解释性仪表板,您可以通过点击查看解释来访问。有关此内容的更多详细信息,请参阅参考资料。现在,点击VotingEnsemble链接以转到使用该管道训练模型的特定运行。然后,点击部署按钮。
![图 13.33 – 将最佳模型部署到 Web 服务]()
图 13.33 – 将最佳模型部署到 Web 服务
右侧将出现一个新表单,要求提供有关要在 Web 服务上部署的模型的信息。只需给模型端点起一个名字(
titanic-model),选择Azure 容器实例作为计算类型,因为这将不是一个生产环境,并激活启用认证功能。在生产环境中,Azure Kubernetes 服务(AKS)是最佳选择。然后,点击部署并等待模型部署。当部署状态字段在模型摘要中变为成功时,点击titanic-model端点链接。 -
端点详细信息页面包含有关服务的所有信息。部署至少 10 分钟后,它必须处于健康部署状态才能使用。你可以点击测试选项卡,通过提供测试输入数据来测试你的端点。我们最感兴趣的选项卡是消费,其中指示了从外部系统调用 REST API 的坐标(REST 端点 URL 和认证密钥)。你还可以在消费选项部分直接复制允许你在 Python 中消费服务的代码片段。我们将使用此代码的变体在 Power Query 中对测试观测值进行评分。
到目前为止,模型已经准备好在 Web 服务上通过 REST API 进行消费。现在让我们在 Power Query 中使用它。
在 Power BI 中消费已部署的 Azure ML 模型
使用 Azure ML Studio 中端点消费选项卡提出的 Python 代码的变体,我们创建了一个函数,该函数接受端点 URL、API 密钥以及包含要评分观测值的 dataframe 作为参数。在输出中,我们得到一个只包含predicted_label列的 dataframe,其中包含每个观测值的评分。
从通过 Azure AutoML 训练并在 Azure 容器实例上作为 Web 服务部署的模型中获取测试数据集预测的步骤如下:
-
点击获取数据,选择文本/CSV,然后点击连接:
-
在
Chapter13文件夹中选择titanic-test.csv文件,然后点击打开。 -
你将看到测试数据的预览。点击转换数据。
-
在功能区点击转换,然后点击运行 Python 脚本。
-
在
Chapter13\Python文件夹中的06-use-azure-ml-web-service-in-power-bi.py文件中输入脚本。请记住相应地编辑端点 URL 和密钥。 -
我们只对
scored_df数据框感兴趣。因此,点击其表值。 -
你将看到包含一个额外列 –
predicted_label的测试数据集的预览。 -
在功能区点击主页,然后点击关闭并应用。
太棒了!你能够在没有 PPU 许可证或高级容量的情况下,消费在 Azure 机器学习上训练并部署到 Azure 容器实例中的模型。
在 Power Query 中使用认知服务
Azure 认知服务 文本分析 API 是一种提供文本挖掘和分析自然语言处理 (NLP) 功能的服务。提供的功能包括情感分析、意见挖掘、关键词提取、语言检测和命名实体识别。
首先,您需要通过 Azure 门户部署文本分析资源。
配置文本分析
您必须拥有 Azure 订阅才能使用这些服务。然后,您需要按照以下步骤创建文本分析资源:
-
前往 Azure 门户 (
portal.azure.com/) 并点击 创建资源 的加号图标。 -
在搜索框中开始输入文本字符串,文本分析 选项将出现。点击它。然后,点击 文本分析 页面上的 创建 按钮。
-
忘记选择 自定义问答 选项,而是点击 继续创建您的资源。
-
在 创建文本分析 页面上,选择您喜欢的区域并给服务命名(在我们的例子中,
textanalytics555;您可以使用您选择的唯一名称)。将您的资源分配给名为text-analytics的新资源组。然后,选择 免费 F0 定价层,勾选 负责任的 AI 通知 选项,并点击 审查 + 创建。然后,在下一页上点击 创建。 -
资源部署完成后,点击 转到资源 并在下一页上点击 API 密钥 链接。然后,注意 KEY 1 的详细信息(您可以在其右侧点击 复制到剪贴板 图标),以及端点 URL。您将在 Python 代码中使用这些信息。
您的资源现在可以通过专门的 Python SDK 使用了。
配置您的 Python 环境和 Windows
为了使用文本分析,您必须首先按照以下步骤安装 Microsoft Azure Text Analytics Python 客户端库:
-
打开您的 Anaconda Prompt。
-
通过输入以下命令切换到您的 PyCaret 环境:
conda activate pycaret_env。 -
通过输入以下命令安装客户端库:
pip install azure-ai-textanalytics==5.1.0。
之后,为了避免在 Windows 10 中出现“Python 的 ssl 模块不可用”错误,您需要将 pycaret_env\Library\bin 路径添加到 Windows 环境变量 PATH 中。以下是执行此操作的步骤:
-
点击屏幕左下角的 Windows 开始 图标,并开始数字化 x
变量.string 这将搜索所有名称中包含字符串变量的 Windows 选项。然后,点击 编辑您的账户的环境变量。 -
在 环境变量 窗口中,在 用户变量 下双击 Path 变量(如果你为所有用户安装了 Miniconda,你需要更改 Path 系统变量)。在出现的 编辑环境变量 对话框中,点击 新建 按钮,并添加路径
C:\<你的路径>\miniconda3\envs\pycaret_env\Library\bin。然后,在所有窗口上点击 确定。 -
你需要重新启动系统以使更改生效。
你现在可以准备好使用 Power Query 消费服务了。
在 Power BI 中消费文本分析 API
在本节中,我们将向您展示如何通过在虚构公司 Fabrikam Fiber 上的文本分析进行情感分析。它在美国提供有线电视和相关服务,允许用户在其网站上留下评论。你的目标是定义每个评论的积极程度、中立程度和消极程度。
基本上,一旦客户端通过 URL 和密钥进行认证,你就可以通过 analyze_sentiment() 方法轻松地进行情感分析,而无需了解任何 NLP 基础。请注意,文本分析免费层仅限于一次处理 10 个文档(在我们的案例中是评论)。因此,我们构建的代码包括将评论分成每组 10 个,并对每个组调用 API。
让我们看看如何做到这一点:
-
点击 获取数据,选择 文本/CSV,然后点击 连接:
-
在
Chapter13文件夹中选择FabrikamComments.csv文件,然后点击 打开。 -
你将看到 Fabrikam 数据集的预览。然后,点击 转换数据。
-
在功能区上点击 转换,然后点击 运行 Python 脚本。
-
在
Chapter13\Python文件夹中找到07-use-text-analytics-in-power-bi.py文件中的脚本。请记住,要适当地替换你之前在 Azure 门户中复制的服务 URL 和其密钥。 -
我们只对
sentiment_enriched_df数据框感兴趣。所以点击它的 表 值。 -
你将看到 Fabrikam 数据集的预览,其中增加了以下附加列:
comment_sentiment、overall_positive_score、overall_neutral_score和overall_negative_score:![图 13.34 – 额外的情感分析列]()
图 13.34 – 额外的情感分析列
-
在功能区上点击 主页,然后点击 关闭并应用。
这太棒了!多亏了 Python 库 azure.ai.textanalytics,你只用几行代码就能以非常简单的方式执行情感分析。同样容易,你还可以使用 Power BI 中的其他认知服务提供的服务,这些服务通过其他 Python SDK 实现。
摘要
在本章中,你学习了 Power BI 如何通过数据流功能默认与 Microsoft AI 服务交互。你还了解到,通过使用 AutoML 平台,你可以绕过 Power BI 与 Microsoft AI 服务接口所需的许可问题(PPU 许可证或高级容量)。你使用了一个本地 AutoML 解决方案(PyCaret)和 Azure AutoML 在云端解决了一个二元分类问题。你还使用了认知服务的文本分析,直接使用 Python SDK 进行了一些情感分析。
你已经了解到,通过人工智能的丰富功能主要发生在 Power Query 中(它允许访问互联网),尽管你看到过一种情况,直接在 Python 视图中使用机器学习模型可能更方便。
在下一章中,你将看到如何在 Power BI 中实现数据集的探索。
参考文献
对于额外的阅读,请参阅以下书籍和文章:
-
使用数据流实现 AI](https://docs.microsoft.com/en-us/power-bi/transform-model/dataflows/dataflows-machine-learning-integration)
-
《Azure 自动化机器学习(AutoML)综述](https://medium.com/microsoftazure/a-review-of-azure-automated-machine-learning-automl-5d2f98512406)
-
《使用 Microsoft Azure 进行自动化机器学习,作者:Dennis Michael Sawyers,Packt 出版社》](https://www.amazon.com/Automated-Machine-Learning-Microsoft-Azure/dp/1800565313/)
-
机器学习中概念漂移的温和介绍](https://machinelearningmastery.com/gentle-introduction-concept-drift-machine-learning/)
-
使用 K 最近邻算法进行机器学习基础知识](https://towardsdatascience.com/machine-learning-basics-with-the-k-nearest-neighbors-algorithm-6a6e71d01761)
-
Python 的「predict_proba」实际上并不预测概率(以及如何修复它)](https://towardsdatascience.com/pythons-predict-proba-doesn-t-actually-predict-probabilities-and-how-to-fix-it-f582c21d63fc)
-
使用可解释性包在 Python 中解释 ML 模型和预测](https://docs.microsoft.com/en-us/azure/machine-learning/how-to-machine-learning-interpretability-aml)
-
《开始使用 Tidymodels》](https://www.tidymodels.org/start/)
第十五章:14 探索性数据分析
在第十三章在不使用高级或嵌入式容量的情况下使用机器学习中,我们提到,在数据集上盲目地使用自动机器学习(AutoML)解决方案通常不会导致非常精确的模型。这是因为需要通过在早期阶段使用统计工具来理解数据集的最内在特征,以便提取有用信息,从而获得更好的模型。
用于此类数据集分析的方法被称为探索性数据分析(EDA),它最初由 John Turkey 提出,旨在鼓励统计学家探索数据并制定可能导致新的数据收集和实验的假设,从而最终丰富数据集中变量之间的模式。
在本章中,你将了解以下主题:
-
EDA 的目标是什么?
-
使用 Python 和 R 进行探索性数据分析
-
Power BI 中的探索性数据分析
技术要求
本章要求你拥有一个正常工作的互联网连接,并且Power BI 桌面版已经安装在你的机器上。你必须已经按照第二章配置 Power BI 中的 R和第三章配置 Power BI 中的 Python中概述的方式正确配置了 R 和 Python 引擎以及 IDE。
EDA 的目标是什么?
EDA 的目标是确保随后用于更复杂过程的数据库首先必须是干净的,也就是说,它没有缺失值和异常值,这些异常值可能会扭曲可能后续的分析。此外,在这个阶段选择实际上带有信息的变量,并尝试删除那些主要决定噪声的变量,这对于消除可能导致后续过程得出结论的不准确来源至关重要。在这个阶段,研究变量之间的关联并从分析的数据中获得见解,以证明任何随后要应用的更复杂处理是重要的。
最终,数据探索性分析(EDA)的阶段如下:
-
理解你的数据
-
清洗你的数据
-
发现变量之间的关联
让我们详细看看它们涉及哪些类型的分析。
理解你的数据
在这个第一阶段,理解每个变量在数据集所代表的问题背景下的含义是至关重要的。一旦与变量关联的可度量的业务实体变得清晰,就更容易得出关于它们如何相互作用的结论。
了解数据集的大小,即变量数量和观测数量(行数),有助于你获得将要处理的数据大小的初步概念。之后,立即识别并定义所涉及的变量类型(可以是数值型或分类型)对于以最合适的方式可视化它们至关重要。
然后,了解数据集中数值变量的描述性统计有助于对它们的值有更大的敏感性。对于每个数值变量,你可以通过突出其偏度和箱线图来表示其直方图,以便更好地分析其分布,正如你在第十一章中已经学习过的,添加统计洞察:关联性。
对于分类变量而言,另一方面,了解不同类别的数量以及它们所识别的观测特征非常重要。可以使用条形图表示每个独特类别的频率和相对于总数的相对百分比。以下是一个考虑泰坦尼克号灾难数据集的例子:

图 14.1 – Pclass 的柱状图
重要的是检查是否有一个以上的类别具有非常低的频率,因为这可能意味着将它们合并为一个类别是有意义的。这通常在机器学习项目的后续阶段(称为特征工程)中完成,以避免例如拥有一个不够通用的模型,该模型无法适用于它必须适应的所有情况(过度拟合的现象),因此确定其性能不佳。
同样非常重要的一点是,给分析师提供图形化表示一对变量的可能性,以便理解它们如何相互作用(数据集的多元分析)。显然,可视化的类型会根据涉及的变量数据类型而变化:在两个数值变量的情况下,你将有一个散点图;在两个分类变量的情况下,你将有一个马赛克图;在一个数值变量和一个分类变量的情况下,你将为每个类别有几个箱线图(或雨云图)。
在这个阶段,我们可以继续描述处理数据清洗的阶段。
清洗你的数据
如你在第十二章中学习的,添加统计洞察:异常值和缺失值,获得一个尽可能接近其所代表真实情况的统计稳健数据集最重要的活动是这两个:
-
确定和处理异常值
-
填充缺失值
在 EDA 过程中,利用所有有助于直接检测异常值和缺失值的有用视觉和非视觉工具非常重要。这将允许在后续阶段修复发现的任何异常。
如你之前所学的,对于单变量异常值分析,使用箱线图表示数值变量的单个分布非常有用。而且,在通过 Yeo-Johnson 变换转换变量后,能够看到哪些异常值仍然存在或被添加进来也非常有用。这样你可以看到是否值得尝试将分布标准化,以处理尽可能少的异常值。
考虑到一个数值变量和一个分类变量,进行异常值的双变量分析也很方便,这样你可以研究数值变量分布的异常,这些异常被分类变量的每个标签所分解。
在仔细研究和识别可能的异常值之后,还很重要的一点是要有一个方便的变量之间缺失值的组合和交集的可视化。因此,目标不仅是了解每个变量的缺失值的数量和相对百分比值,还要通过 upset 图方便地可视化这些缺失值是如何相互作用的。
最后,让我们转向变量之间关联的分析。
发现变量之间的关联
如你在第十一章添加统计洞察:关联中学到的,了解数据集中变量之间的关联程度当然有助于你理解哪些变量携带了最多的信息,哪些变量主要产生噪音。选择最有信息的变量并将它们用于训练机器学习模型,无疑有助于你获得更稳定和性能更好的结果。
拥有可视化工具来解释成对数值-数值、数值-分类和分类-分类变量之间的关联,无疑有助于特征选择的艰难任务。
有一些工具可以让你自动生成 EDA 报告。让我们看看它们是什么。
使用 Python 和 R 进行探索性数据分析
如果你必须仅使用 Python 或 R 进行数据探索,有一些工具可以自动生成一系列可视化,使你的生活变得更轻松。以下有两个列表,一个用于 Python 工具,另一个用于 R 工具,以防你需要任何工具。在 Python 中找到交互式数据分析工具比在 R 中更容易。R 中可用的包通常提供包装器,这极大地简化了通过编码进行的 EDA。
用于 EDA 的 Python 库如下:
-
SweetVIZ (
pypi.org/project/sweetviz/): 一个开源的 Python 库,通过仅用两行代码即可生成美观、高密度的可视化,从而启动 EDA。 -
Lux (
lux-api.readthedocs.io/): 一个通过自动化可视化过程和数据分析流程来简化快速和轻松数据探索的 Python 库。 -
Pandas Profiling (
pandas-profiling.github.io/pandas-profiling/): 从 pandas 数据框生成用于数据分析的概要报告。 -
PandasGUI (
github.com/adamerose/pandasgui): 一个用于查看、绘图和分析 pandas 数据框的图形用户界面。 -
D-Tale (
github.com/man-group/dtale): 结合 Flask 后端和 React 前端,为您提供一个轻松查看和分析 pandas 数据结构的方法。
EDA 的 R 包如下:
-
ExPanDaR (
github.com/joachim-gassen/ExPanDaR): ExPanD 是一个基于 Shiny 的应用程序,支持交互式数据探索分析。 -
ggquickeda (
github.com/smouksassi/ggquickeda): 一个 R Shiny 应用程序/包,使您能够快速探索数据,并通过散点图、点图、箱线图、条形图等实时检测趋势。 -
summarytools (
github.com/dcomtois/summarytools): 这是一个用于数据清洗、探索和简单报告的 R 包。它与 R 中常用的报告软件和工具(如 RStudio)集成良好。 -
DataExplorer (
boxuancui.github.io/DataExplorer/): 目的是自动化大部分数据处理和可视化,使用户能够专注于研究数据和提取见解。
查看参考文献以获取有关上述工具的更多详细信息。
如果您想利用 Power BI 作为可视化工具,不幸的是,您无法使用上述 Python 工具自动生成的许多可视化。因此,我们将使用 R 包通过代码生成必要的用于基本 EDA 的内容,因为 R 可以非常容易地获得任何复杂性的高质量图形。让我们看看如何。
Power BI 中的数据探索分析
在本节中,我们将广泛使用GGplot2包,这是一个基于图形语法的高级 R 库,用于创建图表。我们并不打算详细介绍该包所暴露的每个功能,即使它在伴随章节的代码中得到了相当广泛的应用。我们的目标是,一如既往地,提供易于定制的代码,用于在其他项目中使用,最重要的是,提供一个更详细查看所用函数的起点。有关更多详细信息,请参阅本章的参考文献。
除了 Tidyverse 在 R 中提供的工具(包括ggplot2)之外,我们还将使用summarytools和DataExplorer包在 Power BI 中创建 EDA 报告。因此,安装它们是必要的:
-
打开 RStudio 并确保它引用的是您最新的 CRAN R(在我们的例子中是版本 4.0.2)。
-
点击控制台窗口并输入以下命令:
install.packages('Rcpp')。这是一个summarytools的依赖项。然后,按Enter键。 -
输入以下命令:
install.packages('summarytools')。然后,按Enter键。 -
输入以下命令:
install.packages('DataExplorer')。然后,按Enter键。
在这一点上,我们可以开始实施我们的 EDA 报告。然而,首先重要的是强调,在您的报告中使用的每个 R 脚本中都必须定义变量的数据类型。
重要提示
为了进行数据探索分析(EDA),定义要分析的数据集中的变量数据类型非常重要。根据特征的数据类型,应用不同的分析策略。数据模型中 Power BI 表的列的数据类型不能与 R 处理的数据类型一一对应。因此,需要在 R 脚本中直接定义列的数据类型,以防止 Power BI 覆盖它们。
此外,您将在Chapter14\R文件夹中找到的00-init-dataset.R脚本将用作 EDA 报告在 Power BI 中的官方 CSV 连接器,因为在这种情况下,将数据直接加载到 R 中用于每个脚本更为方便。在加载您的数据后,您需要定义要删除的列,包括分类和整数字段。指定为整数的列将被视为数值或分类变量,由分析师根据最能描述分析现象的函数来选择。所有其他列都应视为数值:
library(readr)
library(dplyr)
dataset_url <- 'http://bit.ly/titanic-dataset-csv'
src_tbl <- read_csv(dataset_url)
vars_to_drop <- c('PassengerId')
categorical_vars <- c('Sex', 'Ticket', 'Cabin', 'Embarked')
integer_vars <- c('Survived', 'Pclass', 'SibSp', 'Parch')
如果您需要导入 Excel 文件而不是 CSV 文件,您已经知道如何根据在第七章中学到的知识对代码进行更改。
然后脚本将定义应用适当转换的tbl tibble:
tbl <- src_tbl %>%
mutate(
across(categorical_vars, as.factor),
across(integer_vars, as.integer)
) %>%
select( -all_of(vars_to_drop) )
此脚本将预先加载到您为 EDA 报告开发的每个其他 R 脚本中,以确保您在内存中准备好具有精确数据类型的特定数据集。
因此,让我们从数据集的基本统计信息开始开发报告。
数据集摘要页面
EDA 报告的第一页负责向分析师提供数据集中包含数据的概述。一些基本信息将通过DataExplorer包的introduce()函数的输出得到展示。这些信息如下:
-
行和列的数量
-
离散(分类)和连续(数值)列的数量
-
所有缺失值的列数
-
数据集中缺失值的总数
-
完整行的数量,即没有缺失值
-
数据集中观测值的总数,即行数乘以列数
-
内存使用量(以 KB 为单位)
-
重复行的数量
然后从summarytools包的dfSummary()函数的输出开始计算整个数据集的摘要统计表。它包含的每个变量的信息如下:
-
变量名和数据类型
-
根据数据类型的基本统计信息
-
唯一有效值的数量(非空)
-
有效值的频率
-
有效值的数量及其百分比
-
缺失值的数量及其百分比
最后,由于 summarytools 包的 descr() 函数,提供了更多关于数值变量的详细统计信息。除了最常见的统计信息外,还添加了以下统计信息:
-
中位数绝对偏差 (mad): 与方差和标准差不同,它是对数据集分布广泛程度的鲁棒性度量。它通常用于非正态分布。
-
四分位距 (iqr): 正如您在箱线图中看到的,它是分布值大部分所在位置的度量。
-
变异系数 (cv): 它是相对变异性的度量,因为它是有标准差与平均值的比率。
-
偏度系数 (skewness): 它是衡量分布对称性缺乏的度量。
-
峰度 (kurtosis): 它是衡量数据相对于正态分布是重尾还是轻尾的度量。
通过这些信息,分析师可以全面了解数据集中包含的数据的形状。
让我们看看如何在 Power BI 中实现它们。首先,确保 Power BI Desktop 在 选项 中引用了正确的 R 版本。然后按照以下步骤操作:
-
点击 获取数据,搜索
script,选择 R 脚本,然后点击 连接。 -
将您在文件
01-basic-info.R中找到的脚本输入到Chapter14\R文件夹中。确保init_path变量指向同一文件夹中可以找到的00-init-dataset.R文件的正确绝对路径。 -
在导航对话框中,选择
basic_info_tbl、numeric_vars_descr_stats_tbl、summary_tbl和sample_tbl表。然后点击 加载。 -
在 Power BI Desktop 右侧的 字段 面板上加载了四个表之后,您可以通过勾选它们的度量来简单地将它们添加到您的报告画布中。例如,展开
basic_info_tbl内容,并按此顺序检查字段attribute(字符串字段)和value(数值字段),以获取表格图表(如果您首先点击数值字段,您将得到柱状图)。画布上会出现一个表格:![图 14.2 – 首次状态的基本信息表]()
图 14.2 – 首次状态的基本信息表
在这个例子中,总计 行没有意义。因此,必须在视觉的 格式 属性上将其消除。
-
确保选择了表格视觉,然后点击右侧 可视化 面板上的 格式 选项(墙刷图标)。展开 总计 部分,关闭 总计 选项。然后展开 字段格式化 部分,从组合框中选择
value字段,并在 值小数位数 文本框中输入0。最后,打开 标题 选项并展开其选项。然后在文本框中输入标签Basic Dataset Info。 -
现在,在画布上的一个空白位置单击,展开
summary_tbl字段,并按照以下顺序检查:变量,统计值,唯一有效,有效频率,有效,和缺失。之后,展开视觉的右侧边缘,以便查看所有列。 -
如同在步骤 5中对
basic_info_tbl表格所做的那样,也删除此表格的总计行,并使用此字符串作为标签:Dataset Summary。 -
现在,在画布上的一个空白位置单击,展开
numeric_vars_descr_stats_tbl字段,并按照以下顺序检查:变量,平均值,标准差,最小值,四分位数 1,中位数,四分位数 3,最大值,绝对偏差,四分位距,变异系数,偏度,和峰度。之后,展开视觉的右侧边缘,以便查看所有列。 -
对于这个表格,也删除总计行,并使用以下字符串作为标签:
Descriptive Statistics for Numeric Variables。 -
现在,在画布上的一个空白位置单击,展开
sample_tbl字段,并在检查名称之后按照你想要的顺序检查所有字段(在开始检查非数字字段时,默认选择的视觉将是表格)。 -
对于这个表格,也删除总计行,并使用以下字符串作为标签:
Dataset Sample。 -
在画布上重新定位视觉元素并稍微扩展单个表格列之后,你将得到你的第一个 EDA 报告页面,如下所示:
![图 14.3 – EDA 报告的摘要页面]()
图 14.3 – EDA 报告的摘要页面
-
只需使用标签
摘要重命名页面(右键单击底部页面标签并点击重命名页面)。
请记住以下几点:
重要提示
为了显示数据集的样本细节,你必须在 Power BI Desktop 中正确选择其字段。这意味着,如果你想更改源数据集,你必须手动编辑样本细节视觉。
干得好!你刚刚开发了你的 EDA 报告的第一页。
让我们现在尝试开发一个专门用于分析缺失值的页面。
缺失值探索
虽然一些关于缺失值的信息以文本格式出现在新开发的摘要页面上,但提供一个包含专门针对缺失值的视觉的页面对于分析师来说绝对是一个优势。
除了显示表示每个变量缺失值百分比的简单棒棒糖图外,你还将使用** upset 图来允许分析师找出哪些变量的组合被发现完全缺失,正如你在第十二章,添加统计洞察:异常值和缺失值**中学到的那样。
因此,让我们开始开发关联页面:
-
如果尚未存在,通过在画布底部的标签附近单击加号图标创建一个新的 Power BI 报告页面。将创建一个名为页面 1的新标签。
-
首先,在报告画布上的一个空白处点击。然后点击可视化面板中的R Visual图标,并在提示时启用它。之后,将其移动到页面左侧,并调整大小以填充一半。
-
保持选中状态,点击
basic_info_tbl表的attribute字段。你不会在 R Visual 代码中使用这个字段,但你必须选择至少一个度量来启用可视化中的脚本。 -
现在点击 R Visual 的格式选项卡,并在标题部分输入字符串
缺失值百分比作为标题。 -
将文件
02-missing-values-plot-1.R的代码复制到Chapter14\R文件夹中,并将其粘贴到 R Visual 脚本编辑器中。然后点击 R 脚本编辑器右上角的运行脚本箭头图标(每次请求时启用 R Visual)。你将在其中得到棒棒糖图。 -
再次在报告画布上的一个空白处点击。然后点击可视化面板中的R Visual图标,并在提示时启用它。之后,将其移动到页面右侧,并调整大小以填充一半。
-
保持选中状态,点击
basic_info_tbl表的attribute字段。你不会在 R Visual 代码中使用这个字段,但你必须选择至少一个度量来启用可视化中的脚本。 -
现在点击 R Visual 的格式选项卡,并在标题部分输入字符串
变量同时缺失作为标题。 -
将文件
03-missing-values-plot-2.R的代码复制到Chapter14\R文件夹中,并将其粘贴到 R Visual 脚本编辑器中。然后点击 R 脚本编辑器右上角的运行脚本箭头图标(每次请求时启用 R Visual)。你将在其中得到 upset 图。 -
只需将当前页面重命名(在页面底部的标签上右键单击并点击重命名页面)使用标签
缺失值分析。
很好!现在分析师理解缺失值对数据集的影响要容易得多。整个页面将如下所示:

图 14.4 – EDA 报告的缺失值分析页面
现在让我们转向开发一个专门用于数据集单变量分析的页面。
单变量探索
数据集的一维分析包括对其组成变量的分布的分析。根据变量的数据类型,用于辅助这一分析的可视化工具不同。
对于数值变量的分析,使用几个图表来更好地描绘它们的分布:第一张图中有直方图和密度图重叠,而在第二张图中有一个雨云图(密度图和箱线图的组合)。在图 14.5中,你可以找到一个数值变量图表的例子:

图 14.5 – 年龄的直方图和密度图
对于分类变量,使用简单的条形图,目的是限制显示的不同类别的最大数量,以免超过预定义的数量,并将其他类别分组到虚拟的其他类别中:

图 14.6 – 舱位变量的条形图
在 Power BI 中显示变量的选择是通过下拉筛选器完成的。如果一个变量是整型,它将同时出现在数值变量的下拉列表和分类变量的下拉列表中,这样分析师就可以以他们认为更合适的方式可视化它们。
由于图表将通过 R Visuals 显示,如果你希望将 EDA 报告发布到 Power BI 服务,你必须确保要使用的 R 包与 Power BI 服务上找到的 R 引擎版本兼容。记住,Power BI Desktop 一次只能引用一个 R 引擎,这将是你在机器上用于 Power Query 和 R Visuals 的引擎。因此,如果你计划在报告发布后使用最新的 R 引擎进行 Power Query,你也应该安装在此引擎上渲染报告所需的包。这样,你确保在测试期间 Power BI Desktop 上报告也能正确渲染:
-
打开 RStudio 并确保它引用的是你的 CRAN R 专门用于 R Visuals(在我们的案例中是版本 3.4.4)。
-
点击 控制台 窗口并输入以下命令:
install.packages('cowplot')。它类似于ggplot2的扩展,提供对齐、排列和注释图表的主题和函数。然后,按 Enter。 -
输入以下命令:
install.packages('ggpubr')。它提供了一些易于使用的函数,用于创建和自定义基于ggplot2的出版物图表。然后,按 Enter。 -
输入以下命令:
install.packages('ggExtra')。这是一个函数和层的集合,用于增强ggplot2,主要用于其ggMarginal函数,该函数向散点图添加边缘直方图/箱线图/密度图。然后,按 Enter。 -
输入以下命令:
install.packages('RColorBrewer')。它是管理 R 中的颜色和调色板的一个基本工具。然后,按 Enter。 -
输入以下命令:
install.packages('ggmosaic')。它旨在创建分类数据的可视化,特别是 mosaic 图。然后,按 Enter。 -
输入以下命令:
install.packages('ggdist')。它旨在创建分类数据的可视化,特别是 mosaic 图。然后,按 Enter。 -
将 RStudio R 引用更改为你的 CRAN R 专门用于 Power Query(在我们的案例中是版本 4.0.2)并重新启动 RStudio。
-
重复步骤 2 到 7。
现在,你可以开始开发你报告的新页面,该页面专注于数据集的单变量分析:
-
如果尚未存在,通过点击画布底部的标签附近的加号图标创建一个新的 Power BI 报表页面。将创建一个名为页面 1的新标签页。
-
点击获取数据,搜索
script,选择R 脚本,然后点击连接。 -
将文件
04-serialize-univariate-plots.R中的脚本输入到Chapter14\R文件夹中。确保folder变量指向Chapter14文件夹的正确绝对路径。 -
在导航对话框中,选择
basic_info_tbl、numeric_vars_descr_stats_tbl、summary_tbl和sample_tbl表。然后点击加载。 -
此脚本将每个变量相关的图表列表序列化到磁盘,根据其类型。一旦你点击确定,你将在磁盘上找到以下文件:
histodensity_lst.rds、histodensity_transf_lst.rds(根据 Yeo-Johnson 转换的数值变量),以及barchart_lst.rds。当涉及变量的可能组合较少时,预先计算图表列表并将它们序列化到磁盘是一个好策略。 -
在导航对话框中,选择
categorical_df和numeric_df表。正如你所见,numeric_df表包含numeric_col_name列中数值变量的名称,重复两次,一次对应于transf_type列中要应用于它们的每种转换类型(standard和yeo-johnson):![图 14.7 – numeric_df 表的内容]()
图 14.7 – numeric_df 表的内容
然后,点击加载。
-
点击切片器的视觉图标。然后展开字段面板下的
numeric_df表,并检查numeric_col_name度量。 -
点击切片器右上角的向下箭头以选择下拉切片器类型。
-
调整切片器的底部边缘,点击其格式选项,在选择控件下打开单选,关闭切片器标题,并添加标题
数值变量。 -
首先点击报表画布上的空白处。然后点击切片器视觉图标。然后展开字段面板下的
numeric_df表,并检查transf_type度量。 -
点击切片器右上角的向下箭头以选择下拉切片器类型。
-
调整切片器的底部边缘,点击其格式选项,在选择控件下打开单选,关闭切片器标题,并添加标题
转换。 -
现在,首先点击报表画布上的空白处。然后点击可视化面板中的R 视觉图标,并在提示时启用它。之后,将其移动到刚刚创建的两个切片器下面。
-
保持选中状态,点击numeric_df表的两个字段(numeric_col_name和transf_type)。
-
现在,点击 R 视觉的格式选项卡并关闭标题选项。
-
将文件
05-plot-numeric-variables.R的代码复制到Chapter14\R文件夹中,并将其粘贴到 R Visual 脚本编辑器中。然后点击 R 脚本编辑器右上角的 运行脚本 箭头图标(每次请求时启用 R Visual)。你将得到未变换的Age变量的单变量分析:![图 14.9 – 年龄变量的单变量分析]()
图 14.9 – 年龄变量的单变量分析
-
如果你从 变换 下拉菜单中选择
yeo-johnson变换,图表将相应更新:![图 14.10 – Yeo-Johnson 变换后的年龄变量的单变量分析]()
图 14.10 – Yeo-Johnson 变换后的年龄变量的单变量分析
-
让我们现在创建分类变量的切片器。首先点击报告画布上的一个空白位置。然后点击切片器视觉图标。接着展开 字段 面板下的
categorical_df表,并检查categorical_col_name度量。 -
点击切片器右上角的向下箭头以选择 下拉 切片器类型。
-
调整切片器的底部边缘,点击其 格式 选项,在 选择控件 下打开 单选,关闭 切片器标题,并添加 标题
分类变量。 -
首先点击报告画布上的一个空白位置。然后点击 可视化 面板中的 R Visual 图标,并在提示时启用它。之后,将其移动到你刚刚创建的切片器下方。
-
保持选中状态,点击 categorical_df 表的字段(categorical_col_name)。
-
现在点击 R Visual 的 格式 选项卡并关闭 标题 选项。
-
将文件
06-plot-categorical-variables.R的代码复制到Chapter14\R文件夹中,并将其粘贴到 R Visual 脚本编辑器中。然后点击 R 脚本编辑器右上角的 运行脚本 箭头图标(每次请求时启用 R Visual)。你将得到Cabin变量的单变量分析:![图 14.11 – 船舱变量的单变量分析]()
图 14.11 – 船舱变量的单变量分析
-
只需将当前页面重命名(在页面底部的标签上右键单击并点击 重命名页面),使用标签
单变量分析。
很好!你刚刚开发了一个针对单变量数据集分析的出色 EDA 报告页面。通过玩切片器,整个页面将像以下这样:

图 14.12 – 单变量分析 EDA 报告页面
现在是时候开发一个针对数据集多元分析的报告页面了。
多元探索
通常,对单个变量的分析永远不足以让分析师对数据集有一个完整的了解。能够使用允许根据数据类型交互多个变量的视觉工具,无疑是向更准确的数据集分析迈进的一步。
分析两个数值变量交互作用最常用的视觉方法是散点图。在我们的案例中,我们将使用这种图表的增强版本,在边缘添加变量的边缘分布。此外,还可以根据另一个分类变量对点进行着色。这样,分析师可以研究三个变量的交互作用。在图 14.13中,你可以找到一个关于变量Age和Fare按Sex分组的这种图表的示例。

图 14.13 – 按性别划分的年龄和船票散点图,边缘直方图
如你所见,分组变量的类别有多少,就有多少回归线。在缺少分组变量的情况下,回归线是唯一的,在这种情况下,图表还显示了 Pearson 的 R 系数、Spearman 的rho和 Kendall 的tau的值,以及从相应测试中得出的 p 值。简而言之,在这种情况下,p 值表示样本数据中 x 和 y 之间的相关性偶然发生的概率(查看参考文献以了解更多关于 p 值的信息):

图 14.14 – 未分组的年龄和船票(转换后)散点图
如果要研究的交互作用的变量是分类的,mosaic 图将帮助你。在图 14.15中,你可以找到按Sex分组的SibSp和Pclass变量的 mosaic 图:

图 14.15 – 按性别分组的 Pclass 和 SibSp 的 mosaic 图
最后,通过可视化每个类别的雨云图,可以研究数值变量与分类变量之间的交互作用。此外,始终可以通过第三个分类变量来分割图表,就像之前的例子一样。在图 14.16中,你可以看到一个关于变量Fare(通过 Yeo-Johnson 转换以获得更好的可视化)和Pclass以及Sex的示例图表:

图 14.16 – 按性别分组的船票和 Pcalss 变量的雨云图
现在,你可以开始开发你报告的新页面,该页面专门用于数据集的单变量分析:
-
如果还没有,请通过点击画布底部的标签旁边的加号图标来创建一个新的 Power BI 报告页面。将创建一个名为页面 1的新标签页。
-
点击获取数据,搜索
脚本,选择R 脚本,然后点击连接。 -
将在文件
07-create-multivariate-objects.R中找到的脚本输入到Chapter14\R文件夹中。确保folder变量指向Chapter14文件夹的正确绝对路径。 -
在导航器对话框中,选择
multivariate_df表格。这次,生成图表的代码将直接在 R Visual 中编写,无需先序列化再在可视化时调用。这种策略与用于单变量分析页面所用的策略相反,因为在这种情况下,游戏中变量之间的组合数量要高得多,要在列表中生成相同数量的图表并序列化一切并不非常高效。multivariate_df表格是所有参与多元分析的变量之间交叉连接的结果数据框(两个数值变量,每个变量的转换类型相关变量,两个分类变量,以及另一个用于分组的分类变量:总共七个变量):![图 14.17 – multivariate_df 表格的内容]()
图 14.17 – multivariate_df 表格的内容
然后点击加载。
-
点击切片器视觉图标。然后展开字段面板下的
multivariate_df表格,并检查x度量。 -
点击切片器右上角的向下箭头以选择下拉切片器类型。
-
调整切片器的底部边缘,点击其格式选项,在选择控制下切换开启单选,在切片器标题部分下输入
x 数值标题,开启标题部分,并添加分析变量标题。 -
首先在报告画布上的空白处点击,然后点击切片器视觉图标。在字段面板下展开
multivariate_df表格,并检查x_transf_type度量。 -
点击切片器右上角的向下箭头以选择下拉切片器类型。
-
调整切片器的底部边缘,点击其格式选项,在选择控制下切换开启单选,在切片器标题部分下输入
x 转换标题,并保持标题部分关闭。 -
按照步骤 5 到 10 重复操作,以创建y 数值和y 转换切片器,确保分别选择
y和y_transf_type度量。对于它们也保持标题部分关闭。 -
首先在报告画布上的空白处点击,然后点击切片器视觉图标。在字段面板下展开
multivariate_df表格,并检查cat1度量。 -
点击切片器右上角的向下箭头以选择下拉切片器类型。
-
调整切片的底部边缘,点击其 格式 选项,在 选择控件 下启用 单选,在 切片标题 部分下输入
cat1 分类标题,并保持 标题 部分关闭。 -
重复步骤 12 到 14 以创建 cat2 分类 切片。确保选择
cat2度量。 -
首先点击报告画布上的一个空白位置,然后点击切片可视化图标。在 字段 面板下展开
multivariate_df表,并检查grp度量。 -
点击切片右上角的向下箭头以选择 下拉 切片类型。
-
调整切片的底部边缘,点击其 格式 选项,在 选择控件 下启用 单选,在 切片标题 部分下输入
grp 分类标题,并保持 标题 部分关闭。 -
尝试将所有这些滑块对齐到页面顶部的单行中,如下所示:
![图 14.18 – 页面顶部的多元页面切片]()
图 14.18 – 页面顶部的多元页面切片
-
首先点击报告画布上的一个空白位置。然后点击 可视化 面板中的 R 可视化 图标,并在提示时启用它。之后,将其移动到您刚刚创建的切片行下方,并调整大小以填满页面。
-
保持选中状态,点击
multivariate_df表的所有字段。 -
现在点击 R 可视化的 格式 选项卡并关闭 标题 选项。
-
将文件
08-plot-multivariate-plots.R的代码复制到Chapter14\R文件夹中,并将其粘贴到 R 可视化脚本编辑器中。然后点击 R 脚本编辑器顶部的 运行脚本 箭头图标(每次请求时都启用 R 可视化)。您将在其中获得所有多元分析图表。 -
只需将当前页面重命名(在页面底部的标签上右键单击并点击 重命名页面),使用标签
多元分析。
太棒了!这个专门用于多元分析的 EDA 报告页面对于分析师来说非常具有揭示性。通过调整切片,整个页面将像以下这样:

图 14.19 – EDA 报告的多元分析页面
请记住,数值变量 y 用于散点图和雨云图的 y 轴。分类变量 cat1 用作雨云图和错综图的 x 轴。分类变量 grp 用于根据标签对三个图表进行分组。
您刚刚看到了 R 在专业图形开发方面的强大和灵活性。让我们看看如何开发一个用于变量关联的页面。
变量关联
如你在第十一章中学到的,添加统计洞察:关联,了解将两个变量联系起来的相关系数,这给了你评估一个变量对另一个变量的预测能力的机会。从同一章节,你也学到了如何根据涉及的不同类型变量的组合来计算相关系数。
在本节中,我们将把所学知识应用于开发一个包含根据相关系数强度着色的热图的 EDA 报告页面。以下是一个示例:

图 14.20 – 数据集的相关热图
相关系数将根据分析师可以通过两个筛选器做出的选择来计算:一个允许你选择用于数值变量之间相关性的方法,另一个允许你选择用于分类变量之间相关性的方法。
因此,让我们开始开发关联页面:
-
如果尚未存在,请通过点击画布底部标签附近的加号图标创建一个新的 Power BI 报告页面。将创建一个名为页面 1的新标签页。
-
点击获取数据,搜索
script,选择R 脚本,然后点击连接。 -
将你可以在文件
09-create-association-objects.R中找到的脚本放入Chapter14\R文件夹中。确保folder变量指向Chapter14文件夹的正确绝对路径。 -
在导航器对话框中,选择
corr_tbl表。这个表是由一系列转换给出的。首先,对于数值变量和分类变量的相关方法组合,计算了一个包含相应相关系数的列。这通过使用在第九章中引入的 R 的并行计算来加速。之后,执行了一个转置操作,将所有这些列合并到单个列corr中,并创建了一个新的列corr_type,其中包含标识所使用方法组合的字符串。最后,将corr_type列拆分为两个单独的列,一个用于数值变量的相关方法筛选器,另一个用于分类变量的相关方法筛选器。以下是结果表的样本:![图 14.21 – corr_tbl 表的内容]()
图 14.21 – corr_tbl 表的内容
然后点击加载。
-
点击筛选器视觉图标。然后在字段面板下展开
corr_tbl表,并检查numeric_corr_type度量。 -
点击筛选器右上角的向下箭头以选择下拉筛选器类型。
-
调整切片器的底部边缘,点击其格式选项,在选择控件下启用单选,在切片器标题部分下输入
Numeric correlation type标题,启用标题部分,并添加分析变量标题。 -
首先在报告画布上的一个空白处点击,然后点击切片器可视化图标。展开字段面板下的
corr_tbl表,并检查categorical_corr_type度量。 -
点击切片器右上角的向下箭头以选择下拉切片器类型。
-
调整切片器的底部边缘,点击其格式选项,在选择控件下启用单选,在切片器标题部分下输入
Categorical correlation type标题,并保持标题部分关闭。 -
尝试将所有这些滑块对齐到页面顶部的单行,如下所示:
![图 14.22 – 页面顶部的关联页面切片器]()
图 14.22 – 页面顶部的关联页面切片器
-
首先在报告画布上的一个空白处点击。然后点击可视化面板中的R 可视化图标,并在提示时启用它。之后,将其移动到您刚刚创建的切片行下方,并调整大小以填充整个页面。
-
选中后,点击
corr_tbl表中的col、corr和row字段。 -
将文件
10-plot-association-plots.R的代码复制到Chapter14\R文件夹中,并将其粘贴到 R 可视化脚本编辑器中。然后点击 R 脚本编辑器右上角的运行脚本箭头图标(每次请求时启用 R 可视化)。您将在其中获得所有多元分析图。 -
现在点击 R 可视化的格式选项卡,并在标题部分输入字符串
Categorical Heatmap作为标题。 -
只需重命名当前页面(在页面底部的标签上右键单击并点击重命名页面),使用标签
关联分析。
使用 R 和 Ggplot 创建相关性热图竟然如此简单,令人印象深刻!
玩转切片器,整个页面将类似于以下这样:

图 14.23 – EDA 报告的关联分析页面
哇!您已经完成了您的第一个 EDA 报告。您不得不承认,结果令人惊叹!
摘要
在本章中,您学习了什么是探索性数据分析(EDA)以及它帮助实现的目标。
您还学习了最常用的工具,用于使用 Python 和 R 进行自动化的 EDA。
最后,您使用 R 及其创建图形最流行的包开发了一个完整且动态的 EDA 报告,用于分析数据集。
在下一章中,您将看到如何开发高级可视化。
参考文献
对于进一步阅读,请参阅以下书籍和文章:
-
使用 Sweetviz 只需两行代码即可进行强大的 EDA(探索性数据分析) (
towardsdatascience.com/powerful-eda-exploratory-data-analysis-in-just-two-lines-of-code-using-sweetviz-6c943d32f34) -
Lux:用于智能视觉数据发现的 Python API (
www.youtube.com/watch?v=YANIids_Nkk) -
泰坦尼克数据集的 pandas 分析 (
pandas-profiling.github.io/pandas-profiling/examples/master/titanic/titanic_report.html) -
pandasGUI 演示 (
www.youtube.com/watch?v=NKXdolMxW2Y) -
关于多维数据有效可视化的图形语法全面指南 (
towardsdatascience.com/a-comprehensive-guide-to-the-grammar-of-graphics-for-effective-visualization-of-multi-dimensional-1f92b4ed4149) -
完整的 ggplot2 教程 (
r-statistics.co/Complete-Ggplot2-Tutorial-Part1-With-R-Code.html) -
关于解释相关性的所有你需要知道的内容 (
towardsdatascience.com/eveything-you-need-to-know-about-interpreting-correlations-2c485841c0b8)
第十六章:15 高级可视化
正如您在第十四章、探索性数据分析中已经看到的,得益于ggplot2包及其扩展,您可以创建非常专业的图表。在本章中,您将了解如何创建一个非常高级且吸引人的自定义图表。您将学习以下主题:
-
选择圆形条形图
-
在 R 中实现圆形条形图
-
在 Power BI 中实现圆形条形图
技术要求
本章要求您在计算机上已经安装了可工作的互联网连接和Power BI Desktop。您必须已按照第二章、配置 Power BI 中的 R和第三章、配置 Power BI 中的 Python中概述的方式正确配置了 R 和 Python 引擎以及 IDE。
选择圆形条形图
非常常见的情况是我们需要使用条形图(或条形图)来表示与各种分类实体相关的度量。然而,当要表示的实体数量超过 15 或 20 个时,图表开始变得难以阅读,即使您将其垂直排列:

图 15.1 – 全球武器销售商的条形图
在这种情况下,正如您在第十四章、探索性数据分析中已经看到的,通常在表示尽可能多的实体之后,将后续的个别实体分组到一个单独的类别中(在我们的例子中,是其他类别)是一个好主意。这样,图表的可读性得到了保留,但您想要表示的部分信息却丢失了。
如果严格必要显示所有实体及其度量,我们通常会求助于一种更引人注目的条形图空间组织方式,将其包裹成圆形形状,从而获得圆形条形图:

图 15.2 – 全球武器销售商的圆形条形图
当您不仅需要表示所有实体,而且还需要适当地对它们进行分组时,图表变得更加有趣。考虑一个有 24 位演讲者的活动列表,每位演讲者都从观众那里收到了对其在演讲中展示的一些特征(期望、有趣、有用和口头表达)的反馈(1 到 5 分)。因此,最终您有24 x 4 = 96个实体需要展示。描述这种情况的分组圆形条形图非常有说明性:

图 15.3 – 演讲者反馈组的圆形条形图
图 15.3 中的圆形条形图是我们将在下一节中使用 R 和ggplot2包实现的。
在 R 中实现圆形条形图
本节中你将看到的 R 代码受到了 R Graph Gallery 网站分享给整个 R 社区的代码的启发(www.r-graph-gallery.com/index.html)。除了几个非常小的添加之外,我们使用 tidy evaluation 框架重构和泛化了代码,形成了 circular_grouped_barplot() 函数,以便它可以与任何数据集一起使用。
如果你记得正确的话,在之前章节中你看到的 R 函数中,你将列名作为字符串传递给函数。多亏了 tidy evaluation,你可以使用 tidyverse 语法 将它们传递给函数,也就是说,通过管道直接传递。以下是一个例子:
circular_grouped_barplot(data = speakers_tbl,
grp_col_name = 'Characteristics',
label_col_name = 'SpeakerName',
value_col_name = 'Value')
你将不会看到之前函数的调用,而是以下调用:
speakers_tbl %>%
circular_grouped_barplot(grp_col_name = Characteristics,
label_col_name = SpeakerName,
value_col_name = Value)
注意在最后的脚本中,标识字符串的引号消失了。
让我们一步一步地看看这个函数做了什么。这里提供的代码摘自 Chapter15 文件夹中的 01-circular-grouped-barplot.R 文件。让我们按以下步骤进行:
-
首先,确保
scales包已经安装在你的 CRAN R 专用视觉包中(在我们的案例中,版本为 3.4.4)。只需检查其名称是否在 RStudio 右下角的 包 选项卡中列出的列表中。如果不是,像往常一样使用install.packages('scales')命令进行安装。 -
你将使用的数据集来自
Chapter15文件夹中的Scores.csv文件。正如前文所述,它包含了 24 位演讲者从会议参与者那里获得的关于他们在演讲中展示的一些特征的平均反馈。tibble 的样子如下:![图 15.4 – 讲演者 tibble]()
图 15.4 – 讲演者 tibble
-
在调用
circular_grouped_barplot()函数之后,立即定义了内部的rescale100()函数,该函数用于将整个数据集(在我们的案例中是投票)的值缩放到 0 到 100 的范围内。 -
通过
enquo()函数传递给函数的参数执行被延迟(更多细节请参阅参考文献):grp_var <- enquo(grp_col_name) label_var <- enquo(label_col_name) value_var <- enquo(value_col_name) -
输入数据集的分类变量被转换成因子,并且通过之前定义的函数对值进行缩放,所有这些操作都使用了
tidy evaluation:data <- data %>% mutate( !!quo_name(grp_var) := as.factor(!!grp_var), !!quo_name(label_var) := as.factor(!!label_var), !!quo_name(value_var) := rescale100(!!value_var) ) -
为了在每个条形图组末尾分隔每个条形图组,添加了一些空条形图。首先,定义一个空条形图 DataFrame:
empty_bars <- 3 # Create the empty dataframe to add to the source dataframe data_to_add <- data.frame(matrix(NA, empty_bars * nlevels(data[[quo_name(grp_var)]]), ncol(data)) ) colnames(data_to_add) <- colnames(data) data_to_add[[quo_name(grp_var)]] <- rep(levels(data[[quo_name(grp_var)]]), each = empty_bars)它具有以下形状:
![图 15.5 – 空条形图 DataFrame]()
图 15.5 – 空条形图 DataFrame
-
然后,空 DataFrame 被添加到源 DataFrame(
data)中。一旦结果 DataFrame 按分组变量和值重新排序,空条形图的行将自动分布在每个组的末尾:data <- rbind(data, data_to_add) # Reorder data by groups and values data <- data %>% arrange(!!grp_var, !!value_var) -
添加了一个条形图标识符:
data$id <- seq(1, nrow(data))它用于计算每个标签必须显示的角度:
# Get the total number of bars number_of_bars <- nrow(data) # Subtract 0.5 from id because the label must have the angle of the center of the bars, # Not extreme right(1) or extreme left (0) angles_of_bars <- 90 - 360 * (data$id - 0.5) / number_of_bars -
我们从
dataDataFrame 开始定义标签 DataFrame,考虑到标签与条形的正确对齐以及它们必须具有的正确角度以便可读:label_data <- data label_data$hjust <- ifelse( angles_of_bars < -90, 1, 0) label_data$angle <- ifelse( angles_of_bars < -90, angles_of_bars + 180, angles_of_bars) -
定义了一个用于组基线的 DataFrame。它包含每个组的起始和结束 ID(条形),每个组的平均 ID(用作组文本标签的基准点),以及每个文本标签必须旋转的角度:
base_data <- data %>% group_by(!!grp_var) %>% summarize(start = min(id), end = max(id) - empty_bars) %>% rowwise() %>% mutate(title = floor(mean(c(start, end)))) %>% inner_join( label_data %>% select(id, angle), by = c('title' = 'id')) %>% mutate( angle = ifelse( (angle > 0 & angle <= 90) | (angle > 180 & angle <= 270), angle-90, angle+90 ) ) -
一个
if子句确定是否根据源数据集的内容最初定义一个只有一个组或多个组的条形图。在我们的例子中,由于有四个组,代码如下:p <- data %>% ggplot(aes_string(x = 'id', y = quo_name(value_var), fill = quo_name(grp_var))) + # Add a barplot geom_bar(stat = 'identity', alpha=0.5) + # Add 100/75/50/25 indicators geom_segment(data = grid_data, aes(x = end, y = 100, xend = start, yend = 100), colour = 'grey', alpha=1, size=0.3 , inherit.aes = FALSE ) + geom_segment(data = grid_data, aes(x = end, y = 80, xend = start, yend = 80), colour = 'grey', alpha=1, size=0.3 , inherit.aes = FALSE ) + geom_segment(data = grid_data, aes(x = end, y = 60, xend = start, yend = 60), colour = 'grey', alpha=1, size=0.3 , inherit.aes = FALSE ) + geom_segment(data = grid_data, aes(x = end, y = 40, xend = start, yend = 40), colour = 'grey', alpha=1, size=0.3 , inherit.aes = FALSE ) + geom_segment(data = grid_data, aes(x = end, y = 20, xend = start, yend = 20), colour = 'grey', alpha=1, size=0.3 , inherit.aes = FALSE ) + # Add text showing the value of each 100/75/50/25 lines annotate('text', x = rep(max(data$id), 5), y = c(20, 40, 60, 80, 100), label = c('20', '40', '60', '80', 100) , color='grey', size=3, angle=0, fontface='bold', hjust=1)条形图表示如下:
![图 15.6 – 条形图初稿的表示]()
图 15.6 – 条形图初稿的表示
-
在定义文本对齐向量后,之前的图表通过使用
coord_polar()函数被清理了不必要的图形装饰,并神奇地被包裹成圆形形状:p <- p + # The space between the x-axis and the lower edge of the figure will implicitly define the width of the empty circle inside the plot ylim(-120,120) + theme_minimal() + theme( legend.position = 'none', axis.text = element_blank(), axis.title = element_blank(), panel.grid = element_blank(), plot.margin = unit(rep(-1,4), 'cm') ) + # Wrap all in a circle! coord_polar()你将得到以下初稿:
![图 15.7 – 圆形条形图的初稿]()
图 15.7 – 圆形条形图的初稿
不幸的是,
coord_polar()函数不会旋转或弯曲标签。因此,你必须单独添加它们,并在需要时手动旋转。 -
最后,让我们添加适当旋转的条形标签、组基线和适当旋转的组文本标签:
p <- p + # Add labels geom_text(data = label_data %>% mutate( y_pos = !!value_var + 10), aes_string(x = 'id', y = 'y_pos', label = quo_name(label_var), hjust = 'hjust'), color = 'black', fontface = 'bold', alpha = 0.6, size = 3, angle = label_data$angle, inherit.aes = FALSE) + # Add base lines of groups geom_segment(data = base_data, aes(x = start, y = -5, xend = end, yend = -5), colour = 'black', alpha=0.8, size=0.6 , inherit.aes = FALSE ) + # Add groups text geom_text(data = base_data %>% mutate(y = -14), aes_string(x = 'title', y = 'y', label=quo_name(grp_var), angle = 'angle'), hjust = text_horiz_justification, colour = 'black', alpha=0.8, size=4, fontface='bold', inherit.aes = FALSE)结果可以在 图 15.3 中看到。
这只是利用图形语法制作复杂图表有效性的一个示例。正如你所看到的,这个框架允许你通过一次添加一个模块化逻辑块来逐步创建复杂图表,并通过直观测试结果。
重要提示
本节中使用的代码使用了
tidy evaluation的旧版本,与较旧的rlang、dplyr和ggplot2版本相关联。这种选择是由于 Power BI 服务使用较旧的 R 引擎版本(3.4.4)以及之前包的较旧版本所迫。从
rlang的 4.0 版本、ggplot2的 3.0.0 版本和dplyr的 1.0.0 版本开始,tidy evaluation所使用的语法已经简化并标准化。你可以在02-circular-grouped-barplot-new-tidy-eval.R文件中找到一个相同功能的示例,该示例为之前包的新版本绘制圆形条形图。
现在我们来看看如何在 Power BI 中实现圆形条形图。
在 Power BI 中实现圆形条形图
正如你在前面的章节中已经看到的,Power BI 能够使用 R 可视化渲染由 ggplot2 开发的图表。因此,无论使用 ggplot2 创建的图表有多复杂,你都可以确信 Power BI 能够很好地处理它。
在 Power BI 中创建圆形条形图,请按照以下步骤操作:
-
确保 Power BI 桌面版在选项中引用的是针对 R 视觉的 CRAN R 版本。
-
点击获取数据,然后点击文本/CSV,接着点击连接。
-
选择
Chapter15文件夹中找到的Scores.csv文件,然后点击打开。 -
你将看到 CSV 文件的预览。请确保选择
65001: Unicode (UTF-8)作为文件来源。这样,演讲者名称中的特殊字符将正确显示。然后,点击加载。 -
在可视化面板中点击R 视觉图标,启用它,然后将视觉调整到所有可用画布的大小。
-
在字段面板下展开选中的 R 视觉,并检查所有字段。
-
点击 R 视觉的格式选项卡,关闭标题。
-
将
01-circular-grouped-barplot.R文件的代码复制到Chapter15文件夹中,并将其粘贴到 R 视觉脚本编辑器中。然后,点击 R 脚本编辑器右上角的运行脚本箭头图标(每次请求时都启用 R 视觉)。你将得到一个圆形条形图。 -
首先在报告画布上的空白处点击,然后点击筛选器视觉图标。然后,在字段面板下展开
Scores表,并检查特征度量。 -
点击筛选器右上角的向下箭头以选择下拉筛选器类型。
-
调整筛选器的底部和右边框,将其移动到圆形条形图的中心,点击其格式选项,然后在选择控件下打开显示全选选项。
现在,你可以使用Ctrl键过滤多个特征,并且圆形条形图将相应更新:

图 15.8 – Power BI 中通过筛选器过滤的圆形条形图
非常令人印象深刻,对吧?通过稍微练习一下ggplot2和 R,你可以得到你想要的任何令人印象深刻的图表来丰富你的分析。
摘要
在本章中,你学习了如何使用 R 和ggplot2实现圆形条形图。你还首次体验了在重构实现圆形条形图功能的代码为函数时使用tidy evaluation。
之后,你在 Power BI 中实现了一个圆形条形图。
在下一章中,你将学习如何在 R 和 Power BI 中开发交互式自定义视觉。
参考文献
对于额外的阅读,请查看以下书籍和文章:
-
谁卖更多的武器? (
www.data-to-viz.com/story/OneNumOneCat.html) -
分组圆形条形图 (
www.r-graph-gallery.com/297-circular-barplot-with-groups.html) -
tidy eval 的兔子洞之旅 — 第一部分(旧的 tidy eval 方法) (
colinfay.me/tidyeval-1/)) -
整洁评估(新的整洁评估方式) (
tidyeval.tidyverse.org/))
第十七章:16 交互式 R 自定义视觉
在第十五章,高级可视化中,您了解到由于ggplot引入的灵活性,可以制作非常复杂的图表。然而,有时您可能会觉得由于缺乏交互性,如工具提示,您无法充分利用图表中显示的信息。在本章中,您将学习如何通过直接使用HTML 小部件将交互性引入使用 R 创建的自定义图形。以下是我们将要涵盖的主题:
-
为什么需要交互式 R 自定义视觉?
-
使用 Plotly 添加一点交互性
-
利用 HTML 小部件提供的交互性
-
将所有内容打包成 Power BI 自定义视觉
-
在 Power BI 中导入自定义视觉包
技术要求
本章要求您拥有一个正常工作的互联网连接,并在您的机器上安装Power BI 桌面版。您必须已按照第二章,配置 Power BI 中的 R和第三章,配置 Power BI 中的 Python中概述的方式正确配置了 R 和 Python 引擎以及 IDE。
为什么需要交互式 R 自定义视觉?
让我们从您已经在 R 中实现的图形开始。例如,考虑第十四章,探索性数据分析中引入的Fare与Pclass变量的 raincloud 图:

图 16.1 – 票价(转换后)和 Pclass 变量的 Raincloud 图
请暂时只关注您在图 16.1中看到的箱线图。尽管Fare变量已经根据 Yeo-Johnson 进行了转换以尝试减少偏度,但根据分类变量Pclass描述的每个乘客类别仍然存在一些极端的异常值。例如,如果您想了解与左侧箱线图的胡须(围栏)对应的转换变量Fare的值,以便您然后确定位于这些胡须之外的异常值,那么当您将鼠标移至该箱线图附近时,这些值出现将会很方便,就像图 16.2中所示:

图 16.2 – 在第一类的票价(转换后)箱线图中显示的主要标签
当您将鼠标悬停在表示特定孤立异常值的点上时,了解其实际值将会更有趣,就像图 16.3中所示:

图 16.3 – 高亮显示的异常值对应的票价(转换后)和 Pclass 的值
毫无疑问,如果引入这些交互式功能,分析图表的读者将会欢迎。
因此,让我们看看如何将这些交互式功能添加到使用 ggplot 开发的现有图形中。
使用 Plotly 添加一点交互性
这是一个开源的 JavaScript 数据可视化库,它是声明性和高级的,允许您创建数十种交互式图表,名为 Plotly.js。这个库是其他 Plotly 客户端库的核心,为 Python、Scala、R 和 ggplot 开发。特别是,为 R 开发的库,名为 Plotly.R (github.com/ropensci/plotly),提供了 ggplotly() 函数,为我们完成了所有的魔法:它检测了使用 ggplot 开发的现有图形中包含的所有基本属性,并将它们转换为交互式网络可视化。让我们看一个例子。
首先,您需要通过 install.packages('plotly') 脚本在您最新的 CRAN R 引擎上安装 Plotly.R 库(github.com/ropensci/plotly)。
重要提示
为了简单起见,我们将确保在最新的 CRAN R 引擎上运行自定义视觉,因为所有必要的库已经在之前的章节中安装了。如果目标是发布带有自定义视觉的报告到 Power BI,您必须确保自定义视觉在 Power BI 上(在我们的案例中,CRAN R 3.4.4)正确渲染。
然后,运行您可以在 Chapter16 文件夹中的 01-interactive-boxplots.R 文件中找到的脚本。脚本的内容是从 第十四章,数据探索 中使用的各种脚本中提取的,所以对您来说没有什么新的。您之前没有见过的脚本部分如下:
plotly::ggplotly(rc, tooltip = c('x', 'y'))
这是将静态图形转换为基于 HTML 的动态图形的代码部分,如您所见,它是对 ggplotly() 函数的一个简单调用。结果如下:

图 16.4 – 将 ggplotly() 函数应用于 raincloud 图的结果
如您所见,在 RStudio 中,结果不再显示在 Plots 选项卡中,而是在 Viewer 选项卡中显示,该选项卡专门用于 HTML 输出。您还会注意到右上角存在 Modebar,它允许您对图表设置一些操作,例如缩放和悬停选项。
但最引人注目的是,您没有看到一系列如预期中的 raincloud 图,而是简单的箱线图!如果您查看 RStudio 控制台,您会注意到有三个相同的警告信息,每个信息对应一个本应表示的 raincloud 图:
In geom2trace.default(dots[[1L]][[3L]], dots[[2L]][[1L]], dots[[3L]][[1L]]) :
geom_GeomSlabinterval() has yet to be implemented in plotly.
If you'd like to see this geom implemented,
Please open an issue with your example code at
https://github.com/ropensci/plotly/issues
你可能已经注意到,只有密度图消失了。这意味着在 Plotly 当前版本(4.9.4.1)中,由ggdist库创建的对象尚未得到管理。此外,ggplotly()将填充和颜色美学渲染为不同的,这与ggplot静态图形不同。当然,这项功能仍需改进,而且很可能会在未来版本中解决这些错误。无论如何,图形仍然可用,并展示了如何轻松地将交互性添加到 ggplot 图形中。
到目前为止,你可能认为任何用 HTML 和 JavaScript 制作的图表都可以在 Power BI 中使用。显然,情况并非如此。
重要提示
在 Power BI 中使用的每个交互式图形都必须是HTML 小部件。
事实上,Plotly.R 通过 HTML 小部件暴露图形。让我们看看这是怎么回事。
利用 HTML 小部件提供的交互性
HTML 小部件是允许你构建交互式网页的 R 包。这些包由一个框架生成,该框架用于在 R 和 JavaScript 库之间创建绑定。这个框架由 RStudio 开发的htmlwidgets包提供。HTML 小部件始终托管在 R 包中,包括源代码和依赖项,以确保即使无法访问互联网,小部件也能完全可重复。有关如何从头开始开发 HTML 小部件的更多详细信息,请参阅参考资料。
除了能够在RMarkdown文件(带有 R 的动态文档)或Shiny应用程序(直接从 R 构建的交互式 Web 应用程序)中嵌入 HTML 小部件之外,htmlwidgets包还允许你通过saveWidget()函数将它们保存为独立的网页文件。
话虽如此,有成百上千的 R 包通过 HTML 小部件暴露其功能。你可以通过htmlwidgets 图库(gallery.htmlwidgets.org/)来搜索可能适合你的交互式图形。
好吧,在 Power BI 中可视化这些 HTML 小部件是可能的,无论是图库中的还是通过 Plotly 制作的,这听起来不错。但如何将用 HTML 和 JavaScript 制作的动态图形嵌入到 Power BI 中呢?你必须通过pbiviz 工具编译一个由 R 驱动的视觉。
让我们看看如何操作。
将所有内容打包成 Power BI 自定义视觉
Power BI 视觉工具(pbiviz)是构建 Power BI 中定制视觉元素的最简单方法。它们是用 JavaScript(使用 Node.js)编写的,并用于编译 .pbiviz 软件包的源代码。一个 .pbiviz 软件包是 Power BI 视觉项目的压缩版本,而 Power BI 视觉项目又是一组文件夹、脚本和资产,用于创建你想要实现的定制可视化。通常,标准的 Power BI 视觉项目是通过 pbiviz 命令行工具从模板创建的。模板内容取决于你想要创建定制视觉的方法(TypeScript、R Visual 或 R HTML)。
重要提示
pbiviz 工具不支持任何在后台使用 Python 的技术,例如 ipywidget 小部件。
在这种情况下,为了能够使用 R Visual 和 R HTML 模式开发有趣的定制视觉元素,值得稍微学习 R 和 ggplot。此外,如 第二章,配置 Power BI 中的 R 的结尾所述,请注意以下事项。
重要提示
Power BI 中的 发布到 Web 选项不允许你发布包含标准 R 视觉元素的报告。你可以通过创建允许与该选项一起发布的 R 定制视觉来绕过这个限制。
现在我们来看看如何安装 pbiviz 工具。
安装 pbiviz 软件包
pbiviz 命令行工具提供了你在 Power BI 服务上开发视觉元素和测试报告和仪表板所需的一切。正因为如此,你需要安装一个 SSL 证书,以便你的笔记本电脑可以安全地与 Power BI 服务交互。
让我们一步一步地看看如何做:
-
前往
nodejs.org/en/并安装为所有用户推荐的 Node.js 版本,按照安装程序的默认选项进行。 -
重新启动你的笔记本电脑,因为这是 步骤 4 命令正常工作的必要条件。
-
点击 Windows 开始 按钮,开始输入字符串
“power”,然后点击 Windows PowerShell 应用程序。 -
在 PowerShell 控制台中输入以下命令:
npm i -g powerbi-visuals-tools。如果你收到一些弃用警告,不要担心,等待安装完成。 -
在 PowerShell 控制台中输入以下命令:
pbiviz --install-cert。它返回一个位置,其中创建了 PFX 文件(应该是C:\Users\<你的用户名>\AppData\Roaming\npm\node_modules\powerbi-visuals-tools\certs)和一个数字密码。注意这两个,因为你稍后需要它们。 -
按 Win+R 并在 打开 文本框中输入
mmc。它将打开 Microsoft 管理控制台。 -
点击 文件,然后点击 添加/删除插件…。
-
选择 证书,然后点击 添加,接着选择 我的用户帐户 并点击 完成。
-
在 添加或删除插件 窗口中点击 确定。
-
在主窗口中,展开证书节点和受信任根证书颁发机构节点,选择此节点下的证书。您将在中间面板中看到所有证书的列表。在右侧的操作面板上,点击更多操作,选择所有任务…,然后点击导入…。
-
在证书导入向导的欢迎窗口中点击下一步。在下一个窗口中点击浏览,导航到您在步骤 5中记录的位置。从文件名文本框附近的文件类型组合框中选择个人信息交换 (*.pfx, *.p12)。然后会出现
PowerBICustomVisualTest_public.pfx文件。选择它并点击打开。然后点击下一步。 -
输入您在步骤 5中记录的数字密码,保留默认的导入选项,并点击下一步。
-
将受信任根证书颁发机构作为证书存储,点击确定。在主窗口中点击下一步,然后点击完成。您将看到一个安全警告窗口询问您是否要安装证书。点击是,将出现一个导入成功对话框。点击确定。
-
为了验证一切是否顺利,返回到 PowerShell 控制台,输入
pbiviz命令并按Enter。您应该看到以下输出:

图 16.5 – pbiviz 已正确安装
太好了!现在您已经正确配置了 pbiviz 工具,并准备好编译自定义视觉。
让我们现在用 R HTML 自定义视觉来测试它们。
开发您的第一个 R HTML 自定义视觉
为了创建一个 R HTML 自定义视觉,您必须首先从 pbiviz 工具提供的模板开始生成一个标准的 Power BI Visual 项目,类型为 R HTML。然后,您只需修改项目提供的脚本,以创建您想要开发的视觉。
在本节中,您将包装在前一节中遇到的动态箱线图图形。
获取.pbiviz包的步骤如下:
-
如果您上次使用后已关闭,请从开始菜单打开Windows PowerShell控制台。控制台将默认启动的文件夹是
C:\Usersers\<您的用户名>。 -
使用
md Power-BI-Custom-Visuals命令创建一个名为Power-BI-Custom-Visuals的文件夹,专门用于自定义视觉。 -
使用
cd Power-BI-Custom-Visuals命令进入您刚刚创建的文件夹。 -
使用
pbiviz new interactiveboxplots -t rhtml命令从模板生成一个标准的 R HTML Power BI Visual 项目。 -
在 VS Code 中打开您刚刚创建的模板文件夹。您会看到如下内容:
![图 16.6 – VS Code 中交互式箱线图文件夹的内容视图]()
图 16.6 – VS Code 中交互式箱线图文件夹的内容视图
您将在 GitHub 仓库的
Chapter16\PBI-Visual-Project\interactiveboxplots文件夹中找到一个完整的、准备好编译的项目。您可以用它作为下一步的参考。 -
打开您刚刚创建的模板的
pbiviz.json文件,以输入您将要编译的自定义视觉的基本信息。请正确格式化(右键点击文档并点击 格式文档)。在 图 16.7 和随后的图中,您可以在左侧找到模板代码的一部分,在右侧,您可以找到应该如何修改:![图 16.7 – 在 VS Code 中编辑 pbiviz.json 文件内容]()
图 16.7 – 在 VS Code 中编辑 pbiviz.json 文件内容
下面是编辑
"visual"节点属性详情:-
"name": <您的自定义视觉名称> -
"displayName": <显示的自定义视觉名称> -
"description": <自定义视觉的描述> -
"supportUrl": <支持联系网址>
下面是编辑
"author"节点属性详情:-
"name": <作者的全名> -
"email": <作者的电子邮件>
-
-
现在打开
capabilities.json文件。它用于声明可视化接受的哪些数据类型,在属性面板中放置哪些可自定义属性,以及其他创建可视化所需的信息。它包含几个根对象。您需要编辑的第一个是dataRoles(bit.ly/pbiviz-dataroles)。在这个部分,您可以定义您的视觉期望的数据字段。默认模板只有唯一的Values字段作为默认值,就像标准的 R Visual 一样。在我们的案例中,多元方差箱线图视觉需要三个字段:![图 16.8 – 编辑 capabilities.json 文件中的 dataRoles 部分]()
图 16.8 – 编辑 capabilities.json 文件中的 dataRoles 部分
-
根据在
dataRoles中添加的项目,您必须相应地更改capabilities.json文件的dataViewMappings根对象内容(bit.ly/pbiviz-dataviewmappings)。它们描述了数据角色之间的关系,并允许您指定数据可视化的条件要求。在我们的案例中,我们需要声明在dataRoles中创建的三个字段作为脚本输入数据集的组成部分:![图 16.9 – 编辑 capabilities.json 文件中的 dataViewMappings 部分]()
图 16.9 – 编辑 capabilities.json 文件中的 dataViewMappings 部分
如您所见,模板中引用的
“script”子部分指的是rcv_script对象。我们将在下一节中看到它是如何定义的。 -
capabilities.json文件的objects部分描述了与可视化关联并可出现在格式面板中的可自定义属性(bit.ly/pbiviz-objects)。在我们的情况下,我们想要参数化可以应用于变量 y 的转换类型。因此,我们将确保用户可以从位于常规部分下方变量设置部分的Y 转换类型组合框中选择转换类型:![图 16.10 – 将自定义参数添加到格式面板]()
图 16.10 – 将自定义参数添加到格式面板
实现如图 16.10 所示效果的脚本更改如下:
![图 16.11 – 编辑文件的对象部分]()
图 16.11 – 编辑
capabilities.json文件的对象部分suppressDefaultTitle参数允许你抑制通常出现在每个 R 可视化左上角的标题。正如你在图 16.11 中看到的那样,dataViewMappings部分中引用的rcv_script对象在此部分定义。与刚刚添加的不同,rcv_script对象不应在格式面板中显示,而仅用于描述定义 R 脚本的source和provider对象的属性。为了实际上声明要在格式面板中显示的参数,你需要对settings.ts文件进行一些小的修改。让我们看看如何做。 -
打开位于 Power BI Visual 项目
src文件夹中的settings.ts文件。它包含要在你的可视化中显示的元素的 TypeScript 设置。我们不是显示rcv_script对象,而是显示包含与要对 y 变量应用转换类型关联的参数的settings_variables_params对象:![图 16.12 – 编辑文件]()
图 16.12 – 编辑
settings.ts文件关于此脚本中使用的类的更多详细信息,请参阅参考资料。
-
打开
dependencies.json文件。它包含了对生成可视化的 R 代码中使用的每个库的引用。除了已经存在的(ggplot2、plotly、htmlwidgets和xml2)之外,你还需要添加以下库:RColorBrewer、cowplot、dplyr、purrr、forcats和recipes。只需遵循现有库已使用的语法,同时记住你可以在displayName中放入任何字符串。 -
最后,您可以在
script.r文件中输入生成视觉的 R 代码。您可以用在 GitHub 仓库中共享的 Power BI 视觉项目中的同名文件内容替换其全部内容。在脚本的开头,您会找到一些用于在 RStudio 中调试任何问题的注释行。然后是一个source()命令,它将从r_files文件夹中提供的flatten_HTML.r文件加载实用函数。它们有助于将 Plotly 或小部件对象转换为自包含的 HTML。接下来的代码与您在前面章节中看到的非常相似。这里集成了处理传递给视觉作为输入数据的字段和用于处理变量 y 转换类型的参数的代码片段。以下是一个示例:y_transf_name <- 'standard' if(exists("settings_variable_params_y_transf_name")){ y_transf_name <- as.character(settings_variable_params_y_transf_name) }settings_variable_params_y_transf_name变量名由包含该参数的节名称和参数本身的名称的并集给出。最后,在脚本末尾有两段代码。一段用于从 Plotly Modebar 中删除一些图标:disabledButtonsList <- list( 'toImage', 'sendDataToCloud', 'zoom2d', 'pan', 'pan2d', 'select2d', 'lasso2d', 'hoverClosestCartesian', 'hoverCompareCartesian') p$x$config$modeBarButtonsToRemove = disabledButtonsList p <- config(p, staticPlot = FALSE, editable = FALSE, sendData = FALSE, showLink = FALSE, displaylogo = FALSE, collaborate = FALSE, cloud=FALSE)另一个是针对 Plotly 错误的解决方案,即使传递了
outlier.shape = NA参数给geom_boxplot(),它也会显示箱线图的异常值:hideOutliers <- function(x) { if (x$hoverinfo == 'y') { x$marker = list(opacity = 0) x$hoverinfo = NA } return(x) } p[["x"]][["data"]] <- purrr::map(p[["x"]][["data"]], ~ hideOutliers(.))最后,
internalSaveWidget(p, 'out.html')命令使用脚本开头加载的其中一个实用函数来生成一个自包含的 HTML 格式的扁平化视觉,并以标准名称out.html正确管理,由 Power BI 管理。最后的命令调用了ReadFullFileReplaceString()函数。它允许您替换由代码生成的out.html文件中的字符串,以便修改由 Plotly 生成的默认配置。具体来说,这里使用的命令纠正了生成的 HTML 小部件的填充设置。 -
现在回到 Windows PowerShell 控制台,确保您位于
Power-BI-Custom-Visuals\interactiveboxplots文件夹中。如果您在Power-BI-Custom-Visuals文件夹中,只需使用cd interactiveboxplots命令。然后,输入pbiviz package命令来编译包含您的自定义视觉的.pbiviz 包。在 pbiviz 工具的编译操作结束时,您将看到类似以下内容:

图 16.13 – 自定义视觉编译成功
干得非常好!您已使用 pbiviz 工具编译了您的第一个 R HTML 自定义视觉。好吧,编译好的包在哪里?别担心,查看您的 Power BI 视觉项目中的dist文件夹。

图 16.14 – 您刚刚编译的.pbiviz 包
就在这里!现在让我们将其导入 Power BI。
将自定义视觉包导入 Power BI
现在大部分工作已经完成,将自定义视觉导入 Power BI 就变得非常简单。首先,您需要在 R 引擎中安装 xml2 包,因为它被提供的实用函数所使用:
-
打开 RStudio 并确保它引用的是您最新的 CRAN R(在我们的案例中是版本 4.0.2)。
-
点击 控制台 窗口并输入以下命令:
install.packages('xml2')。如果您记得,这个库在之前章节中看到的依赖文件中列出。然后,按 Enter。
现在让我们将自定义视觉导入 Power BI:
-
确保 Power BI Desktop 在 选项 中引用的是正确的 R 引擎(最新的一个)。
-
点击 获取数据,搜索
web,选择 Web,然后点击 连接。 -
输入以下 URL 作为源:
bit.ly/titanic-dataset-csv。然后按 确定。 -
确保文件源是 65001: Unicode (UTF-8) 并按 加载。
-
点击 视觉 面板下的省略号,然后点击 从文件导入视觉:
![图 16.15 – 从文件导入自定义视觉]()
图 16.15 – 从文件导入自定义视觉
-
在下一个打开的窗口中,移动到以下文件夹:
C:\Users\<your-username>\Power-BI-Custom-Visuals\interactiveboxplots\dist。然后选择您的.pbiviz包并点击 打开。在下一个对话框中点击 确定。 -
如您所见,在 视觉 面板上出现了一个新的图标:
![图 16.16 – 从文件导入自定义视觉]()
图 16.16 – 从文件导入自定义视觉
请记住,如果您想为您的视觉使用自定义图标,只需在编译前用
assets文件夹中的icon.png文件替换它。点击它以将您的自定义视觉添加到报告画布上。然后在下一个对话框中点击 启用。 -
扩大自定义视觉区域,然后在 字段 面板上展开 titanic-dataset-csv 表,首先检查 舱等 字段,然后是 票价 字段:
![图 16.17 – 选择舱等和票价字段作为 X 和 Y 变量]()
图 16.17 – 选择舱等和票价字段作为 X 和 Y 变量
查看您的自定义视觉。您将看到类似以下内容:
![图 16.18 – 显示票价与舱等对比的箱线图的自定义视觉]()
图 16.18 – 显示票价与舱等对比的箱线图的自定义视觉
-
现在点击视觉的 格式 图标,展开 变量设置 部分,并选择 Yeo-Johnson 作为 Y 变换类型:
![图 16.19 – 选择 Yeo-Johnson 作为 Y 变换类型]()
图 16.19 – 选择 Yeo-Johnson 作为 Y 变换类型
现在查看您的自定义视觉。您将看到类似以下内容:
![图 16.20 – 显示票价(转换后)与舱等对比的箱线图的自定义视觉]()
图 16.20 – 显示票价(转换后)与舱等对比的箱线图的自定义视觉
-
现在回到 titanic-dataset-csv 表格上的 字段 面板,检查 Sex 字段(它将与视觉的 分组变量 关联),然后再看看这个视觉。它将如下所示:

图 16.20 – 您的自定义可视化,显示票价(转换后)与 Pclass 按性别分组的箱线图
真的很令人印象深刻!您的自定义交互式可视化效果棒极了,而且工作得非常好!
摘要
在本章中,我们学习了在某些情况下使用交互式可视化相对于静态可视化的优势。我们学习了如何通过 Plotly 为使用 Ggplot 开发的图表添加一些基本交互性。
我们了解到,在 Power BI 上制作交互式视觉的关键在于它们基于 HTML 小部件。因此,我们通过 pbiviz 工具逐步实现了自定义可视化。
最后,我们将编译好的包导入 Power BI 以测试其功能。至此,本书的内容就结束了。希望这次旅程是富有成效且令人满意的!
参考文献
对于进一步阅读,请参阅以下书籍和文章:
-
Plotly R 开源绘图库 (
plotly.com/r/) -
[课程] R: 使用 htmlwidgets 进行交互式可视化 (
www.linkedin.com/learning/r-interactive-visualizations-with-htmlwidgets/welcome) -
Power BI 可视化项目结构 (
docs.microsoft.com/en-us/power-bi/developer/visuals/visual-project-structure) -
pbiviz.json 文件中使用的模式 (
github.com/microsoft/PowerBI-visuals-tools/blob/main/templates/visuals/.api/v1.13.0/schema.pbiviz.json) -
capabilities.json 文件中使用的模式 (
github.com/microsoft/PowerBI-visuals-tools/blob/main/templates/visuals/.api/v1.13.0/schema.capabilities.json) -
Power BI 自定义可视化第五部分 – 格式化 (
shetland.azurewebsites.net/2021/02/18/power-bi-custom-visual-part-5-formatting/)































































































































































































































浙公网安备 33010602011771号