SQLServer2017-与-R-机器学习服务-全-

SQLServer2017 与 R 机器学习服务(全)

原文:annas-archive.org/md5/bdd1eee2028e59d46863473336d2f170

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

自 SQL Server 2016 以来,SQL Server 已支持机器学习功能。之前称为 SQL Server 2016 R 服务,SQL Server 2017 机器学习服务提供两种版本,R 和 Python。本书为数据专业人士、数据库管理员和数据科学家提供实际参考和学习材料,介绍如何使用 SQL Server 和 R 安装、开发、部署、维护和管理数据科学和高级分析解决方案。无论您是 SQL Server 的新手,还是经验丰富的 SQL Server 专业人士,使用 R 的 SQL Server 机器学习服务实践指南包含实用的解释、技巧和示例,帮助您充分利用将分析保持靠近数据以实现更好的效率和安全性。

本书面向对象

本书面向有一定或无 R 语言经验的数据库分析师、数据科学家和数据库管理员,他们渴望在日常工作或未来的项目中轻松地提供实用的数据科学解决方案。

本书涵盖内容

第一章,R 和 SQL Server 简介,开启我们在 SQL Server 中的数据科学之旅,从 SQL Server 2016 之前开始,并引领我们到今天 SQL Server R 集成。

第二章,微软机器学习服务器和 SQL Server 概述,简要概述了微软机器学习服务器,重点介绍了 SQL Server 机器学习服务,同时探讨了其工作原理和不同版本的 R 环境。这包括对其架构、不同的计算环境、系统间集成方式以及如何实现并行性和负载分配的关键讨论。

第三章,管理 SQL Server 2017 和 R 的机器学习服务,涵盖了安装和设置,包括如何使用 PowerShell。它涵盖了探索资源管理器的功能,为用户设置角色和安全权限以使用 SQL Server 机器学习服务与 R 协同工作,处理会话和日志,安装任何缺失或额外的 R 包用于数据分析或预测建模,以及使用sp_execute_external_script外部过程的第一步。

第四章,数据探索与数据可视化,探讨了 R 语法在数据浏览、分析、整理和可视化以及预测分析中的应用。掌握这些技术对于本章及本书后续章节所涵盖的下一步至关重要。本章介绍了各种用于可视化和预测建模的有用 R 包。此外,读者还将学习如何将 R 与 Power BI、SQL Server Reporting Services(SSRS)和移动报表集成。

第五章,《RevoScaleR 包》,讨论了使用 RevoScaleR 在大型数据集上进行可扩展和分布式统计计算的优势。使用 RevoScaleR 可以提高 CPU 和 RAM 的利用率,并提高性能。本章介绍了 RevoScaleR 在数据准备、描述性统计、统计测试和抽样方面的功能,以及预测建模。

第六章,《预测建模》,专注于帮助那些第一次进入预测建模世界的读者。使用 SQL Server 和 SQL Server 机器学习服务中的 R,读者将学习如何创建预测、执行数据建模、探索 RevoScaleR 和其他包中可用的高级预测算法,以及如何轻松部署模型和解决方案。最后,调用和运行预测并将结果暴露给不同的专有工具(如 Power BI、Excel 和 SSRS)完成了预测建模的世界。

第七章,《R 代码的运营化》,提供了在运营化 R 代码和 R 预测方面的技巧和窍门。读者将了解到稳定和可靠的过程流程对于在生产中将 R 代码、持久数据和预测模型结合在一起的重要性。在本章中,读者将有机会探索采用现有和创建新的 R 代码的方法,然后通过 SQL Server Management Studio (SSMS)和 Visual Studio 等各种现成的客户端工具将其集成到 SQL Server 中。此外,本章还涵盖了读者如何使用 SQL Server Agent 作业、存储过程、CLR 与.NET 以及 PowerShell 来产品化 R 代码。

第八章,《部署、管理和监控包含 R 代码的数据库解决方案》,介绍了在集成 R 代码时如何管理数据库部署的部署和变更控制。本章提供了如何进行解决方案的集成部署以及如何实施持续集成,包括自动化部署和管理版本控制的方法。在这里,读者将学习到监控解决方案、监控代码的有效性和部署后的预测模型的有效方法。

第九章,《使用 R 为数据库管理员提供的机器学习服务》,探讨了数据库管理员日常、每周和每月任务中的监控、性能和故障排除。通过简单的示例说明 R 服务也可以对 SQL Server 中涉及的其他角色有用,本章展示了如何将 R 服务集成到 SQL Server 中,使数据库管理员能够通过将基本的监控活动转变为更有用的可执行预测来获得更多权力。

第十章,R 和 SQL Server 2016/2017 功能扩展,介绍了如何将 SQL Server 2016 和 2017 的新特性和 R 服务结合使用,例如利用 R 语言的新 JSON 格式,使用内存 OLTP 技术的新改进来提供几乎实时的分析,结合列存储索引和 R 的新功能,以及如何充分利用它们。它还考虑了如何利用 PolyBase 和 Stretch DB 超越本地,达到混合和云的可能性。最后,查询存储包含执行计划的大量统计数据,而 R 是进行更深入分析的理想工具。

为了充分利用本书

为了使用 SQL Server 机器学习服务,并运行本书中的代码示例,您将需要以下软件:

  • SQL Server 2016 和/或 SQL Server 2017 开发者或企业版

  • SQL Server Management Studio (SSMS)

  • R IDE,如 R Studio 或带有 RTVS 扩展的 Visual Studio 2017

  • 安装以下扩展的 Visual Studio 2017 社区版:

    • R Tools for Visual Studio (RTVS)

    • SQL Server 数据工具 (SSDT)

  • VisualStudio.com 在线账户

本书中的章节在介绍软件时将介绍安装和配置步骤。

下载示例代码文件

您可以从 www.packtpub.com 的账户下载本书的示例代码文件。如果您在其他地方购买了此书,您可以访问 www.packtpub.com/support 并注册,以便将文件直接通过电子邮件发送给您。

您可以通过以下步骤下载代码文件:

  1. www.packtpub.com 登录或注册。

  2. 选择“支持”选项卡。

  3. 点击“代码下载与勘误”。

  4. 在搜索框中输入书籍名称,并按照屏幕上的说明操作。

文件下载完成后,请确保使用最新版本解压或提取文件夹:

  • Windows 下的 WinRAR/7-Zip

  • Mac 下的 Zipeg/iZip/UnRarX

  • Linux 下的 7-Zip/PeaZip

本书代码包也托管在 GitHub 上,网址为 github.com/PacktPublishing/SQL-Server-2017-Machine-Learning-Services-with-R。我们还有其他来自我们丰富图书和视频目录的代码包,可在 github.com/PacktPublishing/ 上找到。查看它们!

下载彩色图像

我们还提供包含本书中使用的截图/图表彩色图像的 PDF 文件。您可以从这里下载:www.packtpub.com/sites/default/files/downloads/SQLServer2017MachineLearningServiceswithR_ColorImages.pdf

使用的约定

本书使用了多种文本约定。

CodeInText: 表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“为了计算交叉表——两个(或更多)变量之间的关系——我们将使用两个函数:rxCrossTabsrxMargins。”

代码块设置如下:

> df <- data.frame(unlist(var_info)) 
> df 

任何命令行输入或输出都应如下编写:

EXECsp_execute_external_script
          @language = N'R'
          ,@script = N'
                      library(RevoScaleR)
    df_sql <- InputDataSet 
                      var_info <- rxGetInfo(df_sql)
                      OutputDataSet <- data.frame(unlist(var_info))'
                      ,@input_data_1 = N'
    SELECT 
                       BusinessEntityID
                      ,[Name]
                      ,SalesPersonID
                      FROM [Sales].[Store]'

粗体: 表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词在文本中显示如下。以下是一个示例:“你可以始终检查外部脚本启用的run_value,如果它设置为 1。”

警告或重要注意事项如下所示。

技巧和窍门如下所示。

联系我们

我们始终欢迎读者的反馈。

一般反馈: 请通过feedback@packtpub.com发送电子邮件,并在邮件主题中提及书籍标题。如果您对本书的任何方面有疑问,请通过questions@packtpub.com发送电子邮件给我们。

勘误: 尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将不胜感激,如果您能向我们报告这一点。请访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。

盗版: 如果你在互联网上以任何形式遇到我们作品的非法副本,如果你能提供位置地址或网站名称,我们将不胜感激。请通过copyright@packtpub.com与我们联系,并附上材料的链接。

如果您有兴趣成为作者: 如果您在某个主题上具有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com

评论

请留下评论。一旦您阅读并使用过这本书,为什么不在此购买书籍的网站上留下评论呢?潜在读者可以查看并使用您的客观意见来做出购买决定,Packt 公司可以了解您对我们产品的看法,我们的作者也可以看到他们对书籍的反馈。谢谢!

有关 Packt 的更多信息,请访问packtpub.com

第一章:R 和 SQL Server 简介

SQL Server 2016 带来了许多新功能,其中之一是将 R 集成到 SQL Server 中,部分是为了高级分析,部分是为了新的可编程能力。Microsoft R Services for SQL Server 是新扩展性家族的一部分,旨在实现高度可扩展和并行的高级分析。R Services 允许您在数据库中存储的大量数据上执行高级分析(统计、多元统计、预测分析、机器学习和深度学习)。Microsoft 将 R Services 作为Microsoft R ServerMRS)的一部分发布,该服务器专门设计用于在相同的 SQL Server 计算环境中直接从 SQL Server 数据库读取数据。

在本章中,我们将涵盖以下方面:

  • 在 SQL Server 2016 之前使用 R

  • 微软对开源 R 语言的承诺

  • 利用 SQL Server R 集成增强分析

  • 本书概要

在 SQL Server 2016 之前使用 R

R 语言自 90 年代以来就在社区中(尽管它是在十年前开发的)。凭借其开源 GNU 许可证,R 因其无需麻烦的安装和能够调用任何可用的包以提供额外的统计学习功能而受到欢迎。这对于 R 来说是一个明显的优势,因为在 80 年代和 90 年代,市场上可用的统计程序并不多;此外,大多数都不是免费的。与核心 R 引擎的新包的扩展性为更广泛的社区和用户提供了更多使用 R 语言的多用途能力,除了其强大的统计分析能力和预测建模能力。

SQL Server 2005 引入了SQL Server Analysis ServicesSSAS)数据挖掘功能,以便应用于存储在 SQL Server 和 SSAS OLAP 立方体中的客户现有丰富数据。此功能允许用户使用数据挖掘表达式DMX)来创建预测查询。在接下来的几年里,在 SQL 论坛、博客和社区网站上出现了许多关于额外统计和预测方法和方法的疑问、请求和想法。

回到 2011 年,我开始着手扩展 SQL Server 2008 R2 中统计分析功能的想法,这得益于开源 R 语言。做出这一决定的一个原因是希望能够灵活地运行统计分析(从数据提供到多元分析),而无需首先将数据输入到 OLAP 立方体中,另一个原因是更偏向于商业,需要从所有参与数据准备、数据整理和数据清洗的人员那里获得更快、更深入的统计洞察。

我开始着手构建一个基于 T-SQL 存储过程和 R 包 RODBC(cran.r-project.org/web/packages/RODBC)的框架。这个想法很简单;获取事务或 OLAP 数据,选择你想要进行分析的列,以及分析本身(从简单的预测分析到超越 SSAS、T-SQL 或 CLR 能力的分析):

图片

图 1:框架的处理流程

这个框架远非简单,调用该过程相当于调用 R 代码、T-SQL 选择语句和配置 R 引擎的混合体。

包含所有参数的存储过程看起来是这样的:

EXECUTE AdventureWorks2012.dbo.sp_getStatistics
 @TargetTable = '[vStoreWithAddresses]'
 ,@Variables = 'Name'
 ,@Statistics = '8'
 ,@ServerName = 'WORKSTATION-31'
 ,@DatabaseName = 'AdventureWorks2012'
            ,@WorkingDirectory = 'C:\DataTK'
            ,@RPath = 'C:\Program Files\R\R-3.0.3\bin'; 

对于这个框架的“螺丝和螺母”解释超出了本书的范围,并且已在www.sqlservercentral.com/articles/R+Language/106760/上进行了很好的记录。

回顾这个框架以及社区和论坛上人们的反馈,它得到了积极的接受,许多人评论说他们需要类似的东西来处理日常业务。

总体而言,这个框架除了开创性的想法和将 R 引擎更近一步地引入 SQL Server 之外,还有很多缺陷。最主要的是安全问题。因为它需要访问一个工作本地目录来生成由 vanilla R 引擎运行的 R 文件,所以它需要启用xp_cmdshell。以下重新配置是强制性的,许多系统管理员可能不会同意:

EXECUTE SP_CONFIGURE 'xp_cmdshell', 1;
GO
RECONFIGURE;
GO

EXECUTE SP_CONFIGURE 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO  

此外,框架需要访问 R 引擎安装,以及执行所需代码的 R 包。安装开源程序并提供读写访问在安全性和企业软件决策方面又是一个缺点。尽管如此,更大的问题之一——在所有东西都安装并投入生产后——是性能和内存问题。R 是基于内存的,这意味着所有的计算都是在内存中完成的。所以,如果你的数据集大小超过了可用内存的大小,你将得到的唯一结果将是错误信息。性能问题的另一个方面也是速度。由于没有并行和分布式计算,框架的性能取决于包作者的灵活性。例如,如果包是用 C 或 C++编写的,而不是 Fortran,那么框架的性能会更好。

这个框架的亮点在于能够更快地提供统计分析或预测建模的结果,因为它可以处理需要统计分析的 OLTP 或任何其他数据。此外,统计学家和数据科学家可以准备存储在表中的 R 代码,这些代码随后由数据整理者、数据分析师或数据管理员运行。因此,可以保持一个版本的真相,因为没有必要移动或复制数据,所有用户都在读取相同的数据源。在预测建模方面,该框架还使用户能够利用各种额外的预测算法(例如,决策森林、glm、CNN、SVM 和词云)的优势,这些算法当时并不属于 SSAS 数据挖掘。

除了优点和缺点之外,该框架是成功尝试获取更多易于在不同业务单元之间分发的数据洞察的初步尝试,通过在 SQL Server 报告服务中推送可视化。在 SQL Server 2016 发布之前的那几年里,我遇到了来自 SQL Server 社区的开发类似框架的人,目的是将预测推送到 SQL Server 数据库以支持业务应用和解决方案。随着 SQL Server 2016 的推出,许多类似的解决方案被内部化,并更接近 SQL Server 引擎以实现更好的性能,并解决许多问题和缺点。

微软对开源 R 语言的承诺

随着其日益增长的受欢迎程度和社区规模,R 已经成为并在继续成为高级分析和数据可视化领域的重要参与者。R 和机器学习服务器(或服务)不仅仅是下一个 SQL Server 周期中会被遗忘的流行语,它们正在越来越多地渗透到开源和公司软件的不同层次。在过去五年中,许多大型分析公司都引入了 R 集成、解释器和 R 语言的包装,因为这种语言的实用性、可用性以及跨学科和开源导向。随着微软大胆且战略性地向开源友好型转变,将 R 集成到 SQL Server 中的用例正在增长,这使得这一举措更加自然,并且是在正确的时间点。这一举措在 SQL 社区和商业界都受到了高度赞赏。

与其他大型分析工具相比,微软对集成非常重视。它解决了语言本身的许多问题和限制,并创建了 R 与 SQL Server 的完整集成,以提供最佳的用户体验。许多竞争对手(如 SAS、IBM、SAP 和 Oracle)已经进行了类似的集成,但未能考虑到许多对整体用户体验有贡献的方面。微软宣布加入 R 联盟将使他们能够帮助 R 语言的开发并支持未来的发展。此外,微软还创建了自己的软件包仓库,称为 MRAN(来自 CRAN,其中 M 代表微软),并为 R 提供支持和 SLA 协议,尽管语言和引擎基于 Open R(一个免费、开源的版本)。所有这些步骤都告诉我们微软在将开源、统计和编程语言引入 SQL Server 环境中的承诺。

我们只能期待 R 集成将更多地融入其他服务。例如,Power BI 自 2016 年 10 月起支持原生 R 可视化(powerbi.microsoft.com/en-us/blog/r-powered-custom-visuals),自 2015 年 12 月起支持 R 语言。因此,我坚信 R 将很快成为整个 SQL Server 生态系统(如 SSAS、SSIS 和 SSRS)的组成部分,并且以原生方式集成。随着 Azure Analysis Services 的推出,R 又向分析服务迈进了一步。

利用 SQL Server R 集成增强分析

数据科学是 SQL Server 和 R 集成的最前沿。DBA、系统管理员、分析师、数据整理员或任何其他与 SQL Server 一起工作的角色执行的任务都可以通过任何类型的统计、数据相关性、数据分析或数据预测得到支持。R 集成不应仅限于数据科学领域。相反,它应该在所有任务中探索和使用。DBA 可以通过从监控任务切换到理解和预测可能或将要发生的事情来从 R 集成中受益。同样,这个想法可以应用到系统管理员、数据整理员等等。R 集成还使不同角色的个人更接近理解统计、指标、度量,并学习如何通过统计分析预测来改进它们。

除了将孤立的个体团队合作转变为更加协调和紧密的团队之外,R 集成还减少了数据移动,因为现在不同的用户可以在 R 代码的帮助下执行、深入挖掘并感受数据,而不是等待首先准备、导出和再次导入数据。随着工作流程的更加顺畅,部署时间也更快,无论是简单的报告、预测模型还是分析。这使得数据所有权的边界转移到洞察力所有权,这是对业务需求更快反应的积极方面。

在过去的一年里,我们也看到了对微软数据科学栈中数据科学的高度兴趣。随着 R 的集成、Azure 机器学习和 Power BI,所有想要学习新技能和美德的用户都有很多起点,可以从现有的产品中选择。

摘要

从 SQL Server 2016 开始,R 的集成成为了 SQL Server 平台的一个重要部分。自从 SQL Server 2016 公开发布以来,直到 2018 年 2 月(撰写本文时),社区已经很好地接纳了 R 和 Python,使数据探索和数据分析成为一般数据库任务的一部分。微软解决了许多问题,并扩大了 SQL Server 作为产品的范围。在 SQL Server 2017 中,Python 被添加为次要分析语言,使更多的社区和企业受益,同时,也关注数据的可扩展性、性能和安全。

在下一章中,我们将介绍不同的 R 发行版和 IDE 工具,用于将 R 作为独立工具或 SQL Server 内部使用,以及在选择时它们之间的区别。

第二章:微软机器学习服务器和 SQL 服务器概述

在本章中,我们将探讨为什么微软决定将 R 语言添加到 SQL Server 2016,以及这次集成克服了哪些负担。本章将涵盖以下主题:

  • 解决分析障碍和 R 限制

  • 微软 R 服务器平台

  • 微软 R 服务架构

  • 探索计算环境

分析障碍

许多公司在尝试分析他们的数据时遇到障碍。这些障碍通常是知识匮乏(并非所有部门都有分析数据的知识)和数据分散(通常数据来自不同的来源)。

企业根据员工的职责或职能划分责任。这种工作划分有积极的效果,特别是当企业规模较大时。通常,中小型企业也会采用这样的角色,但由于员工数量较少,它们通常在更高层次上细分。

随着市场变化的快速、新技术的出现和更快适应的需求,许多专家已经注意到以下许多障碍:

  • 数据稀缺和数据分散

  • 复杂(许多时候过时)的架构

  • 知识缺乏

  • 低效率

  • 市场变化适应缓慢(市场进入时间长)

许多企业至少面临这些障碍中的一个(如果不是更多),微软通过将 R 语言开放给 SQL Server 来解决这些障碍。拥抱开源语言和开源技术,它扩大了知识库,使企业能够使用社区知识和社区解决方案,同时也开放和民主化分析。不再需要忍受和等待具有学科学术知识的数据科学家和专家,现在这个知识库可以轻松共享,许多数据整理和数据工程任务可以转交给其他角色和人。这个过程也弥合了传统上 IT 和统计学家之间的差距,这在过去导致了低效率和缓慢的生产力。现在,通过在 SQL Server 中使用 R 混合不同的角色和任务,可以克服知识和技能的差距(这意味着数据整理员或数据管理员可以查阅 R 代码,这有助于他们获得数据洞察,而无需真正理解统计学的复杂性)。现在,对于理解或了解不同的平台没有惊喜,因为许多 IT 人员可以使用统计学家和数据科学家提供的 R 语言。此外,数据科学家可以开始学习在 IT 中找到的技能和语言。

两个或更多不同部门之间相互关联和共享的知识也将提高生产力。当生产力提高时,统计和预测模型可以更快地部署、更改或适应消费者和市场变化,并为数据工程师、数据整理者和分析师提供支持。这无疑是企业改善创新路径、最大化开源潜力以及拓宽使用不同方法和模型进行实验的沙盒范围的方式。

解决这些障碍的最后一步是解决数据稀缺和复杂基础设施的问题。规则是,企业规模越大,基础设施越复杂的可能性越高。在复杂基础设施的情况下,我们可以理解数据位于不同的层级或不同的粒度,在不同的平台、不同的供应商和不同的孤岛中,使得数据分析比实现更远一步。通过引入 R,这种复杂性可以通过更简单的数据连接器、更便捷的数据提取方式来克服。

随着 R 语言在分析领域的日益流行和重要,它可以为不同规模的企业做好准备,并且可以设计为超越供应商,无论您的解决方案是在本地还是在云端。由于可以直接从任何混合系统中访问和读取数据,并且只提取所需的数据,因此数据移动的需求也降低了。现在,企业中存在的所有障碍都可以更快地解决,官僚主义减少,集成更好,努力更少。

接受开源语言的另一个重要方面,许多大型企业仍在努力实现,就是开源解决方案的普遍提供。这一方面不应被忽视,必须予以考虑。微软通过以下步骤将 R 语言纳入其体系:

  1. 在 R 联盟中担任一员,该联盟负责支持 R 基金会以及紧密合作开发、分发和维护 R 引擎及其相关基础设施项目的关键组织。其中一项项目是 RHub 项目(由加博尔·卡尔迪领导),该项目提供了一种用于开发、测试和验证 R 包的服务。

  2. 在 CC 许可下创建 R 包的 MRAN 仓库,并使 CRAN 包与微软 R 引擎分发兼容。

  3. 将 Intel MKL数学内核库)计算函数用于提高 R 统计计算的效率,当你从 MRAN 仓库下载 R 引擎时,这些函数将直接可用。基本线性代数子程序BLAS)和线性代数包LAPACK)是一组用于线性代数的函数,这些函数经过并行计算优化。这些函数包括矩阵分解、Cholesky 矩阵分解、向量矩阵加法、标量乘法等等。

  4. 将许多 R 函数从 Fortran 重新编写为 C++ 语言以提高性能。

当我们比较 CRAN 和 MRAN 上的 R 引擎分布时,我们可以快速支持 MKL 计算函数的理论。正如我们已经看到的,BLAS 和 LAPACK 是向量或矩阵的替代品,因此我们将比较(基准测试)两个 R 引擎分布之间的矩阵计算。

比较是在 CRAN R 3.3.0 和 MRAN R Open 3.3.0 上进行的,以下代码:

# Matrix creation 
set.seed (2908) 
M <- 20000 
n <- 100 
Mat <- matrix (runif (M*n),M,n) 

# Matrix multiply 
system.time ( 
  Mat_MM <- Mat%*% t(Mat), gcFirst=TRUE 
)[1] 

# Matrix multiply with crossprod 
system.time ( 
  Mat_CP <- crossprod(Mat), gcFirst=TRUE 
)[1] 

以下是在以下时间(以秒为单位)的结果:

时间(秒) CRAN 3.3.2 MRAN 3.3.2
MM 26,69 2,75
CP 0,19 0,01

在下面的图中,你可以看到 CRAN 和 MRAN R 引擎之间的性能差异:

图 1

图表显示了一个简单的线性代数,它使用矩阵或向量,并且比测试(在我的本地客户端-Inter I7,4 CPU,20 GB RAM 上测试)快 10 倍。请注意,当你在你自己的机器上运行此测试时,你应该观察 RAM 和磁盘存储消耗;你将看到与 CRAN 操作相比,MRAN 操作在 RAM 消耗方面非常轻量。

当你下载 R Open 的 MRAN 分发版时,请注意,将会有关于 MKL 多线程性能函数的附加信息:

图 2:来源:mran.microsoft.com/download/

许多额外的步骤被采取,以确保消费者、开发者、管理者和来自更大企业的管理者相信 R 语言将长期存在。微软承诺,除了这项规定之外,还有对一般治理的支持,如果公司决定,它还可以在企业层面获得 R 支持。

此外,为了支持使用开源 R 语言的想法,必须了解 R 的一般架构。R 引擎由大约 20 人的核心团队编写,他们可以访问 R 引擎的源代码(尽管只有六人在日常 R 开发中工作)。这个团队不仅维护代码,他们自己也是贡献者、错误修复者和开发者。因此,R 引擎是开源的,这意味着它是免费软件(在 GNU 许可下),但引擎的维护并不那么开放。另一方面,R 库(或包)主要是社区驱动的贡献,这意味着社区中的成员可以自由地开发和创建各种函数来支持统计计算、可视化、处理数据集以及许多其他方面。

在 SQL Server 2016 发布后的几个月里(从 2016 年夏季开始),微软也改变了 SQL Server 不同版本中可用的功能。如果您访问 www.microsoft.com/en-us/sql-server/sql-server-2016-editions 上的 SQL Server 2016 版本概述,您会发现,在高级分析方面,SQL Server 2016 所有版本都提供了基本的 R 集成,而高级 R 集成(在 RevoScaleR 包中实现 ScaleR 函数的完全并行化)仅适用于企业版和开发者版。

Microsoft Machine learning R Server 平台

我们已经简要地提到了 R Open 和企业环境中的 R 的概念。Microsoft Machine Learning R Server 是一个企业级服务器,可以处理高维和大数据集,这些数据集可以并行处理,并且工作负载可以跨节点分配。R Server 可以在 Windows、Linux 服务器或 HDFS 系统上处理这些并行和分布式工作负载,例如 Hadoop、Spark、Teradata 和 HD Insight。R Server 可以使用专为并行和分布式工作负载设计的 Microsoft R 包来实现并行和分布式工作负载。RevoScaleR 包将提供进行高度并行和分布式计算、统计分析、预测分析和机器学习以及深度学习的能力。

通过收购公司 Revolution Analytics,微软重新命名了他们的两个主要产品,Revolution R Open 和 Revolution R Enterprise,为 Microsoft R Open、Microsoft R Server 和 Microsoft SQL Server R Services。除了这两种版本外,他们还增加了 Microsoft R Client 作为另一个独立产品:

图片

图 3

根据不同的版本和企业生态系统,公司可以选择他们需要的使用类型(社区版、非商业版和商业版),并且根据他们的业务需求和希望在哪里设置他们的分析服务器,他们可以选择最适合他们的选项。在商业版本中,提供了一个独立的机器学习 R Server(或简称 R 服务器),以及数据库内机器学习服务(或 SQL Server R 服务)。

在 SQL Server 2017 版本中,Microsoft R Server 已更名为 Machine Learning Server(无论是数据库内还是作为独立版本;从数据库 R 服务到机器学习 R 服务的重新命名是在 SQL Server VNext 的 CTP 版本中引入的,后来改为 SQL Server 2017)。在 图 4 中,可以看到安装 SQL Server 2016(左侧截图)和 SQL Server 2017 中可用的命名(右侧截图):

图 4

命名变更的原因是微软在 SQL Server 2017 中引入了 Python。Python 是一种强大的统计、分析和机器学习语言,因此,名称得到了统一。机器学习服务器的 R 部分(内部)不会改变,但它将获得有用的附加包和改进的功能,如下所示:

serialize/unserialize -> rxSerializeModel/rxUnserializeModel 
InstallPackages -> rxInstallPackages 
Histogram -> rxHistogram 

所有这些功能都已被重写,要么是为了在并行和分布式工作负载中处理大型数据集,要么是为了支持 R 和 SQL Server 的工作。

在 Microsoft R 平台上,以下产品可用:

  • Microsoft Machine Learning R Server

  • Microsoft R Client

  • Microsoft R Open

  • R Tools for Visual Studio

产品描述是根据 Microsoft Docs 描述以及 2016 年 9 月发表在 SQLServerCentral 上的文章([www.sqlservercentral.com/articles/Microsoft/145393/](http://www.sqlservercentral.com/articles/Microsoft/145393/))进行总结的。

Microsoft R Open (MRO)

Microsoft R Open 是一个开源的 R 发行版,100% 免费,通常可用。微软通过添加一个针对基于向量和矩阵的数学和统计计算进行优化的高性能多线程特性 Math Kernel LibraryMKL)来增强这个 R Open 发行版;否则,这个发行版与传统的 R 对象和 R 代码完全兼容。

R Open 也兼容 CRAN 仓库、GitHub 软件包或任何其他仓库,这使得 MRO 得到广泛应用。另一方面,R Open 有一些限制。它是内存受限的,这意味着它只能处理能够装入可用内存(客户端)的数据集。专有的 ScaleR 函数(在 RevoScaleR 包中可用)在 R Open 下不可用,并且它将在所有 SQL Server 2017 版本上运行,除了 Express 或带有工具的 Express,而 Microsoft R Client/Server 只会在 SQL Server 2017 的企业版或开发者版上运行。

Microsoft R 客户端与 Microsoft R Open 相同的 R 发行版,它建立在 100%开源 R 版本之上。Microsoft R 客户端是 Microsoft R 版本家族中的第一个版本,引入了RevoScaleR包(ScaleR 函数)。安装此版本的任何数据整理员、数据科学家、数据工程师(或任何其他角色)都将能够使用并行化和多线程计算,以及使用 ScaleR 的专有函数。

R 客户端版本的局限性包括:内存将限制在具有与 Microsoft R Open 相同限制的本地客户端机器上——数据必须适合本地内存才能进行计算。ScaleR 函数将与该版本一起安装,但处理将仅限于同时仅两个线程(无论客户端机器的技术规格如何),以及本地磁盘和 CPU 速度。否则,任何遗留的 R 包或 R 代码都将兼容。

Microsoft R 客户端还带来了改变计算环境的可能性,这意味着计算负载可以推送到 Microsoft R 服务器或 SQL Server R 服务以及任何 HDFS 系统,以实现最佳性能。通过构建由多个 R 客户端和一个(或几个)R 服务器组成的生态系统,将提供一个高性能的分析环境,而无需在本地安装 Microsoft R 服务器。它与以下 R 服务器版本兼容:Microsoft R 服务器(Linux 版)、Microsoft R 服务器(Teradata DB 版)、Microsoft R 服务器(Hadoop 版)、Microsoft R HDInsight,以及 Microsoft R 服务器独立版和 SQL Server R 服务的两个版本。

Microsoft 机器学习 R 服务器

Microsoft R 服务器是 R 引擎的独立服务器版本,建立在 R 开源平台之上,但经过修改以适应企业环境和对分析目的的大量使用,从统计分析、数据挖掘到机器学习以及任何大数据任务。

它与 CRAN/MRAN/GitHub 存储库以及任何 R 遗留代码完全兼容。RevoScaleR包中可用的 ScaleR 函数和算法能够进行并行和多线程数据处理,不受 R Open 或 R 客户端的限制。计算针对大型数据集进行准备——在大多数情况下,数据集容易超过服务器内存大小。这可以通过一种特殊的外部数据框格式来实现,该格式支持多线程计算,并允许基于 R 的应用程序执行并行计算和可部署解决方案,这得益于 ConnectR 和 DeployR 的帮助。Microsoft R 服务器还提供了磁盘可伸缩性。

Microsoft SQL Server 机器学习 R 服务

R Open 分布的数据库版本作为 Microsoft SQL Server 的机器学习 R 服务提供,涵盖了RevoScaleR包中可用的所有 ScaleR 算法。

在这种情况下,内存和磁盘将由您的 SQL Server 安装管理,R 执行使用的是管理 SQL Server 和 R 引擎之间通信的 SQL Server Trusted Launchpad 服务。

此外,用户可以使用 R 客户端连接到 R 服务,但 DeployRConnectR 函数将受到限制。

Microsoft SQL Server R Services 是 SQL Server 2016 及以后版本的数据库内版本,本书将主要关注这一点。这些数据库内服务可以在安装新的 SQL Server 实例时安装。图 4 显示,R 数据库内服务(或作为 SQL Server 2017 的一部分的机器学习服务(数据库内))作为实例功能安装,当勾选此功能时,R 服务(或 Python 服务)将被安装。还必须理解,Microsoft R Server 是一个独立组件,在安装 R 服务时不会安装。必须在共享服务下明确勾选 R Server(机器学习服务器)。然而,R 服务(数据库内)不会安装 R Server。

R 服务(数据库内)是 R 语言和 R 编程的独立来源,具有 SQL Server 的全栈和功能,包括治理、安全、可靠性和性能的工具和技术。在 SSMS 中,可以通过调用 R 函数并使用 R 脚本进行事务 SQL 来调用 R 分析或预测。这可以通过系统外部过程 sp_execute_external_script 实现。

ScaleR 库是 R 服务(数据库内)安装的一部分,允许数据科学家、开发人员或数据库管理员在 SQL Server 计算机上或 Microsoft R Server 上安全地执行 R 脚本。通过定义计算上下文,用户可以选择将计算和负载推送到哪里,要么留在数据库内 R 运行时,要么使用 ScaleR 库来增强连接性和并行性,并将负载推送到 Microsoft R Server。

为了闭环,通过安装作为独立和单独安装程序提供的客户端 R,您可以开发您的 R 代码并将解决方案部署到运行在 Windows 或 Linux 上的 R 服务或 Microsoft R Server。

图 5 描述了数据科学家或数据分析师如何将客户端/工作站上的负载推送到基于云的解决方案、虚拟机、R 服务器或 R 服务(数据库内)。从客户端的角度来看,也可以轻松选择混合解决方案,尤其是在需要在不同系统之间进行数据集成和数据合并时。这个概念无疑会给企业带来即时的性能提升和提升,以及几乎瞬间就能适应的更快、更好的解决方案:

图片

图 5

R Tools for Visual Studio (RTVS)

RTVS 是 Microsoft 免费版(在 GNU 许可下分发)的 IDE,用于在 Visual Studio 中编写、验证和运行 R 代码。RTVS 支持 Visual Studio 2015 和 Visual Studio 2017 的社区版、专业版和企业版。

R 工具(www.visualstudio.com/vs/rtvs/)将帮助开发人员、分析师和数据科学家操作和执行统计计算、可视化以及数据管理。R 工具将图形界面引入 R 引擎,提供丰富的编辑工作区、交互式窗口、变量和可视化探索器、帮助、IntelliSense 等。它还绑定本地和远程工作区(本地或云中),以便数据分析师可以非常容易地在计算环境之间切换。

RTVS 中工作空间的视图被简化并图形化,以便用户可以在不同的 R 环境之间切换:

图 6

它类似于著名的强大 RStudio (www.rstudio.com),但仍提供了社区和时间带入 RStudio、Emacs、Vim、Eclipse 或其他开发中的所有必需品。微软还通过问卷调查向社区询问 RTVS 用户仍然缺少什么。他们可以在www.surveymonkey.com/r/RTVS1贡献并添加他们的愿望。

RTVS 于 2016 年初宣布并发布(作为测试版),但直到 2017 年才获得版本 1。

微软机器学习 R 服务架构

R 服务器架构涵盖了 R IDE 或 SQL Server 与 R 引擎之间通信所需的所有组件。

为了正确执行 Transact SQL、R 脚本并将所有结果返回到 T-SQL,涉及了几个组件:

图 7

Launchpad 是 SQL Server 2016 中的一个新服务,支持使用 SQL Server 的外部存储过程执行外部脚本。然而,在 SQL Server 2017 中,Python 启动器也被引入,使得 Launchpad 对第二种非 SQL Server 语言也普遍可用。Launchpad 背后的想法是,由于基础设施已经准备就绪,SQL Server 未来应该支持其他语言,例如 JavaScript 和 C++,这样不仅将此服务开放给预测分析和机器学习,还开放给其他独立语言。

Launchpad 服务(服务名称:MSSQLLaunchpad)依赖于 SQL Server 服务(服务名称:MSSQLSERVER),这意味着,当重启或停止 SQL Server 服务时,Launchpad 服务也将被重启或停止。

此外,R 语言还带来了两项额外的服务:R 终端服务(简称 RTerm)和 BxlServer,它们使用 SQL Satellite,这是 SQL 服务器的一个附加扩展,用于在外部运行时(与 Launchpad 服务相关联)和 SQL Server 之间通信;两者都可以作为进程找到。

为了更容易理解这些相关性,让我们从一个简单的 PowerShell 脚本开始,看看LaunchpadBxlServer的所有组件:

Get-Process -Name Launchpad | Select-Object * 
Get-Process -Name BxlServer | Select-Object *  

此外,通过简单的 T-SQL 代码,我们可以包含 R 脚本:

EXEC sp_execute_external_script 
    @language = N'R' 
   ,@script = N'library(RevoScaleR) 
OutputDataSet <- data.frame(rxInstalledPackages())' 

此外,观察进程资源管理器和 Launchpad 进程:

图 8

显然,通信如何从 SQL Server 到 R,然后再返回。

此外,命令行还显示 Launchpad 进程使用RLauncher.dll库,并使用sqlsatellite.dll启动sqlsatelitelaunch

CommandLine: "C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\bin\launchpad.exe" -launcher RLauncher.dll -pipename sqlsatellitelaunch -timeout 600000 -logPath "C:\Program Files\Microsoft  SQL Server\MSSQL14.MSSQLSERVER\MSSQL\LOG\ExtensibilityLog" -workingDir "C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\ExtensibilityData" -satelliteDllPath
"C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Binn\sqlsatellite.dll" 

许多更多的配置会告诉你在这个架构中通信是如何设置和进行的。打开RLauncher.config文件将显示以下配置:

图 9

此外,通过更改TRACE_LEVEL参数,它还可以揭示潜在的日志,显示系统之间的相同路径和通信,正如RevoScaleR日志也显示了每个工作会话的信息:

library(RevoScaleR); sessionDirectory <- 'C:\\PROGRA~1\\MICROS~3\\MSSQL1~1.MSS\\MSSQL\\EXTENS~1\\MSSQLSERVER01\\0FEB1004-867F-4AB6-B9CC-E1C824596645';sessionId <- '0FEB1004-867F-4AB6-B9CC-E1C824596645';scriptFile <- file.path(sessionDirectory, paste(sessionId, '.R', sep=''));rxIgnoreCallResult <- .Call('RxSqlSessionStart', list(sessionDirectory=sessionDirectory, sessionId=sessionId, waitTime=-1));source(scriptFile) 

R 的限制

社区博客和论坛,以及包作者和贡献者,正在讨论 R 语言的长长限制列表。许多是模糊的,有些是基于特定用户群体的经验,但以下限制可以达成共识:

  • 性能问题

  • 内存限制

  • 安全方面

  • 语言语法

性能问题

超过 40%的 R 代码主要是用 C 语言编写的,还有略超过 20%是用 Fortran 编写的(其余部分是 C++、Java 和 R),这使得一些常见的计算任务变得非常昂贵。微软(以及之前的 Revolution analytics)确实重新编写了一些最常用的函数,从旧的 Fortran 到 C/C++,以解决性能问题。

许多包作者做了类似的事情。例如,data.tableR 包的主要作者 Matt Dowle 对语言性能进行了几次提升,以加快最常见的数据处理步骤。

当使用不同的包,如dplyrplyrdata.tablesqldf,在相同的数据集上比较类似操作时,可以看到相同结果的时间性能差异。

以下 R 示例大致显示了一个 80 MiB 大的对象,以及一个简单的分组函数,说明了计算时间差异。dpylrdata.table包突出显示,与plyrsqldf相比,性能提升了 25 倍以上。特别是data.table,它非常高效,这主要归功于 Matt 对data.table包代码进行极端优化的动力,以获得更好的性能:

set.seed(6546) 
nobs <- 1e+07 
df <- data.frame("group" = as.factor(sample(1:1e+05, nobs, replace = TRUE)), "variable" = rpois(nobs, 100)) 

# Calculate mean of variable within each group using plyr - ddply  
library(plyr) 
system.time(grpmean <- ddply( 
  df,  
  .(group),  
  summarize,  
  grpmean = mean(variable))) 

# Calcualte mean of variable within each group using dplyr 
detach("package:plyr", unload=TRUE) 
library(dplyr) 

system.time( 
  grpmean2 <- df %>%  
              group_by(group) %>% 
              summarise(group_mean = mean(variable))) 

# Calcualte mean of variable within each group using data.table 
library(data.table) 
system.time( 
  grpmean3 <- data.table(df)[ 
    #i 
    ,mean(variable)    
    ,by=(group)] ) 

# Calcualte mean of variable within each group using sqldf 
library(sqldf) 
system.time(grpmean4 <- sqldf("SELECT avg(variable), [group] from df GROUP BY [group]")) 

另一方面,微软的RevoScaleR包也得到了优化,在性能和大数据集方面可以超越所有这些包。这只是为了证明微软是如何使 R 准备好处理大数据集以解决性能问题的。

内存限制

R 是内存受限的。这字面意思是说数据集必须适合客户端的 RAM。从先前的例子中,如果有人要运行一个简单的代数运算,一些矩阵计算,或任何基于距离的算法(计算量较大),R 系统将仅限于适合 RAM 的内容。没有溢出到磁盘或任何临时对象,除非用户加载了一个允许这样做的包。

再次,微软通过RevoScaleR包提供了解决方案,覆盖了这一限制。通过引入外部数据帧XDF)格式,该包能够以这种格式存储任何大小的数据集(没有记录的上限),这种格式是持久化在磁盘上的,并且可以从二进制、blob、CSV 或任何其他格式转换为 XDF 格式。这种限制在微软机器学习 R 服务器或机器学习 R 服务中成为可能。

安全方面

R 语言的演变作为 GNU 发行版,在过去几十年中一直作为一个基于客户端的解决方案在发展,甚至在互联网出现之前,所有数据集都安全地存储在用户的机器上。随着数据在互联网、云服务和连接系统的传播,以及越来越多的混合解决方案系统,读取、写入、分析和存储数据的安全性方面从未成为问题。然而,在过去十年中,越来越多的系统正在出现,企业需求发生了变化,人们的角色也发生了变化——但是核心的 R 引擎和 IDE 还没有在这个方向上迈出一步。从这个角度来看,R 似乎想要保持为一个由大学开发和驱动的软件。随着 R 集成到 SQL Server 中,微软解决了这些问题,不仅创建了数据库角色,还增加了使 R 和所有计算安全连接的服务,减少了安全漏洞的可能性。

语言语法

R 语言非常灵活,允许用户更改 S4 方法和 S3 对象、泛型对象以及所有用户定义的对象和变量。由于 R 语言仍然不是一个面向对象的语言,这是一个有争议的问题,是否真的如此(因为目前 R 只理解三种面向对象系统),因为 S3 和 S4 对象都是面向对象的架构。

R 语法可能容易学习,但也容易产生一些疑问。以下是在 R 社区中的一些语法争议:

  • 几个不同的 R 包为用户提供相同的功能(数据处理、数据分析、数据预测)。如前所述,当比较这些包在数据处理目的上的性能和用法时,用户将看到哪个包表现更好或哪个包更适合他们的需求。具有令人困惑结构的独特语法也是包的一部分,并且可以用许多不同的方式使用。

  • 做同样的事情有多种方式(声明变量、构建类、处理数据以及使用相同算法进行预测)。

  • 缺少约定、命名空间和包之间的依赖关系。

  • 有些挑战性的错误信息,缺乏文档/帮助。

  • 区分大小写。

列出所有问题很麻烦,但可以快速了解语言是如何演变的,而且越来越受到社区的欢迎;R 作为一种语言,被认为是迄今为止最受欢迎的。

摘要

本章概述了由微软提供的新 R 服务生态系统和 R 引擎产品系列。

当考虑 R 和 SQL Server 时,必须记住企业中的需求以及如何准备基础设施以充分利用强大的 RevoScaleR 包的性能。

在下一章中,我们将看到如何使用这些 ScaleR 计算函数来提供更好的性能结果,以及如何设置您的生态系统。

第三章:管理 SQL Server 2017 和 R 的机器学习服务

本章将讨论管理 R 服务以及从 SQL Server 数据库启动 R 服务的必要步骤。具体来说,本章将涵盖以下主题:

  • 安装带有 R 服务的 SQL Server

  • 配置环境和安装 R Tools for Visual StudioRTVS

  • 资源管理器

  • 安全性

  • 会话和日志

  • 安装新的 R 包

  • 使用 PowerShell 管理 SQL Server R 服务

  • 了解 sp_execute_external_script 外部过程

与 SQL Server 2016 在 R 首次引入时的操作相比,SQL Server 2017 的安装、配置和设置工作环境并没有太大的不同。如果版本之间存在差异,将会指出。

使用 SQL Server 2016 和 2017 安装带有机器学习 R 服务的 SQL Server 具有最小的硬件要求,这些要求取决于您将要安装的 SQL Server 版本。SQL Server 有几个版本。更多信息可在 Microsoft 网页上找到:

www.microsoft.com/en-us/sql-server/sql-server-2017-editions

最小要求

基本 R 集成几乎在所有 SQL Server 版本中都是可用的,除了 Express 版本(除非它是带有高级服务的 Express 版本)。这意味着标准的 SQL Server 将支持 R 服务,使用纯 R 和本地 R 代码,但不支持高级 R 集成或将计算推送到其他计算环境。相同的逻辑也适用于 SQL Server 2017(截至编写时),版本和功能已在以下 URL 公布:

docs.microsoft.com/en-us/sql/sql-server/editions-and-components-of-sql-server-2017

在操作系统方面,许多 Microsoft 操作系统版本(如 Windows Server 2012 及更高版本、Windows 8 及更高版本)都受到支持,并且从 SQL Server 2016 及更高版本开始,Linux 也得到了支持。要运行 R 服务,至少需要 1 GB,但建议使用 4 GB,并且需要 2.0 GHz 或更快的处理器(x64 处理器类型)来完成任务。此外,主要安装需要 6 GB 的硬盘空间,但根据任何额外的功能或 SQL Server 服务,可能还需要更多的磁盘空间。

选择版本

SQL Server 已不再仅仅是数据库,它已经发展成为一个数据库平台——一个生态系统——它由许多额外的服务(如 SSRS、SSAS 和 SSIS)组成,这些服务支持并扩展了现代数据库的使用能力。当在数据库中安装机器学习 R 服务时,应该考虑生态系统环境以及将使用哪些附加服务。如果业务需求需要高级 R(或 Python)集成和分析,企业版是正确的选择。如果只需要基本的 R 集成,标准版将满足需求。如果你需要分析服务或报告服务,也可以考虑其他分析工具,以及为这些服务所需的发展工具(例如,在 OLAP 立方体上运行 MDX 和针对相同数据集市运行 R 代码)。

当你确定版本后,下载 SQL Server 2017(或 2016)的 ISO 或 CAB 安装文件并开始安装。我将安装 SQL Server 2017 的开发者版本(从安装角度来看,几乎与 2016 版本相同):

图 1:SQL Server 的安装类型

安装新的 SQL Server 将确保机器学习服务(带有 R 或数据库内服务)被正确安装。

在下一步中,必须安装安装功能。为了安装 R 服务,必须在 SQL Server 服务(数据库内)的 R 上打勾,而对于 SQL Server 2017,必须在机器学习服务(数据库内)部分中的 R 上打勾:

图 2:SQL Server 的特征选择

在以下服务器配置步骤中,你必须检查将有权访问服务的用户和账户。对于数据库内的 R 服务,将安装并自动启动 SQL Server 启动板服务。此服务可以在安装后通过 Windows 应用程序-服务启动或停止:

图 3:SQL Server 的服务器配置

在配置数据库引擎后,你将被提示接受安装 Microsoft R Open 的协议,确保你了解 R 在 GNU 许可下的事实。通过请求此同意,微软只想确保管理员同意,并且所有针对 R Open 版本的新更新和补丁都将符合 SQL Server 更新偏好:

图 4:提示安装 Microsoft R Open 的内容

除了同意 R 的协议外,请注意,Microsoft R 服务器的生命周期为两年。如果 MRS 8.0 在 2016 年 1 月发布,官方支持在 2018 年 1 月结束;更准确地说,安全和关键更新将在一年内提供(直到 2017 年 1 月),之后,只有安全更新将继续直到 2018 年 1 月。在这一年中,还将收到升级。请注意,这对于独立产品——Microsoft 机器学习服务器相关,但值得提及支持时间表将持续多长时间。

如果您也在安装 Python,将请求相同的同意:

图 5:提示安装 Python 的内容

在您选择了所有功能、配置和同意后,您将看到要安装的服务和功能的概述:

图 6:准备安装的选定功能

安装完成后,您将拥有机器学习服务的 R 引擎以及可选的 Microsoft 机器学习服务器 R 版本。请注意,R 服务的 R 引擎(数据库内)将具有不同的 R 安装,因为它是一个独立的 Microsoft R 服务器,并且所有安装的包也将不同,位于不同的路径、权限和安全设置下。

图 7:完成安装过程

配置数据库

在安装后过程中,数据库配置将是下一个任务。考虑数据库排序规则,因为 R 语言是区分大小写的,你将提供给 SQL Server 引擎并推送到 Launchpad 的数据类型很重要。某些语言区分小写和大写字母(例如,土耳其语;特别是字母 L),当将 SQL Server 和 R 数据类型配对时,这可能会带来额外的挑战。此外,根据您的生态系统,身份验证在设置环境时也应发挥重要作用。

随着 SQL Server 2016 提供的实时数据评分和 SQL Server 2017 的改进,值得一试。此外,对于任何扩展的机器学习服务使用,文件数据库可能是一个非常有用且强大的方式来存储用于后续分析的图和结果,或者可以公开给 Power BI、SSRS 或外部应用程序的结果。如果您在业务中包含用于处理非结构化数据的 filestream,这也是数据库配置需要额外注意的另一个服务。

配置环境和安装 Visual Studio 的 R 工具(RTVS)

安装完成后,有一些安装后过程需要执行。

必须启用外部脚本服务器,以便存储过程可以调用外部脚本。为此,请在已安装 R 服务(数据库内)的 SQL Server 实例上运行以下命令:

EXEC sp_configure 'show advanced options',1;
GO
RECONFIGURE;
GO
EXEC sp_configure 'external scripts enabled';
GO  

如果你第一次运行此操作,它默认是禁用的,因此必须启用它;否则,运行sp_execute_external_script过程将不可行:

EXEC sp_configure 'external scripts enabled', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO  

你可以始终检查外部脚本启用的run_value是否设置为 1,如下所示:

图 8:设置 sp_configure

如果服务器配置没有生效,你需要重启 SQL Server 服务。在服务中,找到你的MSSQLSERVER服务名称并重启它(停止并启动):

图 9:检查 MSSQLSERVER 服务

重启MSSQLSERVER服务将自动重启所有依赖的服务,包括MSSQLLaunchpad服务。在服务器重启后,外部脚本将被启用。

在安装后过程完成之后,可以设置安全设置。这是可选的,因为默认的数据库读取者已经设置好了,但是,根据你公司的环境,建议你调查并正确设置它。

此外,你还需要安装Visual Studio 的 R 工具包RTVS)。为此,需要 Visual Studio 2015 或更高版本,一旦安装了 VS2015+,你还需要从 Visual Studio 网站下载 RTVS(www.visualstudio.com/vs/rtvs/)。从那时起,安装过程是直接的,不需要任何进一步的说明。

安全性

在配置数据库和任何其他在你的生态系统中使用的高级设置之后,你将想要考虑安全,即谁将有权运行sp_execute_external_script

你可以直接在外部过程上创建安全设置。在这种情况下,你需要向用户添加执行外部脚本的数据库权限。

一个简单的 SQL 登录看起来像这样:

USE [master]
GO
CREATE LOGIN [RR1] WITH PASSWORD=N'Read!2$17', DEFAULT_DATABASE=[SQLR], CHECK_EXPIRATION=ON, CHECK_POLICY=ON
GO
ALTER SERVER ROLE [sysadmin] ADD MEMBER [RR1]
GO
CREATE DATABASE SQLR;
GO

USE [SQLR]
GO
CREATE USER [RR1] FOR LOGIN [RR1]
GO
USE [SQLR]
GO
ALTER USER [RR1] WITH DEFAULT_SCHEMA=[dbo]
GO

现在,让我们开始外部过程:


EXECUTE AS USER = 'RR1';
GO

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'OutputDataSet<- InputDataSet'
      ,@input_data_1 = N'SELECT 1 AS Numb UNION ALL SELECT 2;'
WITH RESULT SETS
((
 Res INT
))

REVERT;
GO

错误信息将是,用户RR1没有权限:

Msg 297, Level 16, State 101, Procedure sp_execute_external_script, Line 1 [Batch Start Line 34]
The user does not have permission to perform this action.

你还必须授予数据库一个 datareader 角色,以便执行sp_execute_external_script命令:

USE [SQLR]
GO
ALTER ROLE [db_datareader] ADD MEMBER [RR1]
GO 

你还应该检查是否启用了执行外部脚本:

GRANT EXECUTE ANY EXTERNAL SCRIPT TO [RR1];
GO  

在设置数据库角色和授予执行权限后,重新运行sp_execute_external_script过程,外部脚本执行的结果应该如下所示:

图 10:外部过程的输出结果

如何管理用户身份验证(Windows 或 SQL)和主要安全原则;这应该由本地 DBA、SysAdmin 和架构师协调,以帮助你委派谁将有权访问系统。

一个经验法则是为处理不同层次的数据操作和存储过程级别的访问权限准备存储过程。使用以下命令清理数据:

DROP USER RR1; 
GO 
USE [master]; 
GO 
DROP LOGIN RR1; 
GO 
--DROP TABLE IF EXISTS SQLR; 
GO

资源管理器

资源管理器(Resource Governor)是 R 服务(in-database)中的一个非常受欢迎的功能,因为它允许使用随附的代码数据库 RevoTestDB 中可用的简单数据集来管理服务器的工作负载,它需要首先进行恢复

USE [master]
RESTORE DATABASE [RevoTestDB] FROM  DISK = N'C:\SQLServer2017MLServicesR\CH03\RevoTestDB.bak';
GO  

恢复后,我们将看到对数据库的负载以及如何管理资源:

USE RevoTestDB;
GO

-- TEST query
EXECUTE  sp_execute_external_script
                 @language = N'R'
                 ,@script = N'
            library(RevoScaleR)
f <- formula(as.numeric(ArrDelay) ~ as.numeric(DayOfWeek) + CRSDepTime)
s <- system.time(mod <- rxLinMod(formula = f, data = AirLine))
            OutputDataSet <-  data.frame(system_time = s[3]);'
                ,@input_data_1 = N'SELECT * FROM AirlineDemoSmall'
                ,@input_data_1_name = N'AirLine'
WITH RESULT SETS ((Elapsed_time FLOAT));

使用我在电脑上的这个测试查询,整个运行时间为 21 秒,并且从 R 引擎返回的经过时间(elapsed time)为 1.43 秒。

设置资源管理器以应对峰值和波动。当需要或运行大量数据时,执行操作需要更快,我们需要配置外部资源池和资源池以授予资源:

-- Default value
ALTER EXTERNAL RESOURCE POOL [default] 
WITH (AFFINITY CPU = AUTO)
GO

CREATE EXTERNAL RESOURCE POOL RService_Resource_Pool
WITH (  
      MAX_CPU_PERCENT = 10  
      ,MAX_MEMORY_PERCENT = 5
);  

ALTER RESOURCE POOL [default] WITH (max_memory_percent = 60, max_cpu_percent=90);  
ALTER EXTERNAL RESOURCE POOL [default] WITH (max_memory_percent = 40, max_cpu_percent=10);  
ALTER RESOURCE GOVERNOR reconfigure;

ALTER RESOURCE GOVERNOR RECONFIGURE;  
GO 

在最后一步,必须创建和重新配置分类函数:

CREATE FUNCTION RG_Class_function()
RETURNS sysname
WITH schemabinding
AS  
BEGIN  
    IF program_name() in ('Microsoft R Host', 'RStudio') RETURN 'R_workgroup';  
    RETURN 'default'
    END;  
GO  

ALTER RESOURCE GOVERNOR WITH  (classifier_function = dbo.RG_Class_function);  
ALTER RESOURCE GOVERNOR reconfigure;  
GO 

之后,我可以再次运行相同的查询:

-- TEST 2 - performance normal; with governor enabled
EXECUTE  sp_execute_external_script
 @language = N'R'
,@script = N'
library(RevoScaleR)
f <- formula(as.numeric(ArrDelay) ~ as.numeric(DayOfWeek) + CRSDepTime)
s <- system.time(mod <- rxLinMod(formula = f, data = AirLine))
OutputDataSet<-  data.frame(system_time = s[3]);'
,@input_data_1 = N'SELECT * FROM AirlineDemoSmall'
,@input_data_1_name = N'AirLine'
WITH RESULT SETS ((Elapsed_time FLOAT));  

最后,性能比较很明显。在我的测试环境中,因为我为执行 R 代码分配了更多的 CPU 和 RAM 资源,所以我得到了三个秒的运行时间,R 的经过时间为 0.63 秒。你客户端的结果可能不同,但从默认的资源管理器设置到新设置的更改应该非常明显。

安装新的 R 包

R 包是包含用于特定目的的函数、相关二进制源代码(通常是 C、C++ 或 Fortran)、文档和示例数据的容器。包是这些文件集合,位于库文件夹中。如果你导航到你的 R 库文件夹,你将看到为你的 R 引擎安装的所有包。包也可能被称为二进制包或 tarball,具体取决于操作系统。

包不等于库,也不应该被误认为是库。在 R 语言中,安装包时使用 install.packages 命令。library() 是一个函数,它将特定包中的函数加载到你的 R 环境中。从 Windows 操作系统衍生而来,共享对象被称为 动态链接库DLLs)。因此,使用“库”这个词,它指的是常见和共享的对象。所以,要将包加载到你的 R 环境中,使用 library() 函数,并在括号中指定包名。

在 R 脚本中引用 R 库非常简单;只需添加库或使用 require() 方法。但在系统中,库首先必须安装。

库是通过安装常见存储库中可用的包来安装的,例如在微软存储库的情况下,CRAN、Biocondutor、GitHub 和 MRAN。在 R 语言中,通过调用以下命令来安装库:

install.packages("Package_Name")  

在 SQL Server 2016 中,无法通过运行带有外部存储过程的 R 脚本来安装此类包,代码返回了错误,如下所示:

--Install Package using sp_execute_external_script
EXECUTE sp_execute_external_script
 @language = N'R'
,@script = N'install.packages("AUC")'  

此 T-SQL 代码返回一个错误,表明该包不适合我的 R 版本。然而,我们稍后将看到如何安装相同的包:

图片

图 11:安装 AUC 包时的警告信息

因此,我们可以扩展原始存储过程,如下所示:

EXECUTE sp_execute_external_script
        @language = N'R'
,@script = N'   library(Hmisc)
               u <- unlist(rcorr(Customers_by_invoices$InvoiceV, Customers_by_invoices$CustCat, type="spearman"))
statistical_significance<-as.character(u[10])
OutputDataSet <- data.frame(statistical_significance)'

,@input_data_1 = N'SELECT
SUM(il.Quantity) AS InvoiceQ
,SUM(il.ExtendedPrice) AS InvoiceV
,c.CustomerID AS Customer
,c.CustomerCategoryID AS CustCat

                                         FROM sales.InvoiceLines AS il
                                         INNER JOIN sales.Invoices AS i
                                         ON il.InvoiceID = i.InvoiceID
                                         INNER JOIN sales.Customers AS c
                                         ON c.CustomerID = i.CustomerID
                                         GROUP BY
c.CustomerID
,c.CustomerCategoryID'

,@input_data_1_name = N'Customers_by_invoices'

WITH RESULT SETS (( statistical_significance FLOAT(20) ));
GO 

如果我们这样做,我们将能够计算两个变量之间相关性的统计显著性。这里的要点是我们使用适当的包名引用 R 函数library(Hmisc)。以下是对脚本详细部分的说明:

-- part of R script with reference to call method library
,@script = N'   library(Hmisc)
 u <- unlist(rcorr(Customers_by_invoices$InvoiceV, Customers_by_invoices$CustCat, type="spearman"))
statistical_significance <-as.character(u[10])
OutputDataSet <- data.frame(statistical_significance)'  

当引用库时,我们需要已经预先安装该包;否则,您将收到一个错误,表明该包不存在。如果您拼写包名错误,并且由于 R 语言区分大小写,在Hmisc包的情况下,如果您将其误拼为hmisc(没有大写的H),将会发生错误:

图片

图 12

包信息

包始终保存在library文件夹中,但根据您使用的 R 版本(Open、Client 或 Server),SQL Server 实例名称和路径可能会有所不同。

通常,Client 或 Server 版本将在您的主驱动器上存储您的库。对于 Client 版本,默认路径是C:\Program Files\Microsoft\R Client\R_SERVER\library。您可以在以下屏幕截图中查看文件夹内容:

图片

图 13

在 R Server 版本中,您将在默认 SQL Server 实例的路径上找到库:C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\R_SERVICES\library。以下是一个 Server 安装的内容:

图片

图 14

子文件夹代表已安装和可用的包的名称。要找到您包的默认路径,您可以执行以下代码:

-- Path to libraries on your computer/server
EXECUTE sp_execute_external_script
 @language = N'R'
,@script = N'OutputDataSet <- data.frame(.libPaths());'
WITH RESULT SETS (([DefaultLibraryName] VARCHAR(MAX) NOT NULL));
GO  

在我的情况下,以下是在 R Server 版本中 R 包的默认路径:

图片

图 15

使用 R 函数installed.packages()可以检索更多信息。在这个例子中,我们提取了关于包的更多信息,并将其插入到 SQL Server 表中:

-- You can create a table for libraries and populate all the necessary information
CREATE TABLE dbo.Libraries
       (
               ID INT IDENTITY NOT NULL CONSTRAINT PK_RLibraries PRIMARY KEY CLUSTERED
,Package NVARCHAR(50)
,LibPath NVARCHAR(200)
,[Version] NVARCHAR(20)
,Depends NVARCHAR(200)
,Imports NVARCHAR(200)
,Suggests NVARCHAR(200)
,Built NVARCHAR(20)
       )

INSERT INTO dbo.Libraries
EXECUTE sp_execute_external_script
              @language = N'R'
,@script=N'x <- data.frame(installed.packages())
OutputDataSet <- x[,c(1:3,5,6,8,16)]'

SELECT * FROM dbo.Libraries
DROP TABLE dbo.Libraries 

通过查询此表,您可以在一次sp_execute_external_script执行中获取有关库依赖项、版本、导入和构建的信息:

图片

图 16

在以下内容中,我们将探讨如何安装缺失的 R 包。在 SQL Server 2016 中,有几种(官方或非官方)方法,将在 SQL Server 2017 中,我们有一个优雅的方法来使用rxInstall包或创建外部库。SQL Server 2017 中引入的这两种新方法都是安装缺失包的更好、更安全、更快的途径。

使用 R Tools for Visual Studio (RTVS) 2015 或更高版本

在 SQL Server 2016 中,Microsoft 建议通过 RTVS 安装缺失的包。用户应该在客户端上安装 RTVS(www.visualstudio.com/vs/rtvs/),以便安装包。检查默认路径:

sessionInfo()
.libPaths()  

这也返回了我机器上库文件夹的路径——对于 R 服务器(默认为C:/Program Files/Microsoft SQL Server/MSSQL14.MSSQLSERVER/R_SERVICES/library)和当前使用 RTVS 的用户(C:/Users/Tomaz/Documents/R/win-library/3.2):

图 17

检查“选项...”部分:

图 18

然后选择 R Tools | 高级(如下所示):

图 19

你会看到 R 引擎已定义了一个路径。这是安装 R 包的根路径。R 包安装在子文件夹库(C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\R_SERVICES\Library)下。

通过运行 install.packages("AUC"),我可以看到服务器路径被忽略,库被安装到用户指定的文件夹中:

图 20

要在 R 服务器文件夹中安装包,你需要有管理员级别的访问权限,或者让管理员为你安装。Microsoft 在 MSDN 上讨论了这个问题(docs.microsoft.com/en-us/sql/advanced-analytics/r/installing-and-managing-r-packages)。以管理员身份运行 Visual Studio 和 RTVS 不会改变结果。为了避免这个问题,你需要声明你想要安装包的 R 服务器库文件夹的路径:

install.packages("AUC", dependencies = TRUE, lib = "C:/Program Files/Microsoft SQL Server/MSSQL14.MSSQLSERVER/R_SERVICES/library")  

包将自动安装到 R 服务器仓库文件夹中:

图 21

使用 CMD 中的 R.exe

当以管理员身份在 R 服务器的二进制文件夹(C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\R_SERVICES\bin)中执行R.exe时,你将看到一个命令窗口:

图 22

从那里,用户可以始终安装缺失的包。

install.packages("AUC")  

使用 XP_CMDSHELL

这种方法到目前为止是最快的,当共享 T-SQL 代码时非常有用,但它会牺牲使用命令行,许多用户和数据库管理员都不愿意这样做。通过在配置中启用xp_cmdshell并使用此存储过程,你可以安装任何缺失的包。使用带有-e开关的代码,你可以轻松地安装库:

R cmd -e install.packages("Hmisc")  

以下代码将在 R 服务器上安装缺失的包:

-- enable xp_cmdshell
EXECUTE SP_CONFIGURE 'xp_cmdshell','1';
GO

RECONFIGURE;
GO 
EXEC xp_cmdshell '"C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\R_SERVICES\bin\R.EXE"cmd -e
install.packages(''Hmisc'')'; 
GO  

如果用户已被授予权限,则可以运行 Vanilla R 并添加install.packages()函数。结果如下:

图 23

使用 CMD 和-e开关,可以执行和简化 R 代码的部署和计算,但鉴于这不是官方方法,我不建议使用,因为它会暴露安全风险。

复制文件

初看复制文件可能有点奇怪,但对于大多数包来说,将整个库文件夹复制到目标文件夹也能完成任务。想象一下,你是一名系统管理员,负责配置新的 R 环境或沙盒。在这种情况下,你不想运行数百行代码来安装所有需要的包;所以,你只需从镜像或备份中复制/粘贴文件夹。这将快一百倍,并且更加方便。此外,这些包已经与 R 引擎进行了测试。

使用简单的复制/粘贴、拖放操作,我在我的 R 客户端环境中复制并安装了 acepack 包(从 R 服务器复制):

图片

图 24

使用 rxInstallPackages 函数

在 SQL Server 2017 的新版本中,SQL Server R 服务中已经公开了一个非常需要的 R 函数——在 RevoScaleR 包中。使用 rxInstallPackages 函数,用户将能够为所需的计算环境安装任何额外的 R 包。

使用以下代码,可以真正加快包的安装速度,无需担心任何变通方法、打开额外的工具或其他任何事情。

从 RTVS 运行代码如下:

RSQLServicesCC <- RxInSqlServer(connectionString = "Driver=SQL Server;Server=myServer;Database=TestDB;Trusted_Connection=True;")

rxInstallPackages("AUC", verbose = TRUE, scope = "private", computeContext = RSQLServicesCC)  

从 SQL Server 运行相同代码如下:

EXECUTE sp_execute_external_script 
 @language = N'R' 
 ,@script = N'

 packagesToInstall <- c("caret","tree","party") library(MicrosoftML) 
 RSqlServerCC <- RxInSqlServer(connectionString = "Driver=SQL Server; +Server=SICN-KASTRUN\\SQLSERVER2017C2;Database=SQLR; +Trusted_Connection=True;") 
 rxInstallPackages(pkgs = packagesToInstall, owner = '', +scope = "shared", computeContext = "SqlServerCC");'; 
GO

这听起来太简单而不可能是真的,但它确实是。在运行此代码之前,请确保做几件事情:

  • 将计算环境设置为你的包安装位置

  • 设置正确的权限和访问

  • 检查 TCP/IP 协议

rxInstallPackages 函数中,使用 computeContext 参数设置 Local 或你的 SqlServer 环境;你也可以使用范围作为共享或私有(区别在于,如果你以共享方式安装包,它可以在不同数据库的不同用户之间使用)。如果你不是从 db_owner 角色运行此命令,你也可以指定所有者。

使用 PowerShell 管理 SQL Server R 服务

PowerShell 非常适用于管理远程机器、虚拟机器,甚至 Azure VM 机器。除了这项常见的管理工作之外,它还有许多其他积极和有用的好处。

可以根据需要运行、安排或处理任何任务计划作业或 SQL Server 作业,比如每天获取数据或重新计算和初始化预测模型,甚至运行模型。

PowerShell 的另一个非常有用的方面是可以在不同的环境和系统之间进行 API 通信。

对于系统管理员来说,PowerShell 将非常有用,可以在客户端机器或甚至 R Server 服务器之间分发和维护任何额外的 R 包。

了解 sp_execute_external_script 外部过程

SP_EXECUTE_EXTERNAL_SCRIPT是一个存储过程,它将提供的脚本作为参数在提供的语言(在这种情况下,是 R 语言)的外部脚本中执行。在 SQL Server 2017 中,Python 也得到了支持,据说在未来,其他语言,如 Java、C#和 C++也将得到支持(通过 Launchpad)。

sp_execute_external_script是一个系统过程,它调用并发送传递的代码到外部引擎,并将结果以表的形式返回到 SQL Server。脚本有一组参数,这些参数将导航到 T-SQL 代码和数据到 R 引擎,其中包含 R 代码。

以下参数可用:

sp_execute_external_script
 @language = N''
,@script = N'', 
,@input_data_1 =  'input_data_1'
,@input_data_1_name =  N'input_data_1_name'
,@output_data_1_name = 'output_data_1_name'
,@parallel = 0 | 1 
,@params =  N''
,@parameter1 = ''  

参数

  • @language:这指定了外部过程中将使用哪种语言。对于 SQL Server 2016,R 语言可用,并且随着 SQL Server 2017 的推出,Python 也变得可用。该参数是sysname内置数据类型,且限制为 128 个 Unicode 字符。这就是为什么我们在值前使用 N,以表示nvarchar类型。

  • @script:使用此参数将本机 R 或 Python 代码传递给 Launchpad 服务。此外部代码(相对于 SQL Server)必须经过验证和正确格式化(仅在 Python 的情况下),因为 SSMS 或 RTVS 不会验证此参数。因此,最简单的方法是使用 Visual Studio,并在 RTVS 中验证您的 R 代码或使用Python Tools for Visual StudioPTVS)验证您的 Python 代码。此字段是nvarchar类型。

  • @input_data_1:此参数将您的 T-SQL 代码作为外部过程的数据源。此代码将由 SSMS 或 VS 进行验证和检查。此字段也是nvarchar类型,可以执行任何 T-SQL 代码。由于外部引擎(R 语言)的限制,输入的数据类型有一些限制。与 SQL Server 2016 相比,R 语言支持的数据类型较少。支持的数据类型包括逻辑、数值、整数、复数、字符和原始数据。因此,可以立即看出以下数据类型(除非转换为 R 数据类型)将导致一些问题。让我们只列举一些 SQL Server 数据类型:

    • Cursor

    • Timestamp(哈希格式,而非日期时间格式)

    • Datetime2, datetimeoffset, time

    • 所有 Unicode 文本数据类型:nvarchar, nchar, ntext

    • Sql_variant

    • Text, image

    • XML(包括 JSON,因为在 SQL Server 2017 中它是一个 XML 数据类型格式)

    • Hierarchy, geometry, geography

    • 使用.NET 框架或任何 Launchpad 服务支持的任何语言构建的任何 CLR 或 assembly 绑定数据类型

除了某些数据类型的限制之外,还有一些 T-SQL 子句和语句不能作为输入数据参数的一部分使用。以下是一些例子:

  • 存储过程(UDF,表值函数)

  • 逻辑IFWHILE, FOR循环

  • 临时变量或表

  • 没有更新、插入或删除操作(仅选择)

  • GO语句或分号

  • 来自 DML 语句的OUTPUT子句

  • 引用任何 CURSORS

除了 SELECT 语句之外,还可以使用以下语句:

  • SELECT 与多个 JOIN 操作

  • WITH 公共表表达式

  • UNIONUNIONALLEXCEPTINTERSECT

  • 任何 SETSTRINGLOGICALCOMPARISONBITWISEARITHMETICCOMPOUND 操作符

  • COLLATE

如前所述,DML 语句中的 @input_data_1 参数也不支持任何 DDL 语句或子句。

为了避免 T-SQL 语言与外部脚本之间的数据类型冲突,最佳实践是在将所需数据集投入参数之前尽可能多地进行数据准备、整理和优化。此外,许多限制可以通过转换为 R 封闭数据类型来妥协。

  • @input_data_1_name: 这个参数保存了在 R 脚本中将用作输入数据集的数据集名称。默认情况下,外部过程 sp_execute_external_script 将使用以下名称:- InputDataSet 用于输入数据,- OutputDataSet 用于返回结果数据(两者都是默认值)。请注意,R 语言是区分大小写的,因此在此参数中提供的您数据集的名称也必须在 R 代码中以相同的方式书写。

  • @output_data_1_name: 这个参数保存了从外部脚本返回的结果定义,作为返回给任何存储过程的变量。返回的数据集必须在 R 语言中为 data.frame 格式。data.frame 是一系列向量的集合,是表的表示,T-SQL 服务器可以导入并使用任何其他 T-SQL 子句或语句进一步使用:或者它可以直接将结果存储到 SQL Server 表中。此参数是 sysname 数据类型。

  • @parallel: 这是一个参数,将明确告诉 R 引擎在 R 中并行化计算。此参数是在 SQL Server 2016 的后续版本(SP/CU)中引入的,对于任何不使用 RevoScaleR 计算函数的 R 代码、函数或包来说都是非常受欢迎的。当然,这在简单 R 脚本和大型输入数据集的情况下是正确的。如果包本身是用 C++(而不是原始 Fortran)编写的,并且 R 脚本不包括相对复杂的数据整理/处理指令,特别是 plyr/dplyrdata.table 函数,那么来自特定 R 包的 R 函数将提供更好的性能结果。只需记住,越简单越好。

要检查工作负载是否可以并行分布,只需观察执行计划,在运行 T-SQL 代码时查找计划中的并行度。此外,请注意,任何 MAXDOP 的本地设置也会产生影响。换句话说,如果 MAXDOP 关闭,并且您设置了参数 @parallel = 1,不要期望有太多的分布式工作负载,因为默认情况下并行化是关闭的。

当运行和使用RevoScaleR包中可用的任何计算函数时,Launchpad 将自动处理并行性,使用 R Server 或 SQL Server R Services 中可用的分布式 R 函数。

  • @params:这是一个参数,其中可以声明并指定为可以在 R 脚本中使用的变量(变量)。参数可以是输入或输出,当将预测模型输入到 R 代码中或从 R 代码(除了指定的结果集)导出额外信息时非常方便。使用此参数作为输出可以返回单个值/列,而不是表。

  • @parameter1:这是一个参数,其中指定并使用参数值的输入或输出变量形式在 R 脚本中。

摘要

本章涵盖了机器学习服务(数据库内)的安装、服务的配置以及如何管理服务。它还探讨了缺失包的安装,并涉及了安全和资源管理器。在最后一节中,本章还解释了如何使用外部过程以及带有所有参数的sp_execute_external_script。通过深入探讨安全问题和缺失包的安装,已经介绍了几个示例。缺失包的安装在很大程度上依赖于 SQLServerCentral 上的文章。

机器学习服务的介绍和使用外部过程将是以下所有章节的基础,这些章节都将严重依赖于对配置和使用此过程的好理解。

第四章:数据探索和数据可视化

在实施预测建模之前,了解数据探索和数据可视化技术对于理解数据至关重要。借助现有的开源 R 软件包中的统计和数学算法,数据专业人员可以轻松地探索他们的数据,并发现那些在其他情况下在关系型数据库中难以做到的图案/趋势。使用 SQL Server 机器学习服务ML 服务)与 R 结合意味着数据探索和数据可视化不再是孤立的作业,从而引领更快、更简单的预测建模路径。

本章概述了开发人员在使用 R 进行数据探索和数据可视化时必须了解的基本技巧和窍门。您将学习如何在 T-SQL 中集成 R 进行数据探索和数据可视化,然后将这些技术整合到 SSRS 和 Power BI 中。如果您已经熟悉 R 的数据探索和数据可视化,请自由跳到本章的最后部分。

理解 SQL 和 R 数据类型

在我们深入探讨使用 R 在 T-SQL 中探索数据之前,让我们先从了解在 R 中存储数据的数据类型开始。当在 T-SQL 中使用 R 时,首先且最重要的数据类型是数据框。SQL Server 中 sp_execute_external_script 的输入和输出参数是由 R 接收和发送的数据框。对于数据整理而言,其他重要的数据类型,并且与数据框非常相似的是矩阵和数据表,这些内容超出了本章的范围。

除了数据框之外,R 还支持有限数量的标量数据类型,如字符、复数、日期/时间、整数、逻辑、数值和原始数据类型。因此,当您在 R 脚本中提供来自 SQL Server 的数据时,当需要时,数据将隐式转换为 R 中的兼容数据类型。当无法自动执行转换时,R 将返回 Unhandled SQL data type。以下表格提供了一个数据类型转换的简短示例。有关隐式数据类型转换的更多信息,请访问 docs.microsoft.com/en-us/sql/advanced-analytics/r/r-libraries-and-data-types#changes-in-data-types-between-sql-server-2016-and-earlier-versions 上的 R Libraries and R Data Types

SQL Server 数据类型(sp_execute_external_script 的输入参数) R 类 结果集数据类型(sp_execute_external_script 的输出参数)
datetime POSIXct Datetime
numeric(p,s) numeric float
int integer int
varbinary(n) raw varbinary(max)
varchar(n) character varchar(max)

R 中的数据框

数据框包含行和列,就像 SQL Server 中的表一样,其中每一列可以有不同的基本数据类型,例如整数、字符等。

使用?了解更多关于函数的信息:

? [函数]

关于数据框的更多信息,您可以在R Tools for Visual StudioRTVS)、R Studio 或您喜欢的其他 R 集成开发环境(IDE)中输入以下命令:

> ? data.frame  

默认情况下,R 使用内存。因此,一旦输入数据框通过sp_execute_external_script传递,R 就会在内存中存储输入数据。机器学习服务(数据库内)由 SQL Server 中的资源管理器管理,如第三章的管理 SQL Server 2017 和 R 的机器学习服务中所述。

一个一般性指南是努力在 SQL Server 和 R 之间找到一个好的计算类型平衡。这包括是否在 R 中还是在 SQL Server 中进行数据清洗/操作。

R 中提供了一些内置的数据框,例如mtcarsiris

让我们看看 R 中的一个数据框。在 RTVS 中运行以下代码:

> mtcars;

输出应该如下所示:

图 4.1 - mtcars 数据

要检查数据类型,您可以在变量上调用call函数:

> class(mtcars); 
[1] "data.frame" 

数据探索和数据清洗

在 R 中,数据清洗可以独立于 SQL Server 完成。以下图示说明了开发者在使用 SQL Server 和 R 时可以遵循的推荐高级流程。如果您之前在 R 中工作过,您可能熟悉在 R 中直接执行的步骤 2、3 和 5。

请注意,步骤 3 和 4 是可选的,将在第六章的预测建模和第七章的将 R 代码投入运行中进一步讨论:

图 4.2 - 使用 R 的 SQL Server 机器学习服务的高级开发流程

让我们从在 R 中进行数据清洗开始。具体来说,在本节中,我们将使用 R 环境,以便在我们将其与 T-SQL 结合之前了解它在 R 中的工作方式。如果您熟悉 R,您可以跳过本节。以下为本节的前提条件:

  1. 一个 R IDE,例如作为 Visual Studio 2015 或 2017 一部分的 RTVS。有关 RTVS 的更多信息,请访问aka.ms/rtvs

  2. WideWorldImporters数据库恢复到 SQL Server 2016 或更高版本。请参阅aka.ms/wwi下载您可以在环境中恢复的完整 SQL 备份文件。

将 SQL Server 数据导入 R

从 R 连接到 SQL Server 最常见的方式是使用 RODBC 包。请注意,在 SQL Server 2016 之前,这是您可能需要执行的步骤。

在下面的示例中,我们希望从 SQL Server 实例MsSQLGirl和数据库WideWorldImporters中检索与 2015 年销售人员月订单相关的数据集,使用的是信任连接(Windows 身份验证)。

使用 RTVS,按照以下步骤执行:

  1. 创建一个新的脚本名为 Chapter04_01.R。确保通过输入以下内容来加载 RODBC 库:
library(RODBC); 
  1. 定义连接字符串并获取连接句柄:
connStr <- "Driver=SQL Server;Server=MsSQLGirl; 
  Database=WideWorldImporters;trusted_connection=true"; 
dbHandle <- odbcDriverConnect(connStr); 
  1. 定义你想要在 SQL Server 中运行的查询。这也可以是一个调用存储过程、函数、视图等的查询。在这个特定示例中,查询将获取 2015 年按销售人员划分的月度订单:
# Define the query to be run 
order_query =  
"SELECT DATEFROMPARTS(YEAR(o.[OrderDate]),  
      MONTH(o.[OrderDate]), 1) AS OrderMonth, 
    sp.[PreferredName] AS SalesPerson, 
    COUNT(DISTINCT o.[OrderID]) AS OrderCount, 
    SUM(ol.[Quantity] * ol.[UnitPrice]) AS TotalAmount 
FROM [Sales].[Orders] o 
    INNER JOIN[Sales] .[OrderLines] ol 
        ON ol.[OrderID] = o.[OrderID] 
    INNER JOIN[Application] .[People] sp 
        ON sp.[PersonID] = o.[SalespersonPersonID] 
WHERE sp.[ValidTo] >= GETDATE() 
    AND o.[OrderDate] BETWEEN '20150101' AND '20151231' 
GROUP BY 
DATEFROMPARTS(YEAR(o.[OrderDate]),  
MONTH(o.[OrderDate]), 1), 
    sp.[PreferredName];" 

  1. 执行查询并将输出存储到 orders 变量中:
# Get the data set from SQL into the orders variable in R 
orders <- sqlQuery(dbHandle, order_query); 
  1. 输入以下内容以查看 orders 中的数据集:
orders;  

或者,在 RTVS 中,你可以转到如图 4 - 2 所示的变量资源管理器窗口,展开订单以查看变量的详细信息。使用放大镜工具(图片)查看如图 4 - 3 所示的输出:

图片

图 4 - 3 RTVS 中的变量资源管理器

图片

图 4 - 4 在变量资源管理器中查看订单

这里是 orders 变量的数据字典。熟悉以下列对我们来说很有用,因为我们将在本章中使用订单数据框及其派生变量:

列名 描述
OrderMonth 订单发生的月份
OrderCount 销售人员的月订单数量
TotalAmount 订单的美元金额
SalesPerson 销售人员的姓名

在 R 语言中探索数据

在 R 语言中探索数据有多种方法:以下是一些有用的函数。df 表示数据框变量,col 表示 df 中的列:

  • head(df) 返回数据框 df 的前几行,默认为 6 行。

  • tail(df) 返回数据框 df 的最后几行,默认为 6 行。

  • summary(df) 为数据框 df 中的每个列提供基本的摘要统计信息。

  • names(df) 返回数据框 df 的列名。

  • str(df) 返回关于数据框 df 的基本信息。

  • describe(df$col) 描述 df 数据框中 col 值的分布/偏度。这对于构建量表和项目分析非常有用。这需要首先安装 psych 包。

  • 以下是一个使用 str 函数对数据框 orders 进行操作的示例:

> str(orders) 

这就是输出看起来像什么:

图片

图 4-5 str(orders) 的输出

与 SQL Server 不同,R 语言对函数和变量都是大小写敏感的。请确保你正确地输入它们。

R 语言中的数据整理

数据整理是将一种 原始 格式转换为另一种格式以便更易于消费的手动过程,无论是供人类使用还是供后续过程(如报告、数据可视化、统计分析、预测分析等)使用。

有许多 R 语言包可用于数据整理。R 语言预装了用于简单数据转换和可视化的包。在本节中,你将学习两个常用的数据整理超级强大包:dplyrreshapelubridate

在数据框中添加/删除行/列

添加或删除行或列可以轻松实现。以下是一些示例,展示了如何使用 R 以及dplyr来实现:

  1. 添加新行:让我们创建一个新的数据框,orders_newrows,其中包含 2 行新数据,我们希望将其追加到orders的末尾。然后我们想使用tail函数查看orders的最后几行:
> orders_newrows <- data.frame( 
    OrderMonth = c("2015-12-01", "2015-12-01"), 
    SalesPerson = c("Julie", "Tomaz"), 
    OrderCount = c(201, 2017), 
    TotalAmount = c(340000, 370000)); 

> orders <- rbind(orders, orders_newrows); 
> tail(orders); 

这将触发以下输出:

    OrderMonth SalesPerson OrderCount TotalAmount 
105 2015-12-01        Lily        194    442645.5 
106 2015-03-01      Hudson        389   1020488.6 
107 2015-10-01         Taj        195    437388.4 
108 2015-08-01        Lily        182    517126.3 
109 2015-12-01       Julie        201    340000.0 
110 2015-12-01       Tomaz       2017    370000.0 

使用dplyr,你可以调用bind_rows来追加多个数据框。例如,以下显示了ordersorders_newrows被追加两次:

> bind_rows(orders, orders_newrows, orders_newrows);
  1. 添加新列:为了说明,让我们创建一个新的数据框orders_tax,其中包含每行的序列 ID 和 10%的销售税金额。我们使用cbind函数将orders变量与orders_tax变量绑定:
> orders_discount <- data.frame( 
    Discount = orders$TotalAmount * 0.25) 
> orders <- cbind(orders, orders_ discount); 
> names(orders) 

这将给出以下输出:

[1] "OrderMonth"  "SalesPerson" "OrderCount"  "TotalAmount" 
[5] "Discount"  

使用dplyr,你可以调用bind_cols来添加新列:

> orders_tax <- data.frame( 
    RowID = seq(1:nrow(orders)), 
    SalesTax = orders$TotalAmount * 0.1 
    ) 
> orders <- bind_cols(orders,data.frame(orders_tax)); 
> names(orders) 

输出如下:

[1] "OrderMonth"  "SalesPerson" "OrderCount"  "TotalAmount"  
[5] "Discount"   "RowID"       "SalesTax"    

或者你可以添加一个名为TotalPlusTax的新列:

> mutate(orders, TotalPlusTax = TotalAmount * 0.125); 
  1. 删除列:现在让我们从orders中删除RowID
> orders <- orders[, !names(orders) == "RowID"] 

命令names(orders)列出了orders中的列名。因此,!names(orders) == "RowID"排除了列名RowID

使用dplyr,你可以调用select来选择一组列。例如,以下是从orders中排除RowID的示例:

> select(orders, -RowID); 

你也可以轻松选择以Order开头的列:

> select(orders, matches("Order"));  

让我们显示以SalesPerson开头的orders。首先,为了获取以J开头的索引,我们可以使用grep函数:

> grep("^J.*", orders$SalesPerson); 
[1]   2  17  21  25  28  37  43  45  52  71  78 102 109 
> orders[grep("^J.*", orders$SalesPerson),]; 
    OrderMonth SalesPerson OrderCount TotalAmount SalesTax 
2   2015-06-01        Jack        206    502828.7 50282.87 
17  2015-05-01        Jack        203    493282.0 49328.21 
21  2015-11-01        Jack        210    473676.4 47367.64 
25  2015-02-01        Jack        176    454979.3 45497.93 
28  2015-10-01        Jack        205    522954.2 52295.42 
37  2015-07-01        Jack        205    466244.0 46624.40 
43  2015-04-01        Jack        188    520575.8 52057.58 
45  2015-01-01        Jack        182    413761.0 41376.10 
52  2015-12-01        Jack        209    474157.7 47415.77 
71  2015-03-01        Jack        181    469591.0 46959.10 
78  2015-08-01        Jack        171    359710.5 35971.06 
102 2015-09-01        Jack        249    552961.4 55296.14 
109 2015-12-01       Julie        201    340000.0 34000.00 

使用dplyr,你可以调用select来选择一组列。例如,以下是从orders中排除RowID的示例:

> filter(orders, grepl("^J.*", SalesPerson)); 

你可能已经注意到在最后的几个dplyr示例中,dplyr的语法更加友好。例如,在filter函数中,不需要指定列所属的变量。

  > orders[grep("^J.*", orders$SalesPerson),]; # base

> filter(orders, grepl("^J.*", SalesPerson)); # dplyr 此外,选择函数更加友好。

  > orders <- orders[, !names(orders) == "RowID"] # base

  > select(orders, -RowID); # dplyr

使用 dplyr 进行更多数据处理

以下是一些有用的数据处理活动、函数和示例的快速列表。df表示数据框变量。

目的 函数
重命名列 rename(df, new_column_name = old_column_name)
排序/排序数据 arrange(df, col1)col1对数据框df进行排序。
去重数据 distinct(df)``distinct(df, [列名])当提供时,对df[列名]去重。
管道 x %>% f(y)执行f(x,y)。你可以嵌套语法。例如:x %>% f(y)**%>% g(z)等价于x %>% g(f(x,y),z)

查找缺失值

R 有非常简短和简单的方法来查找缺失值,即is.na(df)。它返回df中带有缺失值的行索引。

转置数据

在 SQL Server 中对数据集进行转置不是一件简单的事情。在 R 中使用 t(df) 来交换数据框 df 的行和列。

透视/逆透视数据

reshape 包在数据透视和逆透视方面非常有用。

使用 cast 透视数据如下:

library(reshape) 
x <- data.frame(OrderMonth = orders$OrderMonth, 
                SalesPerson = orders$SalesPerson, 
                TotalAmount = orders$TotalAmount) 
x1 <- cast(x, OrderMonth ~ SalesPerson) 
names(x1) 

使用 melt 逆透视数据如下:

melt(x1,id=c(OrderMonth)) 

示例 - 使用 R 在 T-SQL 中进行数据探索和整理

如前所述,R 中有一些非常实用的数据处理和探索技术。现在让我们将它们全部在 T-SQL 中拼接起来。在下面的示例中,我们想要获取 2015 年每月销售人员订单数量和总金额的统计摘要,具体为最小值、最大值、1^(st) 分位数、中位数、3^(rd) 分位数,以了解数据范围和每月每个销售人员的订单分布:


USE WideWorldImporters 
GO 

-- Part 1: Get Monthly Order count and Order amount  
-- per Sales Person in Year 2015\. 
DECLARE @SQLScript NVARCHAR(MAX) 
SET @SQLScript = N'SELECT DATEFROMPARTS(YEAR(o.[OrderDate]),  
MONTH(o.[OrderDate]), 1) AS OrderMonth, 
    sp.[PreferredName] AS SalesPerson, 
    COUNT(DISTINCT o.[OrderID]) AS OrderCount, 
    SUM(ol.[Quantity] * ol.[UnitPrice]) AS TotalAmount 
FROM [Sales].[Orders] o 
    INNER JOIN [Sales].[OrderLines] ol 
        ON ol.[OrderID] = o.[OrderID] 
    INNER JOIN [Application].[People] sp 
        ON sp.[PersonID] = o.[SalespersonPersonID] 
WHERE sp.[ValidTo] >= GETDATE() 
    AND YEAR(o.[OrderDate]) = 2015 
GROUP BY 
DATEFROMPARTS(YEAR(o.[OrderDate]),  
MONTH(o.[OrderDate]), 1), 
    sp.[PreferredName];' 

-- Part 2: Prepare the R-script that will summarize the dataset. 
DECLARE @RScript NVARCHAR(MAX) 
SET @RScript = N'OutputDataSet <- as.data.frame(t(sapply(InputDataSet[, c("OrderCount", "TotalAmount")], summary))); 
OutputDataSet <- cbind(Column = row.names(OutputDataSet), OutputDataSet);' 

-- Part 3: Execute R in TSQL to get the monthly sales person's  
-- order count and total amount. 
EXECUTE sp_execute_external_script 
     @language = N'R' 
    ,@script = @RScript 
    ,@input_data_1 = @SQLScript 
WITH RESULT SETS (( 
            [Columns] NVARCHAR(30), [Min] FLOAT, 
            [Q1] FLOAT, [Median] FLOAT, 
            [Mean] FLOAT,  [Q3] FLOAT, 
            [Max] FLOAT)); 
GO 

R 中的数据可视化

良好的数据可视化可以从大量数据中提取洞察力,并作为与观众沟通的媒介。幸运的是,R 有强大的内置函数以及可以帮助你创建良好数据可视化的包。在本节中,我们将探讨一些内置的图形函数和 R 库,以展示它们的特性。然后我们将通过一个示例来展示如何与 T-SQL 结合使用。你还将学习如何在 SQL Operations Studio 中显示 R 的图形。与上一节类似,我们将使用 orders 数据集,并创建一个名为 d 的数据框来缩小对销售人员 Amy、Jack 和 Hudson 的分析。

折线图

R 中的 plot() 函数绘制一个简单的散点图,显示两个变量之间的关系和分布趋势/异常值。

这是一个脚本示例,用于可视化 2015 年 AmyJackHudson 的订单数量与每月销售金额之间的关系:

> d <- orders[orders$SalesPerson %in% c("Amy", "Jack", "Hudson"), ]; 
> plot(x = d$TotalAmount, y = d$OrderCount,  
main = "Monthly Orders", xlab = "Total Amount ($)",  
ylab = "Number of Orders", col = d$SalesPerson, pch = 19,  
xaxt = "n"); 
> axis(side = 1, at = x <- signif(seq(from = 0,  
to =  max(orders$TotalAmount), length.out = 6), 2), 
labels = paste(x / 1000, "k", sep = "")); 

以下图表显示了 2015 年每个月每个销售人员所下的订单金额和订单数量。使用这样的图表可以让我们轻松地看到,用蓝色点表示的销售人员表现非常出色:

图片

图 4-5 使用基本绘图函数绘制的散点图

显然,前面的折线图需要几个步骤来绘制,你还需要添加一个 legend() 调用来将颜色映射到销售人员。以下是一个使用一个调用绘制折线图的更简单方法。

ggplot2 库提供了一个简单的方法来使用 qplot 函数创建类似的图表。以下脚本与之前的调用等效:

> library(ggplot2)  
>  qplot(x = TotalAmount, y = OrderCount, data = d,  
  color = SalesPerson, main = "Monthly Orders"); 

下面的图表包含一个图例,有助于显示 Hudson 是表现最好的销售人员:

图片

图 4-6 使用 ggplot 函数绘制的散点图

直方图

R 中的 hist() 函数绘制一个直方图,表示数据集的频率分布。

这是一个绘制 2015 年每月销售人员总金额频率分布的脚本:

> hist(orders$TotalAmount, main = "Monthly Orders",  
  xlab = "Total Amount ($)") 

使用以下直方图,我们可以轻松地看到最常见的月度总金额(每个销售人员)每月在 40 万至 50 万美元之间:

图 4-7 使用基本 hist 函数的直方图图表

盒形图

盒形图图表使我们能够显示每个销售人员的异常值。这可以通过在 R 中使用boxplot()函数轻松实现。然而,ggplot函数非常易于使用和定制。以下是一个使用ggplot()编写盒形图图表的示例:

ggplot(orders,  
       aes( x = SalesPerson, 
            y = TotalAmount)) + 
      geom_boxplot(outlier.color = "red", outlier.size = 3) + 
      ggtitle(label = "Monthly Orders") + 
      xlab("Sales Person") + ylab("Total Amount ($)"); 

以下图表显示了 2015 年每个销售人员制作的月度订单的分布:

图 4-8 使用 ggplot 函数的盒形图图表

散点图

在 R 中,scatterplot()可以用来理解变量之间的关系/趋势。

以下是一个使用散点图来理解 2015 年 Amy、Hudson 和 Jack 月度总金额趋势的示例:

library(car) # For the scatterplot function 
library(RColorBrewer) # For choosing color palette more easily 

# Prepare d  
d$SalesPerson <- factor(d$SalesPerson); 
d$OrderMonth <- as.Date(d$OrderMonth); 

# Configure the palette to use 
my_colors <- brewer.pal(nlevels(as.factor(d$SalesPerson)), "Set2") 

# Map the monthly orders 
scatterplot(TotalAmount ~ OrderMonth | SalesPerson, data = d, 
    xlab = "Month", ylab = "Total Amount",  
    main = "Monthly Orders", col = my_colors, 
    cex = 1.5, lwd = 3) 

根据以下图表,我们可以得出结论,尽管 Hudson 的月度总金额总体上高于 Amy 和 Jack,但它们略有下降趋势。我们还可以看到 Amy 的月度总金额急剧下降:

图 4-9 使用 scatterplot 函数的散点图图表

ggplot函数还可以用于创建散点图,并使用平滑的线条叠加显示销售人员的月度模式:

# Use the ggplot version 
ggplot(data = d, 
       aes(x = OrderMonth, y = TotalAmount, color = SalesPerson)) + 
    geom_point() + geom_smooth(method = "loess") + 
    scale_y_continuous(label = scales::dollar) + 
    scale_color_brewer(palette = "Set2") + 
    ggtitle(label = "Monthly Orders"); 

图 4-10 使用 ggplot 函数的散点图图表

树形图

预测建模的数据准备包括探索数据结构和可视化预测值的决策规则。这些值可以是分类的,也可以是连续的,分别表示为分类树和回归树。

下面是一个示例脚本,用于创建一个树形图,描述预测给定销售人员和OrderCount的月度总金额的决策规则:

library(rpart) 
library(rpart) 
library(rattle) 
library(rpart.plot) 
fit <- rpart(TotalAmount ~ OrderCount + SalesPerson , data = d, method="anova"); 
fancyRpartPlot(fit, sub = "Monthly Sales Person") 

运行前面的脚本将给出一个整洁的树形图,节点上的第一行是平均月度总金额(即,科学记数法中的619e+3实际上是$619,000),后面跟着 n 作为观测数的数量和构成节点的百分比:

图 4-11 使用 rpart 函数的树形图

示例 - T-SQL 中的 R 数据可视化

现在我们已经学习了几个使用 R 进行数据可视化的示例,让我们将它们与 SQL Operations Studio 中的 T-SQL 结合起来。请注意,SSMS 无法渲染 T-SQL 生成的 R 图像。

docs.microsoft.com/en-us/sql/sql-operations-studio/download下载 SQL Operations Studio。

执行以下步骤以在 T-SQL 中运行 R,生成可以在 SQL Operations Studio 中可视化的图像。

  1. 打开 SQL Operations Studio。

  2. 在 SQL Operations Studio 中连接到服务器上的WideWorldImporters数据库。

  3. 从示例中复制Part 1到数据可视化在 T-SQL 中,因为我们想重用@SQLScript变量定义:

-- Part 2: Prepare the R-script that will produce the visualization. 
DECLARE @RScript NVARCHAR(MAX) 
SET @RScript = N'library(ggplot2);  
    image_file = tempfile();  
    jpeg(filename = image_file, width=1000, height=400);  
    d <- InputDataSet[InputDataSet$SalesPerson %in% c("Amy", "Jack", "Hudson"), ]; 
    print(qplot(x = TotalAmount, y = OrderCount, data = d, color = SalesPerson, main = "Monthly Orders")); 
    dev.off() 
    OutputDataSet <- data.frame( 
            data=readBin(file(image_file,"rb"), 
            what=raw(),n=1e6));' 

-- Part 3: Execute R in TSQL to get the binary representation of the image. 
EXECUTE sp_execute_external_script 
     @language = N'R' 
    ,@script = @RScript 
    ,@input_data_1 = @SQLScript 
WITH RESULT SETS ((plot VARBINARY(MAX))); 
  1. 在 SQL Operations Studio 中执行前面的脚本,你将得到一个包含名为plot的列的结果集。

  2. 点击图表查看器,然后从图表类型中选择图像:

图片

图 4-12 SQL Operations Studio 中 R 的数据可视化输出

在报告和可视化中集成 R 代码

在本节中,我们将深入了解 Microsoft BI 堆栈中可用的熟悉报告和可视化工具,例如 SQL Server Reporting Services (SSRS)、Power BI 和移动报告。

将 R 图形与 SQL Server 集成的三个主要用例。

  1. 获取表示数据/统计分析、训练模型或预测模型的数据集输出:

图片

图 4-13 SQL Server 机器学习服务在 R 中的数据分析过程

执行sp_execute_external_script以运行 R 并生成如图 (1) + (2) + (3) 所示的数据集输出。数据集输出 (3) 可以来自数据/统计分析、训练模型、预测输出等。在 SQL Server 中,我们可以选择进一步处理输出(4),例如,将其保存到表中或将它传递给另一个存储过程。

  1. 获取包含 R 图形输出 varbinary 表示的数据集输出。

图片

图 4-14 SQL Server R 服务的数据可视化过程

执行sp_execute_external_script以运行 R 并生成如图 (1) + (2) + (3) 所示的数据集输出。在这种情况下,数据集输出 (3) 将包含图形输出的 varbinary (max) 表示。在 SQL Server 中,我们可以进一步插入输出(4),例如,将图像作为 varbinary (max) 保存到表中。

  1. 将 R 图形输出保存到文件中,并将文件路径存储在数据集输出中。当首选离线渲染时,这是理想的:

图片

图 4-17 SQL Server 机器学习服务的数据可视化到文件的过程

执行sp_execute_external_script以运行 R 并生成如图 (1) + (2) + (3) 所示的数据集输出。在这种情况下,数据集输出 (3) 包含图形输出需要驻留的文件路径。在 SQL Server 中,我们可以选择进一步处理输出(4)。您还可以如 Tomaž Kaštrun 的 博客中所述,为此解决方案集成 Filestream:

tomaztsql.wordpress.com/2016/09/25/filetable-and-storing-graphs-from-microsoft-r-server/

在 SSRS 报告中集成 R

SSRS 报告可以从查询或存储过程中读取数据集。本质上,这为我们提供了足够的灵活性,以选择我们想要如何将 R 输出作为 SSRS 中的即席或操作报告的一部分。

现在,让我们看看几个将 R 集成到 SSRS 报告中的例子。假设一个数据分析师想要快速进行统计分析,以了解销售人员与总金额之间关系的强度。使用 SSRS 做这件事非常简单。以下是一个示例,说明您如何实现这一点。

  1. 使用 Visual Studio 2017SQL Server Management Studio 连接到 WideWorldImporters

  2. 创建一个名为 dbo.usp_AnalyzeOrdersUsingAnova 的新存储过程:

CREATE PROCEDURE dbo.usp_AnalyzeOrdersUsingAnova 
( 
  @StartDate DATE = '20150101', 
  @EndDate DATE = '20151231' 
) 
/********************************************************** 
 * Purpose: Determine if Monthly Orders (Total Amount in $)  
 *      has no dependency on Sales Person. 
 * Parameters:   
 *  @StartDate  - The start date of the Orders table 
 *  @EndDate  - The end date of Orders table 
 * Example on how to execute: 
 *  EXEC dbo.usp_AnalyzeOrdersUsingAnova 
 *     @StartDate = '20150101' 
 *    ,@EndDate = '20151231' 
 *****************************************************************/ 
AS 
BEGIN  

  DECLARE @input_query NVARCHAR(MAX);  
  DECLARE @RAOV NVARCHAR(MAX); 

  -- The SQL query representing Input data set. 
  -- Get the monthly orders from each Sales between  
  -- specific date and time. 
  SET @input_query = N' 
  SELECT 
    DATEFROMPARTS(YEAR(o.[OrderDate]),  
       MONTH(o.[OrderDate]), 1) AS OrderMonth, 
    sp.[PreferredName] AS SalesPerson, 
    COUNT(DISTINCT o.[OrderID]) AS OrderCount, 
    SUM(ol.[Quantity] * ol.[UnitPrice]) AS TotalAmount 
  FROM [Sales].[Orders] o 
    INNER JOIN[Sales] .[OrderLines] ol 
      ON ol.[OrderID] = o.[OrderID] 
    INNER JOIN[Application] .[People] sp 
      ON sp.[PersonID] = o.[SalespersonPersonID] 
  WHERE sp.[ValidTo] >= GETDATE() 
    AND o.[OrderDate] BETWEEN ''' +  
CAST(@StartDate AS VARCHAR(30)) + ''' AND ''' + 
CAST(@EndDate AS VARCHAR(30)) + ''' 
  GROUP BY 
    DATEFROMPARTS(YEAR(o.[OrderDate]),  
      MONTH(o.[OrderDate]), 1), 
    sp.[PreferredName];' 

  -- The R code that tests if Total Amount has no strong  
  -- dependency to Sales Person 
  -- Note: Null Hypothesis (H0) in this case is Total Amount  
  --    has no strong dependency to Sales Person. 
  --    The closer p-value to 0 we can reject the H0\. 
  SET @RAOV = N'a = aov(TotalAmount ~ SalesPerson, 
 data = InputDataSet); 
    m <- summary(a); 
    library(plyr); 
    x <- data.frame(RowID = 1:nrow(m[[1]]),  
      Attribute = rownames(m[[1]])); 
    OutputDataSet <- cbind(x, ldply(m, data.frame));' 

  -- Using R Services produce the output as a table 
  EXEC sp_execute_external_script @language = N'R' 
    ,@script = @RAOV  
    ,@input_data_1 = @input_query 
    ,@input_data_1_name = N'InputDataSet' 
    ,@output_data_1_name = N'OutputDataSet'  
    WITH RESULT SETS (([RowID]  INT, 
          [Attribute]  NVARCHAR(50),  
          [DF]    NUMERIC(20,10), 
          [SumSq]  NUMERIC(20,10), 
          [MeanSq]  NUMERIC(20,10), 
          [FValue]  FLOAT, 
          [Pr(>F)]  FLOAT 
          )); 

END 
  1. 使用 Visual Studio 2017 或 Report Builder 创建一个新的报告。

  2. 通过按 Ctrl+S 或转到文件菜单并点击保存来将此文件保存为 SQL+R_Chapter04_SSRS_Anova_01.rdl

  3. 创建一个新的数据源,并保存名为 WideWorldImporters 的数据源,该数据源连接到您服务器上的 WideWorldImporters 数据库。

  4. 然后创建一个新的数据集,其查询定义如下,然后点击刷新字段:

EXEC dbo.usp_AnalyzeOrdersUsingAnova 

图片

图 4-16 指定要执行的存储过程

  1. 创建一个 Tablix 来表示 AOV_SalesPerson 列:

图片

图 4-17 添加包含 AOV_SalesPerson 所有列的 Tablix

  1. 可选地添加另一个 Tablix,其 DataSetName 映射到 AOV_SalesPerson。在 Tablix 体的第一个单元格中,使用以下表达式描述如何解释分析:
="Since the p-value of " & Fields!Pr__F_.Value & " is " & IIf(Fields!Pr__F_.Value < 0.05, "less", "greater") & " than the .05 significance level, we " & IIf(Fields!Pr__F_.Value < 0.05, "reject", "accept") & " the null hypothesis that the mean of monthly Total Amount of " & Fields!Attribute.Value & " are all equal. This means that there is " &  IIf(Fields!Pr__F_.Value < 0.05, "", "no") & " dependency between " & First(Fields!Attribute.Value, "AOV_SalesPerson") & " and Monthly Orders Total Amount"
  1. 点击运行以预览报告:

图片

图 4-18 预览报告

另一个常见场景是使用 R 绘制数据可视化。在以下示例中,我们将比较销售人员 2015 年的表现。从这里,我们可以看到销售人员每月订单的趋势以及他们在 12 个月的表现。

  1. 使用 Visual Studio 2017SQL Server Management Studio 连接到 WideWorldImporters

  2. 创建一个名为 dbo.usp_CreateMonthlySalesPlot 的新存储过程:

 CREATE PROCEDURE dbo.usp_CreateMonthlySalesPlot 
( 
  @StartDate DATE = '20150101', 
  @EndDate DATE = '20151231' 
) 
/********************************************************** 
 * Purpose: Determine if Monthly Orders (Total Amount in $)  
 *      has no dependency on Sales Person. 
 * Parameter:   
 *  @StartDate  - Observation start date in the Orders table 
 *  @EndDate  - Observation end date in the Orders table 
 * Example on how to execute: 
 *  EXEC dbo.usp_AnalyzeOrdersUsingAnova 
 *     @StartDate = '20150101' 
 *    ,@EndDate = '20151231' 
 **********************************************************/ 
AS 
BEGIN  

  DECLARE @input_query NVARCHAR(MAX);  
  DECLARE @RPlot NVARCHAR(MAX); 

  -- The SQL query representing Input data set. 
  -- Get the monthly orders from each Sales between  
    specfic date and time. 
  SET @input_query = N' 
  SELECT 
    DATEFROMPARTS(YEAR(o.[OrderDate]),  
      MONTH(o.[OrderDate]), 1) AS OrderMonth, 
    sp.[PreferredName] AS SalesPerson, 
    COUNT(DISTINCT o.[OrderID]) AS OrderCount, 
    SUM(ol.[Quantity] * ol.[UnitPrice]) AS TotalAmount 
  FROM [Sales].[Orders] o 
    INNER JOIN [Sales] .[OrderLines] ol 
      ON ol.[OrderID] = o.[OrderID] 
    INNER JOIN [Application] .[People] sp 
      ON sp.[PersonID] = o.[SalespersonPersonID] 
  WHERE sp.[ValidTo] >= GETDATE() 
    AND o.[OrderDate] BETWEEN ''' +  
      CAST(@StartDate AS VARCHAR(30)) +  
      ''' AND ''' +  
      CAST(@EndDate AS VARCHAR(30)) + ''' 
  GROUP BY 
    DATEFROMPARTS(YEAR(o.[OrderDate]), MONTH(o.[OrderDate]), 1), 
    sp.[PreferredName];' 

  -- The R code that produces the plot. 
  SET @RPlot = N'library(ggplot2);  
  image_file = tempfile();  
  jpeg(filename = image_file, width=600, height=800);  
  a <- qplot(y = TotalAmount, x = OrderMonth,  
        data = InputDataSet, 
        color = SalesPerson,  
        facets = ~SalesPerson, 
        main = "Monthly Orders"); 
  a + scale_x_date(date_labels = "%b");     
  plot(a); 
  dev.off();  
  OutputDataSet <-  data.frame( 
    data=readBin(file(image_file,"rb"), 
    what=raw(),n=1e6));   
' 
  EXEC sp_execute_external_script @language = N'R' 
    ,@script = @RPlot  
    ,@input_data_1 = @input_query 
    ,@input_data_1_name = N'InputDataSet' 
    ,@output_data_1_name = N'OutputDataSet'  
    WITH RESULT SETS (( [plot] VARBINARY(MAX))); 

END 
  1. 在 Report Builder 中,打开之前创建的 SQL+R_Chapter04_SSRS_Anova_01.rdl,创建一个新的数据集,其查询定义如下,然后点击刷新字段。创建的字段名为 Plot,应该有一行:
EXEC dbo.usp_CreateMonthlySalesPlot

图片

图 4-19 指定要执行的存储过程

  1. 插入一个新图像,具有以下常规属性:

选择图像源:数据库

使用此字段:=First(Fields!plot.Value, "MonthlyPlot")

使用此 MIME 类型:image/jpeg

图片

图 4-20 配置图像以渲染图表

  1. 可选地,转到左侧面板的“大小”项,并将显示更改为原始大小。

  2. 点击运行以预览报告:

图片

图 4-21 预览带有图表的报告

此 RDL 文件现在可以发布到 SSRS 报告服务器

关于 SSRS 的更多信息,以下 Microsoft Docs 网站非常有用:

docs.microsoft.com/sql/reporting-services/create-deploy-and-manage-mobile-and-paginated-reports

在 Power BI 中集成 R

Power BI 是一个强大的数据可视化工具。与 R 结合使用,Power BI 可以渲染具有无妥协动态交互性的美丽图像。在本例中,您将学习如何创建与我们在上一节中在 SSRS 中创建的类似的数据可视化:

图 4-22 使用 R 脚本编辑器的 Power BI 可视化

为了简单起见,我们将使用 Power BI 桌面,但您也可以从在线PowerBI.com版本创建:

  1. 启动Power BI 桌面并创建一个新文件。

  2. 主页菜单中,单击获取数据|SQL Server

  3. 连接到包含WideWorldImporters数据库的 SQL Server 实例。

  4. 然后单击高级选项,在 SQL 语句字段提供以下查询:

SELECT 
    DATEFROMPARTS(YEAR(o.[OrderDate]), 
    MONTH(o.[OrderDate]), 1) AS OrderMonth, 
    sp.[PreferredName] AS SalesPerson, 
    COUNT(DISTINCT o.[OrderID]) AS OrderCount, 
    SUM(ol.[Quantity] * ol.[UnitPrice]) AS TotalAmount 
FROM [Sales].[Orders] o 
    INNER JOIN[Sales] .[OrderLines] ol 
        ON ol.[OrderID] = o.[OrderID] 
    INNER JOIN[Application] .[People] sp 
        ON sp.[PersonID] = o.[SalespersonPersonID] 
WHERE sp.[ValidTo] >= GETDATE() 
GROUP BY 
    DATEFROMPARTS(YEAR(o.[OrderDate]), MONTH(o.[OrderDate]), 1), 
    sp.[PreferredName]; 

对话框现在应该看起来像这样:

图 4-23 SQL Server 数据库数据源详细信息

  1. 单击确定以查看查询预览。

  2. 然后在预览窗口中单击加载

图 4-24 查询预览

  1. 在可视化窗格中,单击 R 脚本图标。

  2. OrderMonthSalesPersonTotalAmount列从字段窗格拖放到值框中。

请注意,您的表可能被命名为Query1,您可以将其重命名为更有意义的名称,例如MonthlyOrders,如下所示:

图 4-25 将字段作为 R 的输入选择

  1. 使用OrderMonth,而不是默认的日期层次结构,从值字段的下拉列表中选择OrderMonth

图 4-26 选择订单月份而不是数据层次结构来显示

  1. 确保 R 脚本图形框仍然处于焦点。可选地,您可以调整其大小以使其更宽或更高:

图 4-27 Power BI 桌面中的 R 可视化框

  1. 然后,在 Power BI 屏幕下半部分的R 脚本编辑器中,输入以下 R 代码:
dataset$OrderMonth <- as.Date(dataset$OrderMonth); 

library(ggplot2); 
a <- qplot(y = TotalAmount, x = OrderMonth, data = dataset, 
        color = SalesPerson, facets = ~SalesPerson, 
        main = "Monthly Orders"); 
a + scale_x_date(date_labels = "%b"); 
a + scale_y_continuous(label = scales::dollar);
  1. 在 R 脚本编辑器栏的右侧单击运行脚本按钮。

  2. 添加一个切片器,然后拖放OrderMonth

  3. 字段中,通过单击OrderMonth列表中的 X 取消选择除年份之外的所有OrderMonth层次结构:

图 4-28 年份切片器

  1. 现在,您的 Power BI 报告应该看起来像这样:

图 4-29 Power BI 报告

摘要

在本章中,你已经学习了数据准备在预测建模中的重要性,这包括数据探索和数据可视化练习。R 有许多开源包对数据处理非常有用,例如 dplyrreshape 以及更多。挑战在于在 SQL Server 和 R 之间找到合适的平衡点,进行数据处理活动。SQL Server 机器学习服务的美丽之处在于它允许轻松与 SQL Server 报告服务集成。此外,Power BI 也支持使用 R 可视化进行交互式数据探索。在下一章中,你将了解更多关于 RevoScaleR 库的信息,该库用于便携、可扩展和可分发的 R 函数。

第五章:RevoScaleR 包

RevoScaleR 包与 Microsoft Machine Learning R Server 和 R Services 一起提供。它也适用于 R Client,但有一些限制在 第二章 中讨论,Microsoft Machine Learning Server 和 SQL Server 概述。鉴于快速发展和持续升级,本章将涵盖 8.X 和 9.X 版本——后者也适用于 SQL Server 2017。9.X 版本的变更和升级不容忽视,也将被涵盖。

本章涵盖了以下主题:

  • 挑战 R 的限制

  • 可扩展和分布式计算环境

  • 数据准备函数

  • 描述性统计函数

  • 统计测试和抽样函数

  • 预测建模函数

主要来说,这个 R 包旨在在客户端连接到 Microsoft R Server 以执行 R 代码的生态系统中使用,以便在更强大的服务器上执行,该服务器可能包含整个数据集,而不仅仅是客户端机器上工作的人处理的小部分。

克服 R 语言限制

在 SQL Server 2016(和 2017)之前,BI 和数据科学家有 OLAP 立方体、DMX 语言以及所有超级酷炫的 Microsoft 算法,这些都可在 SQL Server Analysis Services (SSAS) 中使用。但随着快速变化和更大的市场需求,集成开源产品(无论是 R、Python、Perl 还是其他任何产品)的需求实际上已经存在。接下来的逻辑步骤是将它与之一集成。微软寻求解决方案,最终收购了 Revolution Analytics,这使他们再次走上正轨。Revolution R 解决了 R 语言的主要问题。

微软解决了 R 的限制。其中许多限制旨在加快 R 中的数据探索和并行编程技术。此外,还增强了 MKL 计算,因此矩阵计算甚至更快,包括标量计算和笛卡尔积计算。

以下限制得到了解决:

  • 通信开销在由大量相对较小的任务组成的细粒度并行计算中尤其是一个问题

  • 负载平衡是指计算资源没有平等地贡献给问题

  • 使用 RAM 和虚拟内存的影响,如缓存未命中和页面错误

  • 影响性能和通信开销的网络效应,如延迟和带宽

  • 进程间冲突和线程调度

  • 数据访问和其他 I/O 考虑因素

可扩展和分布式计算环境

RevoScaleR 包提供了以下函数,这些函数将在本章中详细说明。

要获取所有 ScaleR 函数的列表,可以使用以下 T-SQL:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'require(RevoScaleR)
 OutputDataSet <- data.frame(ls("package:RevoScaleR"))'
WITH RESULT SETS
 (( Functions NVARCHAR(200)))  

在 SSMS 中,您会看到一个包含所有可以与RevoScaleR包一起使用的相关rx函数的表格。

根据这些函数的列表,可以准备一个更简单、更好的函数概览:

图 1:RevoScaleR 函数列表(来源:Microsoft)

数据准备函数

导入数据是数据准备过程中的第一步。导入数据是指将数据从任何外部系统通过外部文件或建立与实时数据源的连接引入到您的系统中。在接下来的部分,我们将探讨如何导入存储为 SPSS 或 SAS 文件的数据,以及如何使用 ODBC 连接字符串直接连接到外部实时数据库系统。

从 SAS、SPSS 和 ODBC 导入数据

将数据导入 R 或 SQL Server 表不是RevoScaleR库的主要焦点,但由于这一点在列表中,让我们简要地了解一下。这样,根据您的数据源,RevoScaleR包提供了连接到不同数据源的能力。其中也包括 SAS 和 SPSS——两个非常广泛且常用的数据分析与预测分析统计程序。我们将简单地关注 SAS 软件(www.sas.com/),SPSS Statistics,于 2009 年被 IBM 收购(www.ibm.com/products/spss-statistics),或 SPSS Modeler(www.ibm.com/products/spss-modeler)。

导入 SAS 数据

如果不是统计分析、数据挖掘和机器学习中最受欢迎的程序,SAS 是数据分析中流行的程序之一。因此,让我们创建一个简单的 SAS 文件,并使用ScaleR函数读取它。

使用以下 SAS 代码(代码与本书一起提供),您可以非常容易地创建一个样本数据集:

图 2:SAS 代码的概述

现在,让我们假设我们的 SAS 数据存储在文件sas_data.sas7bdat中,正如代码在PROC DATA语句中所建议的那样。

使用以下 R 代码,我们可以提取并将此数据集导入到 R 的data.frame中:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 SampleSASFile <- file.path(rxGetOption("sampleDataDir"), "sas_data.sas7bdat")
 #import into Dataframe
 OutputDataSet <- rxImport(SampleSASFile)
 '
WITH RESULT SETS
 ((
 income  INT
 ,gender  INT
      ,[count] INT
 ))

确保您的sampleDataDir包含数据样本。您还可以指定其他路径,例如:

SampleSASFile <- file.path(("C:\\Users\\TomazK\\Documents\\CH05"), "sas_data.sas7bdat")  

然而,您需要确保您已授予对此工作文件夹的访问权限。两种方式都会以表格形式呈现结果,如下从 SAS 文件中读取:

图 3:SAS 代码结果的概述

另一种导入 SAS 文件的方法是直接使用RxSasData(在这种情况下,从 R):

SampleSASFile <- file.path(("C:\\Users\\tomazK\\CH05"), "sas_data.sas7bdat")
sasDS <- RxSasData(SampleSASFile, stringsAsFactors = TRUE, 
 colClasses = c(income = "integer", gender= "integer", count="integer"),
rowsPerRead = 1000)
rxHistogram( ~F(gender)|F(income), data = sasDS)  

您可以轻松地从 SAS 数据文件中生成直方图。

导入 SPSS 数据

使用 SPSS,过程类似。以下 SPSS 语法(语法包含在本章中)生成样本数据集,该数据集存储在您的本地机器上:

图 4:SPSS 语法的概述

这涉及到使用从前面 SPSS 语法生成的 SPSS 保存文件将数据导入 R 服务,这与 SAS 文件相对类似:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 SampleSPSSFile <- file.path(rxGetOption("sampleDataDir"), "spss_data.sav")
 #import into Dataframe
 OutputDataSet <- rxImport(SampleSPSSFile)
 '
WITH RESULT SETS
 ((
 income  INT
 ,gender  INT
 ,[count] INT
 ))

此外,RevoScaleR 包有一个特殊函数可以直接读取 SPSS 文件,称为 RxSpssData。以下 R 代码可以完成与前面 T-SQL 代码相同的结果:

SampleSPSSFile <- file.path(("C:\\Users\\tomazK\\CH05"), "spss_data.sav")
spssDS <- RxSpssData(SampleSPSSFile, stringsAsFactors = TRUE, 
                       colClasses = c(income = "integer", gender= "integer", count="integer"),rowsPerRead = 1000)
rxHistogram( ~F(income)|F(count), data = spssDS)

并且 RevoScaleR 直方图可以直接与 SPSS 数据源一起使用,生成简单的直方图:

使用 ODBC 导入数据

使用 ODBC 驱动程序可以扩展对几乎所有可以获取驱动程序且具有通用 RDBM 模型的数据库的访问权限。

RevoScaleR 包扩展了 ODBS 驱动程序的列表,也支持 Linux 和其他系统。使用 ODBC,您可以连接到 MySQL、Oracle、PostgreSQL、Linux 上的 SQL Server、Cloudera 和 Teradata(在这种情况下,使用 RxTeradata 函数会更好)。

以下示例将使用 ODBC 驱动程序从另一个 SQL 服务器实例获取数据,同时使用 RxOdbcDataRxSqlServerData 函数,因为它们可以互换使用:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 sConnectStr <- "Driver={ODBC Driver 13 for SQL Server};Server=TOMAZK\MSSQLSERVER2017;Database=AdventureWorks;Trusted_Connection=Yes"
 sQuery = "SELECT TOP 10 BusinessEntityID,[Name],SalesPersonID FROM [Sales].[Store] ORDER BY BusinessEntityID ASC"
 sDS <-RxOdbcData(sqlQuery=sQuery, connectionString=sConnectStr)
 OutputDataSet <- data.frame(rxImport(sDS))
                            '
WITH RESULT SETS
 ((
 BusinessEntityID  INT
 ,[Name]  NVARCHAR(50)
 ,SalesPersonID INT
 ));

这将与在同一服务器上运行以下命令相同:

USE AdventureWorks;
GO

SELECT
TOP 10
BusinessEntityID
,[Name]
,SalesPersonID
FROM [Sales].[Store]
ORDER BY BusinessEntityID ASC

在使用 RxOdbcData 函数的情况下,您应检查凭证,并且您可能还想检查您正在使用哪个用户来运行脚本。您还可以创建一个新的登录和用户,并使用它来检查和执行脚本:Adventureworks 数据库可以从微软的 GitHub 网站下载(github.com/Microsoft/sql-server-samples/tree/master/samples/databases/adventure-works):

EXECUTE AS USER='MSSQLSERVER01'
GO
-- YOUR CODE
REVERT;
GO

变量创建和数据转换

变量创建和数据转换是在定义数据清洗和数据整理任务时的两个过程。这些任务对于适当的数据准备很重要,并使分析数据以供未来任务更容易。

我们将要探索的函数如下:

  • 变量创建和重新编码

  • 数据转换

  • 处理缺失值

  • 排序、合并和分割数据集

  • 按类别(即总和)汇总,这与 T-SQL 汇总和 Windows 函数类似

本部分将涵盖以下一些函数,主要关注数据转换、处理缺失值和分割数据集:

RxDataSourcerxDataSteprxDataStepXdfRxFileSystemrxFindFileInPathrxFindPackagerxFisherTestRxForeachDoParrxGetInforxGetInfoXdfrxGetJobInforxGetJobInforxGetOptionrxGetVarInforxGetVarNamesrxImportrxImportToXdfrxIsOpenrxOdbcDatarxOptionsrxOpenrxQuantilerxReadXdfrxResultsDFrxSetFileSystemrxSetInforxSetInfoXdfrxSortrxSetVarInfoXdfrxSetVarInforxMarginalsrxMergerxMergeXdf

当使用数据库内 R 服务(或数据库内机器学习服务,包括 SQL Server 2017 的 Python)时,你应该牢记在哪里以及如何进行任何类型的数据转换、数据处理,以及排序和/或合并。经过多次性能和速度测试后,变得非常清楚,许多的整理和数据处理任务应该在将数据集发送到由 sp_execute_external_script 执行之前在数据库内完成。这个函数集是唯一一个需要将计算上下文视为非常重要的函数集。所有其他用于统计测试、描述性统计和预测统计的函数都可以很容易地与外部过程一起使用,而不会影响性能或时间。

rxDataStep 函数开始,它为我们提供了许多使用数据库内 R 提取和生成 XDF 文件的机会:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 df_sql <- InputDataSet 
                  df_sql4 <- data.frame(df_sql)
 outfile <- file.path(rxGetOption("sampleDataDir"), "df_sql4.xdf") 
 rxDataStep(inData = df_sql4, outFile = outfile, overwrite = TRUE)'
 ,@input_data_1 = N'
 SELECT 
 BusinessEntityID
                  ,[Name]
                  ,SalesPersonID
                  FROM [Sales].[Store]'

这将在你的样本数据目录中生成 df_sql4.xdf 文件。如果你对指向此文件夹的位置感兴趣,你可以执行以下操作:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 OutputDataSet <- data.frame(path = file.path(rxGetOption("sampleDataDir")))'

它将类似于以下截图所示的内容:

图片

确保你已经为执行 rxDataStep 代码的用户授予了访问权限,因为代码将在目标位置创建一个物理 XDF 文件。

变量创建和重新编码

使用 rxGetVarInfo 将将 data.frame 的信息暴露给 sp_execute_external_script 输出。很明显,一些这些函数从未被设计为向 data.frame 展示输出,而是仅设计用于探索数据集。一些这些函数(例如,rxGetVarInfo)将在 R 环境中给出一个很好的输出,但在 SQL Server 数据库的数据框中操作输出将很困难:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet 
                  var_info <- rxGetVarInfo(df_sql)
 OutputDataSet <- data.frame(unlist(var_info))'
 ,@input_data_1 = N'
 SELECT 
 BusinessEntityID
 ,[Name]
 ,SalesPersonID
 FROM [Sales].[Store]'

注意,我们正在使用 unlist 函数,该函数将列表的集合取消列表成一个向量。只是为了比较输出,我们可以在 R 环境中运行相同的脚本:

library(RevoScaleR) 
sConnectStr <- "Driver={ODBC Driver 13 for SQLServer};Server=TOMAZK\\MSSQLSERVER2017;Database=AdventureWorks;Trusted_Connection=Yes" 
sQuery = "SELECT  BusinessEntityID,[Name],SalesPersonID FROM [Sales].[Store] ORDER BY BusinessEntityID ASC" 
sDS <-RxOdbcData(sqlQuery=sQuery, connectionString=sConnectStr) 
df_sql <- data.frame(rxImport(sDS)) 

现在,运行 rxGetVarInfo(df_sql) 将给出一个略微不同的导出结果:

> var_info <- rxGetVarInfo(df_sql) 
> var_info 
Var 1: BusinessEntityID, Type: integer, Low/High: (292, 2051) 
Var 2: Name, Type: character 
Var 3: SalesPersonID, Type: integer, Low/High: (275, 290) 

使用 unlist() 函数取消列表后,我们得到相同的信息,但以稍微不同的方式书写:

> df <- data.frame(unlist(var_info)) 
> df 
                         unlist.var_info. 
BusinessEntityID.varType          integer 
BusinessEntityID.storage            int32 
BusinessEntityID.low                  292 
BusinessEntityID.high                2051 
Name.varType                    character 
Name.storage                       string 
SalesPersonID.varType             integer 
SalesPersonID.storage               int32 
SalesPersonID.low                     275 
SalesPersonID.high                    290 

这表明,这些用于变量创建和重新编码的函数更多的是为 R 数据工程师而不是 T-SQL 数据工程师设计的。

rxGetInfo() 函数将获取你的数据集大小以及观测值/变量的数量:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
                  library(RevoScaleR)
 df_sql <- InputDataSet 
                  var_info <- rxGetInfo(df_sql)
 OutputDataSet <- data.frame(unlist(var_info))'
 ,@input_data_1 = N'
 SELECT 
                   BusinessEntityID
                  ,[Name]
                  ,SalesPersonID
                  FROM [Sales].[Store]'

同样的逻辑适用:如果你运行这个 R 环境,你将得到一个更整洁的信息显示:

> rxGetInfo(df_sql) 
Data frame: df_sql  
Number of observations: 701  
Number of variables: 3  

向此函数添加一些额外的参数也会产生更丰富的输出,如下所示:

> rxGetInfo(df_sql, getVarInfo = TRUE) 
Data frame: df_sql  
Number of observations: 701  
Number of variables: 3  
Variable information:  
Var 1: BusinessEntityID, Type: integer, Low/High: (292, 2051) 
Var 2: Name, Type: character 
Var 3: SalesPersonID, Type: integer, Low/High: (275, 290) 

使用 rxGetVarInforxGetInforxGetVarInfo 将生成一个元素列表。rxGetVarInfo 将生成一个列表的列表,其中元组的数量等于变量的数量,而 rxGetInfo 将生成一个包含六个元素的列表,其中每个列表将包含有关对象的信息:

图片

了解这一点后,可以对前面的 T-SQL 执行进行轻微的修改,以便以更易读的格式显示相关信息,通过向结果集呈现元素(元组):

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet 
 get_Info <- rxGetInfo(df_sql) 
 Object_names <- c("Object Name", "Number of Rows", "Number of Variables")
 Object_values <- c(get_Info$objName, get_Info$numRows, get_Info$numVars)
 OutputDataSet <- data.frame(Object_names, Object_values)'
 ,@input_data_1 = N'
 SELECT 
                   BusinessEntityID
                  ,[Name]
                  ,SalesPersonID
                  FROM [Sales].[Store]'
WITH RESULT SETS
 ((
 ObjectName NVARCHAR(100)
 ,ObjectValue NVARCHAR(MAX)
 ));

在 SQL Server Management Studio 中返回的结果:

图片

这看起来非常整洁,多花一些精力肯定能给出格式更好、更容易阅读以及更富有信息量的结果。

在这个例子中,您还看到了如何创建一个新变量。这在清理数据或重新编码/分桶数据时特别有用。

假设您想重新编码数据集中现有变量的值并创建一个新的变量。可以使用以下标准 R 代码完成:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 df_sql <- InputDataSet
 #first create an empty variable
 df_sql$BusinessType <- NA
 df_sql$BusinessType[df_sql$BusinessEntityID<=1000] <- "Car Business"
 df_sql$BusinessType[df_sql$BusinessEntityID>1000] <- "Food Business"
 OutputDataSet <- df_sql'
 ,@input_data_1 = N'
 SELECT 
 BusinessEntityID
 ,[Name]
 ,SalesPersonID
 FROM [Sales].[Store]'
WITH RESULT SETS
 ((
 BusinessEntityID INT
 ,[Name] NVARCHAR(MAX)
 ,SalesPersonID INT
 ,TypeOfBusiness NVARCHAR(MAX)
 ));

或者,您可以使用 rxDataStep() 函数和 transformFunc 参数,以及一个用于通过转换旧值创建新变量的附加函数来完成此操作:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
 df_sql$BusinessEntityID_2 <- NA

 myXformFunc <- function(dataList) {
 #dataList$BussEnt <- 100 * dataList$BusinessEntityID
 if (dataList$BusinessEntityID<=1000){dataList$BussEnt <- "Car Business"} else {dataList$BussEnt <- "Food Business"}
 return (dataList)
 }

 df_sql <- rxDataStep(inData = df_sql, transformFunc = myXformFunc)
 OutputDataSet <- df_sql'
 ,@input_data_1 = N'
 SELECT 
 BusinessEntityID
 ,[Name]
 ,SalesPersonID
 FROM [Sales].[Store]'
WITH RESULT SETS
 ((
 BusinessEntityID INT
 ,[Name] NVARCHAR(MAX)
 ,SalesPersonID INT
 ,TypeOfBusiness NVARCHAR(MAX)
 ));

rxDataStep()X 是一个非常强大的函数,主要用于数据选择、子集化、数据转换以及为所需数据集创建新变量。

数据集子集化

使用 rxDataStep() 函数对数据进行子集化也是相对直接的:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
 df_sql_subset <- rxDataStep(inData = df_sql, varsToKeep = NULL, rowSelection = (BusinessEntityID<=1000))
 OutputDataSet <- df_sql_subset'
 ,@input_data_1 = N'
 SELECT 
 BusinessEntityID
 ,[Name]
 ,SalesPersonID
 FROM [Sales].[Store]'
WITH RESULT SETS
 ((
 BusinessEntityID INT
 ,[Name] NVARCHAR(MAX)
 ,SalesPersonID INT
 ));

请记住,使用 R 代码进行子集化操作可能会带来不必要的内存和 I/O 成本,尤其是在将整个数据集泵入 R 而不是事先对数据进行子集化时。在前面的例子中,rxDataStep 中的 rowSelection 参数可以很容易地用 @input_data_1 参数中的 WHERE 子句替换。所以请记住这一点,并始终避免不必要的流量。

数据集合并

rxMerge() 函数将两个数据集合并为一个。数据集必须是数据框(或 XDF 格式),并且操作类似于 T-SQL 中的 JOIN 子句(rxMerge() 函数不应与 T-SQL 的 MERGE 语句混淆)。使用 matchVars 参数根据一个或多个变量合并两个数据集。此外,当使用本地计算上下文(我们将在下一个示例中使用)时,还需要定义数据的排序,因为 R 中的 data.frames 作为向量的集合既没有预先排序,也没有任何排序。因此,如果没有进行预先排序,则必须将 autoSort 参数设置为 true (autosort = TRUE):

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
 someExtraData <- data.frame(BusinessEntityID = 1:1200, department = rep(c("a", "b", "c", "d"), 25), Eff_score = rnorm(100))
 df_sql_merged <- rxMerge(inData1 = df_sql, inData2 = someExtraData, overwrite = TRUE, matchVars = "BusinessEntityID", type = "left" ,autoSort = TRUE)
 OutputDataSet <- df_sql_merged'
 ,@input_data_1 = N'
 SELECT 
 BusinessEntityID
 ,[Name]
 ,SalesPersonID
 FROM [Sales].[Store]'
WITH RESULT SETS
 ((
 BusinessEntityID INT
 ,[Name] NVARCHAR(MAX)
 ,SalesPersonID INT
 ,Department CHAR(1)
 ,Department_score FLOAT
 ));

这段 T-SQL 代码在两个数据集上创建了一个左连接。动态创建的数据框 2(称为 someExtraData),但它可以是任何其他从 XDF 文件读取的数据框或任何手动插入的数据集,并在 R 运行时进行连接。请注意,哪个是第一个数据框,哪个是第二个数据框,以及您正在使用哪种类型的连接。前面的例子指定了以下内容:

inData1 = df_sql, inData2 = someExtraData, type = "left" 

然而,数据框的顺序可以按以下方式更改:

inData1 = someExtraData , inData2 = df_sql, type = "left" 

然后,输出将呈现不同的形式(数据框中列的排序将改变)。

描述性统计函数

描述性统计有助于了解数据。这些是通过总结特征和度量来描述给定数据集的汇总统计,例如集中趋势和离散程度的度量。集中趋势包括计算平均值、中位数、众数,而离散程度的度量包括范围、四分位数、最小值和最大值、方差和标准差,以及偏度和峰度。

这些统计数据由RevoScaleR包中的rx-函数覆盖,这意味着您可以通过调用:rxSummaryrxCrossTabsrxMarginalsrxQuantilerxCuberxHistogram来使用该包的所有计算优势,无需担心性能、内存不足异常或哪个 R 包包含正确的函数。

我们将在AdventureWorks数据库中使用[Sales].[vPersonDemographics]视图来清楚地展示这些函数的可用性:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
 summary <- rxSummary(~ TotalChildren,  df_sql, summaryStats = c( "Mean", "StdDev", "Min", "Max","Sum","ValidObs", "MissingObs"))
 OutputDataSet <- summary$sDataFrame'
 ,@input_data_1 = N'
 SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
WITH RESULT SETS
 ((
 VariableName NVARCHAR(MAX)
 ,"Mean"NVARCHAR(100)
 ,"StdDev"NVARCHAR(100)
 ,"Min"NVARCHAR(100)
 ,"Max"NVARCHAR(100)
 ,"Sum"NVARCHAR(100)
 ,"ValidObs"NVARCHAR(100)
 ,"MissingObs"NVARCHAR(100)
 ));

您可以使用一行 R 代码获取一些汇总统计。我更喜欢使用summaryStats参数来列出统计信息,但请注意,统计信息的顺序并不代表输出顺序相同。此外,使用summary$sDataFrame数据框作为rxSummary的结果将自动生成包含所有汇总的数值变量的数据框。

T-SQL 查询的结果如下:

rxSummary()函数还包含一个公式,您可以通过它指定函数在计算描述性统计时要考虑哪些变量。在我们的例子中,我们只使用了TotalChildren变量:

rxSummary(~ TotalChildren, ... 

但假设我们想要获取所有变量的描述性统计;我们只需写下以下内容:

rxSummary(~.,  .... 

这将为我们提供所有变量的统计数据,如下面的截图所示:

注意,只有整数(连续)类型的变量将被考虑,而像MaritalStatusEducationOccupation这样的变量将显示为NULL,因为这些变量在 R 中被视为分类变量。

对于此,首先,我们需要指定因素变量,基于此,我们将能够运行统计:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
 df_sql_r <- rxFactors(inData = df_sql, sortLevels = TRUE,
factorInfo = list(MS = list(levels = c("M","S"), otherLevel=NULL, varName="MaritalStatus")))
 summary <- rxSummary(~ MS,  df_sql_r)
 OutputDataSet <- data.frame(summary$categorical)'
 ,@input_data_1 = N'
 SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
WITH RESULT SETS
 ((
 MS NVARCHAR(MAX)
 ,"Counts"INT
 ));

此函数将为MaritalStatus因素提供简单的计数:

MS Counts 
M  10011 
S   8473 

同样的逻辑可以应用于所有其他分类变量。rxSummary()函数中的公式还赋予用户组合不同变量的能力。例如,而不是使用以下代码:

rxSummary(~ TotalChildren, df_sql_r) 

我们也可以使用以下代码:

rxSummary(NumberCarsOwned ~ TotalChildren, df_sql_r) 

这将计算两个变量的观察统计:

Name                          Mean     StdDev   Min Max Sum   ValidObs MissObs 
NumberCarsOwned:TotalChildren 3.258656 4.473517 0   20  60233 18484    0          

这也可以用于计算分类变量。这些变量需要首先被重新编码为因素,然后可以计算相同的汇总统计:

rxSummary(~ TotalChildren:F(MS), df_sql_r) 

使用sp_execute_external_script的完整 R 和 T-SQL 代码如下:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
 df_sql_r <- rxFactors(inData = df_sql, sortLevels = TRUE,factorInfo = list(MS = list(levels = c("M","S"), otherLevel=NULL, varName="MaritalStatus")))
 summary <- rxSummary(~F(MS):TotalChildren, df_sql_r, summaryStats = c( "Mean", "StdDev", "Min", "Max", "ValidObs", "MissingObs", "Sum"), categorical=c("MS"))
 OutputDataSet <- data.frame(summary$categorical)'
 ,@input_data_1 = N'
 SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
WITH RESULT SETS
 ((
 Category NVARCHAR(MAX)
 ,"MS"NVARCHAR(MAX)
 ,"Means"FLOAT
 ,"StDev"FLOAT
 ,"Min"INT
 ,"Max"INT
 ,"Sum"INT
 ,"ValidObs"INT
 ));

以下是对每个因素级别的结果:

Name               Mean     StdDev   Min Max Sum   ValidObs MissingObs
 TotalChildren:F_MS 1.844352 1.612408 0   5   34091 18484    0 

Statistics by category (2 categories):

Category                  F_MS Means    StdDev   Min Max Sum   ValidObs
TotalChildren for F(MS)=M M    2.080412 1.583326 0   5   20827 10011 
TotalChildren for F(MS)=S S    1.565443 1.601977 0   5   13264  8473 

分位数和十分位数也非常有用,可以查看数据分布,RevoScaleR包提供了rxQuantile函数。使用 T-SQL,结果集可以返回如下:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
                  df_sql <- InputDataSet
                  quan <- rxQuantile(data = df_sql, varName = "TotalChildren")
                  quan <- data.frame(quan)
 values <- c("0%","25%","50%","75%","100%")
 OutputDataSet <- data.frame(values,quan)'
 ,@input_data_1 = N'
 SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
 WITH RESULT SETS
      ((
            Quartile NVARCHAR(100)
            ,QValue FLOAT
 ));

这给我们以下结果:

0%  25%  50%  75% 100% 
0    0    2    3    5 

我们也可以通过稍微修改rxQuantile()函数来修改和计算十分位数:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
 dec <- rxQuantile(data = df_sql, varName = "TotalChildren",  probs = seq(from = 0, to = 1, by = .1))
 dec <- data.frame(dec)
 values <- c("0%","10%","20%","30%","40%","50%","60%","70%","80%","90%","100%")
 OutputDataSet <- data.frame(values,dec)'
 ,@input_data_1 = N'
 SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
WITH RESULT SETS
 ((
 Decile NVARCHAR(100)
 ,DValue FLOAT
 ));

计算交叉表(两个或更多变量之间的关系)我们将使用两个函数:rxCrossTabsrxMargins。交叉表通常以列联表或其他[n][m]表格格式表示;这实际上取决于每个变量将有多少个级别。

我们将使用我们的两个变量NumberCarsOwnedTotalChildren来探索rxCrossTabs

EXEC sp_execute_external_script
 @language = N'R'
      ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
                  crosstab <- rxCrossTabs(N(NumberCarsOwned) ~ F(TotalChildren),  df_sql, means=FALSE) #means=TRUE
 children <- c(0,1,2,3,4,5)
                  OutputDataSet <- data.frame(crosstab$sums, children)'
 ,@input_data_1 = N'
 SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
WITH RESULT SETS
      ((
             NumberOfCarsOwnedSUM INT
            ,NumberOfChildren INT
 ));

使用rxCrossTabs计算交叉表可以提供两种类型的统计:给定交叉类别的观测值的计数和平均值。这是通过means = TRUEmeans = FALSE参数来操作的。该函数以需要依赖变量和独立变量的方式运行,在我们的例子中,信息可以从以下结果中检索:

Cross Tabulation Results for: N(NumberCarsOwned) ~ F(TotalChildren)
Data: df_sql
Dependent variable(s): N(NumberCarsOwned)
Number of valid observations: 18484
Number of missing observations: 0 
Statistic: sums 

为了成功计算交叉表,独立变量必须以因素的形式呈现。在这种情况下,TotalChildren变量被F()函数包装,表示在运行时进行因素转换。

这可以使用基础包或 R 中的标准条形图进行可视化:

使用以下代码使用barplot函数绘制直方图:

library(RColorBrewer) 
barplot(OutputDataSet$V1, xlab = "Number of children",ylab = "Number of cars owned", 
        legend.text = c("0 Child","1 Child","2 Child","3 Child","4 Child","5 Child"), col=brewer.pal(6, "Paired")) 

使用分类变量时,无需进行显式转换:

EXEC sp_execute_external_script
      @language = N'R'
 ,@script = N'
            library(RevoScaleR)
            df_sql <- InputDataSet
 crosstab <- rxCrossTabs(NumberCarsOwned ~ MaritalStatus,  df_sql, means=FALSE) 
            status <- c("M","S")
            OutputDataSet <- data.frame(crosstab$sums, status)'
 ,@input_data_1 = N'
 SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
WITH RESULT SETS
      ((
             NumberOfCarsOwnedSUM INT
             ,MaritalStatus NVARCHAR(100)
 ));

此外,转换参数可以用来重新编码、重新计算或以某种方式转换任何变量。从rxCrossTabs派生的列联表边缘统计可以使用rxMarginals函数调用,该函数简单地将rxCrossTabs包装起来。

边缘统计将为您提供所需变量的每一行或每一列的总和、计数或平均值:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
                  library(RevoScaleR)
                  df_sql <- InputDataSet
                  mar <- rxMarginals(rxCrossTabs(NumberCarsOwned ~ F(TotalChildren), data=df_sql, margin=TRUE, mean=FALSE))
 OutputDataSet  <- data.frame(mar$NumberCarsOwned$grand)'
 ,@input_data_1 = N'
      SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
WITH RESULT SETS
 ((
            GrandTotal INT
 ));

结果如下:

> mar$NumberCarsOwned$grand 
[1] 27776 

探索数据也可以使用图表进行,RevoScaleR包提供了折线图和条形图,两者都旨在处理大型数据集。

以下是一个变量的简单预览:

使用以下 R 代码行:

rxHistogram(~NumberCarsOwned, data=df_sql) 

这已经按照以下方式转换为婚姻状况因素:

rxHistogram(~F(MS), data=df_sql_r) 

此外,变量可以组合如下(婚姻状况与拥有汽车的数量):

rxHistogram(~ NumberCarsOwned | F(MS), title="Cars owned per Marital Status",  numBreaks=10, data = df_sql_r) 

我们得到以下图表,显示了婚姻状况(M - 已婚;S - 未婚)和拥有汽车的总数作为一个分类变量(0 - 没有汽车,1 - 拥有一辆汽车,2 - 拥有两辆汽车,3 - 拥有三辆汽车,以及 4 - 拥有四辆汽车):

除了条形图,我们还可以使用折线图,但这次使用不同的变量:

rxLinePlot(as.numeric(log(TotalPurchaseYTD)) ~ as.factor(DateFirstPurchase), data = df_sql_r, rowSelection=  
             DateFirstPurchase >= "2001-07-01 00:00:00.000"&amp; DateFirstPurchase <= "2001-07-17 00:00:00.000", type="p") 

对于超过半年时间(从 2001 年 1 月 1 日到 7 月 17 日)的时间段,图表显示的是这段时间内总购买量的对数变量。在这种情况下,我们需要对日期变量进行因式分解,并且我们还在使用log()函数来平衡购买量。而不是使用rxHistogram,我们使用另一个RevoScaleR函数rxLinePlot来为大型数据集绘制图表。rxLinePlot代表折线图:

图片

因此,最终我们可以使用par()函数将所有三个图表结合起来,排列成两列,每列包含一个或两个图表:

# combined 
h1 <- rxHistogram(~NumberCarsOwned, data=df_sql) 
h2 <- rxHistogram(~F(MS), data=df_sql_r) 
p1 <- rxLinePlot(as.numeric(log(TotalPurchaseYTD)) ~ as.factor(DateFirstPurchase), data = df_sql_r, rowSelection=  
             DateFirstPurchase >= "2001-07-01 00:00:00.000"&amp; DateFirstPurchase <= "2001-07-17 00:00:00.000", type="p") 

print(h1, position = c(0, 0.5, 0.5, 1), more = TRUE) 
print(h2, position = c(0.5, 0.5, 1, 1), more = TRUE) 
print(p1, position = c(0.5, 0.05, 1, 0.5)) 

使用图表对于讲故事、客户旅程或通过结合最有信息量的变量快速理解数据都是有益的。另一种好方法是使用 Markdown 文档,并在一个块中包含多个图表。当使用par()函数与rxHistogramrxLinePlot结合时,它可能并不总是按预期显示图表。这是由于与par()函数的一些兼容性问题。作为替代,使用print()函数并定位每个图表是另一种方法,这样可以避免可能的问题:

图片

统计测试和抽样的函数

统计测试对于确定两个(或更多)变量之间的相关性及其相关方向(正相关、中性或负相关)非常重要。从统计学的角度来看,相关性是衡量两个变量之间关联强度及其方向的度量。RevoScaleR包支持计算卡方、费舍尔和肯德尔秩相关。根据变量的类型,你可以区分肯德尔、斯皮尔曼或皮尔逊相关系数。

对于卡方测试,我们将使用rxChiSquareTest()函数,该函数使用列联表来查看两个变量是否相关。小的卡方测试统计量意味着观察到的数据与预期数据非常吻合,表示存在相关性。计算卡方的公式如下:

图片

在计算这个统计独立性测试之前,我们必须有xCrossTab或 xCube 格式的数据。因此,T-SQL 查询需要首先生成交叉表,以便计算卡方系数。

卡方是在两个分类变量上生成的如下:

EXEC sp_execute_external_script
 @language = N'R'
 ,@script = N'
 library(RevoScaleR)
 df_sql <- InputDataSet
 df_sql_r <- rxFactors(inData = df_sql, sortLevels = TRUE,factorInfo = list(MS = list(levels = c("M","S"), otherLevel=NULL, varName="MaritalStatus")))
 df_sql_r$Occupation <- as.factor(df_sql_r$Occupation)
 df_sql_r$MS <- df_sql_r$MS
 testData <- data.frame(Occupation = df_sql_r$Occupation, Status=df_sql_r$MS)
 d <- rxCrossTabs(~Occupation:Status,  testData, returnXtabs = TRUE)
 chi_q <- rxChiSquaredTest(d)

 #results
 xs <- chi_q$''X-squared''
 p <- chi_q$''p-value''
 OutputDataSet <- data.frame(xs,p)'
 ,@input_data_1 = N'
 SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
WITH RESULT SETS
 ((
 Chi_square_value NVARCHAR(100)
 ,Stat_significance NVARCHAR(100)
 ));

返回了以下结果:

Chi-squared test of independence between Occupation and Status 
 X-squared df p-value
 588.2861  4  5.312913e-126

使用 Kendall Tau,你可以使用 R 代码计算排名与先前相关性的相关性:

rxKendallCor(d, type = "b")

以下是一些结果:

 estimate 1 p-value
 -0.05179647       0
 HA: two.sided 

这个同样的原则也可以用在 T-SQL 查询中:

EXEC sp_execute_external_script
          @language = N'R'
          ,@script = N'
                library(RevoScaleR)
                df_sql <- InputDataSet
                df_sql_r <- rxFactors(inData = df_sql, factorInfo = list(MS = list(levels = c("M","S"), otherLevel=NULL, varName="MaritalStatus")))
                df_sql_r$Occupation <- as.factor(df_sql_r$Occupation)
                df_sql_r$MS <- df_sql_r$MS
                testData <- data.frame(Occupation = df_sql_r$Occupation, Status=df_sql_r$MS)
                d <- rxCrossTabs(~Occupation:Status,  testData, returnXtabs = TRUE)
                ken <- rxKendallCor(d, type = "b")

                k <- ken$`estimate 1`
                p <- ken$`p-value`

                #results
                OutputDataSet <- data.frame(k,p)'
          ,@input_data_1 = N'
          SELECT * FROM [Sales].[vPersonDemographics] WHERE [DateFirstPurchase] IS NOT NULL'
    WITH RESULT SETS
          ((
                 Kendall_value NVARCHAR(100)
                ,Stat_significance NVARCHAR(100)
          ));

返回了许多其他可以用来计算变量之间相关性的原则。但这些都超出了本书的范围,因此我们只关注了必要的部分。

预测建模的相关函数将在下一章介绍 - 第六章,预测建模

摘要

本章介绍了数据操作和数据整理的重要函数(以及其他许多函数)。这些步骤对于理解数据集的结构、数据集的内容以及数据的分布至关重要。这些步骤主要用于理解频率、描述性统计,以及一些统计抽样和统计相关性。

在进行数据清洗和数据合并之前,必须(或应该)完成这些步骤,以便更好地理解数据。数据清洗是最重要的,因为异常值可能会将敏感数据(或任何类型的数据)引向奇怪或错误的结论:它也可能使结果偏向其他方向。因此,通过使用强大的rx-函数(或类)将这些步骤视为高度重要,应该是每位数据工程师、数据整理员以及数据科学家的任务。下一章将专注于RevoScaleR的预测建模函数,主要关注创建模型以及在这些模型上运行预测。

第六章:预测建模

预测建模是一个过程,它使用高级统计和概率算法来预测结果,基于预训练和构建的模型或函数。这些算法可以根据预测变量的结果分为算法家族。结果通常是预测的值,它解释了未来的行为。几个变量或输入数据组成一个数学函数,也称为模型(因此也称为数据建模),这些输入数据试图解释或预测结果。为了更好地理解预测建模,本章将包括以下主题:

  • 数据建模

  • 高级预测算法

  • 预测分析

  • 部署和使用预测解决方案

  • 在 SQL Server 数据库中使用 R 服务进行预测

本章的重点将在于深入探讨如何在 SQL Server 2016/2017 中使用 R 来解决典型的商业问题,以了解预测建模的应用。在企业环境中,一个商业问题可以从一个非常广泛的方面来定义。例如,在医学领域,预测建模可以帮助理解和解决的一个典型问题可能是:药物 C 中成分 A 和 B 的变化是否有助于治愈疾病?此外,在冶金行业,我们能否模拟防腐蚀涂料漆随时间老化的过程——或者在零售业,顾客如何根据他们的需求或行为在商店中选择更好的产品?可以说,我们的日常生活与预测和预测息息相关。通常,我们所有人面对的每一个物流问题都是一个关于可能非常相关主题的简单问题:如果我晚 5 分钟离开家去上班,如果我走一条捷径,这会影响我的驾驶时间吗?等等。实际上,我们可以说,我们的日常决策是我们采取的所有行动的输出总和。

数据建模

数据建模是一个过程,我们试图找到一组独立变量或输入数据的一个函数(或所谓的模型)。就像在数据仓库中,建模是指基于物理数据结构建立概念框架,并在 ORM 或 UML(甚至 CRC)图的帮助下探索数据结构,这与在预测分析中探索结构时看到的是一样的。在后一种情况下,数据建模是探索两个或多个变量之间的结构(或关系)。这些关系可以表示为一个函数,并且本质上存储为模型。

要开始建模,我们将使用以下 GitHub 仓库中可用的 Microsoft 数据:

github.com/Microsoft/sql-server-samples/tree/master/samples/features/machine-learning-services/python/getting-started/rental-prediction

不要在这个 Python 示例中感到困惑:

下载此数据库将下载TutorialDB.bak文件,你只需将其恢复到已安装 R in-database 的 SQL Server 实例中。此数据库是本章附带代码的一部分。

建模数据的一部分是设置对后续预测工作方式的了解。因此,在这个阶段,我们将创建对变量及其相互关系的理解。从下载的文件中创建恢复,并运行以下从备份 T-SQL 命令的恢复:

USE [master]
BACKUP LOG [TutorialDB] TO DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup\TutorialDB_LogBackup_2018-01-01_23-59-09.bak'
WITH NOFORMAT, NOINIT, NAME = N'TutorialDB_LogBackup_2018-01-01_23-59-09', NOSKIP, NOREWIND, NOUNLOAD, NORECOVERY , STATS = 5

RESTORE DATABASE [TutorialDB] FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup\TutorialDB.bak' 
WITH FILE = 2, MOVE N'TutorialDB' TO N'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\TutorialDB.mdf', 
MOVE N'TutorialDB_log' TO N'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\TutorialDB_log.ldf', NOUNLOAD, STATS = 5

GO 

或者,你可以在 SSMS 中简单地使用RESTORE命令:

现在,你将拥有已恢复的数据库和dbo.rental_data表可供使用。目前,这已经足够了。

数据集准备就绪后,我们现在可以通过探索和理解变量及其相互关系来开始建模数据。这种快速探索可以在 SQL Operation Studio 中进行(下载链接:docs.microsoft.com/en-us/sql/sql-operations-studio/download),我们将使用一个简单的查询:

SELECT RentalCount,Day,Month, Holiday, Snow FROM rental_data  

除了标准的表格视图结果外,这还将提供一个很好的图表查看器,其中变量的简单图形表示将帮助你更好地了解数据:

但如果没有对描述性统计学的总体理解,我们就不会继续。因此,使用RevoScaleR包中的rxSummary函数将得到期望的结果:

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
 dr_rent <- InputDataSet
 dr_rent <- data.frame(dr_rent)
    summary <- rxSummary(~ RentalCount  + Year + Month + Day  + WeekDay + Snow + Holiday , data = dr_rent)
    OutputDataSet <- summary$sDataFrame'
,@input_data_1 = N'SELECT  RentalCount, Year, Month, Day, WeekDay, Snow, Holiday FROM rental_data'
WITH RESULT SETS ((
 [Name]   NVARCHAR(100)
 ,Mean   NUMERIC(16,3)
 ,StdDev  NUMERIC(16,3)
 ,[Min]   INT
 ,[Max]  INT
 ,ValidObs  INT
 ,MissingObs INT
));
GO

以下是一个简单的描述性统计表格的结果:

探索单变量和多变量统计是前一章第五章,“RevoScaleR 包”的一部分,但在这里我们将更多地关注双变量和多变量统计。在我们开始之前,让我们进一步探索相关性。根据探索变量名称和描述性统计,常识会告诉我们,在假期期间,租赁数量应该更高。可以通过相关系数来检查这一点。以下是一个简单的例子:

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
      dr_rent <- InputDataSet
      OutputDataSet <- data.frame(cor(dr_rent$Holiday, dr_rent$RentalCount))
'
,@input_data_1 = N'SELECT  Holiday, RentalCount FROM rental_data'
WITH RESULT SETS ((
 cor NUMERIC(10,3)
 ));
GO

这将给出0.332的双变量关系的概念。这是一个弱相关性,但却是正相关的:

这仅仅意味着,如果RentalCount变量值增加,假期的数量也会增加。这确实是有道理的,因为如果假期越来越多,预期的租赁数量也会增加。

现在,我们可以通过结合每个变量来继续探索和寻找相关性。这类似于进行 CROSS JOIN,但还有更简单的方法来做这件事。其中一种方法当然是,当然,通过使用常识并选择有意义的关联:

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
    dr_rent <- InputDataSet
    dr_rent <- data.frame(dr_rent)
    cor_HR <- cor(dr_rent$Holiday, dr_rent$RentalCount)
    cor_FR <- cor(as.numeric(dr_rent$FWeekDay), dr_rent$RentalCount)
    cor_MR <- cor(dr_rent$Month, dr_rent$RentalCount)
    cor_YR <- cor(dr_rent$Year,dr_rent$RentalCount)
    d <- data.frame(cbind(cor_HR, cor_FR, cor_MR, cor_YR))
    OutputDataSet <- d'
    ,@input_data_1 = N'SELECT  Holiday, RentalCount,Month,FWeekDay, Year FROM rental_data'
WITH RESULT SETS ((
 cor_HR NUMERIC(10,3)
,cor_FR NUMERIC(10,3)
,cor_MR NUMERIC(10,3)
,cor_YR NUMERIC(10,3)
));
GO

如下图中所示,我们得到了以下结果。对结果的理解和解释非常重要。因此,假期时间到目前为止是与租赁计数变量最相关的变量。无论是星期几还是年份,都没有起到任何显著的作用。MonthRentalCount之间存在一个非常微小的负相关性-0.110,这可以理解为月份较高的月份可能有较低的租赁计数,反之亦然。由于这种相关性非常弱,因此没有必要对这种特定的相关性大惊小怪(即使它有意义或没有意义):

图片

同样,可以通过绘制箱线图来探索每个变量中值的分布:

图片

第二种方法是绘制变量之间的相关性图。一种方法是调用corrplot R 库,它为你提供了一个非常强大和有用的可视化。我倾向于创建以下代码:

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
 library(corrplot)  # * footnote
 dr_rent <- InputDataSet

 dr_rent$FWeekDay <- as.numeric(dr_rent$FWeekDay)
 dr_rent$FHoliday <- as.numeric(dr_rent$FHoliday)
            dr_rent$FSnow <- as.numeric(dr_rent$FSnow)

    cor.mtest <- function(mat, ...) {
         mat <- as.matrix(mat)
         n <- ncol(mat)
         p.mat<- matrix(NA, n, n)
         diag(p.mat) <- 0
         for (i in 1:(n - 1)) {
             for (j in (i + 1):n) {
             tmp <- cor.test(mat[, i], mat[, j], ...)
             p.mat[i, j] <- p.mat[j, i] <- tmp$p.value
             }
         }
         colnames(p.mat) <- rownames(p.mat) <- colnames(mat)
         p.mat
         }
    p.mat <- cor.mtest(dr_rent)

    R<-cor(dr_rent)

    col <- colorRampPalette(c("#BB4444", "#EE9988", "#FFFFFF", "#77AADD", "#4477AA"))

    image_file = tempfile(); 
    jpeg(filename = image_file); 

plot_corr <- corrplot(R, method="color", col=col(200), 
 type="upper", order="hclust", 
 addCoef.col = "black", # Add coefficient of correlation
 tl.col="black", tl.srt=45, #Text label color and rotation
 # Combine with significance
 p.mat = p.mat, sig.level = 0.01, insig = "blank", 
 # hide correlation coefficient on the principal diagonal
 diag=FALSE)
 dev.off(); 
OutputDataSet <- data.frame(data=readBin(file(image_file, "rb"), what=raw(), n=1e6));  '

,@input_data_1 = N'SELECT  *  FROM rental_data'
WITH RESULT SETS ((
 correlation_plot varbinary(max)
));
GO

从 corrplot lattice 文档复制并稍作修改的代码。

这个过程可以直接在 SSRS 或 Power BI 套件或 Excel 中实现和使用;可视化效果如下:

图片

在单一图表中,有经验的眼睛会立即看到相关性和它们的统计显著性。因此,这里可以看到0.33 RentalCountHoliday的相关性,同时RentalCountSnow也有0.19的正相关性。但如果我们想探索值分散(方差)的行为,我们也可以包括方差分析。

如果你正在处理大型数据集或 XDF 数据格式,RevoScaleR包还配备了计算和计算相关矩阵的函数。以下是一个使用rxCovCor(或者,也可以使用rxCorrxCov)的 R 代码:

Formula_correlation =  ~ RentalCount + Year + Month + Day  + WeekDay + Snow + Holiday 
allCor <- rxCovCor(Formula_correlation, data = dr_rent, type = "Cor")
allCor

这给出了与所有之前计算的相关性相同的结果:

图片

此输出还具有查看标准差、平均值和权重总和的能力,但最好的部分是它将结果存储在数据框中,可以轻松导入或与其他 T-SQL 表一起使用。结果可以使用allCov$CovCor(R 语言将结果存储为列表对象,可以通过使用美元符号$并引用列表名称来检索每个列表——在这种情况下,CovCor)来调用。

当我们想要进一步调查到目前为止最高的RentalCountHoliday之间的相关性时,方差分析(ANOVA)将是适当的方法。我们将比较变量Holiday的两个组(或水平)(0不是假期,而1是假期)之间的租赁计数是否有差异。通过这样做,计算 F 统计量和其显著性将告诉我们组间方差与组内方差的比率:

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
            #ANOVA
    ANOVA <- aov(RentalCount ~ Holiday, data = InputDataSet) 
            F_Stat<- unlist(summary(ANOVA))[7]
            F_Stat_Sig <- unlist(summary(ANOVA))[9]
            df <- cbind(F_Stat, F_Stat_Sig)
OutputDataSet <- data.frame(df)'

,@input_data_1 = N'SELECT  RentalCount,Holiday FROM rental_data'
WITH RESULT SETS ((
 F_Statistic NVARCHAR(200)
,Statistical_Significance NUMERIC(16,5)
));
GO

在使用 R 代码运行 T-SQL 代码进行 ANOVA 统计计算后,输出结果以这种方式创建,返回 F 统计量和统计显著性。以下图显示了返回的结果:

结果告诉我们,F 统计量在统计上具有显著性——尽管它很小——这意味着均值很可能不相等(在这种情况下,我们会拒绝零假设)。要找到差异所在,TukeyHDS测试会给我们更多信息。

只为了说明差异,因为我们不会深入细节,我们可以使用租赁的假日分布差异的stripchart可视化:

使用 R 代码:

stripchart(RentalCount ~ Holiday, vertical=TRUE, pch=9, 
               data=dr_rent_ANOVA, xlab="Holiday day (Yes/No)", ylab="Rental count", method="jitter", jitter=0.09)

案例的分布可以告诉我们,在假日,平均租赁次数为400或更高,而在正常日子里,1050之间有大量的计数密度。

在确定哪些特征(变量)适合进一步分析和预测算法时,我们可以使用减少基尼均值的计算。randomForest包中有一个 Gini 均值函数可用,因此,让我们调用该函数并查看要使用的变量:

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
 library(randomForest)
 dr_rent  <- InputDataSet
 fit_RF <- randomForest(factor(dr_rent$RentalCount)~., data=dr_rent)
 vp_rf <- importance(fit_RF)

      vp_rf<- data.frame(vp_rf)
      imena <- row.names(vp_rf)
      vp_rf <- data.frame(cbind(imena, vp_rf))
    OutputDataSet <- vp_rf'

,@input_data_1 = N'SELECT  *  FROM rental_data'
WITH RESULT SETS ((
    Variable NVARCHAR(200)
,MeanDecreaseGini NUMERIC(16,5)
));
GO    

使用 T-SQL 代码,我们返回减少的基尼系数:

基尼系数也可以用散点图的形式直观表示,这样用户可以立即确定哪些变量对模型贡献最大。为了简洁起见,此图的代码包含在代码中,但不在书中。

现在可以确定以下哪些变量在模型中扮演任何角色或有所贡献。MeanDecreaseGini被绘制为varImpPlot(fit_RF)。从技术上讲,这是确定哪些变量或输入参数影响最小或最大的方法,但每种技术都会给出一些方面——模型中可能好的,以及可能不好的。比较相关矩阵和平均减少图中的Holiday变量,你可以看到它给出了不同的方法和不同的结果。最显著的是那些通过几种不同的方法,特定变量根本不起任何重要作用的情况。

高级预测算法和分析

到目前为止,我们已经检查了RevoScaleR包中可用的数据准备和数据分析函数。除了这些函数之外,预测分类或回归问题也可以完成,尤其是在处理大型数据集时。

我将只提及其中的一些。完整的列表可在网上找到(docs.microsoft.com/en-us/machine-learning-server/r-reference/revoscaler/revoscaler),以下是一些要点:

  • rxLinMod:这是用于构建和预测线性模型

  • rxLogit:这个函数用于构建和预测逻辑回归模型

  • rxGlm:这个函数用于创建广义线性模型

  • rxDTree:这个函数用于创建分类或回归树

  • rxBTrees:这个函数用于构建分类或回归决策森林——即使用随机梯度提升算法

  • rxDForest:这个函数用于构建分类或回归决策森林模型

  • rxNaiveBayes:这个函数用于构建朴素贝叶斯分类模型

所有这些算法都是监督算法家族的一部分,其中RevoScaleR包中唯一的无监督(或非指导)算法是rxKMeans,它用于处理聚类。

使用我们之前使用的相同数据集,我们插入并开始使用rxLinModrxGlm来演示如何在 T-SQL 中使用它们:

USE RentalDB;
GO

-- rxLinMod

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
            library(RevoScaleR)
            dr_rent <- InputDataSet
            Formula_supervised =  RentalCount ~ Year + Month + Day  + WeekDay + Snow + Holiday             
            #Create Linear Model 
            rent_lm <- rxLinMod(formula=Formula_supervised, data = dr_rent)

            #PREDICT   
            rent_Pred <- rxPredict(modelObject = rent_lm, data = dr_rent, extraVarsToWrite = c("RentalCount","Year","Month","Day"), writeModelVars = TRUE)
            OutputDataSet <- data.frame(rent_Pred)
'
,@input_data_1 = N'SELECT RentalCount,Year, Month, Day, WeekDay,Snow,Holiday  FROM rental_data'
WITH RESULT SETS ((
 RentalCount_Pred    NUMERIC(16,3)
,RentalCount  NUMERIC(16,3)
,YearINT
,MonthINT
,DayINT
,WeekDayINT
,Snow  INT
,Holiday INT
));
GO

-- rxGlm

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
            library(RevoScaleR)
            dr_rent <- InputDataSet
            Formula_supervised =  RentalCount ~ Year + Month + Day  + WeekDay + Snow + Holiday             

            #PREDICT   
                rent_glm <- rxGlm(formula = Formula_supervised, family = Gamma, dropFirst = TRUE, data = dr_rent)
                rent_Pred <- rxPredict(modelObject = rent_glm, data = dr_rent, extraVarsToWrite=c("RentalCount","Year","Month","Day"), writeModelVars = TRUE)
            OutputDataSet <- data.frame(rent_Pred)'
,@input_data_1 = N'SELECT RentalCount,Year, Month, Day, WeekDay,Snow,Holiday  FROM rental_data'
WITH RESULT SETS ((
 RentalCount_Pred    NUMERIC(16,3)
,RentalCount  NUMERIC(16,3)
,YearINT
,MonthINT
,DayINT
,WeekDayINT
,Snow  INT
,Holiday INT
));
GO

-- rxDTree

EXEC sp_execute_external_Script
@LANGUAGE = N'R'
,@script = N'
            library(RevoScaleR)
            dr_rent <- InputDataSet
            Formula_supervised =  RentalCount ~ Year + Month + Day  + WeekDay + Snow + Holiday             

            #PREDICT   
                        rent_dt <- rxDTree(formula = Formula_supervised, data = dr_rent)
                        rent_Pred <- rxPredict(modelObject = rent_dt, data = dr_rent, extraVarsToWrite=c("RentalCount","Year","Month","Day"), writeModelVars = TRUE)
            OutputDataSet <- data.frame(rent_Pred)

                '
,@input_data_1 = N'SELECT RentalCount,Year, Month, Day, WeekDay,Snow,Holiday  FROM rental_data'
WITH RESULT SETS ((
 RentalCount_Pred    NUMERIC(16,3)
,RentalCount  NUMERIC(16,3)
,YearINT
,MonthINT
,DayINT
,WeekDayINT
,Snow  INT
,Holiday INT
));
GO  

这两个都会根据输入的数据集提供预测值,以及新的预测值:

一个好奇的眼睛会告诉你,预测值与原始值相差甚远。因此,两种情况下的预测公式都在尝试根据变量:Year(年)、Month(月)、Day(日)、WeekDay(星期几)、Snow(雪)和Holiday(假日)来预测变量RentalCount。公式设置如下:

Formula_supervised =  RentalCount ~ Year + Month + Day  + WeekDay + Snow + Holiday 

比较变量RentalCount_PredRentalCount将显示真实值和预测值之间的差异/偏移量。

在前面的示例中,您还可以比较所有三个算法的结果。如果您对所有三个数据集进行逐个观察的比较,您可以立即看到哪些算法表现最好:

因此,黄色条形表示原始值,迄今为止,决策树算法在给定上述公式并理解数据洞察的情况下,是最准确的。图表仅代表随机选取的观察结果。这也可以通过计算准确度或衡量标准来实现,该标准计算了与原始值之间的偏差程度。

部署和使用预测解决方案

在开发数据库内解决方案并为其持续开发(以及部署)创建时,应考虑几个方面。首先,数据科学家将工作的环境。你可能给他们一个强大的独立服务器,甚至在云中分配适当的座位。他们需要它,尤其是在训练模型时。这非常重要,因为你不希望你的高薪统计学家和数学家等待模型计算和生成。因此,启用通往高度可扩展的 CPU 和 RAM 强大计算的路是由必要的。其次,你必须将数据放在那里。无论是在云中还是在本地,将数据放在那里(以及稍后返回)不应被忽视,因为这可能是你失去宝贵时间的地方。最后,拥有设置正确的环境、环境变量、包以及所有专有软件路径的启用也是非常重要的。

RevoScaleR包自带了一个用于轻松切换计算环境的函数。我们现在将在 R 中调用一个简单的命令:

rxSetComputeContext(local)
rxSetComputeContext(sql)  

通过这样做,你可以设置本地计算环境(即客户端的机器)或服务器端,在这种情况下,一个独立的 R 服务器将驻留。通过简单的函数调用,计算上下文(或简单环境)就会切换,当然,要考虑到所有数据都存储在两边(这样你可以避免不必要的传输),并且所有服务器环境变量都设置正确。

对于训练模型,可以选择一些良好的实践。将数据分割用于训练、测试或训练、测试和验证是几种实践。此外,一个非常好的实践是测试训练/测试/验证数据集的百分比。你可能得到 50/50 或 60/40 或 70/30 的百分比,但通常你会在挖掘数据时进行这项操作并做出决定。之后,你还应该考虑数据的验证;有几种方法可供选择,从留一法LOO)或 10 折法或 5 折法来选择用于验证结果的数据。

不深入探讨这个主题,为了使这个演示更简单,我们可以现场决定采用 70/30 的百分比。由于我们有幸使用 T-SQL 数据库,我们可以选择并将训练子集存储在表中,或者创建一个视图,或者决定我们想要取的 70%。

-- We can set 70% of the original data
-- IN SQL Server
SELECT
TOP (70)PERCENT
*
INTO dbo.Train_rental_data
FROM rental_data
ORDER BY ABS (CAST(BINARY_CHECKSUM(RentalCount,NEWID())asint))ASC
-- (318 rows affected) 

-- Or we can set by the year; year 2013 and 2014 for training and 2015 for testing? making it cca 70% for training as well
SELECT COUNT(*),YEAR FROM rental_data GROUP BY YEAR

这也严重依赖于你的商业模式。第一种方法简单地从原始数据集中取出 70%的数据,而第二种选择语句大致取出原始数据的 70%,但基于租赁年份进行分割。这可能会对模型的行为产生关键影响,也会影响你希望决策如何受到这种影响,尤其是商业模式。

清晰并覆盖这些内容后,一个最佳实践是将训练好的模型存储在表中以实现更快的预测。现在我们将创建一个如下所示的表:

-- or in R
EXEC sp_execute_external_Script
        @language = N'R'
        ,@script = N'
                        library(caTools)

                        set.seed(2910) 
                        dr_rent <- InputDataSet
                        Split <- .70
                        sample = sample.split(dr_rent$RentalCount, SplitRatio = Split)
                        train_dr_rent <- subset(dr_rent, sample == TRUE)
                        test_dr_rent  <- subset(dr_rent, sample == FALSE)
            OutputDataSet <- data.frame(train_dr_rent)

                '
,@input_data_1 = N'SELECT * FROM rental_data'
WITH RESULT SETS ((
         [Year] INT
        ,[Month] INT
        ,[Day] INT
        ,[RentalCount] INT
        ,[WeekDay] INT
        ,[Holiday] INT
        ,[Snow] INT
        ,[FHoliday] INT
        ,[FSnow] INT
        ,[FWeekDay] INT
));
GO 

由于set.seed已被定义,无论你在哪里运行此代码,你都将始终得到相同的子集。如果你想得到不同的结果,你应该将其注释掉。

采样再次完成后,根据你预测的问题,你需要定义你的预测公式。在这种情况下,我正在使用公式转换器来创建一个合适的公式:

-- Variables to keep
-- and creating formula
EXEC sp_execute_external_Script
        @language = N'R'
        ,@script = N'
        dr_rent <- InputDataSet
            variables_all <- rxGetVarNames(dr_rent)
            variables_to_remove <- c("FSnow", "FWeekDay", "FHoliday")
            traning_variables <- variables_all[!(variables_all %in% c("RentalCount", variables_to_remove))]
            #use as.formula to create an object
        formula <- as.formula(paste("RentalCount ~", paste(traning_variables, collapse = "+")))
                #formula <- paste("RentalCount ~", paste(traning_variables, collapse = "+"))
            OutputDataSet <- data.frame(formula)'
,@input_data_1 = N'SELECT * FROM dbo.Train_rental_data'
WITH RESULT SETS ((
         [Formula_supervised] NVARCHAR(1000)
));
GO 

通过一个过程创建公式,使其不是硬编码的,也是一种非常实用的方法,尤其是在企业环境中,数据科学家会设置独立变量的池,然后在数据工程师选择要包含哪些变量之前,将数据推送到计算模型并部署它。

双变量和多变量统计过程也可以给数据工程师和管理员提供更好的洞察力和理解,了解数据和如何操作以及相关性,以及没有不想要的关联或不起作用的变量。

清晰地了解这一点后,我们可以设置和构建运行模型训练并存储在数据库中的过程。由于本章篇幅有限,我将只展示创建一个过程的示例;其余的过程可以在配套的章节材料中找到:

T-SQL 中随机森林的流程看起来是这样的:

-- Random forest

DROP PROCEDURE IF EXISTS dbo.forest_model;
GO

CREATE OR ALTER PROCEDURE dbo.forest_model(
         @trained_model VARBINARY(MAX)OUTPUT
        ,@accuracy FLOATOUTPUT
        )
AS
BEGIN
EXEC sp_execute_external_script
@language = N'R'
,@script = N'
                        library(RevoScaleR)
                        library(caTools)
            library(MLmetrics)

                        dr_rent <- InputDataSet
                        set.seed(2910) 
                        Split <- .70
                        sample = sample.split(dr_rent$RentalCount, SplitRatio = Split)
                        train_dr_rent <- subset(dr_rent, sample == TRUE)
                        test_dr_rent  <- subset(dr_rent, sample == FALSE)

                        y_train <- train_dr_rent$RentalCount
                        y_test <- test_dr_rent$RentalCount

            variables_all <- rxGetVarNames(dr_rent)
            variables_to_remove <- c("FSnow", "FWeekDay", "FHoliday")
            traning_variables <- variables_all[!(variables_all %in% c("RentalCount", variables_to_remove))]
            formula <- as.formula(paste("RentalCount ~", paste(traning_variables, collapse = "+")))

                        forest_model <- rxDForest(formula = formula,
                          data = train_dr_rent,
                          nTree = 40,
                          minSplit = 10,
                          minBucket = 5,
                          cp = 0.00005,
                          seed = 5)

                        trained_model <- as.raw(serialize(forest_model, connection=NULL))

                        #calculating accuracy
            y_predicted<- rxPredict(forest_model,test_dr_rent)

            predict_forest <-data.frame(actual=y_test,pred=y_predicted)
            #ConfMat <- confusionMatrix(table(predict_forest$actual,predict_forest$RentalCount_Pred))
            #accuracy <- ConfMat$overall[1]
            accu <- LogLoss(y_pred = predict_forest$RentalCount_Pred , y_true =predict_forest$actual)
            accuracy <- accu'

        ,@input_data_1 = N'SELECT * FROM dbo.rental_data'
        ,@params = N'@trained_model VARBINARY(MAX) OUTPUT, @accuracy FLOAT OUTPUT'
        ,@trained_model = @trained_model OUTPUT
        ,@accuracy = @accuracy OUTPUT;
END;
GO 

我在过程中添加了一些额外的东西,这样每次训练模型时都会添加一些额外的信息。这是准确性,它也会让数据工程师和后期阶段的管理员对决定哪个模型优于其他模型有很好的洞察力。

你可以简单地按照以下方式运行该过程:

DECLARE @model VARBINARY(MAX);
DECLARE @accur FLOAT;
EXEC dbo.forest_model@model OUTPUT, @accur OUTPUT;
INSERT INTO [dbo].Rental_data_models VALUES ('Random_forest_V1', @model, @accur);
GO  

这将填充存储模型的目标表。结果应存储在表 [dbo].[Rental_data_models] 中:

完成这些后,你需要设置评估过程,这将有助于确定哪个模型表现最好。然而,这部分可以使用 Power BI、报告服务或简单地使用 R 来完成。

这是 R 代码的一部分,可以包含在你的可视化工具中以便更容易理解:

library(RevoScaleR) 
library(caTools) 
library(MLmetrics) 

#evaluate_model function; Source: Microsoft 
evaluate_model <- function(observed, predicted_probability, threshold, model_name) {  

  # Given the observed labels and the predicted probability, plot the ROC curve and determine the AUC. 
  data <- data.frame(observed, predicted_probability) 
  data$observed <- as.numeric(as.character(data$observed)) 
  if(model_name =="RF"){ 
    rxRocCurve(actualVarName = "observed", predVarNames = "predicted_probability", data = data, numBreaks = 1000, title = "RF" ) 
  }else{ 
    rxRocCurve(actualVarName = "observed", predVarNames = "predicted_probability", data = data, numBreaks = 1000, title = "GBT" ) 
  } 
  ROC <- rxRoc(actualVarName = "observed", predVarNames = "predicted_probability", data = data, numBreaks = 1000) 
  auc <- rxAuc(ROC) 

  # Given the predicted probability and the threshold, determine the binary prediction. 
  predicted <- ifelse(predicted_probability > threshold, 1, 0)  
  predicted <- factor(predicted, levels = c(0, 1))  

  # Build the corresponding Confusion Matrix, then compute the Accuracy, Precision, Recall, and F-Score. 
  confusion <- table(observed, predicted) 
  print(model_name) 
  print(confusion)  
  tp <- confusion[1, 1]  
  fn <- confusion[1, 2]  
  fp <- confusion[2, 1]  
  tn <- confusion[2, 2]  
  accuracy <- (tp + tn) / (tp + fn + fp + tn)  
  precision <- tp / (tp + fp)  
  recall <- tp / (tp + fn)  
  fscore <- 2 * (precision * recall) / (precision + recall)  

  # Return the computed metrics. 
  metrics <- list("Accuracy" = accuracy,  
"Precision" = precision,  
"Recall" = recall,  
"F-Score" = fscore, 
"AUC" = auc)  
  return(metrics)  
}  

RF_Scoring <- rxPredict(forest_model, data = train_dr_rent, overwrite = T, type = "response",extraVarsToWrite = c("RentalCount")) 

Prediction_RF <- rxImport(inData = RF_Scoring, stringsAsFactors = T, outFile = NULL) 
observed <- Prediction_RF$RentalCount 

# Compute the performance metrics of the model. 
Metrics_RF <- evaluate_model(observed = observed, predicted_probability = Prediction_RF$RentalCount_Pred , model_name = "RF", threshold=50) 

# Make Predictions, then import them into R. The observed Conversion_Flag is kept through the argument extraVarsToWrite. 
GBT_Scoring <- rxPredict(btree_model,data = train_dr_rent, overwrite = T, type="prob",extraVarsToWrite = c("RentalCount")) 

Prediction_GBT <- rxImport(inData = GBT_Scoring, stringsAsFactors = T, outFile = NULL) 
observed <- Prediction_GBT$RentalCount 

观察到的值应该告诉你哪个模型表现最好。一旦你完成了这个,你就可以选择模型并查看预测是如何进行的。

在 SQL Server 数据库中使用 R 服务进行预测

调用存储过程是组织代码并立即开始预测的最简单方法。

再次,这里只展示如何创建一个存储过程来预测新的数据集的示例:

CREATE OR ALTER PROCEDURE [dbo].[Predicting_rentalCount] 
(
                 @model VARCHAR(30)
                ,@query NVARCHAR(MAX)
)
AS
BEGIN
        DECLARE @nar_model VARBINARY(MAX) = (SELECT model FROM [dbo].[Rental_data_models] WHERE model_name = @model);

        EXEC sp_execute_external_script
                 @language = N'R'
                ,@script = N'

                                #input from query
                                new_data <- InputDataSet

                                #model from query
                                model <- unserialize(nar_model)                      

                                #prediction
                                prediction <- rxPredict(model,data = new_data, overwrite = TRUE, type="response",extraVarsToWrite = c("RentalCount"))
                                Prediction_New <- rxImport(inData = prediction, stringsAsFactors = T, outFile = NULL)

                                OutputDataSet <- data.frame(Prediction_New)

                                '
                ,@input_data_1 =  @query
                ,@params = N'@nar_model VARBINARY(MAX)'
                ,@nar_model = @nar_model
        WITH RESULT SETS((               
                  Prediction_new NVARCHAR(1000)
                 , OrigPredictecCount NVARCHAR(1000)
        ))
END; 

完成这些后,你可以开始使用以下代码进行预测:

-- Example of running predictions against selected model
EXEC [dbo].[Predicting_rentalCount]  
         @model = N'Random_forest_V1'
        ,@query = N'SELECT 
                                        2014 AS Year
                                        ,5 AS Month
                                        ,12 AS Day
                                        ,1 AS WeekDay
                                        ,0 AS Holiday
                                        ,0 AS Snow
                                        ,0 AS RentalCount'

结果,你将得到YearMonthDayWeekDayHolidaySnow变量的预测值:

故意将字段OrigPredictedCount设置为0,但新的预测值是278.996,这是基于输入变量的。在检查模型学习情况时,最好也检查原始值:

SELECT
*
FROM Rental_data
WHERE [year] = 2014
AND [day] = 12  

我们看到在month= 5`中没有值,因此模型必须从其他值中学习:

现在我们已经涵盖了监督预测算法,让我们快速跳入聚类——RevoScaleR包支持的唯一无向算法的一部分。

以下是如何创建简单聚类的示例:

library("cluster") 
# and remove the Fholidays and Fsnow variables 
DF <- DF[c(1,2,3,4,5,6,7)] 
XDF <- paste(tempfile(), "xdf", sep=".") 
if (file.exists(XDF)) file.remove(XDF) 
rxDataStep(inData = DF, outFile = XDF) 

# grab 3 random rows for starting  
centers <- DF[sample.int(NROW(DF), 3, replace = TRUE),]  

Formula =  ~ Year + Month + Day + RentalCount + WeekDay + Holiday + Snow  

# Example using an XDF file as a data source 
z <- rxKmeans(formula=Formula, data = DF, centers = centers) 
clusplot(DF, z$cluster, color=TRUE, shade=TRUE, labels=4, lines=0, plotchar = TRUE) 

以下输出是聚类的展示:

要探索聚类并尝试不同的聚类数量,直接使用 R 代码或创建一个报告来使用 Power BI、Excel 或 SSRS 探索聚类特征,这是肯定的事情。

添加有关聚类中心、统计信息如withinSSbetweenSStotSS等额外信息也将帮助我们理解聚类。

Scree 图也是选择正确聚类数量的额外且非常棒的表现方式。将此类图形添加到报告中也有助于用户选择正确的聚类数量,并帮助他们理解聚类是如何形成的。

Scree 图 R 代码用于确定肘部在哪里发生,以及它是否有正确的聚类数量;如果是的话,三个聚类将是一个最佳数量:

wss <- (nrow(DF) - 1) * sum(apply(DF, 2, var)) 
for (i in 2:20) 
  wss[i] <- sum(kmeans(DF, centers = i)$withinss) 
plot(1:20, wss, type = "b", xlab = "Number of Clusters", ylab = "Within groups sum of squares") 

在这个图表中,我们可以看到肘部在哪里形成,我们可以确定最佳解决方案是三个聚类:

将所有内容整合到报告中(SSRS 报告)使探索更加完善:

用户可以通过选择所需的聚类数量来更改聚类数量,报告将相应地更改。该报告基于三个额外的过程,这些过程基于输入的聚类数量导出图形:

CREATE OR ALTER  PROCEDURE [dbo].[Clustering_rentalCount] 
(
                 @nof_clusters VARCHAR(2)
)
AS
BEGIN

DECLARE @SQLStat NVARCHAR(4000)
SET @SQLStat = 'SELECT  * FROM rental_data'
DECLARE @RStat NVARCHAR(4000)
SET @RStat = 'library(ggplot2)
                library(RevoScaleR)
library(cluster)
              image_file <- tempfile()
              jpeg(filename = image_file, width = 400, height = 400)
 DF <- data.frame(dr_rent)
               DF <- DF[c(1,2,3,4,5,6,7)]
XDF <- paste(tempfile(), "xdf", sep=".")
               if (file.exists(XDF)) file.remove(XDF)
                rxDataStep(inData = DF, outFile = XDF)

                centers <- DF[sample.int(NROW(DF), 3, replace = TRUE),] 
Formula =  ~ Year + Month + Day + RentalCount + WeekDay + Holiday + Snow 
                rxKmeans(formula = Formula, data = XDF, numClusters='+@nof_clusters+')
                z <- rxKmeans(formula=Formula, data = DF, numClusters='+@nof_clusters+')
                clusplot(DF, z$cluster, color=TRUE, shade=TRUE, labels=4, lines=0, plotchar = TRUE)
dev.off()
                    OutputDataSet <- data.frame(data=readBin(file(image_file, "rb"), what=raw(), n=1e6))'

EXECUTE sp_execute_external_script
        @language = N'R'
       ,@script = @RStat
       ,@input_data_1 = @SQLStat
       ,@input_data_1_name = N'dr_rent'
WITH RESULT SETS ((plot varbinary(max)))
END;
GO 

在 SSMS 中运行此操作将给出一个 var binary 字符串,但将此过程的结果作为图像添加到 SSRS 或 Power BI/Excel 中,将得到一个由 R 生成的图表。

在构建预测分析系统后,为探索性项目添加一个漂亮的可视化效果,对于商业用户、数据整理员和工程师来说,无疑是一个非常好的总结。

摘要

在本章中,我们介绍了RevoScaleR包的可扩展功能,以便基于探索的数据集提供快速和良好的预测。在上一章《使用 RevoScaleR 包进行统计学习》中,我们介绍了数据探索、准备以及简单和双变量统计。本章展示了RevoScaleR包是如何设计用来处理大型数据集的(克服了 RAM 和单 CPU 的限制),实现了数据溢出到磁盘和多线程。同样的程序也可以在 R 的数据库实例中使用,以便将预测结果发送到你的业务和数据库中的数据。我们也涵盖了这一方面,探索了不同的算法并比较了解决方案。一旦你选定了模型,你可能想使用PREDICT子句。这是 SQL Server 2017 的一个新特性,具有略微不同的架构。请注意,目前(在撰写本章时)如果你想使用PREDICT子句,模型大小不能超过 100 MB。目前,只有RevoScaleRMicrosoftML包支持使用这个子句,而且并不是所有的RevoScaleR(和 MicrosoftML)算法都支持——目前支持的是rxLinModrxLogitrxBTreesrxDtreerxdForest。然而,使用PREDICT子句进行的实时评分将在 SQL Server 的下一个版本中肯定会有发展和演变。

我们需要预测一个分类或回归问题。大多数问题都可以使用RevoScaleR包来支持,其中许多算法也得到了MicrosoftML包中提供的新附加分类器的支持。探索这两个包将给你的决策提供急需的推动。此外,将序列化的模型存储到数据库中是存储和调用经过训练的模型(函数)的最佳方式,这些模型可以通过添加简单的逻辑实现使用 SQL Server 代理或触发器进行重新训练。

在第七章《将 R 代码投入运行》中,你将学习如何将你的模型和解决方案投入运行,并探索不同的实现方法和一些良好的实践。

第七章:实施 R 代码

如您在上一章中学习了预测建模的基础知识并探索了 RevoScaleR 包中可用的高级预测算法,现在是学习如何实施它的好时机。本章讨论了您如何在 SQL Server 2016 和 SQL Server 2017 中实施 R 预测模型。

将 SQL Server 和机器学习结合在一起的想法是将分析保持接近数据并消除成本以及安全风险。此外,使用 Microsoft R 库有助于提高您 R 解决方案的可扩展性和性能。

本章概述了将您的 R 预测模型实施为一个强大工作流程的步骤,该工作流程集成在 SQL Server 中。首先,我们将讨论使用扩展性框架、本地评分(SQL Server 2017)和实时评分将现有 R 模型集成到 SQL Server 中的概念。然后,我们将讨论如何在 SQL Server 中运行 R 模型如何管理角色和权限。您还将学习如何使用正确的工具在 SQL Server 中实施 R 模型,以及如何将 R 模型作为工作流程的一部分执行,包括 PowerShell、SQL Server Agent 作业和 SSIS。

集成现有 R 模型

本节将现有的 R 代码(生成 R 模型并针对 SQL Server 数据集运行)纳入一个工作流程,其中模型可以定期刷新和评估,然后用于预测分析。以下图显示了 R 脚本中的典型预测建模工作流程:

图 7.1:典型的预测建模工作流程

要在 SQL Server 中集成此脚本,您需要将工作流程分为三个步骤:

  1. 准备训练数据

  2. 使用 T-SQL 训练和保存模型

  3. 实施模型

在本节中,最后两个步骤将使用 sp_execute_external_script,该命令调用一个 R 进程。这些步骤使用 SQL Server 扩展性框架,将在后面进行描述。

前提条件 - 准备数据

我们将使用来自 R: In-Database Analytics for SQL Developers 教程的纽约出租车样本数据,如 github.com/Microsoft/sql-server-samples/blob/master/samples/features/r-services/predictive-analytics/scripts/Lab.md 中所述。

您还可以从 Packt 代码文件存储库下载 nyctaxi_sample.csv 文件,并执行以下 bcp 命令:

bcp <database name>.dbo.nyctaxi_sample in <file path> -c -t, -T -S<server name>

其中:

  • <database name> 是数据库名称

  • <file path>nyctaxi_sample.csv 文件的位置

  • <server name> 是您的服务器名称。

例如:

bcp NYCTaxi.dbo.nyctaxi_sample in c:\nyctaxi_sample.csv -c -t, -T -SMsSQLGirl

在这个场景中,目标是预测小费的可能性。作为这个过程的一部分,我们将创建一个逻辑回归模型,以及模型的接收者操作特征(ROC)曲线和其曲线下面积(AUC)。ROC 是针对诊断测试的各种阈值点的真阳性率与假阳性率的图表。曲线越接近 ROC 空间的对角线,测试的准确性就越低。

曲线越接近左上角边界,就越准确。AUC 以数值形式提供了准确性的测试。幸运的是,ROC 图和 AUC 值都可以很容易地在 R 中计算。

一旦我们确信模型足够准确,我们就可以分享它,并基于提供的输入重新使用它来预测出租车司机是否会收到小费。

这里是我们将用于训练的纽约出租车数据集的表定义:

CREATE TABLE [dbo].nyctaxi_sample NOT NULL, 
   [hack_license] varchar NOT NULL, 
   [vendor_id] char NULL, 
   [rate_code] char NULL, 
   [store_and_fwd_flag] char NULL, 
   [pickup_datetime] [datetime] NOT NULL, 
   [dropoff_datetime] [datetime] NULL, 
   [passenger_count] [int] NULL, 
   [trip_time_in_secs] [bigint] NULL, 
   [trip_distance] [float] NULL, 
   [pickup_longitude] varchar NULL, 
   [pickup_latitude] varchar NULL, 
   [dropoff_longitude] varchar NULL, 
   [dropoff_latitude] varchar NULL, 
   [payment_type] char NULL, 
   [fare_amount] [float] NULL, 
   [surcharge] [float] NULL, 
   [mta_tax] [float] NULL, 
   [tolls_amount] [float] NULL, 
   [total_amount] [float] NULL, 
   [tip_amount] [float] NULL, 
   [tipped] [int] NULL, 
   [tip_class] [int] NULL 
) ON [PRIMARY] 
GO 

有一些变量我们可以开始使用来分析出租车司机收到小费的可能性。正如你在上一章中学到的,你可能需要尝试几个变量和算法来确定哪个更准确。这可能涉及几个迭代过程,这就是数据科学的魅力——你不断地进行实验。

首先,让我们使用以下变量:

变量 类型 列名
出租车司机是否收到小费(是/否) 输出 tipped
乘客数量 输入 passenger_count
行程时间(秒) 输入 trip_time_in_seconds
根据出租车计价器显示的行程距离 输入 trip_distance
基于两个位置之间的经度和纬度的直接距离 输入 pickup_longitude pickup_latitude dropoff_longitude dropoff_latitude

为了更容易计算直接距离,让我们定义以下函数:

CREATE FUNCTION [dbo].[fnCalculateDistance]  
(@Lat1 FLOAT, @Long1 FLOAT, @Lat2 FLOAT, @Long2 FLOAT) 
-- User-defined function calculate the direct distance  
-- between two geographical coordinates. 
RETURNS FLOAT 
AS 
BEGIN 
  DECLARE @distance DECIMAL(28, 10) 
  -- Convert to radians 
  SET @Lat1 = @Lat1 / 57.2958 
  SET @Long1 = @Long1 / 57.2958 
  SET @Lat2 = @Lat2 / 57.2958 
  SET @Long2 = @Long2 / 57.2958 
  -- Calculate distance 
  SET @distance = (SIN(@Lat1) * SIN(@Lat2)) + (COS(@Lat1) * COS(@Lat2) * COS(@Long2 - @Long1)) 
  --Convert to miles 
  IF @distance <> 0 
  BEGIN 
    SET @distance = 3958.75 * ATAN(SQRT(1 - POWER(@distance, 2)) / @distance); 
  END 
  RETURN @distance 
END 

这里是我们想要存储在数据库中的训练预测模型(的)的表定义。将训练好的预测模型(的)存储在表中的一个优点是,我们可以轻松地稍后重用它,并且可以版本控制我们的实验。

请注意,有一个名为IsRealTimeScoring的列。SQL Server 2017 为实时评分添加了一个新功能,这将在集成 R 模型进行实时评分部分中讨论。如果你使用的是 SQL Server 2016,请忽略此值:

CREATE TABLE [dbo].NYCTaxiModel NOT NULL, 
   [AUC] FLOAT NULL, 
   [CreatedOn] DATETIME NOT NULL 
         CONSTRAINT DF_NYCTaxiModel_CreatedOn DEFAULT (GETDATE()), 
   [IsRealTimeScoring] BIT NOT NULL  
         CONSTRAINT DF_NYCTaxiModel_IsRealTimeScoring DEFAULT (0) 
) ON [PRIMARY]  

第 1 步 - 使用 T-SQL 训练和保存模型

在这一步,您可以通过存储过程将一个预测模型(以及可选的分数)创建到一个表中。这样做的原因是,我们不想每次智能应用需要做预测时都创建一个新的模型,而是希望保存模型以便重复使用:

图 7.2:创建预测模型并将其存储在 SQL Server 中

图 7.2中,我们假设数据清洗部分已经完成,输入数据集已经准备好供 R 计算使用,以便训练和评分模型。

这里是一个示例存储过程,它基于纽约出租车样本数据集生成预测模型,并将其保存到表中。该模型预测小费的可能性。模型和模型的 AUC 都保存在dbo.nyc_taxi_models_v2表中:

CREATE PROCEDURE [dbo].[uspTrainTipPredictionModel] 
AS 
BEGIN 
   DECLARE @auc FLOAT; 
   DECLARE @model VARBINARY(MAX); 

   -- The data to be used for training 
   DECLARE @inquery NVARCHAR(MAX) = N' 
         SELECT  
               tipped,  
               fare_amount,  
               passenger_count, 
               trip_time_in_secs, 
               trip_distance, 
               pickup_datetime,  
               dropoff_datetime, 
               dbo.fnCalculateDistance(pickup_latitude,  
                     pickup_longitude,   
                     dropoff_latitude,  
                     dropoff_longitude) as direct_distance 
         FROM dbo.nyctaxi_sample 
         TABLESAMPLE (10 PERCENT) REPEATABLE (98052)' 

  -- Calculate the model based on the trained data and the AUC. 
  EXEC sp_execute_external_script @language = N'R', 
                                  @script = N' 
         ## Create model 
         logitObj <- rxLogit(tipped ~ passenger_count +  
                           trip_distance +  
                           trip_time_in_secs +  
                           direct_distance,  
                           data = InputDataSet); 
         summary(logitObj) 

         ## Serialize model             
         model <- serialize(logitObj, NULL); 
         predOutput <- rxPredict(modelObject = logitObj,  
                     data = InputDataSet, outData = NULL,  
                     predVarNames = "Score", type = "response",  
                     writeModelVars = FALSE, overwrite = TRUE); 

         library(''ROCR''); 
         predOutput <- cbind(InputDataSet, predOutput); 

         auc <- rxAuc(rxRoc("tipped", "Score", predOutput)); 
         print(paste0("AUC of Logistic Regression Model:", auc)); 
         ', 
     @input_data_1 = @inquery,      
     @output_data_1_name = N'trained_model', 
     @params = N'@auc FLOAT OUTPUT, @model VARBINARY(MAX) OUTPUT', 
     @auc = @auc OUTPUT, 
     @model = @model OUTPUT; 

  -- Store the train model output and its AUC  
  INSERT INTO [dbo].[NYCTaxiModel] (Model, AUC) 
  SELECT @model, @auc; 

END 
GO 

一旦定义了这个存储过程,你就可以执行它来生成模型和 AUC。例如:

EXEC [dbo].[uspTrainTipPredictionModel] 

然后,通过执行以下语句查看NYCTaxiModel表的内容:

SELECT [Model], [AUC], [CreatedOn], [IsRealTimeScoring] 
FROM [dbo].[NYCTaxiModel] 

如果存储过程执行正确,你应该会看到以下类似的记录:

第 2 步 - 操作化模型

一旦模型创建并作为前一步骤的一部分存储在表中,我们现在就可以创建一个存储过程,智能应用程序可以调用它来进行小费预测:

图 7.3:在 SQL Server 中获取预测

图 7.3 展示了将预测模型操作化的存储过程的流程。

这里是一个示例,展示了我们如何使用一个已保存的模型以及我们想要预测的数据集。我们使用的是最新创建的模型:

CREATE PROCEDURE [dbo].[uspPredictTipSingleMode]  
   @passenger_count int = 0, 
   @trip_distance float = 0, 
   @trip_time_in_secs int = 0, 
   @pickup_latitude float = 0, 
   @pickup_longitude float = 0, 
   @dropoff_latitude float = 0, 
   @dropoff_longitude float = 0 
AS 
BEGIN 

  DECLARE @inquery nvarchar(max) = N' 
   SELECT  
         @passenger_count as passenger_count, 
         @trip_distance as trip_distance, 
         @trip_time_in_secs as trip_time_in_secs, 
         [dbo].[fnCalculateDistance] ( 
               @pickup_latitude, 
               @pickup_longitude, 
               @dropoff_latitude, 
               @dropoff_longitude) as direct_distance'; 

  DECLARE @lmodel2 varbinary(max); 

  -- Get the latest non-real-time scoring model 
  SET @lmodel2 = (SELECT TOP 1 
               [Model] 
               FROM [dbo].[NYCTaxiModel] 
               WHERE IsRealTimeScoring = 0 
               ORDER BY [CreatedOn] DESC); 

  EXEC sp_execute_external_script @language = N'R', 
   @script = N' 
         mod <- unserialize(as.raw(model)); 
         print(summary(mod)) 
         OutputDataSet<-rxPredict(modelObject = mod,  
data = InputDataSet,  
                           outData = NULL, predVarNames = "Score",  
                           type = "response",  
writeModelVars = FALSE,  
overwrite = TRUE); 
               str(OutputDataSet) 
               print(OutputDataSet)', 
         @input_data_1 = @inquery, 
         @params = N'@model varbinary(max), 
@passenger_count int, 
@trip_distance float, 
                           @trip_time_in_secs INT , 
                           @pickup_latitude FLOAT , 
                           @pickup_longitude FLOAT , 
                           @dropoff_latitude FLOAT , 
                           @dropoff_longitude FLOAT',
 @model = @lmodel2, 
         @passenger_count =@passenger_count , 
         @trip_distance=@trip_distance, 
         @trip_time_in_secs=@trip_time_in_secs, 
         @pickup_latitude=@pickup_latitude, 
         @pickup_longitude=@pickup_longitude, 
         @dropoff_latitude=@dropoff_latitude, 
         @dropoff_longitude=@dropoff_longitude 
  WITH RESULT SETS ((Score FLOAT)); 

END 
GO 

一旦创建了[dbo].[uspPredictTipSingleMode],你的应用程序现在就可以使用这个存储过程来获取评分(小费概率);例如:

EXEC [dbo].[uspPredictTipSingleMode]  
    @passenger_count = 2 
   ,@trip_distance   = 10 
   ,@trip_time_in_secs     = 1950 
   ,@pickup_latitude = 47.643272 
   ,@pickup_longitude      = -122.127235 
   ,@dropoff_latitude      = 47.620529 
   ,@dropoff_longitude     = -122.349297 

输出应该类似于以下内容。在这种情况下,值 0.64 表示得到小费的概率——即 64%:

Score 
---------------------- 
0.640058591034195 

快速批量预测

如前所述,模型训练步骤和预测步骤都调用sp_execute_external_script,该脚本调用 R 进程。实时评分和本地评分允许你进行预测而不调用 R 进程。因此,这些评分方法提高了预测操作的性能。

此外,实时评分和本地评分允许你使用机器学习模型而无需安装 R。只要获得一个兼容格式的预训练模型并将其保存到 SQL Server 数据库中,就可以轻松调用预测操作。

先决条件

实时评分

SQL Server 2016 和 SQL Server 2017 都支持使用sp_rxPredict进行实时评分。

此存储过程是一个使用UNSAFE集合的 CLR 存储过程,需要您将数据库设置为TRUSTWORTHY

这里是一个将PREDICT函数作为SELECT语句一部分调用的示例:

EXEC dbo.sp_rxPredict @model, 
@inputData = @query; 

在这种情况下:

  • @model: 包含之前准备好的实时评分模型

  • @query: 要评分的数据的查询定义

原生评分

SQL Server 2017 引入了一个新函数,PREDICT,允许您使用原生评分获取预测值。您不再需要使用带有 R 脚本的sp_execute_external_script进行预测,而是可以在SELECT语句的FROM子句中调用它,这使得预测分析的实用化变得更加容易。此外,使用PREDICT意味着您不再需要在每次进行预测时调用额外的 R 进程。

这个PREDICT函数是 T-SQL 的新功能,不要与现有的 DMX 的PREDICT函数混淆。

这里是一个将PREDICT函数作为SELECT语句一部分调用的示例:

SELECT  d.Input1, d.Input2, p.Output_Pred 
FROM PREDICT( MODEL = @model,  DATA = d)  
     WITH (Output_Pred FLOAT) p; 

在这种情况下:

  • d: 数据源,例如表、视图或公共表表达式。

  • Input1, Input2: 数据源中的列。

  • @model: 包含已之前准备好的实时评分模型。

  • Output_Pred: 正在预测的输出值。通常,列名由预测值的列名加上一个_Pred后缀组成;例如,Tipped_Pred,其中Tipped是正在预测的列的名称。

集成 R 模型以实现快速批量预测

在继续下一步之前,请遵循先决条件 - 准备数据部分。

第 1 步 – 使用 T-SQL 训练和保存实时评分模型

在这一步中,您可以通过存储过程将实时评分和原生评分的预测模型,以及可选的 AUC,创建到一个表中。目标是构建一个可重用的模型。如果您已经在 SQL Server 表中创建并存储了一个兼容的模型,则可以跳过此步骤。

以下存储过程使用rxSerializeModel,它允许您以原始格式序列化 R 模型。这然后允许您以VARBINARY格式保存模型,该格式可以加载到 SQL Server 中进行实时评分。要为在 R 中使用而反转序列化,您可以使用rxUnserializeModel

CREATE PROCEDURE [dbo].[uspTrainTipPredictionModelWithRealTimeScoring] 
AS 
BEGIN 
   DECLARE @auc FLOAT; 
   DECLARE @model VARBINARY(MAX); 

   -- The data to be used for training 
   DECLARE @inquery NVARCHAR(MAX) = N' 
         SELECT  
               tipped,  
               fare_amount,  
               passenger_count, 
               trip_time_in_secs, 
               trip_distance, 
               pickup_datetime,  
               dropoff_datetime, 
               dbo.fnCalculateDistance(pickup_latitude,  
                     pickup_longitude,   
                     dropoff_latitude,  
                     dropoff_longitude) as direct_distance 
         FROM dbo.nyctaxi_sample 
         TABLESAMPLE (10 PERCENT) REPEATABLE (98052)' 

  -- Calculate the model based on the trained data and the AUC. 
  EXEC sp_execute_external_script @language = N'R', 
                                   @script = N' 
         ## Create model 
         logitObj <- rxLogit(tipped ~ passenger_count +  
                           trip_distance +  
                           trip_time_in_secs +  
                           direct_distance,  
                           data = InputDataSet); 
         summary(logitObj) 

         ## Serialize model             
         ## model <- serialize(logitObj, NULL); 
         model <- rxSerializeModel(logitObj,  
realtimeScoringOnly = TRUE); 
         predOutput <- rxPredict(modelObject = logitObj,  
                     data = InputDataSet, outData = NULL,  
                     predVarNames = "Score", type = "response",  
                     writeModelVars = FALSE, overwrite = TRUE); 

         library(''ROCR''); 
         predOutput <- cbind(InputDataSet, predOutput); 

         auc <- rxAuc(rxRoc("tipped", "Score", predOutput)); 
         print(paste0("AUC of Logistic Regression Model:", auc)); 
         ', 
     @input_data_1 = @inquery,      
     @output_data_1_name = N'trained_model', 
     @params = N'@auc FLOAT OUTPUT, @model VARBINARY(MAX) OUTPUT', 
     @auc = @auc OUTPUT, 
     @model = @model OUTPUT; 

  -- Store the train model output and its AUC  
  INSERT INTO [dbo].[NYCTaxiModel] (Model, AUC, IsRealTimeScoring) 
  SELECT @model, @auc, 1; 

END 
GO 

要将 R 中创建的模型存储在 SQL Server 表中,您必须首先对其进行序列化。在 R 中,序列化的模型在使用它进行预测之前必须先反序列化。

第 2 步 a – 使用实时评分操作化模型

以下是一个示例脚本,其中我们使用实时评分模型通过sp_rxPredict预测翻盘的可能性:

DECLARE @logit_model VARBINARY(MAX) =  
   (SELECT TOP 1 [Model]  
   FROM [dbo].[NYCTaxiModel] 
   WHERE [IsRealTimeScoring] = 1 
   ORDER BY [CreatedOn] DESC); 

EXEC dbo.sp_rxPredict @model = @logit_model, 
@inputData = N'SELECT 
                     2 AS passenger_count,  
                     10 AS trip_distance,  
                     1950 AS trip_time_in_secs,  
                     dbo.fnCalculateDistance(47.643272,  
                           -122.127235,   
                           47.620529,  
                           -122.349297) AS direct_distance'; 

输出结果应仅为您提供通过以下行推送的预测值:

tipped_Pred
----------------------
0.640058591034195

(1 row affected)

第 2 步 b – 使用原生评分操作化模型

以下是一个示例脚本,其中我们使用 PREDICT 函数和一个 R 实时评分模型来预测小费的可能性。SQL Server 2017 中的 PREDICT 函数可以从上一步读取存储的序列化模型以进行预测分析:

DECLARE @logit_model VARBINARY(MAX) =  
   (SELECT TOP 1 [Model]  
   FROM [dbo].[NYCTaxiModel] 
   WHERE [IsRealTimeScoring] = 1 
   ORDER BY [CreatedOn] DESC); 

WITH d AS ( 
   SELECT      2 AS passenger_count,  
               10 AS trip_distance,  
               1950 AS trip_time_in_secs,  
               dbo.fnCalculateDistance(47.643272,  
                     -122.127235,   
                     47.620529,  
                     -122.349297) AS direct_distance) 
SELECT  * 
FROM PREDICT( MODEL = @logit_model, DATA = d)  
WITH (tipped_Pred FLOAT) p; 

输出应包括您在 SELECT 语句中指定的任何列,并且应如下所示:

tipped_Pred passenger_count trip_distance trip_time_in_secs direct_distance
----------- --------------- ------------- ----------------- ---------------
0.640058591 2               10            1950              10.4581575644

(1 row affected)

管理工作负载的角色和权限

将 R 脚本作为扩展性框架工作负载的一部分运行,以及使用实时评分和本地评分进行预测操作,需要首先设置一些角色和权限。

扩展性框架工作负载

本节概述了使用 sp_execute_external_script 从 SQL Server 运行 R 的典型安全要求。可以使用 SQL Server 登录或 Windows 用户账户直接从 SQL Server 使用存储过程运行 R 脚本。以下是为该账户设置足够权限的步骤:

  1. 允许访问 R 脚本将运行的数据库的权限。

  2. 允许读取受保护对象(如表)的数据的权限。这包括(但不限于)可能存储模型的表以及用于训练模型或作为预测输入的表/视图。

  3. 如果 R 脚本需要将新数据写入表,例如模型或评分结果,允许写入新数据的权限。

  4. 如果 R 脚本需要在运行时安装 R 包,允许安装新包的权限。

通常,创建角色来管理权限集更容易,然后将用户分配到这些角色,而不是单独设置用户权限。

以下是一个示例,说明如何创建一个角色并将其分配给名为 JulieGuest2 的登录,按照步骤 1、2 和 3 进行:

-- Create a new role  
CREATE ROLE TutorialDBRUser AUTHORIZATION dbo 
GO 

-- Assign the role to a new member JulieGuest2 so that the login 
-- can connect to the database Tutorial DB. 
ALTER ROLE TutorialDBRUser ADD MEMBER JulieGuest2 
GO 

-- Allow members of TutorialDBRUser to read and write.  
ALTER ROLE db_datareader ADD MEMBER TutorialDBRUser 
GO 

ALTER ROLE db_datareader ADD MEMBER TutorialDBRUser 
GO 

-- Allow members of TutorialDBRUser to run external script 
GRANT EXECUTE ANY EXTERNAL SCRIPT TO [TutorialDBRUser] 
GO 

-- Allow members of TutorialDBRUser to run a specific  
-- stored procedure. 
GRANT EXECUTE ON [dbo].[predict_rentals] TO [TutorialDBRUser] 
GO 

快速批量预测工作负载

只有在使用实时评分或本地评分时,才遵循以下步骤:

  • 对于使用 sp_rxPredict 进行实时评分,您需要将执行此存储过程的用户添加到 rxpredict_users

  • 对于使用 SQL Server 2017 中新提供的 PREDICT 语法进行本地评分,您需要在数据库上授予 EXECUTE 权限

前面的步骤假设用户有权读取预测操作的真实时评分模型和输入数据集。

外部包

从 SQL Server 2017 开始,您也可以通过 CREATE EXTERNAL LIBRARY 添加外部库,只要您有 ALTER ANY EXTERNAL LIBRARY 权限:

GRANT ALTER ANY EXTERNAL LIBRARY TO [TutorialDBRUser] 
GO 

您必须首先从源下载包;例如,从 CRAN (ggplot2 cran.r-project.org/web/packages/ggplot2/index.html) 下载到 SQL Server 可以访问的路径:

CREATE EXTERNAL LIBRARY ggplot2pkg  
FROM  
  (CONTENT = 'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\ggplot2.zip')  
WITH (LANGUAGE = 'R'); 

如果您正在使用 SQL Server 2016,要安装新的 R 包,您需要在机器上具有管理员访问权限。安装步骤在 SQL Server 之外,直接在关联到 SQL Server R 服务的 R 上执行。详细步骤在第三章中概述,管理 SQL Server 2017 和 R 的机器学习服务

工具

在 T-SQL 中嵌入的 R 代码的操作化有三个主要选项。所有这些工具都是免费的:

  • SQL Server 管理工作室 (SSMS)

  • Visual Studio 的 R 工具 (RTVS)

  • SQL Server 数据工具 (SSDT)

本节概述了工具如何帮助您将 R 代码在 SQL Server 中作为工作流程的一部分进行操作化。

将 SSMS 作为操作化 R 脚本的一部分使用

SSMS 是一个强大的工具,它允许您将上一节中的预测分析操作化。SSMS 还提供了管理与在 SQL Server 中操作化 R 代码和维护相关的各种管理任务的能力,例如:

  • 管理权限,如本章前面所述。

  • 管理 R 包(在 SQL Server 2017 中),如本章前面所述。

  • 管理集成 R 代码的存储过程,如前文所述。

  • 管理 SQL Server R 服务的资源,如第三章中所述,管理 SQL Server 2017 和 R 的机器学习服务

  • 使用内置自定义报告和 DMVs 监控 SQL Server R 服务,如使用 SQL Server R 服务的自定义报告中所述。

  • 创建和管理执行 R 脚本的工作。请参阅本章后面的安排训练和预测操作

要获取最新版本的 SSMS 以帮助您使用 SQL Server R 服务开发和管理工作流程,请访问docs.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms

使用 SQL Server R 服务的自定义报告

在 GitHub 上提供了针对 SQL Server R 服务的自定义报告:github.com/Microsoft/sql-server-samples/tree/master/samples/features/r-services/ssms-custom-reports

以下是一个自定义报告列表以及它们可以帮助您实现的内容:

报告 用途
R 服务 - Configuration.rdl 查看 R 服务的安装设置和 R 运行时的属性。安装后配置 R 服务。
R 服务 - Packages.rdl 查看 SQL Server 实例上安装的 R 包及其属性,例如名称和版本。
R 服务 - Resource Usage.rdl 查看 SQL Server 和 R 脚本执行的资源消耗。查看外部资源池的内存设置。
R Services - Extended Events.rdl 查看扩展事件以了解 R 脚本执行情况。
R Services - Execution Statistics.rdl 查看 R 服务的执行统计信息,包括但不限于 R 脚本执行次数、并行执行次数以及RevoScaleR函数。

首次添加自定义报告

从前面的 GitHub 位置下载自定义报告后,请按照以下步骤添加自定义报告:

  1. 前往 SSMS | 对象资源管理器。

  2. 在对象资源管理器中右键单击 SQL Server 实例的名称,然后选择报告 | 自定义报告...

  3. 从下载位置添加 RDL 文件。

添加后,您可能会看到一个如下警告对话框:

图 7.4:SSMS 中的自定义报告运行警告

点击运行表示您确认希望运行这些报告。

图 7.5展示了成功导入的 R 服务 - 执行统计报告。它表示有 38 次执行中的 24 次 R 脚本执行错误,最受欢迎的RevoScaleR函数是rxPredict_rxLogit

图 7.5:SSMS 中的 SQL Server R 服务 - 执行统计报告

查看 R 服务自定义报告

第一次添加 R 服务自定义报告后,您可以再次访问它。以下是步骤:

  1. 前往 SSMS | 对象资源管理器。

  2. 右键单击 SQL Server 实例的名称。

  3. 选择报告 | 自定义报告。如果您已添加所有自定义报告,您应该看到类似以下内容:

图 7.6:在 SSMS 中查看自定义报告

使用 DMVs 管理 SQL Server 机器学习服务

有各种 DMVs 可供帮助您监控您已部署的 R 脚本。本节将 SQL Server 机器学习服务的 DMVs 分为以下两个类别,具体如下。

系统配置和系统资源

您可能熟悉sys.dm_exec_sessionssys.dm_os_performance_counter,分别用于了解活动会话和系统性能计数器。以下是一个您应该了解更多以跟踪和监控 SQL Server 中 R 脚本执行性能和使用的 DMVs 列表:

  • sys.dm_exec_sessions: 查看用户会话和系统会话的详细信息,分别标识为with session_id >= 51< 51

  • sys.dm_os_performance_counters: 查看每个系统性能计数器的详细信息,包括与 R 脚本相关的计数器。以下是一个与 SQL Server R 服务相关的特定脚本的示例:

SELECT *  
FROM sys.dm_os_performance_counters  
WHERE object_name LIKE '%External Scripts%' 
  • sys.dm_external_script_requests: 查看当前实例上的活动外部脚本:
SELECT  
   [external_script_request_id]  
  , [language] 
  , [degree_of_parallelism] 
  , [external_user_name] 
FROM sys.dm_external_script_requests; 
  • sys.dm_external_script_execution_stats: 通过计数器查看新外部脚本的整体使用情况。

资源管理器

在 SQL Server 2016 中,新增了两个 DMV 来帮助监控外部资源池:sys.resource_governor_external_resource_poolssys.dm_resource_governor_external_resource_pool_affinity。如果你熟悉一般性的资源管理器的跟踪和管理,你可能会知道以下列出的另外两个 DMV:

  • sys.resource_governor_resource_pools:查看当前资源池状态、资源池的当前配置及其统计信息。

  • sys.resource_governor_workload_groups:查看工作负载组的统计信息和当前配置。此 DMV 新增了一个列来显示与工作负载组关联的外部池的 ID。

  • sys.resource_governor_external_resource_pools:查看外部资源池的当前配置值。在撰写本文时,SQL Server 2016/2017 企业版允许你配置额外的资源池,使得在 SQL Server 中运行的 R 作业的资源将隔离于来自远程客户端的资源。

  • sys.dm_resource_governor_external_resource_pool_affinity:此 DMV 允许你查看与特定资源池亲和的处理器和资源。

使用 Visual Studio 实现 R 代码的运营化

使用R Tools for Visual StudioRTVS)开发 R 脚本或使用 R 脚本的 T-SQL 现在变得简单。如果你已经使用 SQL Server Data Tools 作为你的 SQL Server 数据库项目的 IDE,你可以在解决方案中简单地添加一个新的 R 项目。这种集成在 Visual Studio 2017 中得到了改进。

如果你还没有安装 Visual Studio,请访问www.visualstudio.com/downloads/

RTVS 作为数据科学和分析应用程序工作负载的一部分进行安装。

从 Visual Studio 安装程序中,你可以将数据科学和分析应用程序工作负载添加到你的 Visual Studio 2017 安装中,如图图 7.7所示:

图片

图 7.7:在 Visual Studio 2017 安装程序中选择数据科学和分析应用程序选项

以下是一些使用 RTVS 开始使用的额外提示:

  1. 在 RTVS 中通过选择文件 | 新建 | 项目来创建一个新的 R 项目。项目名称和文件路径与以下类似:

图片

图 7.8:创建一个新的 R 项目

  1. 在 RTVS 中,你可以选择运行 R 脚本的工空间。如果你已经按照第四章中提到的安装了 R 服务的 SQL Server,即数据探索和数据可视化,你会看到类似以下的内容:

图片

图 7.9:显示 RTVS 可连接的所有工空间

前往 R Tools | Windows | Workspaces 或按Ctrl + 9显示工作空间窗口。

  1. 您可以从 R 交互式窗口运行 R 代码或在 R 项目中保存 R 文件。您可以参考docs.microsoft.com/en-us/visualstudio/rtvs/了解 RTVS 功能的更多信息。

  2. 您也可以通过在项目上右键单击并选择“添加新项”,然后选择 SQL 查询来在项目中添加一个 SQL 查询文件,如下所示:

图 7.10:选择要添加到 R 项目中的新项/文件

  1. RTVS 还允许您通过模板将 R 代码集成到 SQL Server 存储过程中。要访问此功能,只需像上一步一样点击“添加新项”,然后选择 SQL 存储过程。有关更多信息,请参阅docs.microsoft.com/en-us/visualstudio/rtvs/sql-server

在 SQL Server 之外集成 R 工作负载和预测操作

在本节中,您将学习如何将您在上一节中创建的 R 工作负载和预测操作包含在 SQL Server 之外。我们将讨论如何在 PowerShell、SQL Agent 作业和SQL Server Integration Services (SSIS)中运行工作负载和操作。

请注意,您也可以使用 SQLCMD、C#在 SSIS、Azure 以及 Linux 上的 Bash 来执行这些工作负载/预测操作。这次讨论超出了本章的范围。

通过 PowerShell 执行 SQL Server 预测操作

假设您已经创建了从 SQL Server 执行 R 脚本的存储过程,例如前面示例中的[dbo].[uspTrainTipPredictionModel],您可以轻松地将此命令作为 PowerShell 工作流的一部分执行。

这里是一个从 PowerShell 调用存储过程的简单示例:

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=.;Database=Taxi;Integrated Security=True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "EXEC [dbo].[uspPredictTipSingleMode] 
 @passenger_count    = 2
 ,@trip_distance   = 10
 ,@trip_time_in_secs     = 35
 ,@pickup_latitude = 47.643272
 ,@pickup_longitude      = -122.127235
 ,@dropoff_latitude      = 47.620529
 ,@dropoff_longitude     = -122.349297
 "
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$DataSet.Tables[0] 

安排训练和预测操作

在 SSMS 中,您可以创建一个新的 SQL Server 作业,允许您一次性或按特定计划运行 R 代码。

例如,您可以执行一个计划离线预测分析工作负载。为此,只需通过 SSMS 创建一个作业:

  1. 要创建作业,您需要是 SQL Server Agent 固定数据库角色或 sysadmin 固定服务器角色的成员。只有作业所有者或 sysadmin 角色的成员才能更新作业的定义。

  2. 在 SSMS 的对象资源管理器中,展开您想要在其中创建 SQL Server Agent 作业的 SQL Server 实例。

  3. 展开 SQL Server Agent,然后在“作业”文件夹上右键单击,然后选择“新建作业...”:

图 7.11:使用 SSMS 创建新的 SQL Server Agent 作业

  1. 在“常规”页面上提供详细信息:

图 7.12:在新建作业窗口中添加更多详细信息

  1. 在新建作业窗口的左侧菜单中点击“步骤”,然后在新建作业窗口的底部点击“新建...”。

  2. 在新建作业步骤中提供要执行的详细信息。在这个例子中,我们想要更新纽约出租车训练模型。然后点击“确定”:

图 7.13:在 SQL Server 代理作业中调用 R 集成存储过程作为步骤

  1. 在新建作业窗口中,从左侧菜单选择“计划”。

  2. 从新建作业窗口的底部点击“新建...”。

  3. 提供你希望此作业受其约束的计划详情。

  4. 在新建计划窗口中点击“确定”,然后点击新建作业窗口以保存更改。

将 R 脚本作为 SSIS 的一部分进行操作

R 脚本可以轻松地作为 SSIS 工作流程的一部分进行集成。主要有两种方式:作为执行进程任务的一部分运行,以及作为执行 SQL 任务的一部分运行:

  1. 在执行进程任务中运行 R 代码(不是作为 SQL Server R 服务的一部分)可以通过调用 Rscript.exe 来简单地完成。如果你已经有一个准备执行的可执行 R 文件,那么只需在 SSIS 包中添加执行进程任务。你还可以将 SSIS 包中的执行进程任务的输入/输出编织到 R 文件中:

图片

图 7.14:在 SSIS 执行进程任务中外部执行 R 脚本

  1. 在 SQL Server 中使用 SSIS 中的执行 SQL 任务运行预测操作:如果你已经有一个执行预测(或训练模型)的存储过程,那么只需从 SSIS 中的执行 SQL 任务调用此存储过程。还可能将 SSIS 包中的输入/输出编织到执行 SQL 任务中:

图片

图 7.15:在 SSIS 中将 R 集成存储过程作为执行 SQL 任务步骤执行

摘要

在本章中,你学习了将现有的预测分析 R 代码集成到 SQL Server R 外部的步骤,使用了扩展性框架。你还看到了 SQL Server 2017 中新 PREDICT 函数的简单性和强大功能,它允许在不安装 R 的情况下进行本地评分。在预测操作中管理运行预测分析工作负载所需的安全性也很重要。你学习了如何使用 RTVS 将 SQL 查询添加到 R 项目中。最后,你发现了将 R 代码和预测操作集成到现有工作流程中的不同可能性,作为 SQL Server 存储过程、SQL Server 代理作业、PowerShell 脚本和 SSIS 项目。

带着这些新技能,我们准备好管理数据科学解决方案的下一个构建块,作为数据库生命周期的一部分:管理实践。在下一章中,你将学习如何在 持续集成/持续交付CI/CD)和持续模型性能监控中管理数据科学解决方案。

第八章:部署、管理和监控包含 R 代码的数据库解决方案

在 SQL Server 数据库中运营 R 代码意味着数据科学家/数据库开发者也可以利用将数据科学解决方案作为数据库生命周期管理(DLM)的一部分进行生产化。这包括以下内容:

  • 将 R 代码作为 SQL Server 数据库项目的一部分提交到版本控制

  • 将数据科学解决方案的存储过程作为 SQL Server 单元测试的一部分添加

  • 将数据科学解决方案集成到 持续集成/持续交付CI/CD)流程中

  • 定期监控生产中数据科学解决方案的性能

在本章中,我们将使用 Visual Studio 2017 和 Visual Studio Team Services 中的 SQL Server 数据工具SSDT)来执行此 DLM 工作流程。然而,这个基本概念可以应用于您或您的团队可能已经使用的任何其他 CI/CD 平台。

将 R 集成到 SQL Server 数据库生命周期工作流程中

在第七章“将 R 预测模型投入运营”中,我们讨论了如何在 Visual Studio 2017 中创建 R 项目。我们还讨论了将 R 代码集成到 SQL Server 中的 sp_execute_external_script 作为一部分。在这里,我们将重新审视 Visual Studio 2017,特别是在将 R 代码集成到 sp_execute_external_script 作为 SQL Server 数据库项目的一部分,以及作为数据库生命周期工作流程的一部分。

为数据库生命周期工作流程准备您的环境

在本节中,我们将讨论数据库生命周期工作流程的阶段以及我们将使用的工具。对于工作流程中的每个阶段,还将有一些建议供您探索。

  1. 编码和管理 SQL Server 数据库项目/解决方案:管理构成 SQL Server 数据库项目的 SQL Server DML/DDL 脚本有几种不同的方法。Visual Studio 2017 (VS2017) 中的 SQL SSDT 是一个成熟的产品,它正式化了数据库模式和对象的创建和修改。在本节中,我们将使用 VS2017 中的 SSDT。

您可以使用 VS2017 Community、Professional 或 Enterprise 版本。请访问 www.visualstudio.com/vs/compare/ 获取这些版本如何比较的最新信息。在本节的演练和示例中,我们将使用 Visual Studio Enterprise Edition,但您可以使用任何版本。您可以从 www.visualstudio.com/vs/ 下载这些版本。

值得尝试的其他替代方案包括:

    • SQL Server Management Studio:RedGate 开发了一些插件,可以丰富 DevOps/数据库生命周期管理

    • SQL Operations Studio(预览版):这个工具是基于 VS Code 构建的,这意味着它也有很高的潜力满足 DevOps 工作流程,包括源代码控制

  1. 单元测试:就像应用程序开发一样,数据库开发将从单元测试框架中受益,特别是如果它可以自动化的话。有两个广为人知的单元测试框架适用于 SQL Server 数据库,即 tSQLt 和集成在 Visual Studio 中的 SQL Server 单元测试。以下是链接:

在本节中,我们将使用 VS2017 中的 SQL Server 单元测试。

另一个值得尝试的工具是:

    • 基于 tSQLt 框架的 RedGate SQL Test,它是 SSMS 的一个扩展
  1. 版本控制:目前有许多流行的版本控制系统可供选择,例如 Git 和 团队基础版本控制TFVC)。在本节中,我们将使用托管在 Visual Studio Team ServicesVSTS)中的 TFVC。VS2017 可以连接到 VSTS 仓库。您可以在网上注册 VSTS 账户:www.visualstudio.com/team-services/

值得尝试的其他替代方案包括:

使用 Visual Studio,您可以连接到在线版本控制主机,如 GitHub 和 VSTS,以及私有的本地版本控制服务器,如 团队基础服务器TFS):

  1. CI/CD:VSTS 支持托管代理和私有代理。托管代理是一个基于云的代理,执行持续集成和持续交付。私有代理是一个基于本地的代理版本,可在 Visual Studio 2017 中使用。设置 CI 意味着当脚本被检查入时,代理将自动构建并可选择执行一系列测试。设置 CD 允许我们仅针对基线测试代码发布和/或模式更改。在本章中,我们将使用带有私有代理的 VSTS 来部署到本地 SQL Server 数据库。

值得尝试的其他替代方案包括:

    • VSTS 支持托管代理,允许您自动部署到 Azure VM

    • VSTS 支持托管代理,允许您将部署到 Azure SQL 数据库,自 2017 年 10 月起,它也支持 R

图 8.1 展示了使用 VSTS 的 CI/CD 工作流程,我们将在此章中使用它来构建我们的示例 SQL Server R 服务解决方案:

图 8.1 使用 VSTS 的 CI/CD 流程

来源:docs.microsoft.com/en-us/vsts/build-release/actions/ci-cd-part-1

本章的先决条件

工具 URL 备注
Visual Studio 2017 下载:www.visualstudio.com/downloads/ 社区版是免费的。
VSTS 注册/登录:www.visualstudio.com/team-services/ 免费注册个人账户。
PowerShell v2.0 或更高版本 下载 PowerShell:www.microsoft.com/en-us/download/details.aspx?id=42554 您需要此软件来本地设置私有代理。

创建 SQL Server 数据库项目

在本节中,我们将向您介绍如何在 VS2017 中创建数据库项目。

  1. 在 VS2017 中,单击文件 | 新建项目。

  2. 在左侧面板中选择已安装的 SQL Server,然后单击 SQL Server 数据库项目模板。

  3. 在“名称”字段中输入Ch08,在“解决方案名称”字段中输入SQL Server R Services Book,如图所示:

图 8.2 Visual Studio 中的新项目

  1. 选择保存解决方案的位置。

如果您已经有了用于版本控制的本地文件夹,您可以在此指定路径。

在这个例子中,我的 VSTS 项目名为 SQL Server R Services Book,它与我的本地文件夹C:\VSTS\SQL Server R Services Book相关联。

  1. 确保已选中“为解决方案创建目录”和“添加到源代码控制”。

  2. 在新建项目对话框中单击“确定”。解决方案资源管理器窗口应显示类似于以下截图的内容:

图 8.3 解决方案资源管理器中的数据库项目

从这里,您可以添加新的对象,例如表、存储过程以及许多其他对象。

将现有数据库导入到项目中

现在我们有一个空白的数据库,我们可以导入从第七章“实现 R 预测模型”中创建的现有数据库:

  1. 在 Ch08 上右键单击,选择导入 | 数据库:

图 8.4 将数据库导入到数据库项目中

  1. 在导入数据库对话框中,单击“选择连接”。然后,指定到您之前在第七章,“实现 R 预测模型”中创建的数据库的连接。

  2. 导入数据库对话框应看起来如下。单击“开始”:

图 8.5 导入数据库对话框

  1. 然后导入数据库对话框显示导入进度的摘要:

图 8.6 数据库项目导入摘要

  1. 解决方案应看起来如下:

图 8.7 导入数据库后解决方案资源管理器显示的数据库项目

  1. 在我们进行任何更多更改之前,让我们通过在根解决方案节点上右键单击并选择“生成解决方案”,或者您也可以单击 Ctrl + Shift + B 来生成解决方案。

注意,输出应包含针对每个存储过程引用sp_execute_external脚本的多个警告,如下所示:

C:\VSTS\SQL Server R Services Book\SQL Server R Services Book\Ch08\dbo\Stored Procedures\uspTrainTipPredictionModelWithRealTimeScoring.sql(27,8): Warning:  SQL71502: Procedure: [dbo].[uspTrainTipPredictionModelWithRealTimeScoring] has an unresolved reference to object [dbo].[sp_execute_external_script].

向存储过程添加新的存储过程对象

这里是向现有数据库项目添加新对象的一个示例:

  1. 要创建新过程,您可以右键单击“存储过程”文件夹,然后点击“添加”|“存储过程...”:

  2. 在名称字段中输入uspTrainTipPredictionModelWithRealTimeScoringDTree作为新的存储过程:

图 8.8 向数据库项目添加新项

  1. 将以下脚本添加到存储过程中:
CREATE PROCEDURE [dbo].[uspTrainTipPredictionModelWithRealTimeScoringDTree] 
AS 
BEGIN 
   DECLARE @auc FLOAT; 
   DECLARE @model VARBINARY(MAX); 

   -- The data to be used for training 
   DECLARE @inquery NVARCHAR(MAX)= N' 
         SELECT  
               tipped,  
               fare_amount,  
               passenger_count, 
               trip_time_in_secs, 
               trip_distance, 
               pickup_datetime,  
               dropoff_datetime, 
               dbo.fnCalculateDistance(pickup_latitude,  
                     pickup_longitude,   
                     dropoff_latitude,  
                     dropoff_longitude) as direct_distance 
         FROM dbo.nyctaxi_sample 
         TABLESAMPLE (10 PERCENT) REPEATABLE (98052)' 

-- Calculate the model based on the trained data and the AUC. 
EXEC sys.sp_execute_external_script @language = N'R', 
                                  @script = N' 
         ## Create model 
         dTreeObj<- rxDTree(tipped ~ passenger_count + 
trip_distance + 
trip_time_in_secs + 
direct_distance, 
                    data = InputDataSet); 

treeCp <- rxDTreeBestCp(dTreeObj); 
         dTreeObjChosen<- prune.rxDTree(dTreeObj, cp = treeCp); 

         ## Serialize model             
         model <- serialize(dTreeObjChosen, NULL); 

         predictTree <- rxPredict(dTreeObjChosen, data = InputDataSet, overwrite = TRUE)               

        library('ROCR'); 
predOutput <- cbind(InputDataSet, predictTree); 

auc <- rxAuc(rxRoc("tipped", "tipped_Pred", predOutput)); 
print(paste0("AUC of Classification Model:", auc)); 
         ', 
     @input_data_1 = @inquery,    
     @output_data_1_name = N'trained_model', 
     @params= N'@auc FLOAT OUTPUT, @model VARBINARY(MAX) OUTPUT', 
     @auc= @auc OUTPUT, 
     @model = @model OUTPUT; 

-- Store the train model output and its AUC  
INSERT INTO [dbo].[NYCTaxiModel] (Model, AUC,IsRealTimeScoring) 
SELECT @model, @auc, 1; 

END 
  1. Ctrl + S保存文件。

  2. 您现在可以使用Ctrl + Shift + B重新构建解决方案。

发布模式更改

有两种将更改发布到环境的方法:

  • 现有数据库

  • 新数据库

在此示例中,NYCTaxi 已经在数据库中存在。您可以识别模式更改并创建更新脚本:

  1. 右键单击“Ch08”并选择“模式比较”。

  2. 确保左侧的源指向数据库项目路径。

  3. 在“选择目标”下拉列表中,点击它以设置目标数据库。

  4. 选择“数据库”并点击“选择连接”。在这里,您可以提供对现有NYCTaxi数据库的连接。

  5. 点击“比较”,应该只显示一个文件:

图 8.9 Visual Studio 中的模式比较

  1. 在这里,您可以点击“更新”直接将更改应用到数据库中,或者点击“生成脚本”图标来生成更改的脚本。

作为最佳实践,尤其是如果您有一个正式的生产变更管理流程,您会选择生成脚本并将其包含在变更管理请求中。

向存储过程添加单元测试

向存储过程或函数等可编程对象添加单元测试是编程的良好实践的一部分:

  1. 通过右键单击存储过程或函数之一(如Ch08|dbo|存储过程|uspTrainTipPredictionModel)来创建单元测试套件。然后,选择“创建单元测试...”:

图 8.10 在 Visual Studio 中创建单元测试

  1. 选择连接:

图 8.11 SQL Server 测试配置

  1. 点击“确定”后,您将看到一个新创建的单元测试项目和创建的单元测试模板示例:

图 8.12 SQL Server 单元测试模板

在右上角面板中,您可以管理您的单元测试用例。由于dbo.uspTrianTipPredictionModel训练样本数据并将模型以及 AUC 存储到dbo.NYCTaxiModel中,我们将创建一个单元测试来确保:

    • 新记录已插入,

    • 创建的 AUC 达到一定的阈值

  1. 复制以下代码:
-- database unit test for dbo.uspTrainTipPredictionModel
DECLARE @RC AS INT;
DECLARE @RowCountBefore AS INT;
DECLARE @RowCountAfter AS INT;
DECLARE @AUC FLOAT;
SELECT @RC = 0;
SELECT @RowCountBefore = IS NULL((SELECT COUNT(1) ROWCOUNT
FROM [dbo].[NYCTaxiModel]
WHERE [AUC] ISNOTNULL), 0);
EXECUTE @RC = [dbo].[uspTrainTipPredictionModel];
-- Expected value: there should be a new record added to NYCTaxiModel
-- where AUC is known.
SELECT @RowCountAfter = ISNULL((SELECTCOUNT(1)ROWCOUNT
FROM [dbo].[NYCTaxiModel]
WHERE [AUC] ISNOTNULL), 0);
SELECT @AUC = (SELECTTOP 1 [AUC]
FROM [dbo].[NYCTaxiModel]
ORDER BY [CreatedOn] DESC);
SELECT
@RowCountAfter - @RowCountBeforeRowCountAdded,
IIF(@AUC > 0.5, 1, 0) AUCOfModel;
  1. 在测试条件面板中,点击inconclusiveCondition1并点击红色十字删除它。

  2. 现在,从测试条件中选择“标量值”并点击加号按钮。

  3. 然后,右键单击 scalarValueCondition1 并点击属性。

  4. 在属性窗口中更新以下值:

    1. 名称TestNYCTaxiModelAdded

    2. 预期值1

    3. 预期为空False

  5. 重复步骤 6 到 8,并在属性窗口中更改以下值:

    1. 名称TestNYCTaxiModelAdded

    2. 预期值1

    3. 预期为空False

一旦设置完毕,你的 Visual Studio 应该看起来像这样:

图片

图 8.13 dbo.uspTrainTipPredictionModel 的 SQL Server 单元测试

  1. 删除 UnitTest.cs

  2. 然后,右键单击 Ch08_Test 项目并点击构建。

  3. 导航到测试资源管理器并点击运行所有。

  4. 几秒钟后,dbo_uspTrainTipPredictionModelTest 出现在已通过测试下。点击它以查看执行摘要。

  5. 点击输出以查看更多详细信息,例如:

图片

图 8.14 测试执行结果

现在,你已经学会了如何创建针对存储过程的单元测试,该测试针对现有的 NYC Taxi Model 上的现有存储过程执行。理想情况下,单元测试是在最近发布的 SQL Server 上运行的。

使用版本控制

从 Visual Studio,我们可以提交解决方案并管理版本控制中的更改。在这个特定实例中,我们正在使用 VSTS 进行提交。假设你已经在一个 VSTS 中创建了一个项目。

下面是本节其余部分的要求:

  1. VSTS 项目:要设置新的 VSTS 项目,只需访问:www.visualstudio.com/team-services/

VSTS 项目的 URL 应遵循以下格式:

https://<你的账户>.visualstudio.com/<VSTS 项目>

本章中提到的 VSTS 项目命名为 SQL Server R Services Book。因此,我的 URL 是 https://mssqlgirl.visualstudio.com/SQL%20Server%20R%20Services%20Book

  1. VSTS 项目映射到本地文件夹。

这里映射到项目的本地文件夹是 C:\VSTS\SQL Server R Services Book。在本章的早期部分,我们在该路径创建了 SQL Server 数据库解决方案。

按照以下步骤从 Visual Studio 提交你的解决方案:

  1. 在解决方案根节点上,右键单击并选择提交。

  2. 在团队资源管理器窗口中,在挂起的更改下,在注释文本框中输入 Initial check-in

  3. 在点击提交之前,请先审查相关的工作项、包含的更改和排除的更改:

图片

图 8.15 检查挂起的更改

  1. 在提交确认对话框中,点击是。

一旦所有文件都成功提交,你还可以在 VSTS 网站上查看它们。例如:

https://mssqlgirl.visualstudio.com/SQL%20Server%20R%20Services%20Book/_versionControl

设置持续集成

持续集成CI)的主要思想是基于一个或多个触发器执行自动化的构建。执行构建的一个触发器是检查事件。另一个可能是计划构建。选择哪个触发器合适取决于各种因素,例如项目的复杂性和团队的文化。在本节中,因为项目较小,我们将自动化由检查触发的构建。我们还将测试作为构建的一部分添加。

VSTS 是一个自动化构建、测试部署和监控的好平台。在本节中,我们将配置构建定义并在 VSTS 中安排持续集成。

确保 Visual Studio 解决方案,包括 SQL Server 数据库项目和 SQL Server 单元测试项目,构建成功。

图 8.16显示了 VSTS 在线上的 SQL Server R Services Book 团队。在接下来的几个部分中,我们将使用浏览器上的 VSTS 来配置 CI:

图片 1

图 8.16 检查挂起的更改

本节其余部分的一个先决条件是:

在 VSTS 中创建构建定义

按照以下步骤在 VSTS 中创建构建定义:

  1. 在 VSTS 项目网站上,点击顶部菜单中的构建和发布,然后选择构建。选择新定义。

  2. 从空流程开始。

  3. 在任务下,转到流程并从代理队列下拉列表中选择私有代理。在 mssqlgirl 账户中,私有代理命名为 Default:

图片 2

图 8.17 选择构建任务中的私有代理(默认)

  1. 审查获取源中的选择。

$(build.sourcesdirectory)下的本地路径是指私有代理的工作空间,用于执行构建和执行其他任务。

  1. 点击第一阶段,并将显示名称值替换为构建阶段。

  2. 在顶部菜单中,从保存和排队下拉列表中选择保存。

  3. 审查保存构建定义并添加注释。

  4. 通过点击加号在构建阶段添加一个任务。

  5. 在添加任务中,搜索 MS Build,然后点击添加。

  6. 将项目更改为$/SQL Server R Services Book/SQL Server R Services Book/SQL Server R Services Book.sln

默认值是**/*.sln,它指的是 VSTS 项目中的所有解决方案文件。

  1. 在构建阶段,添加另一个任务,命名为发布构建工件。这允许我们获取以后可能很重要的文件,例如 DACPAC 文件。

  2. 在发布构建工件任务中,指定以下详细信息:

    1. 发布路径:$(Build.Repository.LocalPath)\SQL Server R Services Book\Ch08\bin\Debug

    2. 工件名称:DACPAC

    3. 工件发布位置:Visual Studio Team Services/TFS

在此步骤中,我们只发布 DACPAC 文件。在 Visual Studio Team Services 区域发布此特定文件允许我们在发布过程中(一个持续交付步骤)稍后引用此 DACPAC。

  1. 点击保存并排队以测试构建定义。

  2. 查看队列构建中 SQL Server R 服务 Book-CI 的选项,然后点击队列。

  3. 页面将显示正在排队构建,类似于以下内容:

图 8.18 添加发布工件任务

如果构建成功,您将看到类似以下内容。现在将是熟悉构建摘要页面和工件页面的好时机:

图 8.19 查看构建结果

当您导航到工件选项卡时,您应该能够看到 DACPAC 文件夹。通过点击探索,您可以看到解决方案内的文件,包括类似于通过 Visual Studio 本地构建的构建输出:

图 8.20 探索从前一个成功构建发布的工件

将构建部署到本地 SQL Server 实例

现在,通过 VSTS 在私有代理上的构建已成功,让我们尝试将数据库部署到 SQL Server 实例。此操作的前提是私有代理必须能够访问 SQL Server 实例。图 8.21 展示了如何使用带有本地(私有)代理的 VSTS 将部署到多个本地服务器/环境:

图 8.21 VSTS 和本地代理/环境的概要布局

来源:docs.microsoft.com/en-us/vsts/build-release/concepts/agents/agents

当 SQL Server 数据库项目构建时,它将生成一个 DACPAC 文件,该文件可以用来创建一个新的数据库。因此,在 SQL Server R 服务 Book-CI 构建定义的构建阶段,我们将添加一个新任务:

  1. 导航到 SQL Server R 服务 Book-CI 构建定义。

  2. 点击构建阶段并添加一个新任务。

  3. 搜索 WinRM - SQL Server DB 部署。然后,点击添加。

如果不存在,点击检查我们的市场。搜索 使用 WinRM 的 IIS Web 应用部署 并将其安装到您的 VSTS 账户。

  1. 在使用 DACPAC 部署时,输入以下详细信息:

    1. 机器:$(UATMachine)

    2. 管理员登录:$(UATAdminUser)

    3. 密码:$(UATAdminPwd)

    4. DACPAC 文件:$(Build.Repository.LocalPath)\SQL Server R 服务 Book\Ch08\bin\Debug\Ch08.dacpac

    5. 指定 SQL 使用:发布配置文件

    6. 发布配置文件:$(System.DefaultWorkingDirectory)$(UATPublishProfilePath)

  2. 添加以下新变量:

名称 秘密
UATMachine {输入您的机器名称(FQDN 或 IP 地址),例如:uatpc.mssqlgirl.com}
UATAdminUser
UATAdminPwd
UATPublisProfilePath \SQL Server R Services Book\Ch08\Ch08-UAT.publish.xml
  1. 点击保存并排队以测试构建。

将测试阶段添加到构建定义中

在本节中,你将学习如何将测试阶段添加到 SQL Server R Services Book-CI 构建定义。此测试阶段将执行我们之前所做的单元测试。

在我们可以开始单元测试之前,我们需要为测试做准备。这包括向dbo.nyctaxisample表填充数据:

  1. 要添加新的测试阶段,转到流程,点击 ...,然后选择添加代理阶段。

  2. 在代理阶段,在显示名称中输入Test Phase

  3. 在测试阶段,添加一个新任务。

  4. 搜索命令行。然后,点击添加

  5. 在命令行任务中输入以下详细信息:

    1. 工具:bcp

    2. 参数:Ch08.dbo.nyctaxi_sample in "$(System.DefaultWorkingDirectory)$(UATSampleFilePath)" -c -t , -r \n -U $(UATDBUser) -P $(UATDBPwd)

  6. 点击保存。

现在,我们可以添加创建和执行单元测试的步骤:

  1. 在测试阶段,添加一个新任务。

  2. 搜索Visual Studio 测试。然后,点击添加。

  3. Visual Studio 测试中输入以下详细信息:

    1. 显示名称:单元测试

    2. 使用以下方式选择测试:Test assemblies

    3. 测试程序集:**\Ch08_test*.dll

    4. 搜索文件夹:$(System.DefaultWorkingDirectory)

    5. 测试平台站:Visual Studio 2017

    6. 测试运行标题:Ch08 SQL Server Testing

  4. 点击保存 & 排队。

  5. 当你查看构建时,你应该能够看到如下内容:

图 8.22 自动化测试成功

自动化 CI 构建过程

现在我们已经定义了带有构建阶段和测试阶段的 SQL Server R Services Book-CI,我们准备自动化它:

  1. 在 VSTS 中编辑 SQL Server R Services Book-CI。

  2. 点击触发器选项卡。

  3. 确保已勾选启用持续集成。

  4. 可选地,点击+添加以添加计划:

图 8.23 配置 CI 和特定计划构建

  1. 点击选项卡。

  2. 在构建属性 | 构建号格式中,输入Build_$(Date:yyyyMMdd)$(Rev:.r)

  3. 点击保存。

现在,为了测试自动化是否工作,让我们对解决方案进行一些更改,例如:

  1. 在 Visual Studio 中打开 SQL Server R Services Book 解决方案。

  2. 从 Ch08 项目中删除以下文件:

    1. nyc_taxi_models.sql

    2. PersistModel.sql

    3. PredictTipBatchMode.sql

    4. PredictTipSingleMode.sql

  3. 现在让我们检查挂起的更改。右键单击解决方案节点并选择签入。

  4. 可选地,在点击签入按钮之前添加注释。

在成功签入后,你应该能够看到更改集编号:

图 8.24 检查 Visual Studio 的更改集信息

在 VSTS 中,你应该能够访问最新的构建并看到匹配的源版本,如下所示:

图 8.25 通过 VSTS 中的更改集信息验证自动化 CI

设置持续交付

持续交付旨在确保我们可以将良好的构建部署到所需的环境。这可能意味着 UAT 环境,或者生产环境。在本节中,我们将使用 VSTS 实现持续交付:

  1. 在 VSTS 中,转到 SQL Server R Services Book 项目。

  2. 从顶部菜单导航到构建和发布 | 发布。

  3. 点击 + | 新定义。

  4. 查看选择模板窗格。从这里,您可以从许多选项中进行选择,包括从测试管理器运行自动化测试。此选项强烈推荐用于定期检查现有模型的准确性,这将在下一步中讨论。现在,让我们选择空并点击添加。

  5. 在顶部标题处,点击铅笔图标以编辑名称为 UAT 发布 的所有定义 | 新发布定义。

  6. 让我们继续到“管道”标签页。有两个框:工件和环境。

  7. 在“工件”框中,点击添加工件。

  8. 提供以下详细信息并点击添加:

    1. 项目:SQL Server R Services Book

    2. (构建定义):SQL Server R Services Book-CI

  9. 在“环境”框中,点击环境 1 的 1 阶段,0 任务。

  10. 在“任务”标签页中,点击第一行显示为“环境 1”。将环境名称更改为 UAT

  11. 在“任务”标签页中,点击代理阶段并提供以下详细信息:

    1. 显示名称:部署到 UAT

    2. 代理队列:默认

  12. 现在,添加一个新任务用于部署到 UAT。

  13. 搜索 WinRM - SQL Server DB Deployment 并点击添加。

  14. 在“使用 Dacpac 部署”中,填写以下详细信息:

    1. 机器:$(UATMachine)

    2. 管理员登录:$(UATAdminUser)

    3. 密码:`$(UATAdminPwd)`

    4. DACPAC 文件:$(System.ArtifactsDirectory)\$(Build.DefinitionName)\DACPAC\Ch08.dacpac

    5. 服务器名称:{指定服务器名称,例如:localhost}

    6. 数据库名称:NYCTaxiUAT

  15. 前往“变量”标签页并添加以下变量:

名称 秘密
UATMachine
UATAdminUser
UATAdminPwd
  1. 然后,点击保存并接受默认值。

  2. 要测试此发布定义,在“新发布定义”下,点击 + 发布并选择创建 发布,然后选择 ...

  3. 在“为新发布定义创建新发布”中,在发布描述中输入 Test UAT deployment。然后,点击创建,如图所示:

图 8.26 基于最新成功构建创建 UAT 环境的新发布

可以使用不同的数据库连接设置部署到多个环境。一个帮助你实现这一点的扩展是 XDT Transform:

marketplace.visualstudio.com/items?itemName=qetza.xdttransform

一旦发布完成,它将看起来如下:

图 8.27 成功发布的成果

要在发布上启用持续交付,你必须编辑定义:

  1. 前往发布视图,点击 UAT 发布的...,然后选择编辑。

  2. 在管道视图中,进入工件框中的 SQL Server R 服务 Book-CI。

  3. 点击此处所示的持续部署触发器:

图 8.28 修改持续部署触发器

  1. 在持续部署触发器窗口中,确保启用滑块处于开启状态。

  2. 点击保存。

要测试 UAT 发布的持续交付设置,你可以在 SQL Server R 服务 Book-CI 上调用一个新的构建。视图应如下所示:

图 8.29 通过持续开发成功发布的成果

在摘要中,详细信息应说明发布是由 SQL Server R 服务 Book-CI 构建 _20180101.1 触发的。因此,我们成功创建了一个基本的持续交付流程。现在可以添加高级步骤,如设置集成测试和负载测试,使用与前面显示的类似步骤。有关在 VSTS 中设置此信息的更多信息,请参阅以下 Microsoft 教程:docs.microsoft.com/en-us/vsts/build-release/test/example-continuous-testing#configure-cd

监控生产化模型的准确性

在第六章“预测建模”中,我们讨论了许多预测建模示例。创建的模型是基于训练数据的。在现实世界中,新数据不断涌现,例如在线交易、出租车交易(记得之前提到的纽约市出租车示例)和航班延误预测。因此,应该定期检查数据模型,以确保它仍然令人满意,并且没有其他更好的模型可以生成。在这方面,优秀的数据科学家会持续至少提出以下四个问题:

  1. 由于数据的变化,是否需要考虑不同的算法?

例如,如果当前模型正在使用逻辑回归(rxLogit),那么决策树算法(rxDTree)是否会因为规模或预期结果的变化而更准确?

  1. 是否有来自新交易的其他特征变得越来越重要?

考虑以下场景:目前,出租车行程的小费预测正在使用乘客数量、行程距离、行程时间和直接距离。也许定期检查其他特征,如一天中的小时、一周中的日子、接单邮编和/或送单邮编、假日季节、出租车的清洁度或客户评分,是否会对小费预测有更大的贡献。

  1. 是否有变化的需求可以导致采取行动来改善业务或客户?

在出租车行程小费预测中,当前的预测是一个二进制值,即真或假。企业可能希望了解出租车清洁度或客户评分如何与无小费、小额小费、中等小费或大量小费相关联。出租车清洁度是司机可以用来提高服务质量的行为。

  1. 性能下降是否由模型执行或输入数据瓶颈引起?

可能随着输入数据集/数据源的增长且未优化,端到端的预测建模也会变慢。

为了捕捉模型的性能,应该记录实际预测或实际数据的合理表示的性能。以下是一个日志表应该看起来像的例子:

数据类型 注释
LogID INT 执行的顺序 ID。
创建时间 DATETIME 模型生成和测试的日期。
模型 ID INT 每个模型的唯一标识符。
模型 VARBINARY(MAX) 这是模型的序列化表示。
RxFunction VARCHAR(50) 这是模型中使用的 rx 函数。
公式 VARCHAR(1000) 预测模型的公式。
训练输入查询 VARCHAR(MAX) 可重复生成的训练数据集
AUC FLOAT 模型的 AUC 表示。这可以是任何其他可以用来比较模型质量的指标。
训练行数 INT 行数的数量。
CPU 时间 INT 生成模型所需的时间(秒数)。

一旦捕获了执行情况,就可以分析 AUC 值和 CPU 时间,如图 8.30 所示:

图 8.30 监控模型在 AUC 和 CPU 时间上的比较

这些图表比较了以下模型的性能:

公式 B 公式 C
rxDTree 模型 ID 2 模型 ID 3
rxLogit 模型 ID 4 模型 ID 5

描述如下:

  • 公式 B 是tipped ~ passenger_count + trip_distance + trip_time_in_secs + direct_distance + payment_type

  • 公式 C 是tipped ~ passenger_count + trip_distance + trip_time_in_secs + payment_type

每个模型都会与以下数据运行:

  • 最后 2 个月的数据

  • 随机选取的 5%数据

根据之前提到的比较,我们可以看到模型 ID 4,即使用公式 B 的rxLogit,具有最高的 AUC 范围和最低的 CPU 时间。因此,这个模型是两个中最好的。接下来需要决定这个模型是否应该替换生产中的模型。

现在你已经学会了比较模型的技术以及预测建模中一些重要的指标,你可以安排这种性能测试,类似于前面展示的。安排可以是 SQL 代理作业,如第七章中所述的“操作 R 代码”,在那里如果新的结果低于某个阈值,你可以收到警报。或者,你可以在 VSTS 中部署一个独立的 SQL Server 数据库单元项目,作为单独的部分来执行,以检查最新的交易数据。

有用参考资料

摘要

Visual Studio 2017 是一个强大的集成开发环境(IDE),数据科学家/开发者可以使用它来管理他们的代码、单元测试和版本控制。结合 Visual Studio Team Services,它们形成了一个完整的工具集,用于执行数据库生命周期管理,这也易于适应 DevOps 实践。本章详细介绍了如何在 SQL Server 数据库项目中、DevOps 实践中以及 CI/CD 工作流中集成 SQL Server 机器学习服务与 R 语言。最后,你也学习了如何监控预测模型随时间变化的准确性。

在下一章中,我们将讨论数据库管理员(DBAs)如何利用 R 语言的优势来利用机器学习服务。

第九章:为 DBAs 提供的 R 机器学习服务

R 集成(以及 SQL Server 2017 中的 Python 集成)提供了一系列可以使用的机会。而且,使用 R 服务的人群在人数(工作角色或部门)方面已经增加。DBAs(以及系统管理员)肯定会从中受益良多。R 和统计学不仅为他们发现和深入了解他们捕获的数据提供了额外的动力,而且也可能帮助他们找到他们之前可能错过的隐藏宝藏。不同语言的混合——我不仅是在谈论 R,还包括其他语言——无疑为跟踪、捕捉和分析捕获数据带来了新的能力。

一件事很清楚,如果你有 R(任何 Python)如此接近数据库,那么几个人可以从监控任务切换到预测任务。这实际上意味着人们现在可以诊断和预测可能会发生的事情,而不仅仅是当事情已经发生时采取行动。我并不是说这是一项容易的任务,因为我们都知道,例如,一个查询突然运行缓慢的复杂性可能有一个或多个隐藏的原因,这些原因可能不会立即被发现,R 在数据库中的集成将肯定有助于在近实时中找到这个隐藏的原因。与 SSAS 中的数据挖掘相比,在我看来,它仍然是一个非常强大和好的工具,但与通过 R 引擎发送和分析数据相比,可能会有更多的延迟。

本章将概述如何帮助 DBAs(或其他角色处理类似问题)获取 R 的优势的重要步骤:

  • 收集对数据库管理员(DBAs)相关的数据

  • 探索和分析数据

  • 使用 R 服务创建预测

  • 通过预测改进监控

收集相关数据

收集数据——尽管这可能很简单——是一项需要精心设计的任务。这有几个原因。首先也是最重要的原因是,我们希望以对生产环境影响最小或零的方式收集数据。这意味着收集和存储数据的过程不应干扰任何正在进行的流程。第二重要的是存储。你希望在何处以及如何存储数据,以及存储数据的保留策略?一开始,这可能会显得非常微不足道,但随着时间的推移,存储本身将扮演一个重要的角色。第三,也是绝对重要的,是你想要收集哪些数据。当然,我们都希望拥有智能数据,也就是说,拥有所有与解决或改进我们的业务流程相关的数据。但在现实中,收集智能数据既不困难也不容易。首先,必须理解数据库功能的概念,而且还要了解如何捕捉相关指标以及这个特定功能将如何运作。

让我们看看在哪里以及如何可以看到性能改进,如果你知道如何寻找它们的话。

例如,延迟耐久性是自 SQL Server 2014 以来 SQL Server 就有的一个功能,但在某些场景下可以帮助提高性能,但会牺牲耐久性(耐久性是 ACID(原子性、一致性、隔离性和耐久性)的一部分,防止在发生故障或系统重启时,已提交的数据没有被保存或保存到错误的状态)。写入前日志(WAL)是 SQL Server 使用的一个系统,这意味着所有更改首先写入日志,然后才允许它们提交到数据库表。

对于这个快速演示,我们将创建一个空数据库,将DELAYED_DURABILITY设置为允许,并设置为NO_WAIT。这个测试的一个重要步骤是将数据库的备份设置为NUL,这与with truncate_only命令类似。这个语句丢弃任何非活动日志(当数据库处于完全或批量日志恢复模式时;对于简单恢复模式,这并不适用)以及从数据库完成完全备份的那一刻起,任何非活动日志记录都会被丢弃(删除)。这可以被模拟。当检查点运行时,尝试备份日志将导致错误消息。换句话说,数据库可能正在以简单恢复模式运行。本质上,NUL命令只是存储和丢弃日志:

USE [master];
GO
CREATE DATABASE [DelayedDurability];
GO

ALTER DATABASE [DelayedDurability] SET DELAYED_DURABILITY = ALLOWED    
WITH NO_WAIT;
GO
BACKUP DATABASE [DelayedDurability] TO DISK = N'nul'
GO  

我将创建一个示例表来进行插入:

USE [DelayedDurability];
GO

DROP TABLE IF EXISTS TestDDTable;
GO

CREATE TABLE TestDDTable
(ID INT IDENTITY(1,1) PRIMARY KEY
,R_num INT
,Class CHAR(10) 
,InsertTime DATETIME DEFAULT(GETDATE())
);
GO  

创建了表之后,我们现在可以测试两种类型的插入,带有延迟耐久性和不带延迟耐久性:

EXECUTE sys.sp_flush_log;
GO

DECLARE @count INT = 0
DECLARE @start1 DATETIME = GETDATE()
WHILE (@count <= 250000)
          BEGIN
                BEGIN TRAN
                      INSERT INTO TestDDTable(R_num, class) VALUES(@count, 'WITHOUT_DD')
                      SET @count += 1
                COMMIT
          END

SET @count = 0
DECLARE @start2 DATETIME = GETDATE()
WHILE (@count <= 250000)
          BEGIN
                BEGIN TRAN
                      INSERT INTO TestDDTable(R_num, class) VALUES(@count, 'WITH_DD')
                      SET @count += 1
                COMMIT WITH (DELAYED_DURABILITY = ON)
          END

SELECT 
DATEDIFF(SECOND, @start1, GETDATE()) AS With_DD_OFF
,DATEDIFF(SECOND, @start2, GETDATE()) AS With_DD_ON

结果很明显:将延迟耐久性设置为开启时,在进行大量插入时可以提高性能:

图片

我也可以使用查询压力来模拟多个线程,每个线程执行相同数量的插入:

图片

在这种重压下的工具测试中,问题是,我们如何监控和跟踪延迟耐久性的行为?一个人可以通过性能监控器来测试性能:

图片

或者,你可以使用活动监控器来测试性能:

图片

但很快你就会意识到,你可能需要存储这些信息以供后续分析,或者你需要获取一些额外的知识,了解在这种情况下哪些性能指标或扩展事件值得监控。

因此,在这种情况下,你需要在登录时检查等待资源:

SELECT * FROM sys.dm_os_wait_stats
WHERE wait_type IN ('WRITELOG');
GO 

你还需要添加一些机制来捕获这些等待统计信息到一个表中,以供后续分析,或者捕获性能监控器或使用分析器、XE 等。在运行时查询数据并捕获统计信息是一项相当繁琐的工作;想象一下将sys.dm_os_wait_stats中的统计信息合并并与sys.dm_io_virtual_file_stats结合。总的来说,你试图收集的数据越多,查询这些统计信息可能就越复杂。

使用性能监控器捕获之前两个查询的结果,截图如下所示:

图片

上述截图显示了左侧(1)延迟持久性如何工作以及日志刷新如何在一段时间内按顺序发生。与右侧(2)相比,我们可以看到延迟持久性被关闭且日志刷新不活跃。

从性能监视器中提取原始数据可能不是正确的方法,但通过扩展事件存储相同的数据集对系统以及用户来说将更加轻量,便于后续分析。

设置您分析所需的扩展事件可以快速且轻松完成。但与其选择过多的事件,不如专注于真正需要的那些,因为日志文件可能会很快变得很大:

-- creating extended event

IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='DelayDurab_Log_flush') 
DROP EVENT session DelayDurab_Log_flush ON SERVER; 

-- Get DelayedDurability database ID
SELECT db_id()

CREATE EVENT SESSION DelayDurab_Log_flush ON SERVER
ADD EVENT sqlserver.log_flush_start
 (WHERE  (database_id=40)),
ADD EVENT sqlserver.databases_log_flush 
 (WHERE (database_id =40)),
ADD EVENT sqlserver.transaction_log
 (WHERE (database_id =40))
-- maybe add batchrequests/second

 ADD TARGET package0.event_file
(
 SET filename     ='C:\CH09\MonitorDelayDurability.xel'
 ,metadatafile ='C:\CH09\MonitorDelayDurability.xem'
)
WITH (MAX_MEMORY=4096KB
 ,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS
 ,MAX_DISPATCH_LATENCY=30 SECONDS
 ,MEMORY_PARTITION_MODE=NONE
 ,STARTUP_STATE=ON);
GO  

在启动事件后,通过分解 XML 结构来读取文件内容:

SELECT 
      CAST(event_data AS XML) AS event_data
FROM sys.fn_xe_file_target_read_file('C:\CH09\MonitorDelayDurability*.xel', 'C:\CH09\MonitorDelayDurability*.xem', null, null) 

此外,从 XML 中获取信息也是正确处理扩展事件的重要任务:

图片

回到起点,为数据库管理员和进一步分析收集数据至关重要。从这个例子中还可以看出:如果我们也添加到日志文件的增长,其中一个日志需要通过添加新的 VLF 文件来额外增长。与关闭延迟持久性的事务相比,添加延迟持久性可以提供更快的插入。有时添加新的XE或度量可以显著增加日志文件,其中数据正在被收集。使用统计分析,我们可以优化度量选择,或者稍后发现它们为我们提供了额外的洞察信息。在探索和后续分析数据方面的工作可以带来巨大的回报,无论是在工作负载方面还是在收集到的不同数据方面。

探索和分析数据

以类似的方式,使用事件特征收集数据可以为您提供丰富的方式来获取大量系统信息数据。从之前的示例中,通过以下演示,我们将看到如何使用服务器的度量值进行高级统计分析,以及如何帮助减少不同信息量,并精确定位相关度量。将创建一个特定的数据库和一个阶段表:

CREATE DATABASE ServerInfo;
GO

USE [ServerInfo]
GO

DROP TABLE IF EXISTS server_info;
GO

CREATE TABLE [dbo].server_info ON [PRIMARY];
GO  

然后,导入附带的代码文件中可以找到的度量。有 433 个测量点来自 32 个不同的扩展事件,目的是理解服务器及其环境设置。

在初始加载后,表将填充不同扩展事件的度量值,这些度量值也已经过离散化和清理,以便进行进一步的数据分析:

图片

boxplot函数使用户能够探索每个度量的分布并找到潜在的异常值。仅使用 R 代码来探索数据:

dbConn <- odbcDriverConnect('driver={SQL Server};server=TOMAZK\\MSSQLSERVER2017;database=ServerInfo;trusted_connection=true') 
server.feature <- sqlQuery(dbConn, 'SELECT * FROM Server_info') 
close(dbConn) 
boxplot(server.feature) 

下面的图表提供了一个快速概述:

图片

箱线图显示有四个事件,其值远远超过平均值和第三四分位数。清理这些异常值将使数据更容易阅读,并且不会导致异常分布和偏斜结果。请注意,有一些特定的分析处理异常值并寻找这样的值。对于这个演示,我们将这些值重新编码为 N/A。

在清理、添加汇总统计量和相关性之后,这是一个相关的方法来查看所有事件是如何相互关联的:

# replace value 25 with N/A
server.feature$XE12[server.feature$XE12=="25"]<-NA
server.feature$XE18[server.feature$XE18=="25"]<-NA
server.feature$XE24[server.feature$XE24=="25"]<-NA
server.feature$XE27[server.feature$XE27=="25"]<-NA

cor.plot(server.feature,numbers=TRUE,main="Server Features")  

服务器特征的关联矩阵是表示哪些事件相关以及如何不相关的一种很好的方式:

截图

进一步来说,让我们减少这些扩展事件度量,因为很明显,并非所有都在扮演重要角色,有些可能只是额外开销。从前面的热图中,很难看出某些度量之间的相关性是否不起作用;因此,我们将使用因子分析。这种分析观察变量之间的相关性,以反映更少的潜在变量。因子是一个潜在变量,即基于观察和相关性变量的结构化变量。结构是通过每个因子加载响应变量来创建的。这意味着因子 1 可以是,例如,变量A加载 25%,变量B加载 65%,变量C加载 10%。因此,因子 1 将主要(65%)从变量A中提取特征,依此类推。

以这种方式,因子分析将尝试减少原始相关变量的数量(我们扩展事件的度量),并尝试创建新的结构化变量。

使用 R 代码探索数据,简单的探索性因子分析可以揭示多个因子:

fa.parallel(server.feature, fa="fa")  

以下屏幕截图显示有七个因子可用于提取:

截图

以以下方式,这也会揭示因子的加载情况;只需调用 R 函数:

fa.model <- fa(server.feature,7,n.obs = 459,fm="pa",scores="regression", use="pairwise",rotate="varimax") #can use WLS - weighted least squares
fa.model.r <- target.rot(fa.model)
fa.diagram(fa.model.r) 

以下图表显示了加载构建每个因子的方式:

截图

将加载存储回数据库以进行进一步分析和因子命名是一种常见做法,并且考虑到可以将因子纳入任何进一步的分析(例如:分类或聚类方法)。现在我们知道因子的数量,我们可以将加载存储到数据库中:

-- Factor Analysis
-- extract factor loadings

DECLARE @Rcode NVARCHAR(MAX)
SET @Rcode = N'
 ## with actual FA funcitons
 library(psych)
 library(Hmisc)
 ## for data munching and visualization
 library(ggplot2)
 library(plyr)
 library(pastecs)

 server.feature <- InputDataSet

 server.feature$XE12[server.feature$XE12=="25"]<-NA
 server.feature$XE18[server.feature$XE18=="25"]<-NA
 server.feature$XE24[server.feature$XE24=="25"]<-NA
 server.feature$XE27[server.feature$XE27=="25"]<-NA

 fa.model <- fa(server.feature
 ,7
 ,fm="pa"
               ,scores="regression"
 ,use="pairwise"
 ,rotate="varimax") #can use WLS - weighted least squares

 fa.loadings <- as.list.data.frame(fa.model$loadings)
      OutputDataSet <- data.frame(fa.loadings)'

 EXEC sp_execute_external_script
 @language = N'R'
 ,@script = @Rcode
      ,@input_data_1 = N'SELECT * FROM server_info'
WITH RESULT SETS
((
 PA1 NUMERIC(16,3)
 ,PA2 NUMERIC(16,3)
 ,PA3 NUMERIC(16,3)
 ,PA4 NUMERIC(16,3)
 ,PA5 NUMERIC(16,3)
 ,PA6 NUMERIC(16,3)
 ,PA7 NUMERIC(16,3)
 ))  

结果可以解释为:值(正或负)越高,特定度量与伴随因子的加载就越多:

截图

因子 1,PA1,主要加载了 XE03 (0.680)、XE13 (0.640)、XE18 (-0.578)和 XE28 (0.652)。这四个都是测量查询交易,如下面的截图所示:

截图

在这里,负值是 tempdb 中的空闲空间(KB),它是负负载的,并且仅表示与因素的关系。但是,能够减少扩展事件的数目,并通过高级统计分析将它们结合起来,是一种处理潜在复杂问题的非常整洁的方法。

在数据库负载中,我还可以通过散点图表示这些因素是如何分布的。我已经将先前查询的结果导出到 Power BI,并使用了聚类视觉。此外,您可以看到这些因素负载的集群以及相似的集群。红色组(在左侧)又是 DBA 和数据科学家应该一起进一步检查的内容:

图片

创建基线和负载,并重新播放

给定减少和创建针对您特定服务器或环境的定制和适应性的新度量标准的能力,现在我们想了解在所有其他参数保持不变的情况下(拉丁语,ceteris paribus)系统是如何表现的。这是基线。有了基线,我们确定了什么是正常的,换句话说,在正常条件下的性能。基线用于比较可能或看似异常或不寻常的情况。它还可以作为任何未来测试的控制组(这在新补丁推出或特定环境/服务器的升级需要执行时尤其有效)。

典型的企业基线可以描述为以下一天(24 小时)内用户或机器发起的数据库请求数量:

图片

当所有请求都表示为每个企业流程的细分时,可以立即看到模式。

企业资源规划(ERP)系统通常在人们在工作场所时达到峰值——在正常工作日的上午 8:00 至下午 5:00 之间,有两个明显的峰值,以及从上午 11:00 至下午 1:00 的非常明显的午餐休息时间:

图片

在日常 ERP 系统维护中添加 ETL 作业,很明显 DBA 和系统管理员通常在哪里以及何时尝试压缩这些重要作业,以及这如何也受到日常 ERP 工作负载的限制和描述:

图片

仓库具有完全不同的行为模式,这意味着它通常在早晨 4:00 AM 和 5:00 AM 达到最高的请求,并且直到傍晚时分保持某种程度的稳定:

图片

相反,面包店对数据库的请求是相反的,因为他们的大多数活动都是从晚上 9:00 开始,直到凌晨 3:00,这样顾客就能在早上得到新鲜面包:

图片

最后,网站可以被视为一个恒定的数据库请求资源,相对而言,每天的变动很小:

图片

所有这些都可以理解为日常基线,当然,如果按月度来考虑,事情会变得更加复杂。同时,模式会立即出现。在周末(第 7 天、第 14 天和第 21 天)请求减少,而在月底结束时,需要关闭财务周期;因此,数据库上有额外的负载:

图片 3

展示所有这些对于理解系统(或环境)基线必须如何理解至关重要。这些数据可以通过性能计数器、许多 DMV、使用查询存储和其他工具收集。我们通常收集的是我们稍后要监控和预测的内容。因此,明智地选择是最重要的任务,因为通过这些措施、计数器和值,您将定义系统何时健康以及何时不健康。但通常,关于系统和数据库的一般信息至关重要。此外,还需要存储 SQL Server 信息、许多可配置参数、与查询相关的信息、数据库-I/O 和与 RAM 相关的信息。

在建立基线后,我们需要创建工作负载。通常,工作负载是在生产服务器上针对基线捕获的,并在测试服务器/环境中回放捕获的统计信息。可以通过在测试环境中回放从生产环境中捕获的工作负载,通过交替更改特定参数的值来改变数据库调整或配置更改。下一个演示是表示通过两个参数表达的工作负载,当相同的工作负载被回放时,这些参数已被更改:

USE [master];
GO

CREATE DATABASE Workloads;
GO

USE Workloads;
GO  

查询表 [dbo].[WLD] 实质上只是重复相同的工作负载,但其中一个或另一个参数有所变化:

图片 2

首先,我们需要对每次有变化时的工作负载进行异常值分析。带有 R 的 T-SQL 代码可以生成一个马氏距离图,清楚地显示异常值所在的位置:

EXEC sp_execute_external_script 
 @language = N'R' 
,@script = N' 
               library(car) 
               library(ggplot2) 
               dataset <- InputDataSet 
               dataset$WL_ID <- as.numeric(recode(dataset$WL_ID, "''WL1''=1; ''WL2''=2;''WL3''=3")) 
               dataset$Param1 <- as.numeric(dataset$Param1) 
               dataset$Param2 <- as.numeric(dataset$Param2) 

               m.dist <- mahalanobis(dataset, colMeans(dataset), cov(dataset)) 
               dataset$maha_dist <- round(m.dist) 

               # Mahalanobis Outliers - Threshold set to 7 
               dataset$outlier_mah <- "No" 
               dataset$outlier_mah[dataset$maha_dist > 7] <- "Yes" 

                image_file = tempfile();   
               jpeg(filename = image_file);   

               # Scatterplot for checking outliers using Mahalanobis  
               ggplot(dataset, aes(x = Param1, y = Param2, color = outlier_mah)) + 
                 geom_point(size = 5, alpha = 0.6) + 
                 labs(title = "Mahalanobis distances for multivariate regression outliers", 
                        subtitle = "Comparison on 1 parameter for three synthetic Workloads") + 
                 xlab("Parameter 1") + 
                 ylab("Parameter 2") + 
                 scale_x_continuous(breaks = seq(5, 55, 5)) + 
                 scale_y_continuous(breaks = seq(0, 70, 5))    + geom_abline(aes(intercept = 12.5607 , slope = 0.5727)) 

                 dev.off();  
               OutputDataSet <- data.frame(data=readBin(file(image_file, "rb"), what=raw(), n=1e6))' 
,@input_data_1 = N'SELECT * FROM WLD' 

该图表已插入 Power BI 中,可以在两个参数中更改工作负载。因此,数据库管理员不仅可以更改工作负载,还可以在回放恢复的工作负载时看到哪些异常值引起了额外关注:

图片 4

ANOVA 或 MANOVA 也可以执行,以查看工作负载中的特定变化。R 代码可以做到这一点:

dataset$Param1 <- as.numeric(dataset$Param1)
dataset$Param2 <- as.numeric(dataset$Param2)
dataset$WL_ID <- as.numeric(recode(dataset$WL_ID, "'WL1'=1; 'WL2'=2;'WL3'=3"))

LM.man <- Anova(lm(cbind(Param1, Param2) ~ WL_ID, data=dataset))
summary(LM.man)    

ANOVA 统计显示工作负载及其参数设置变化之间的差异:

图片 1

使用 R 创建预测 - 磁盘使用情况

预测涉及发现任何未计划、不希望的活动或异常的系统行为,尤其是与基线相比时。以这种方式,拉响警报将导致更少的误报状态。

此外,我们总是遇到磁盘大小问题。基于这个问题,我们将演示数据库增长,存储数据,然后对收集到的数据进行预测,以便最终能够预测 DBA 何时可以期待磁盘空间问题。

为了说明这个场景,我将创建一个 8MB 的小型数据库,没有增长的可能性。我将创建两个表。一个将作为基线,DataPack_Info_SMALL,另一个将作为所谓的日常日志,其中将存储所有意外情况或不良行为。这将持久保存在DataPack_Info_LARGE表中。

首先,创建一个数据库:

USE [master];
GO

CREATE DATABASE FixSizeDB
CONTAINMENT = NONE
ON  PRIMARY
( NAME = N'FixSizeDB', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER2017\MSSQL\DATA\FixSizeDB_2.mdf' , 
SIZE = 8192KB , FILEGROWTH = 0)
LOG ON
( NAME = N'FixSizeDB_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER2017\MSSQL\DATA\FixSizeDB_2_log.ldf',
SIZE = 8192KB , FILEGROWTH = 0)
GO
ALTER DATABASE [FixSizeDB] SET COMPATIBILITY_LEVEL = 140
GO
ALTER DATABASE [FixSizeDB] SET RECOVERY SIMPLE
GO  

DataPack表将作为所有生成的插入和后续删除的存储位置:

CREATE TABLE DataPack
 (
 DataPackID BIGINT IDENTITY NOT NULL
 ,col1 VARCHAR(1000) NOT NULL
 ,col2 VARCHAR(1000) NOT NULL
 )  

使用以下简单的WHILE循环填充DataPack表:

DECLARE @i INT = 1;
BEGIN TRAN
 WHILE @i <= 1000
 BEGIN
 INSERT dbo.DataPack(col1, col2)
 SELECT
 REPLICATE('A',200)
 ,REPLICATE('B',300);
 SET @i = @i + 1;
 END
COMMIT;
GO  

使用以下查询捕获磁盘空间变化对于任务来说非常重要:

SELECT
 t.NAME AS TableName
 ,s.Name AS SchemaName
 ,p.rows AS RowCounts
 ,SUM(a.total_pages) * 8 AS TotalSpaceKB
 ,SUM(a.used_pages) * 8 AS UsedSpaceKB
 ,(SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
FROM
 sys.tables t
INNER JOIN sys.indexes AS i
 ON t.OBJECT_ID = i.object_id
INNER JOIN sys.partitions AS p
 ON i.object_id = p.OBJECT_ID
 AND i.index_id = p.index_id
INNER JOIN sys.allocation_units AS a
 ON p.partition_id = a.container_id
LEFT OUTER JOIN sys.schemas AS s
 ON t.schema_id = s.schema_id
WHERE
 t.NAME NOT LIKE 'dt%'
 AND t.is_ms_shipped = 0
 AND i.OBJECT_ID > 255
 AND t.Name = 'DataPack'
GROUP BY t.Name, s.Name, p.Rows  

Log表将与DataPack表一起填充,以便收集磁盘空间的即时变化:

DECLARE @nof_steps INT = 0
WHILE @nof_steps < 15
BEGIN
 BEGIN TRAN
 -- insert some data
 DECLARE @i INT = 1;
              WHILE @i <= 1000 -- step is 100 rows
 BEGIN
                                         INSERT dbo.DataPack(col1, col2)
 SELECT
                                                             REPLICATE('A',FLOOR(RAND()*200))
                                                            ,REPLICATE('B',FLOOR(RAND()*300));
 SET @i = @i + 1;
 END
 -- run statistics on table
 INSERT INTO dbo.DataPack
 SELECT
                     t.NAME AS TableName
                     ,s.Name AS SchemaName
                     ,p.rows AS RowCounts
                     ,SUM(a.total_pages) * 8 AS TotalSpaceKB
                     ,SUM(a.used_pages) * 8 AS UsedSpaceKB
                     ,(SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
 ,GETDATE() AS TimeMeasure
 FROM 
 sys.tables AS t
                     INNER JOIN sys.indexes AS i
                     ON t.OBJECT_ID = i.object_id
                     INNER JOIN sys.partitions AS p
                     ON i.object_id = p.OBJECT_ID
                     AND i.index_id = p.index_id
                     INNER JOIN sys.allocation_units AS a
                     ON p.partition_id = a.container_id
                     LEFT OUTER JOIN sys.schemas AS s
                     ON t.schema_id = s.schema_id
              WHERE
                            t.NAME NOT LIKE 'dt%'
                     AND t.is_ms_shipped = 0
                     AND t.name = 'DataPack'
                     AND i.OBJECT_ID > 255
              GROUP BY t.Name, s.Name, p.Rows
              WAITFOR DELAY '00:00:02'
       COMMIT;
END

这将作为我们比较结果的基础。当我们查询DataPack_Log_Small表时,结果如下:

DECLARE @RScript nvarchar(max)
SET @RScript = N'
 library(Hmisc) 
 mydata <- InputDataSet
 all_sub <- mydata[2:3]
 c <- cor(all_sub, use="complete.obs", method="pearson")
 t <- rcorr(as.matrix(all_sub), type="pearson")
 c <- cor(all_sub, use="complete.obs", method="pearson")
 c <- data.frame(c)
 OutputDataSet <- c'
DECLARE @SQLScript nvarchar(max)
SET @SQLScript = N'SELECT
 TableName
 ,RowCounts
 ,UsedSpaceKB
 ,TimeMeasure
 FROM DataPack_Info_SMALL'
EXECUTE sp_execute_external_script
 @language = N'R'
 ,@script = @RScript
 ,@input_data_1 = @SQLScript
 WITH result SETS ((RowCounts VARCHAR(100)
 ,UsedSpaceKB  VARCHAR(100)));
GO  

结果,我们在RowCountsUsedSpaceKB列之间得到了一个强烈和正的相关性。这可以很容易地解释为:当RowCounts的值增加时,UsedSpaceKB的值也增加。这是唯一的逻辑解释。出现负相关性会有些奇怪。现在,我们将尝试模拟随机的删除和插入,并使用以下代码观察类似的行为:

DECLARE @nof_steps INT = 0
WHILE @nof_steps < 15
BEGIN
 BEGIN TRAN
 -- insert some data
 DECLARE @i INT = 1;
 DECLARE @insertedRows INT = 0;
              DECLARE @deletedRows INT = 0;
 DECLARE @Rand DECIMAL(10,2) = RAND()*10
 IF @Rand < 5
 BEGIN
 WHILE @i <= 1000 -- step is 100 rows
 BEGIN
                                                                  INSERT dbo.DataPack(col1, col2)
                                                                         SELECT
                                                                                 REPLICATE('A',FLOOR(RAND()*200))  -- pages are filling up differently
                                                                                ,REPLICATE('B',FLOOR(RAND()*300));
                                                               SET @i = @i + 1;
                                                           END
 SET @insertedRows = 1000 
                     END

               IF @Rand  >= 5
 BEGIN 
                                  SET @deletedRows = (SELECT COUNT(*) FROM dbo.DataPack WHERE DataPackID % 3 = 0)
                                  DELETE FROM dbo.DataPack
                                                    WHERE
                                  DataPackID % 3 = 0 OR DataPackID % 5 = 0

                     END
              -- run statistics on table
              INSERT INTO dbo.DataPack_Info_LARGE
              SELECT
                     t.NAME AS TableName
                     ,s.Name AS SchemaName
                     ,p.rows AS RowCounts
                     ,SUM(a.total_pages) * 8 AS TotalSpaceKB
                     ,SUM(a.used_pages) * 8 AS UsedSpaceKB
                     ,(SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
                     ,GETDATE() AS TimeMeasure
                     ,CASE WHEN @Rand < 5 THEN 'Insert'
                             WHEN @Rand >= 5 THEN 'Delete'
                             ELSE 'meeeh' END AS Operation
                     ,CASE WHEN @Rand < 5 THEN @insertedRows
                             WHEN @Rand >= 5 THEN @deletedRows
                             ELSE 0 END AS NofRowsOperation
 FROM 
                           sys.tables AS t
                     INNER JOIN sys.indexes AS i
                     ON t.OBJECT_ID = i.object_id
                     INNER JOIN sys.partitions AS p
                     ON i.object_id = p.OBJECT_ID
                     AND i.index_id = p.index_id
                     INNER JOIN sys.allocation_units AS a
                     ON p.partition_id = a.container_id
                     LEFT OUTER JOIN sys.schemas AS s
                     ON t.schema_id = s.schema_id

              WHERE
                            t.NAME NOT LIKE 'dt%'
                     AND t.is_ms_shipped = 0
                     AND t.name = 'DataPack'
                     AND i.OBJECT_ID > 255
              GROUP BY t.Name, s.Name, p.Rows
              WAITFOR DELAY '00:00:01'
       COMMIT;
END  

我们添加了一个DELETE语句,以及RowCounts,这样演示就不会那么直接。通过计算相关系数,很明显,我们再次得到了一个非常强烈和正的相关性。

现在,我们将通过在不同的数据集上运行相同的相关系数来比较我们的LARGE测试与基线。第一个是在我们的基线(DataPack_Info_SMALL)上,第二个是从我们的测试表(DataPack_Info_LARGE)上:

DECLARE @RScript1 nvarchar(max)
SET @RScript1 = N'
    library(Hmisc) 
                                mydata <- InputDataSet
                                all_sub <- mydata[4:5]
                                c <- cor(all_sub, use="complete.obs", method="pearson")
                                c <- data.frame(c)
                                OutputDataSet <- c'

DECLARE @SQLScript1 nvarchar(max)
SET @SQLScript1 = N'SELECT

                                              TableName
                                             ,RowCounts
                                             ,TimeMeasure
    ,UsedSpaceKB 
                                             ,UnusedSpaceKB
                                             FROM DataPack_Info_SMALL
                                             WHERE RowCounts <> 0'
EXECUTE sp_execute_external_script
 @language = N'R'
 ,@script = @RScript1
 ,@input_data_1 = @SQLScript1
       WITH result SETS ( (
                                          RowCounts VARCHAR(100)
                                         ,UsedSpaceKB  VARCHAR(100)
                                         ));

DECLARE @RScript2 nvarchar(max)
SET @RScript2 = N'
    library(Hmisc) 
                            mydata <- InputDataSet
                            all_sub <- mydata[4:5]
                            c <- cor(all_sub, use="complete.obs", method="pearson")
                            c <- data.frame(c)
                            OutputDataSet <- c'
DECLARE @SQLScript2 nvarchar(max)
SET @SQLScript2 = N'SELECT
                                          TableName
                                         ,RowCounts
                                         ,TimeMeasure
    ,UsedSpaceKB 
                                         ,UnusedSpaceKB
                                         FROM DataPack_Info_LARGE
                                         WHERE NofRowsOperation <> 0
                                         AND RowCounts <> 0'

EXECUTE sp_execute_external_script
 @language = N'R'
 ,@script = @RScript2
 ,@input_data_1 = @SQLScript2
 WITH result SETS ( (
 RowCounts VARCHAR(100)
 ,UsedSpaceKB  VARCHAR(100)
                                         )
                                   );
GO  

结果非常有趣。基线显示UsedSpaceKBUnusedSpaceKB之间没有相关性(它是-0.049),而我们的测试显示几乎 3 倍更强的负相关性(它是-0.109)。关于这个相关性的几点说明:这表明UsedSpaceKBUnUsedSpaceKB呈负相关;这虽然还不足以得出任何具体的结论,但它显示了轻微的变化如何导致简单相关性中的差异。

您可以使用 T-SQL、通过 PowerShell、通过实现.NET 组件或创建 SQL Server 作业,或者任何其他方式来收集磁盘空间使用信息。重要的是,最大的优势是,使用 R 和收集到的数据,现在您不仅能够监控和反应过去的数据,而且还能预测将要发生的事情。

让我们更进一步,假设以下查询和从我们的样本创建的数据集:

SELECT
 TableName
 ,Operation
 ,NofRowsOperation
 ,UsedSpaceKB
 ,UnusedSpaceKB
FROM dbo.DataPack_Info_LARGE

我们将基于历史数据预测 usedSpaceKB 的大小。我们的输入将是用于预测的给定数字的 TableNameOperationNofRowsOperation。我将使用通用线性模型(GLM 算法)来预测 usedDiskSpace!在你们所有人开始说这是荒谬的,由于 DBCC 缓存、页面中断、索引、停滞统计和许多其他参数,无法完成之前,我想指出,所有这些信息都可以添加到算法中,从而使预测更加准确。由于我的查询非常简单,是 INSERTDELETE 语句,你们也应该知道你正在预测哪些类型的查询。此外,这种方法在部署前的代码测试、单元测试和压力测试中也可能很有用。

使用以下 R 代码,我们可以开始创建预测:

-- GLM prediction
DECLARE @SQL_input AS NVARCHAR(MAX)
SET @SQL_input = N'SELECT
 TableName
                                  ,CASE WHEN Operation = ''Insert'' THEN 1 ELSE 0 END AS Operation
 ,NofRowsOperation
 ,UsedSpaceKB
 ,UnusedSpaceKB
 FROM dbo.DataPack_Info_LARGE
 WHERE
 NofRowsOperation <> 0';

DECLARE @R_code AS NVARCHAR(MAX)
SET @R_code = N'library(RevoScaleR)
 library(dplyr)
                DPLogR <- rxGlm(UsedSpaceKB ~ Operation + NofRowsOperation + UnusedSpaceKB, data = DataPack_info, family = Gamma)
                df_predict <- data.frame(TableName=("DataPack"), Operation=(1), NofRowsOperation=(451), UnusedSpaceKB=(20))
 predictions <- rxPredict(modelObject = DPLogR, data = df_predict, outData = NULL, 
                                predVarNames = "UsedSpaceKB", type = "response",checkFactorLevels=FALSE);
                OutputDataSet <- predictions'

EXEC sys.sp_execute_external_script
 @language = N'R'
    ,@script = @R_code
    ,@input_data_1 = @SQL_input
 ,@input_data_1_name = N'DataPack_info'
       WITH RESULT SETS ((
                         UsedSpaceKB_predict INT
                         ));
GO 

现在,我们可以根据以下数据预测 UsedSpaceKB 的大小:

df_predict <- data.frame(TableName=("DataPack"), Operation=(1), NofRowsOperation=(451), UnusedSpaceKB=(20))

我们首先需要澄清一些事情。以下使用 xp_execute_external_script 的 R 代码,作为一个存储过程,对于这些列(TableNameOperationNofRowsOperationUnusedSpaceKB)的输入参数会工作得更好。此外,为了避免在模型构建过程中不必要的计算时间,通常的做法是将序列化的模型存储在 SQL 表中,并在运行预测时进行反序列化。最后,由于这只是一个演示,请确保用于预测的数字是有意义的。正如我们在示例中看到的,如果绝对计算,UsedSpaceKB 的预测会更好,而不是使用累积值。只有后来才会计算累积值。

总结这个相当长的演示,让我们创建一个过程并运行一些预测,看看这是多么高效。存储过程如下:

CREATE PROCEDURE Predict_UsedSpace
    (
     @TableName NVARCHAR(100)
    ,@Operation CHAR(1)  -- 1  = Insert; 0 = Delete
    ,@NofRowsOperation NVARCHAR(10)
    ,@UnusedSpaceKB NVARCHAR(10)
    )
    AS
    DECLARE @SQL_input AS NVARCHAR(MAX)
    SET @SQL_input = N'SELECT
                                      TableName
                                      ,CASE WHEN Operation = ''Insert'' THEN 1 ELSE 0 END AS Operation
                                      ,NofRowsOperation
                                      ,UsedSpaceKB
                                      ,UnusedSpaceKB
                                       FROM dbo.DataPack_Info_LARGE
                                       WHERE
                                             NofRowsOperation <> 0';
    DECLARE @R_code AS NVARCHAR(MAX)
    SET @R_code = N'library(RevoScaleR)
                    DPLogR <- rxGlm(UsedSpaceKB ~ Operation + NofRowsOperation + UnusedSpaceKB, data = DataPack_info, family = Gamma)
    df_predict <- data.frame(TableName=("'+@TableName+'"), Operation=('+@Operation+'), 
                              NofRowsOperation=('+@NofRowsOperation+'), UnusedSpaceKB=('+@UnusedSpaceKB+'))
                    predictions <- rxPredict(modelObject = DPLogR, data = df_predict, outData = NULL,  predVarNames = "UsedSpaceKB", type = "response",checkFactorLevels=FALSE);
                    OutputDataSet <- predictions'

    EXEC sys.sp_execute_external_script
         @language = N'R'
        ,@script = @R_code
        ,@input_data_1 = @SQL_input
        ,@input_data_1_name = N'DataPack_info'

    WITH RESULT SETS ((
                                        UsedSpaceKB_predict INT
                                       ));
    GO

现在我们需要连续运行该过程两次:

EXECUTE Predict_UsedSpace
 @TableName = 'DataPack'
                     ,@Operation = 1
                     ,@NofRowsOperation = 120
                     ,@UnusedSpaceKB = 2;
GO

EXECUTE Predict_UsedSpace
                     @TableName = 'DataPack'
                     ,@Operation = 1
                     ,@NofRowsOperation = 500
                     ,@UnusedSpaceKB = 12;
GO  

对使用空间磁盘的预测都基于我们的演示数据,但也可以用于更大规模和预测。当然,为了获得更好的预测,也可以包括一些基线统计信息。对于每个模型,我们还需要测试预测,看看它们有多好。

摘要

使用 SQL Server R 进行任何类型的 DBA 任务,正如我们在这里所看到的,并不总是涉及核心统计或预测分析;我们可能还需要了解一些简单的统计知识,这些知识隐藏在属性查询、收集的统计信息和索引之间的连接和关系之中。例如,预测和预测执行计划中的信息,以便更好地理解查询覆盖缺失索引,这是一个关键点。参数嗅探或基数估计器也是一项很好的任务,可以与常规统计一起解决。

但我们已经看到,预测通常只有监控的事件可以给 DBA 带来巨大优势,并且对于核心系统来说是一个非常受欢迎的功能。

通过将 R 集成到 SQL Server 中,这些日常、每周或每月的任务可以自动化到以前未曾使用过的不同程度。因此,它可以帮助数据库管理员以及负责系统维护的人员获得不同的见解。

在下一章中,我们将介绍如何扩展 R 外部过程的功能以及如何使用它们。

第十章:R 和 SQL Server 2016/2017 功能扩展

SQL Server 2016 和 2017 提供了许多新的和改进的查询性能功能、可扩展性功能、安全功能以及对开发人员、数据库管理员和数据科学家有用的内置/本地功能。这些新功能和能力可以与 SQL 中的机器学习服务一起使用,提供强大的数据科学解决方案,同时使开发人员/数据科学家的生活变得更加容易。

本章将带您了解一些独特的场景,以展示 R 和 SQL Server 中其他内置功能的结合力量。这些场景包括 JSON 内置功能,展示我们如何处理物联网数据,PolyBase 用于说明超越关系型数据源,以及使用ColumnStore索引的大量数据。我们将深入这些场景,并在 R 中产生我们在前几章中学到的数据可视化和预测分析能力。

内置 JSON 功能

在这个场景中,我们将使用来自奥斯汀市官方开放数据门户的 EMS 事件按月数据(data.austintexas.gov/Public-Safety/EMS-Incidents-by-Month/gjtj-jt2d)。数据本质上包含事件计数,按地点和优先级细分奥斯汀市和特拉维斯县的事件,以及准时合规的百分比。

以下是要开始所需的先决条件:

  1. data.austintexas.gov/resource/bpws-iwvb.json下载数据到本地路径,例如C:\Temp\bpws-iwvb.json

  2. 授予目录读取权限;例如:

图 10.1 授予对 C:\Temp 的 MS SQL Server 访问权限

  1. 为了方便 R 可视化,我们将使用 SQL Operations Studio。您可以从:docs.microsoft.com/en-us/sql/sql-operations-studio/download下载 SQL Ops Studio。

以下是一个 JSON 文件的摘录:

图 10.2:bpws-iwvb.json 摘录

该文件中 JSON 对象的模式相当简单。它是一个包含以下 31 个属性的数组:

    • count_incidents_all

    • count_incidents_coa

    • count_incidents_coa_or_tc

    • count_incidents_coa_p1

    • count_incidents_coa_p2

    • count_incidents_coa_p3

    • count_incidents_coa_p4

    • count_incidents_coa_p5

    • count_incidents_other

    • count_incidents_tc

    • `count_incidents_tc_p1`

    • count_incidents_tc_p2

    • count_incidents_tc_p3

    • count_incidents_tc_p4

    • count_incidents_tc_p5

    • month_key

    • month_start_date

    • percent_on_time_all

    • percent_on_time_coa

    • percent_on_time_coa_p1

    • percent_on_time_coa_p2

    • percent_on_time_coa_p3

    • percent_on_time_coa_p4

    • percent_on_time_coa_p5

    • percent_on_time_target

    • percent_on_time_tc

    • percent_on_time_tc_p1

    • percent_on_time_tc_p2

    • percent_on_time_tc_p3

    • percent_on_time_tc_p4

    • percent_on_time_tc_p5

要查看数据的第一行以及值的显示方式,可以使用以下 T-SQL 代码:

SELECT * 
FROM OPENJSON((SELECT Doc.BulkColumn  
                FROM OPENROWSET(BULK N'C:\Temp\bpws-iwvb.json', SINGLE_CLOB) AS Doc), '$[0]') 

以下为上一条命令的输出:

图 10.3 检查 bpws-iwvb.json 的第一行数据

本节剩余部分将向您展示如何使用 SQL Server 中的内置 JSON 和 R 功能来解析数据并创建可视化,以了解 ATCEMS 收到的 EMS 事件趋势。以下截图显示了我们将通过 SQL Operations Studio 从前面的 JSON 数据构建的数据可视化:

图 10.4 使用 R 生成 EMS 事件的预测

执行以下步骤以构建前面的可视化:

  1. 打开 SQL Operations Studio。

  2. 使用 JSON,获取 Date 和事件计数:

SELECT  
    a.Date,  
    a.TotalIncidents, 
    a.AustinIncidents, 
    a.TravisIncidents 
FROM OPENJSON((SELECT Doc.BulkColumn  
                FROM OPENROWSET(BULK N'C:\Temp\bpws-iwvb.json', SINGLE_CLOB) AS Doc), '$') 
WITH (Date          DATE    '$.month_start_date', 
    TotalIncidents  INT     '$.count_incidents_all', 
    AustinIncidents INT     '$.count_incidents_coa', 
    TravisIncidents INT     '$.count_incidents_tc' 
    ) AS a 
ORDER BY a.Date ASC;

这里,我们使用 OPENROWSETSINGLE_CLOB 来读取明文文件流。

  1. 然后,我们使用 OPEN_JSON 提取 month_start_date 作为 Datecount_incidents_all 作为 TotalIncidientscount_incidents_coa 作为 AustinIncidents,以及 count_incidents_tc 作为 TravisIncidents

输出应类似于以下内容:

图 10.5:对 EMS 事件 JSON 文件运行 OPENJSON 的输出

  1. 现在,让我们使用 DateTotalIncidents 数据构建 R 脚本的 InputDataSet
DECLARE @input_query    NVARCHAR(MAX);  
SET @input_query = 'SELECT  
    a.Date,  
    a.TotalIncidents 
FROM OPENJSON((SELECT Doc.BulkColumn  
    FROM OPENROWSET(BULK N'C:\Temp\bpws-iwvb.json',  
         SINGLE_CLOB) AS Doc), '$') 
   WITH (Date            DATE  '$.month_start_date', 
         TotalIncidents  INT   '$.count_incidents_all' 
        ) AS a;' 
  1. 让我们构建使用前面 InputDataSet 的 R 脚本:

在此脚本中,我们将使用预测包根据 2010 年 1 月和 2017 年 11 月的现有数据进行预测。有关预测包的更多信息,包括依赖包,可在以下网址找到:cran.r-project.org/web/packages/forecast/index.html

在这个特定示例中,事件数量可能是季节性的。因此,我们将使用一些辅助函数来帮助预测:

    • ts 函数将 InputDataSet 数据框转换为时间序列

    • stl 函数将时间序列分解为季节性、趋势和不规则成分

    • 考虑季节性的 forecast 函数:

DECLARE @RPlot          NVARCHAR(MAX); 

SET @RPlot = 'library(forecast); 
    image_file = tempfile();  
    jpeg(filename = image_file, width=1000, height=400);  

    #store as time series  
    myts <- ts(InputDataSet$TotalIncidents,  
        start = c(2010, 1), end = c(2017, 11), 
         frequency = 12);  
    fit <- stl(myts, s.window = "period"); 

    # show the plot 
    plot(forecast(fit), main = "Forecast of EMS incidents"); 
    dev.off();  

    # return the plot as dataframe 
    OutputDataSet <-  data.frame( 
        data=readBin(file(image_file,"rb"), 
        what=raw(),n=1e6));' 
  1. 现在,让我们执行以下脚本:
EXEC sp_execute_external_script @language = N'R' 
    ,@script = @RPlot  
    ,@input_data_1 = @input_query 
    ,@input_data_1_name = N'InputDataSet' 
    ,@output_data_1_name = N'OutputDataSet'  
    WITH RESULT SETS (( [plot] VARBINARY(MAX)));     
  1. 在 SQL Ops Studio 中,结果将以 VARBINARY 数据类型显示:

图 10.6:SQL Operations Studio 中的“以图表查看”功能

  1. 在右侧单击“以图表查看”图标,然后在结果窗格中单击“图表查看器”选项卡,然后选择“图像”从图表类型。输出应类似于以下内容:

图 10.7:在 SQL Operations Studio 中显示 R 生成的图表

使用 PolyBase 访问外部数据源

PolyBase 允许您的 SQL Server 实例使用 T-SQL 访问服务器/数据库之外的数据。在 SQL Server 2016 中,您可以在 Hadoop 中的外部数据上运行查询或从 Azure Blob 存储导入数据:

图 10.8:PolyBase 概念(来源:https://docs.microsoft.com/en-us/sql/relational-databases/polybase/polybase-guide)

在本节中,我们将使用与上一节类似的数据集,这些数据集以 Azure Blob Storage 中的 CSV 文件形式表示。这些 CSV 文件代表 EMS 事件,将在 SQL Server 中作为外部表公开。本演示的目标是了解 EMS 事件的季节性和趋势。我们将在 SQL Server 中使用 R 来完成这项工作,并在 SQL Operations Studio 中查看图表。

以下是要开始所需的先决条件:

  1. SQL Server 实例安装了 PolyBase。此步骤的文档在:docs.microsoft.com/en-us/sql/relational-databases/polybase/get-started-with-polybase

  2. 已安装 Microsoft Azure Storage Explorer。使用 Azure Storage Explorer,您可以上传文件,管理存储账户中的容器,并从存储账户获取主访问密钥:

![img/00186.jpeg]

图 10.9:使用 Microsoft Azure Storage Explorer 连接到 Azure Blob Storage

  1. 从 <插入 GitHub 链接> 下载的 EMS 事件按月下载并上传到您的 Azure Blob Storage。

  2. 已安装 SSMS 或 SQL Operations Studio。

执行以下步骤以创建外部表:

  1. 首先在主数据库上启用高级选项,以启用连接到 Azure Blob Storage 的连接。

将 Hadoop 连接设置为 7 允许连接到 Azure Blob Storage。有关其他支持的数据源的信息,请访问:

docs.microsoft.com/en-us/sql/database-engine/configure-windows/polybase-connectivity-configuration-transact-sql

USE master;   
GO   
EXEC sp_configure 'show advanced option', '1';   
RECONFIGURE;  
GO 
EXEC sp_configure 'hadoop connectivity', 7;  
GO  
RECONFIGURE;  
GO 
  1. 在您希望创建连接到 Azure Blob Storage 中 CSV 文件的外部表的数据库中创建一个主密钥:
USE [AdventureWorks2016] 
GO 
CREATE MASTER KEY ENCRYPTION BY PASSWORD='MsSQLGirlLovesSQLServer2016&2017:)'; 
  1. 创建数据库 MsSQLGirlAtAzureBlobStorage
CREATE DATABASE SCOPED CREDENTIAL MsSQLGirlAtAzureBlobStorage   
WITH IDENTITY = 'credential', Secret = 'Es3duvq+x9G5x+EFbuUmGo0salEi6Jsd59NI20KXespbiBG9RswLA4L1fuqs/59porPBay64YkRj/tvQ7XAMLA=='; 
  1. 在 Azure Blob Storage 中创建指向容器的外部数据源。在这个例子中,open-data-sample 是容器的名称,而 mssqlgirl.blob.core.windows.net 是 Azure Blob Storage 的位置:
CREATE EXTERNAL DATA SOURCE OpenDataSample 
WITH ( 
    TYPE = HADOOP, 
    LOCATION = 'wasbs://open-data-sample@mssqlgirl.blob.core.windows.net/', 
    CREDENTIAL = MsSQLGirlAtAzureBlobStorage 
); 
  1. 创建容器中源文件的文件格式:
CREATE EXTERNAL FILE FORMAT csvformat  
WITH (  
    FORMAT_TYPE = DELIMITEDTEXT,  
    FORMAT_OPTIONS (  
        FIELD_TERMINATOR = ',' 
    )  
); 
  1. 在容器中创建以下源文件:
CREATE EXTERNAL TABLE EMSIncident 
(  
   [Month Key]                         INT, 
   [Month-Year]                        VARCHAR(30), 
   [Total Incidents]                   INT, 
   [Austin Incidents]                  INT, 
   [Travis County Incidents]           INT, 
   [Other Area Incidents]              INT, 
   [Combined Austin & Travis Incidents]      INT, 
   [Austin P1 Incidents]               INT, 
   [Austin P2 Incidents]               INT, 
   [Austin P3 Incidents]               INT, 
   [Austin P4 Incidents]               INT, 
   [Austin P5 Incidents]               INT, 
   [Travis County P1 Incidents]        INT, 
   [Travis County P2 Incidents]        INT, 
   [Travis County P3 Incidents]        INT, 
   [Travis County P4 Incidents]        INT, 
   [Travis County P5 Incidents]        INT, 
   [Overall On-Time Compliance]        VARCHAR(10), 
   [Austin On-Time Compliance]               VARCHAR(10), 
   [Travis County On-Time Compliance]        VARCHAR(10), 
   [Austin P1 On-Time Compliance]            VARCHAR(10), 
   [Austin P2 On-Time Compliance]            VARCHAR(10), 
   [Austin P3 On-Time Compliance]            VARCHAR(10), 
   [Austin P4 On-Time Compliance]            VARCHAR(10), 
   [Austin P5 On-Time Compliance]            VARCHAR(10), 
   [Travis County P1 On-Time Compliance]     VARCHAR(10), 
   [Travis County P2 On-Time Compliance]     VARCHAR(10), 
   [Travis County P3 On-Time Compliance]     VARCHAR(10), 
   [Travis County P4 On-Time Compliance]     VARCHAR(10), 
   [Travis County P5 On-Time Compliance]     VARCHAR(10), 
   [Target On-Time Compliance]               VARCHAR(10) 
)  
WITH  
(  
    LOCATION = '/EMS_-_Incidents_by_Month.csv',  
    DATA_SOURCE = OpenDataSample,  
    FILE_FORMAT = csvformat  
) 
  1. 因此,现在我们可以对外部表执行 SELECT 语句,将其作为 R 脚本的输入:
DECLARE @input_query    NVARCHAR(MAX);  
DECLARE @RPlot          NVARCHAR(MAX); 

SET @input_query = 'SELECT  
    CAST([Month-Year] AS DATE) AS [Date], 
    [Total Incidents] AS [TotalIncidents] 
FROM EMSIncident;' 
SET @RPlot = 'library(ggplot2);  
    library(forecast); 
    image_file = tempfile();  
    jpeg(filename = image_file, width=1000, height=400);  

    #store as time series  
    myts <- ts(InputDataSet$TotalIncidents,  
        start = c(2010, 1), end = c(2017, 11), 
         frequency = 12);  
    fit <- stl(myts, s.window = "period"); 

    # show the plot 
    plot(fit, main = "EMS incidents"); 
    dev.off();  

    # return the plot as dataframe 
    OutputDataSet <-  data.frame( 
        data=readBin(file(image_file,"rb"), 
        what=raw(),n=1e6));' 

EXEC sp_execute_external_script @language = N'R' 
    ,@script = @RPlot  
    ,@input_data_1 = @input_query 
    ,@input_data_1_name = N'InputDataSet' 
    ,@output_data_1_name = N'OutputDataSet'  
    WITH RESULT SETS (( [plot] VARBINARY(MAX))); 

如果您使用 SQL Operations Studio,您可以查看图表,其外观应如下所示:

![img/00187.jpeg]

图 10.10:EMS 事件时间序列

上述图表表明,趋势从 2010 年到 2016 年在增加,然后在总体上显著下降。

使用 ColumnStore 和内存 OLTP 实现高性能

SQL Server 2016 R 服务和 SQL Server 2017 ML 服务提供了高级分析能力,这些能力也可以应用于优化表。在本节中,我们将向您介绍如何使用 R 服务与以下内容进行比较:

  • 具有主键的表

  • 具有聚集 ColumnStore 索引的表

  • 具有内存优化的表

  • 具有聚集ColumnStore索引的内存优化表

想要更多关于 SQL Server 和机器学习的优化技巧和窍门,请访问:

azure.microsoft.com/en-us/blog/optimization-tips-and-tricks-on-azure-sql-server-for-machine-learning-services/

在具有主键的表上测试 rxLinMod 的性能

以下步骤将测试rxLinMod在存储在具有主键的表中的 6,096,762 位数据上的性能。这些航空航班数据可以从packages.revolutionanalytics.com/datasets/AirOnTime2012.xdf下载并存储在C:/Program Files/Microsoft SQL Server/140/R_SERVER/library/RevoScaleR/SampleData中。步骤结束时,我们将记录 CPU 时间:

  1. 创建一个存储过程,用于从 XDF 文件中读取AirFlights样本数据,并将其作为数据帧返回,以便我们可以将其插入到新表中:
CREATE PROCEDURE [dbo].[usp_ImportXDFtoSQL] 
AS 
   DECLARE @RScript NVARCHAR(MAX) 
   SET @RScript = N'library(RevoScaleR) 
         rxOptions(sampleDataDir = "C:/Program Files/Microsoft SQL Server/140/R_SERVER/library/RevoScaleR/SampleData"); 
         outFile <-  file.path(rxGetOption("sampleDataDir"), "AirOnTime2012.xdf"); 
         OutputDataSet <- data.frame(rxReadXdf(file=outFile, varsToKeep=c("ArrDelay", "CRSDepTime","DayOfWeek")))' 

   EXECUTE sp_execute_external_script 
          @language = N'R' 
         ,@script = @RScript 
         WITH RESULT SETS (( 
               [ArrDelay]        SMALLINT, 
               [CRSDepTime]      DECIMAL(6,4), 
               [DayOfWeek]       NVARCHAR(10))); 
GO
  1. 在数据库中创建一个用于存储AirFlights数据的表。此表表示一个以行存储的表,具有主键:
CREATE TABLE [dbo].[AirFlights] 
( 
    [ID]             INT NOT NULL IDENTITY(1,1)  
   ,[ArrDelay]       SMALLINT 
   ,[CRSDepTime]     DECIMAL(6,4) 
   ,[DayOfWeek]      NVARCHAR(10)  
   ,CONSTRAINT PK_AirFlights PRIMARY KEY ([ID]) 
); 
GO 
  1. 将航空航班数据插入到AirFlights表中。在此实例中,我们使用 R 服务来加载数据:
INSERT INTO [dbo].[AirFlights] 
EXECUTE [dbo].[usp_ImportXDFtoSQL] 
  1. 创建一个过程,该过程调用外部脚本来计算从线性模型预测的到达延迟的系数:
CREATE PROCEDURE dbo.usp_TestPerformance (@TableName VARCHAR(50)) 
AS 
   DECLARE @RScript NVARCHAR(MAX) 
   SET @RScript = N'library(RevoScaleR) 
                           LMResults <- rxLinMod(ArrDelay ~ DayOfWeek, data = InputDataSet) 
                           OutputDataSet <- data.frame(LMResults$coefficients)' 

   DECLARE @SQLScript nvarchar(max) 
   SET @SQLScript = N'SELECT ArrDelay, DayOfWeek FROM ' + @TableName  
   SET STATISTICS TIME ON; 
   EXECUTE sp_execute_external_script 
          @language = N'R' 
         ,@script = @RScript 
         ,@input_data_1 = @SQLScript 
   WITH RESULT SETS (( 
                     Coefficient DECIMAL(10,5) 
                     )); 

   SET STATISTICS TIME OFF; 
GO 
  1. 按照以下方式执行过程:
EXEC dbo.usp_TestPerformance '[dbo].[AirFlights]' 

结果应类似于以下内容:

SQL Server parse and compile time:  
   CPU time = 0 ms, elapsed time = 1 ms. 
STDOUT message(s) from external script:  
Rows Read: 6096762, Total Rows Processed: 6096762, Total Chunk Time: 0.075 seconds  
Computation time: 0.080 seconds. 
(8 rows affected) 
 SQL Server Execution Times: 
   CPU time = 2797 ms,  elapsed time = 10463 ms. 
 SQL Server Execution Times: 
   CPU time = 2797 ms,  elapsed time = 10464 ms. 

在具有聚集列存储索引的表上测试 rxLinMod 的性能

以下步骤将测试rxLinMod在存储在具有聚集ColumnStore索引的表中的 6,096,762 位数据上的性能。步骤结束时,我们将记录 CPU 时间:

  1. 从上一节创建一个类似于dbo.AirFlights的表。我们还想创建一个聚集ColumnStore索引并将dbo.AirFlights中的数据插入:
CREATE TABLE AirFlights_CS 
( 
    [ID]             INT NOT NULL IDENTITY(1,1) 
   ,[ArrDelay]       SMALLINT 
   ,[CRSDepTime]     DECIMAL(6,4) 
   ,[DayOfWeek]      NVARCHAR(10)  
); 
GO 

INSERT INTO [dbo].[AirFlights_CS] 
( 
    [ArrDelay]        
   ,[CRSDepTime]      
   ,[DayOfWeek]       
) 
SELECT  
    [ArrDelay]        
   ,[CRSDepTime]      
   ,[DayOfWeek]       
FROM [dbo].[AirFlights];  
GO 

CREATE CLUSTERED COLUMNSTORE INDEX CCI_Airflights_CS ON [dbo].[AirFlights_CS]  
GO 
  1. 执行以下过程:
EXEC dbo.usp_TestPerformance '[dbo].[AirFlights_CS]' 

结果应类似于以下内容:

SQL Server parse and compile time:  
   CPU time = 0 ms, elapsed time = 7 ms. 
STDOUT message(s) from external script:  
Rows Read: 6096762, Total Rows Processed: 6096762, Total Chunk Time: 0.075 seconds  
Computation time: 0.080 seconds. 
(8 rows affected) 
 SQL Server Execution Times: 
   CPU time = 2235 ms,  elapsed time = 10684 ms. 
 SQL Server Execution Times: 
   CPU time = 2235 ms,  elapsed time = 10692 ms. 

在具有主键的内存优化表上测试 rxLinMod 的性能

以下步骤将测试rxLinMod在存储在具有非聚集主键的内存优化表中的 6,096,762 位数据上的性能。步骤结束时,我们将记录 CPU 时间:

  1. 在数据库中为内存优化表创建一个文件组:
ALTER DATABASE PerfTuning  
   ADD FILEGROUP PerfTuningMOD CONTAINS MEMORY_OPTIMIZED_DATA; 

ALTER DATABASE PerfTuning  
   ADD FILE (NAME='PerfTuningMOD',  
   FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\PerfTuningMOD.ndf')  
   TO FILEGROUP PerfTuningMOD; 

ALTER DATABASE PerfTuning  
   SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT=ON   
GO   
  1. 创建dbo.AirFlights的内存优化表版本:
    CREATE TABLE [dbo].[AirFlights_MOD] 
    ( 
           [ID] INT IDENTITY(1,1) NOT NULL PRIMARY KEY NONCLUSTERED
          ,[ArrDelay] SMALLINT
          ,[CRSDepTime]     DECIMAL(6,4)
    ,[DayOfWeek]      NVARCHAR(10) 
    ) WITH (MEMORY_OPTIMIZED=ON, DURABILITY = SCHEMA_AND_DATA);

    GO

    INSERT INTO [dbo].[AirFlights_MOD]
    (
    [ArrDelay] 
    ,[CRSDepTime] 
    ,[DayOfWeek] 
    )
    SELECT 
    [ArrDelay] 
    ,[CRSDepTime] 
    ,[DayOfWeek] 
    FROM [dbo].[AirFlights] 
    go 
  1. 执行以下过程:
EXEC dbo.usp_TestPerformance '[dbo].[AirFlights_MOD]'

结果应类似于以下内容:

SQL Server parse and compile time: 
 CPU time = 2 ms, elapsed time = 2 ms.
STDOUT message(s) from external script: 
Rows Read: 6096762, Total Rows Processed: 6096762, Total Chunk     
Time: 0.072 seconds 
Computation time: 0.077 seconds.
(8 rows affected)
 SQL Server Execution Times:
 CPU time = 2109 ms,  elapsed time = 8835 ms.
 SQL Server Execution Times: 2235 ms,  elapsed time = 10692 ms.

在具有聚集列存储索引的内存优化表上测试 rxLinMod 的性能

以下步骤将测试rxLinMod在存储在具有非聚集主键的内存优化表中的 6,096,762 位数据上的性能。步骤结束时,我们将记录 CPU 时间:

  1. 创建一个具有聚集columstore索引的dbo.AirFlights内存优化表版本:
CREATE TABLE [dbo].[AirFlights_MODCS]  
(   
    [ID] INT IDENTITY(1,1) NOT NULL PRIMARY KEY NONCLUSTERED 
   ,[ArrDelay] SMALLINT 
   ,[CRSDepTime] DECIMAL(6,4) 
   ,[DayOfWeek]      VARCHAR(10)  
) WITH (MEMORY_OPTIMIZED=ON, DURABILITY = SCHEMA_AND_DATA); 
GO 

INSERT INTO [dbo].[AirFlights_MODCS] 
( 
    [ArrDelay]        
   ,[CRSDepTime]      
   ,[DayOfWeek]       
) 
SELECT  
    [ArrDelay]        
   ,[CRSDepTime]      
   ,[DayOfWeek]       
FROM [dbo].[AirFlights]; 
GO 
ALTER TABLE [dbo].[AirFlights_MODCS] 
ADD INDEX CCI_Airflights_MODCS CLUSTERED COLUMNSTORE 
GO 
  1. 执行以下过程:
EXEC dbo.usp_TestPerformance '[dbo].[AirFlights_MODCS]' 

结果应该看起来像这样:

SQL Server parse and compile time:  
   CPU time = 3 ms, elapsed time = 3 ms. 
STDOUT message(s) from external script:  
Rows Read: 6096762, Total Rows Processed: 6096762, Total Chunk Time: 0.088 seconds  
Computation time: 0.093 seconds. 
(8 rows affected) 
 SQL Server Execution Times: 
   CPU time = 1766 ms,  elapsed time = 8659 ms. 
 SQL Server Execution Times: 
   CPU time = 1782 ms,  elapsed time = 8662 ms. 

比较结果

如您从以下快速图表比较中可以看到,SQL Server R 服务可以很好地利用优化表:

图 10.11 比较使用 R 服务时经典主键、Columnstore、内存和内存+Columnstore 的性能

如需有关性能比较的更多信息,您可以访问Tomaž Kaštrun的文章:

github.com/tomaztk/Performance-differences-between-RevoScaleR-ColumnStore-Table-and-In-Memory-OLTP-Table/blob/master/RevoScaleR_vs_ColumStore_vs_InMemoryOLTP.sql

摘要

SQL Server 2016 和 SQL Server 2017 包含了丰富的功能,从 JSON、PolyBase 到高性能特性,例如ColumnStore索引和内存优化表。大多数这些功能都是兼容的,并且可以增强您的高级分析体验。在本章中,您已经学习了创建奥斯汀和特拉维斯县 EMS 事件预测和季节性趋势的步骤。我们使用了 Azure Blob Storage 中的 JSON 数据源和 CSV 文件。最后,您还学习了如何利用ColumnStore和内存优化表来提高 R 服务的性能。

posted @ 2025-09-04 14:10  绝不原创的飞龙  阅读(5)  评论(0)    收藏  举报