Snowpark-终极指南-全-

Snowpark 终极指南(全)

原文:zh.annas-archive.org/md5/6e864634f7fd34b196ed938c35619226

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Snowpark 是一个强大的框架,帮助您在 Snowflake 数据云中解锁无限可能。然而,没有适当的指导,利用 Snowpark 的 Python 功能可能会颇具挑战。本书包含大量实用示例和代码片段,将指导您成功使用 Snowpark 与 Python。

《雪地公园终极指南》 帮助您了解雪地公园及其如何在数据云中整合各种工作负载,例如数据工程、数据科学和数据应用。

从配置到编码风格以及数据操作、收集、准备、转换、聚合和分析等工作负载,本指南将为您提供所需的知识,以充分利用此框架。您将了解如何构建、测试和部署数据管道和数据科学模型。随着您的进步,您将能够在 Snowflake 中本地部署数据应用,并使用 Snowpark 容器服务操作 LLMs。

到最后,您将能够充分利用 Snowpark 的功能,并将您的 Snowflake 开发者职业生涯推向新的高度。

本书面向对象

本书面向数据工程师、数据科学家、数据架构师、应用开发者和寻求深入了解 Snowpark 功能和最佳实践,以使用 Python 编程语言在 Snowpark 中部署各种工作负载的数据从业者。

本书涵盖内容

第一章探索 Snowpark,将引导您了解 Snowpark 及其独特功能。您将学习如何利用 Python 与 Snowpark 协同工作,以及如何将其应用于各种工作负载。到本章结束时,您将掌握 Snowpark 的功能和优势,包括更快的数据处理速度、提高数据质量和降低成本。这些指导章节旨在为您提供对 Snowpark 的全面理解,以及如何利用其价值来解决您的特定用例。

第二章使用 Snowpark 建立基础,教导您如何配置和操作 Snowpark,建立编码风格和结构,并探索工作负载。您还将获得与 Snowpark 高效工作的实际知识和技能,包括设置环境、结构化代码以及利用它处理不同的工作负载。

第三章使用 Snowpark 简化数据处理,教导用户如何在 Snowpark 中处理数据。它涵盖了数据收集、准备、转换、聚合和分析。到本章结束时,用户将获得管理数据源、清理和转换数据以及执行高级分析任务的实际知识和技能。

第四章, 使用 Snowpark 构建数据工程管道,涵盖了构建可靠的数据管道、有效的调试和日志记录、使用 DataOps 进行高效部署以及 Snowpark 的测试驱动开发。本章将为用户提供开发、测试和部署数据管道的实用技能,从而在 Snowpark 中实现可靠和高效的通道。

第五章, 使用 Snowpark 开发数据科学项目,涵盖了 Snowpark 在数据科学项目中的应用,以及探索数据科学管道,包括数据准备、探索和具有 Snowpark 特性的模型训练。这些材料面向希望使用 Snowpark 处理大量数据并构建精确机器学习模型的数据科学家和其他专业人士。

第六章, 使用 Snowpark 部署和管理机器学习模型,探讨了在 Snowpark 中实现机器学习模型并构建特征存储库。此外,读者还可以学习将模型注册集成到 Snowpark 中,并监控和运营他们的机器学习模型。本章面向希望熟练掌握使用 Snowpark 部署和管理机器学习模型技术的数据科学家和专家。

第七章, 使用 Snowpark 开发原生应用程序,将探讨原生应用程序框架以及如何使用 Snowpark 开发、部署、管理和货币化原生应用程序。本章面向渴望在 Snowflake 中构建应用程序的开发者。

第八章, Snowpark 容器服务简介,介绍了 Snowpark 容器服务并讨论了如何在 Snowflake 中部署容器中的应用程序。本章面向在 Snowflake 中构建容器应用的开发者。

要充分利用本书

要充分利用本书,您应该具备基本的 SQL 知识、熟练的 Python 技能、对数据工程和数据分析基础的了解,以及熟悉 Snowflake 数据云平台。

本书涵盖的软件/硬件 操作系统要求
Snowflake Windows, macOS, 或 Linux (任何)
Python Windows, macOS, 或 Linux (任何)
Visual Studio Code Windows, macOS, 或 Linux (任何)
Chrome 或另一个最新浏览器

您需要一个 Snowflake 账户。您可以在signup.snowflake.com/注册试用。

如果您正在使用本书的数字版,我们建议您亲自输入代码或从本书的 GitHub 存储库(下一节中提供链接)获取代码。这样做将帮助您避免与代码复制和粘贴相关的任何潜在错误。

下载示例代码文件

你可以从 GitHub 下载本书的示例代码文件,网址为github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark。如果代码有更新,它将在 GitHub 仓库中更新。

我们还有其他来自我们丰富的书籍和视频目录的代码包,可在github.com/PacktPublishing/找到。查看它们!

使用的约定

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

文本中的代码: 表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“一个名为MY_EVENTS的事件表使用默认列结构创建。”

代码块设置为以下格式:

current_runs = dag_op.get_current_dag_runs(dag)
for r in current_runs:
    print(f"RunId={r.run_id} State={r.state}")

任何命令行输入或输出都按照以下方式编写:

conda create --name def_gui_3.8_env --override-channels --channel https://repo.anaconda.com/pkgs/snowflake python=3.8

粗体: 表示新术语、重要单词或你在屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“模型注册是一个集中式存储库,使模型开发者能够有效地组织、共享和发布机器学习模型。”

提示或重要注意事项

看起来是这样的。

联系我们

我们欢迎读者的反馈。

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

勘误: 尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将不胜感激,如果您能向我们报告,我们将不胜感激。请访问www.packtpub.com/support/errata并填写表格。

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

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

分享你的想法

一旦你阅读了《Snowpark 终极指南》,我们很乐意听听你的想法!请点击此处直接进入此书的亚马逊评论页面并分享你的反馈。

你的评论对我们和科技社区都很重要,并将帮助我们确保我们提供高质量的内容。

下载本书的免费 PDF 副本

感谢您购买本书!

你喜欢在路上阅读,但无法携带你的印刷书籍到处走吗?

你的电子书购买是否与您选择的设备不兼容?

别担心,现在每购买一本 Packt 书籍,您都可以免费获得该书的 DRM 免费 PDF 版本。

在任何地方、任何设备上阅读。直接从您最喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。

优惠远不止这些,您还可以获得独家折扣、时事通讯和每日邮箱中的精彩免费内容。

按照以下简单步骤获取福利:

  1. 扫描二维码或访问以下链接

packt.link/free-ebook/9781805123415

  1. 提交您的购买证明

  2. 就这些了!我们将直接将您的免费 PDF 和其他福利发送到您的邮箱

第一部分:Snowpark 基础和设置

在本部分,我们将探讨 Snowpark 框架在 Python 中的基本和高级功能。本部分重点关注 Snowpark 平台以及开始使用 Snowpark 所需的设置。

本部分包括以下章节:

  • 第一章探索 Snowpark

  • 第二章在 Snowpark 中建立基础

第一章:探索 Snowpark

Snowpark 是 Snowflake 最近发布的一项重大创新,它提供了一套直观的库和运行时,用于在 Snowflake 中进行大规模的数据查询和处理。本章旨在引导您了解 Snowpark 的独特功能。此外,本章还帮助您学习如何利用 Python 与 Snowpark 一起使用,并在各种工作负载中实现它,如数据工程、数据科学和数据应用。到本章结束时,您将掌握 Snowpark 的功能和优势,包括更快的数据处理、可扩展性和降低成本。

在本章中,我们将涵盖以下主要主题:

  • 介绍 Snowpark

  • 利用 Python 为 Snowpark 提供支持

  • 理解 Snowpark 在不同工作负载中的应用

  • 实现使用 Snowpark 的价值

介绍 Snowpark

Snowflake 成立于 2012 年,通过完全重新设计数据世界并重新思考如何为云构建一个可靠、安全、高性能和可扩展的数据处理系统,开始了其数据云之旅。它从提供基于云的数据仓库服务开始,通过一个托管 软件即服务SaaS)平台来加载、分析和处理大量数据。Snowflake 的成功在于它是一个云原生托管解决方案,它建立在主要的公共云提供商之上,如亚马逊网络服务、微软 Azure 和谷歌云平台,通过自动提供可靠、安全、高性能和可扩展的数据处理系统,为组织提供支持,无需部署硬件或安装或配置任何软件。

与任何云数据仓库一样,Snowflake 支持作为首选语言的 美国国家标准协会ANSI)SQL。尽管 SQL 是一种强大的声明性语言,允许用户对数据进行提问,但它仅限于数据仓库工作负载,限制了高级工作负载(如数据科学和数据工程)的支持,这些工作负载需要开发者用其他编程语言编写解决方案,从而导致他们需要将数据从 Snowflake 移出以执行这些工作负载。

雪花解决方案应对这一挑战的是 Snowpark,这是一个创新的开发者框架,它简化了构建复杂数据管道的过程。通过 Snowpark,数据科学家和开发者可以直接使用他们偏好的编程语言与 Snowflake 进行交互,使他们能够快速且安全地部署 机器学习ML)模型,执行数据管道,并在 Snowflake 的虚拟计算仓库中无服务器地开发数据应用,而无需将数据转移到 Snowflake 之外。

Snowpark 通过原生支持在 Python、Scala 或 Java 中使用 DataFrame 风格的编程,暴露了与这些语言的深度集成接口,从而增强了 Snowflake 原始的 SQL 语言,并最小化了管理不同环境以进行高级数据管道的复杂性。这导致开发者能够利用 Snowflake 强大且可扩展的计算能力,将代码发送到数据中,而无需将其导出到 Snowflake 之外的其他环境中。

在本节中,我们简要介绍了 Snowpark,并学习了它如何融入 Snowflake 生态系统以及它如何帮助开发者。下一节将介绍如何利用 Python 进行 Snowpark 开发。

利用 Python 进行 Snowpark 开发

2022 年 6 月,Snowflake 发布了一项重大公告,揭示了备受期待的Snowpark for Python。这个新版本迅速成为 Snowpark 的首选编程语言,为用户提供在 Snowflake 中编程数据的更广泛选项。此外,Snowpark 简化了数据架构的管理,使用户能够更快、更有效地操作。

Snowpark for Python 是一个前沿的、企业级的、开源的创新,集成到 Snowflake 数据云中。因此,该平台为数据科学家和开发者提供了一种无缝、统一的体验。此外,Snowpark for Python 包建立在 Snowflake Python 连接器之上。Python 连接器使用户能够在 Snowflake 和 Snowpark 中执行 SQL 命令和其他基本功能,而 Snowpark for Python 则赋予用户执行更高级数据应用的能力。

例如,该平台允许用户在 Snowflake 中直接运行用户定义函数UDFs)、外部函数存储过程。这一强大的新功能使数据科学家、工程师和开发者能够在 Snowflake 内创建强大且安全的数据管道和机器学习模型。因此,他们可以利用平台优越的性能、弹性和安全功能来提供高级见解并推动有意义的业务成果。总的来说,Snowpark for Python 代表了 Snowflake 向前迈出的重要一步,为用户提供增强的功能和灵活性,同时保留了平台卓越的性能和安全功能。

Snowpark for Python 通过集成在 Snowflake 虚拟计算仓库内运行的 Anaconda 环境中的预审开源包来支持预审的开源包。这个环境为开发者提供了一个熟悉的界面。集成的 Anaconda 包管理器对开发者来说非常有价值,因为它附带了一套全面的精选开源包,并支持解决不同包和版本之间的依赖关系。这节省了大量时间,并帮助开发者避免处理“依赖地狱”的问题。

Snowpark for Python 的功能

Snowpark for Python 在 Snowflake 的所有云实例中普遍可用。它有助于加速不同的工作负载,并附带一系列丰富的功能,如下所示:

  • 它允许开发者直接在 Snowflake 中编写 Python 代码,使他们能够直接利用 Python 库和框架在 Snowflake 中的强大功能

  • 它支持流行的开源 Python 库,如 pandas、NumPy、SciPy 和 scikit-learn,以及其他库,允许开发者直接在 Snowflake 中执行复杂的数据分析和 ML(机器学习)任务

  • 它还提供了访问外部数据源,如 AWS S3、Azure Blob 存储和 Google Cloud Storage,允许开发者处理存储在 Snowflake 之外的数据

  • 它提供了与 Snowflake SQL 引擎的无缝集成,允许开发者使用 Python 编写函数式编程方法编写的查询,这些查询编译为 SQL

  • 它还支持分布式处理,允许开发者扩展其 Python 代码以处理大型数据集和复杂逻辑

  • 它使开发者能够构建自定义的 UDF(用户定义函数),这些函数可以在 SQL 查询中使用,从而提供更大的灵活性和数据处理的定制化

  • Snowpark 在 Snowflake 中提供了一个 Python 开发环境,允许开发者直接在 Snowflake UI 中编写、测试和调试 Python 代码

  • 它使开发者能够处理各种数据格式,如 CSV、JSON、Parquet 和 Avro,提供数据处理和分析的灵活性

  • 它提供了一个统一的数据处理体验,在单一环境中与 SQL 和 Python 协同工作

  • 它使开发者能够使用 Python 代码创建自定义数据管道,使得将 Snowflake 与其他数据源和数据处理工具集成更加容易

  • 它可以处理实时和批量数据处理,使得构建数据密集型工作负载更加容易

  • 它提供了一个基于 Snowflake 的强大框架,确保数据隐私并符合行业标准,如健康保险可携带性和问责制法案HIPAA)、通用数据保护条例GDPR)和安全运营中心SOC

  • Snowpark 通过利用Snowflake Marketplace来增强数据

Snowpark for Python 集成了许多功能,帮助开发者高效地在 Snowflake 的各种工作负载和用例中使用它。

为什么选择 Python 用于 Snowpark

虽然 Snowpark 支持 Python、Scala 和 Java,但本书将仅关注 Python,这是 Snowpark 开发的实际标准。Python 通过具有动态类型和绑定的高级内置数据结构而日益流行,使其非常适合数据操作。此外,该语言对开发者来说非常灵活且易于学习。其力量在于丰富的开源生态系统,得到了越来越多的流行包的支持。

Python 是一种通用、多用途的编程语言,适用于各种目的,如数据工程、数据科学和数据应用。它使开发者能够学习一种编程语言来满足所有需求。

Snowflake 也在大力投资 Python,以使数据科学家、工程师和应用开发者能够在数据云中构建更多内容,而无需做出治理权衡。

在本节中,我们介绍了 Snowpark for Python 的功能以及为什么 Python 是开发 Snowpark 的首选语言。下一节将介绍 Snowpark 如何用于不同的工作负载。

理解 Snowpark 在不同工作负载中的应用

Snowpark 的发布将 Snowflake 转变为一个完整的数据平台,旨在支持各种工作负载。Snowpark 支持多种工作负载,如数据科学和机器学习、数据工程和数据应用。

数据科学和机器学习

Python 是数据科学家的首选语言。Snowpark for Python 支持流行的库和框架,如 pandas、NumPy 和 scikit-learn,使其成为数据科学家在 Snowflake 中进行机器学习开发的理想框架。此外,数据科学家可以使用 DataFrames API 与 Snowflake 内部的数据进行交互,并在 Snowflake 内进行批量训练和推理。开发者还可以使用 Snowpark 进行特征工程、机器学习模型推理和端到端机器学习管道。Snowpark 还提供了一个 SnowparkML 库,以支持 Snowpark 中的数据科学和机器学习。

数据工程

数据清洗和 ELT 工作负载非常复杂,仅使用 SQL 构建数据管道正是 Snowpark 可以发挥巨大作用的地方。Snowpark 允许开发者编写易于阅读和重用的代码,同时提供更好的单元测试能力。此外,借助 Anaconda 的支持,开发者可以使用开源 Python 库来构建可靠的数据管道。数据处理的其他主要挑战是,基础设施需要大量的手动努力和维护。Snowpark 通过高性能解决这一问题,使数据工程师能够快速高效地处理大型数据集,构建复杂的数据管道,并处理大量数据而不会出现性能问题。

数据治理和安全

Snowpark 支持开发结合数据治理和安全的解决方案。数据治理至关重要,并增强了数据科学和数据工程用例。Snowpark 通过帮助组织理解和改进数据质量来简化治理态势。开发者可以快速创建一个函数来执行数据测试和检测异常。Snowpark 可以利用数据分类功能来检测个人身份信息PII)并分类对组织至关重要的数据。在 Snowpark 中开发的自定义函数可以使用强大的动态数据掩码功能来屏蔽敏感数据,如信用卡号码,同时保留 Snowflake 中现有的安全模型。

数据应用

Snowpark 帮助团队开发直接在 Snowflake 上运行而不需要将数据移出 Snowflake 的动态数据应用。使用 Snowflake 收购的强大开源库Streamlit,开发者可以使用熟悉的 Python 环境构建原生应用。利用基于角色的访问控制,可以在 Snowflake 管理的平台上安全地开发和共享交互式 ML 应用,利用其规模、性能和治理优势。Snowflake 原生应用框架提供了一个通过 Snowflake Marketplace 货币化应用的简化路径,在那里你可以让你的应用可供其他 Snowflake 客户使用,并开辟新的收入机会。

Snowpark 支持不同的工作负载,使 Snowflake 成为一个完整的数据云解决方案。以下部分将突出 Snowpark 的技术和业务优势。

实现使用 Snowpark 的价值

传统的大数据方法在业界已经存在很长时间,不适合现代基于云的可扩展工作负载。传统架构存在许多挑战,如下所述:

  • 将计算和数据解耦到独立的系统中

  • 为不同的语言运行独立的处理集群

  • 系统管理的复杂性

  • 数据孤岛和数据重复

  • 缺乏统一的安全和治理

Snowflake 通过 Snowpark 解决了传统系统的挑战,为数据生态系统和 Snowflake 用户提供了巨大的价值。以下图表显示了传统方法与 Snowflake 简化方法的区别:

图 1.1 – 传统方法与 Snowflake 方法的对比

图 1.1 – 传统方法与 Snowflake 方法的对比

如您从两种方法的区别中可以看到,Snowpark 的简化方法通过提供一种灵活、高效且成本效益高的方式来构建与业务需求同步扩展的数据,从而既有利于业务也有利于开发者。使用 Snowpark 的一些显著价值如下:

  • Snowpark 可以通过 DataFrame API 编程访问数据,这使得数据摄取和集成保持一致,因为你可以集成各种结构化和非结构化数据

  • Snowpark 通过将数据处理管道标准化为 Python 代码,实现了数据处理方法的标准化;它们可以接受测试和部署,并且更容易理解和解释

  • Anaconda 为 Snowpark 提供 Python 支持,并提供了对开源的第三方 Python 库的便捷访问,这增强了数据处理能力,并赋予开发者更多的能力

  • Snowpark 与现有的 Snowflake 虚拟仓库无缝集成和运行,允许开发者构建旨在无需额外基础设施即可扩展的数据应用

  • Snowpark 的框架支持各种工作负载,如数据工程、数据科学和数据应用,为数据云上的开发提供了一个统一的使用体验

  • Snowpark 提供了一个安全、受控的环境,因为它易于执行治理策略,并且没有数据在 Snowflake 之外移动

让我们总结本章内容。

摘要

Snowflake 的 Snowpark 完美地融合了 SQL 和 Python,在 Snowflake 数据云中运行复杂的数据处理作业,并使数据工程师、数据科学家和开发者能够利用 Snowflake。在本章中,我们看到了 Snowpark 的优势以及为什么 Python 是首选的开发语言。我们还介绍了 Snowpark 支持的不同工作负载。

在下一章中,我们将详细探讨配置和操作 Snowpark,并学习如何使用 Snowpark 处理各种工作负载。

第二章:使用 Snowpark 建立基础

在上一章中,你学习了 Snowpark 的基础知识、其优势以及它如何允许开发者使用 Python 开发复杂的数据应用程序。本章将专注于使用 Snowpark 建立坚实的基础。在这里,你将学习如何配置和操作 Snowpark,选择编码风格和结构,并深入探索 Snowpark 的基本原理。这将帮助你获得实际的知识和技能,以高效地使用 Snowpark,包括设置环境、结构化代码以及利用它处理不同的工作负载。

本章将涵盖以下主要内容:

  • 配置 Snowpark 开发环境

  • 使用 Snowpark 操作

  • 为 Snowpark 建立项目结构

技术要求

对于本章,你需要一个活跃的 Snowflake 账户,并在本地安装了配置好的 Anaconda Python。你可以参考以下文档以获取安装说明:

本章的支持材料可在本书的 GitHub 仓库中找到,网址为github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark

配置 Snowpark 开发环境

开发 Snowpark 的第一步是设置 Snowpark 开发环境。开发者有很大的灵活性,可以选择他们可以用来开始使用 Snowpark 的集成开发环境IDEs);他们唯一需要做的是安装 Snowpark 客户端应用程序编程接口API)并连接到他们的 Snowflake 账户。开发环境可以是一个包含你喜欢的 IDE 的本地 Python 环境,或者 Snowsight 中的新 Snowflake Python 工作表。本节将涵盖设置 Snowpark Python 工作表和本地开发环境。

Snowpark Python 工作表

Snowflake 发布了 Snowflake Worksheets for Python,这是一种新的工作表类型,允许在 Snowflake 内部开发基于 Python 的 Snowpark 环境。这一颠覆性功能允许开发者轻松利用 Snowpark Python 的力量在 Snowsight 中执行数据处理和创建数据管道、机器学习ML)模型和应用,通过直接将 Snowpark Python 集成到浏览器中,无需设置 Python 环境或在客户端安装开源库。相反,开发者可以轻松使用 Anaconda 中的预存软件包或从阶段导入自己的 Python 文件到工作表中。此外,他们可以快速将 Python 工作表作为存储过程部署。

使用 Python 工作表的前提条件

要启用和使用 Snowflake Python 工作表,您必须首先在 Snowsight 中确认 Anaconda 的服务条款。以下步骤应由组织管理员执行:

  1. 首先,您必须登录到您的 Snowflake 账户。在左侧面板的 切换角色 部分中,切换到用户上下文中的 ORGADMIN 角色:

图 2.1 – Snowflake 中的 ORGADMIN 角色

图 2.1 – Snowflake 中的 ORGADMIN 角色

  1. 然后,转到 管理员 | 账单与 条款

图 2.2 – 管理员 | 账单与条款

图 2.2 – 管理员 | 账单与条款

  1. 点击 Anaconda Python 软件包 旁边的 启用

图 2.3 – 启用 Anaconda Python 软件包

图 2.3 – 启用 Anaconda Python 软件包

  1. 您将看到一个弹出窗口,如下面的截图所示。点击 确认并继续 以启用软件包:

图 2.4 – Anaconda 的条款和服务

图 2.4 – Anaconda 的条款和服务

每次创建新的 Snowflake 环境,您都需要启用 Anaconda 软件包。现在我们已经启用了这些软件包,让我们看看如何使用 Python 工作表。

在 Snowflake Snowsight 中创建数据库和模式

要使用 Snowflake 的 Snowsight UI 界面创建名为 SNOWPARK_DEFINITIVE_GUIDE 的数据库和名为 MY_SCHEMA 的模式,请执行以下步骤:

  1. 前往 Snowsight 网络界面,使用您的 Snowflake 凭据登录,并导航到 数据 | 数据库 部分。这通常位于左侧导航菜单中:

图 2.5 – 数据库部分

图 2.5 – 数据库部分

  1. 在右上角寻找一个按钮或链接,上面写着 + 数据库 并点击它。在出现的对话框中,输入 SNOWPARK_DEFINITIVE_GUIDE 作为新数据库的名称。如有需要,您可以指定其他设置,例如 注释,然后点击 创建

图 2.6 – 新数据库对话框

图 2.6 – 新数据库对话框

  1. 创建数据库后,点击 SNOWPARK_DEFINITIVE_GUIDE 数据库。这将带您进入以下页面:

图 2.7 – SNOWPARK_DEFINTIVE_GUIDE 页面

图 2.7 – SNOWPARK_DEFINTIVE_GUIDE 页面

  1. 在此页面上,寻找一个写着+ 模式的按钮或链接并点击它。在出现的对话框中,将新模式的名字输入为MY_SCHEMA。可选地,你可以指定其他设置,例如注释管理访问。点击创建来创建模式:

图 2.8 – 新模式对话框

图 2.8 – 新模式对话框

在本书中,除非特定章节明确指示,否则我们将始终使用相同的数据库和模式。

在 Snowflake 中使用 Python 工作表

Python 工作表附带一个示例代码模板,可以作为起点使用。整个体验都基于 Snowflake 的 Snowsight UI。按照以下步骤创建和使用 Snowflake 中的 Python 工作表:

  1. 从 Snowsight UI 的菜单导航到工作表部分:

图 2.9 – 工作表菜单选项

图 2.9 – 工作表菜单选项

  1. 工作表面板中,点击右侧的+图标并选择Python 工作表来创建 Python 工作表:

图 2.10 – 创建 Python 工作表

图 2.10 – 创建 Python 工作表

  1. 选择一个数据库和模式,这样你就可以在工作表上下文中工作。Python 代码的作用域将基于这些细节:

图 2.11 – 工作表上下文

图 2.11 – 工作表上下文

  1. 工作表有一个处理函数,当工作表执行时会被调用。设置菜单允许你配置你的工作表。默认的处理函数是main(),返回类型可以指定为Table()VariantString。结果将以你选择的格式显示:

图 2.12 – 工作表设置

图 2.12 – 工作表设置

工作表首选项,如代码检查和换行,也可以在此处自定义。

  1. 可以通过点击工作表右上角的运行按钮来执行 Python 代码:

图 2.13 – 执行工作表

图 2.13 – 执行工作表

你可以在工作表中开始开发和执行 Python 代码以查看结果。

在 Python 工作表中管理 Anaconda 包

Python 工作表附带一个集成的 Anaconda 环境,支持导入最常见的 Anaconda 库,无需担心依赖管理。它还支持从内部阶段导入自定义包。在本节中,我们将探讨如何管理 Python 中的 Anaconda 包。为此,请执行以下步骤:

  1. 从菜单导航到标签并选择Anaconda 包。这将显示预安装的包及其版本:

图 2.14 – Anaconda 包

图 2.14 – Anaconda 包

  1. 您可以使用搜索栏搜索和安装所需的包:

图 2.15 – 搜索包

图 2.15 – 搜索包

  1. 您还可以通过从相应的下拉列表中选择可用版本来修改包的版本:

图 2.16 – 包版本

图 2.16 – 包版本

  1. 您可以使用以下查询通过 SQL 检查 Snowflake 中可用的 Python 包:

    SELECT distinct package_name
    FROM information_schema.packages
    WHERE language = 'python';
    

    此查询从信息模式中的包视图获取结果:

图 2.17 – Anaconda 包查询

图 2.17 – Anaconda 包查询

  1. 如果您需要检查特定包的版本信息,可以通过包的名称过滤查询:

    SELECT *
    FROM information_schema.packages
    WHERE (package_name='numpy' AND language = 'python');
    

    上述代码的输出如下:

图 2.18 – Anaconda Python 包查询

图 2.18 – Anaconda Python 包查询

此查询显示了我账户中可用的 Anaconda 包列表。在下一节中,我们将学习如何管理自定义 Python 包。

在 Python 工作表中管理自定义包

Python 工作表也支持导入自定义 Python 包的功能,这些包可以在 Python 代码中使用。但是,必须将包上传到内部阶段,这是 Snowflake 账户的存储部分,并从中导入。在 Snowsight 中,您可以将文件加载到命名的内部阶段区域,这样您就可以方便地在 Python 工作表中查看和使用它们,或者使用 SQL 将数据加载到表中。但是,需要注意的是,Snowsight 不支持将文件加载到用户或表阶段。

要创建一个命名的内部阶段,请确保您使用的角色在相关数据库和模式上具有使用权限,以及在模式上具有创建阶段权限。让我们开始吧:

  1. 登录 Snowsight。

  2. 访问数据部分并导航到数据库

  3. 选择您想要创建阶段并加载文件的数据库和模式。

  4. 点击创建,选择阶段,然后点击Snowflake 管理的

图 2.19 – 创建阶段

图 2.19 – 创建阶段

  1. 为阶段提供一个名称,并选择启用目录表以使阶段可视化。完成后,点击创建

图 2.20 – 创建内部阶段

图 2.20 – 创建内部阶段

要使用 Snowsight 将文件加载到 Snowflake 管理的命名内部阶段,请按照以下步骤操作:

  1. 访问数据部分并选择数据库

  2. 选择您创建阶段的数据库模式,并选择阶段本身。

  3. 点击+ 文件将所需的文件加载到阶段。

  4. 在出现的上传您的文件对话框中,选择您想要上传的文件(可以同时选择多个文件):

图 2.21 – 上传自定义包

图 2.21 – 上传自定义包

  1. 可选地,在阶段内指定或创建一个路径,用于存储文件。

  2. 点击 上传

  3. 一旦文件成功加载到阶段,您就可以根据需求执行各种操作。

这样,包已经被上传到阶段,并准备好导入。

重要提示

允许的最大文件大小为 50 MB。您需要在数据库和模式上具有 USAGE 权限,以及在阶段上具有 WRITE 权限。

一旦创建阶段并上传了包,您就可以导入模块,以便在程序中使用它。要导入包,请按照以下步骤操作:

  1. 菜单中选择 阶段包

图 2.22 – 阶段包

图 2.22 – 阶段包

  1. 输入包的路径。您可以使用 @Stage/path/to/package.py 在同一数据库和模式中引用阶段。如果阶段在不同的数据库和模式中,您可以使用 @Database.Schema.Stage/path/to/package.py

图 2.23 – 导入阶段包

图 2.23 – 导入阶段包

  1. 点击 导入 以安装包。现在模块将在 已安装包 下可见:

图 2.24 – 安装阶段包

图 2.24 – 安装阶段包

  1. 您可以通过使用 import <包名> 将包导入到您的代码中。

在下一节中,我们将介绍如何使用 UI 部署 Python 存储过程。

部署 Python 存储过程

我们工作表中的 Python 脚本可以无缝地部署为存储过程。然后可以在常规 SQL 上下文中使用它,或安排为任务执行。要部署存储过程,请按照以下步骤操作:

  1. 点击工作表右上角的 部署 按钮。

  2. 给存储过程起一个名称;也可以指定一个可选的注释,提供有关它的信息。如果存储过程已存在,则勾选 如果存在则替换 复选框以替换现有存储过程:

图 2.25 – 部署存储过程

图 2.25 – 部署存储过程

  1. 点击 部署 以部署存储过程。它将在您在工作表上下文中提供的数据库和模式下部署。

  2. 现在您可以使用以下代码在工作表中执行存储过程:

    CALL DATABASE.SCHEMA.MY_STORED_PROCEDURE();
    

在下一节中,我们将介绍 Python 工作表的各项功能。

Python 工作表的功能

Python 工作表包含一些对开发者友好的功能,并支持生产力。以下是 Python 工作表的某些独特特性:

  • 交互式 Python 环境:工作表支持 Python 语言,并支持 Snowpark用户自定义函数UDFs)和存储过程。诸如语法高亮、关键字类型敏感的自动完成以及诸如未声明的变量或无效方法使用等便捷的诊断功能有助于提高开发者的生产力:

图 2.26 – 交互式 Python 环境

图 2.26 – 交互式 Python 环境

  • Python 库支持:Python 工作表附带一个集成的 Anaconda 环境,支持导入最常见的 Anaconda 库,无需担心依赖管理。它还支持从内部阶段导入自定义包。

  • Snowpark 调试:Python 工作表可以使用show()print()函数在 Snowsight 中显示 DataFrame 的结果。DataFrame 的预览也可以返回以显示表格格式的输出,这在调试 Python 程序时非常有用:

图 2.27 –  Snowpark 调试

图 2.27 – Snowpark 调试

  • 协作:Snowpark Python 工作表可以与开发者共享。这使得协作变得更加容易,因为多个开发者可以同时访问并工作在工作表上:

图 2.28 – 工作表协作

图 2.28 – 工作表协作

  • 图表和数据探索:Python 工作表提供了一种方便的方式来可视化和探索数据以及 DataFrame 作为图表,这有助于数据探索并提供了一种快速分析数据的方法:

图 2.29 – 图表和数据探索

图 2.29 – 图表和数据探索

在下一节中,我们将介绍 Python 工作表的局限性。

Python 工作表的局限性

Python 工作表在 Snowsight 中相对较新,在功能方面存在一些局限性。让我们更仔细地看看:

  • 默认情况下,低于WARN级别的日志级别不会出现在结果区域中。

  • Python 工作表不支持断点或代码部分的选执行。相反,整个代码都在工作表中运行。

  • Python 工作表无法显示它生成的 Python 代码中的图像或其他元素。它只能显示通过 DataFrame 返回的结果。

  • 它仅支持 Python 3.8 以及使用 Python 3.8 的库。

Snowflake 不断更新 Python 工作表的功能,为开发者提供改进。因此,Python 工作表使得开发者能够在 Snowflake 环境中更容易、更快地编写 Python 代码。

在本地环境中进行 Snowpark 开发

Snowpark 也可以方便地从本地环境使用您喜欢的 IDE 进行开发。Snowflake 与 Anaconda 的合作的关键优势在于 Anaconda 的 Snowflake Snowpark for Python 通道,其中包含运行 Snowpark 所需的 Python 包。要使用此通道,Anaconda 或 Miniconda 应该安装在机器上。在本节中,我们将向您介绍如何使用 Anaconda 在本地开发环境中设置 Snowpark。

重要提示

本书将使用 Anaconda 进行 Snowpark 开发,因为这是一种推荐的方法,并利用 Anaconda 的包管理器的优势,轻松设置和管理 Snowpark 开发环境。

Snowpark API 需要安装 Python 3.8。Snowflake 建议您使用 Anaconda 以便于包管理。您可以通过在 命令行界面CLI) 中运行以下命令来检查您拥有的 Python 版本:

python –-version

您的输出应类似于以下内容:

图 2.30 – Python CLI 版本

图 2.30 – Python CLI 版本

您也可以通过运行以下命令在 Python 代码中检查 Python 版本:

from platform import python_version
print(python_version())

这将输出类似于以下内容:

图 2.31 – Python 版本

图 2.31 – Python 版本

Python 安装完成后,需要创建虚拟环境。因此,让我们创建一个。

创建虚拟环境

建议您创建一个 Python 虚拟环境,以确保在处理 Snowpark 时拥有无缝的开发者体验;它隔离了 Snowpark API,并允许您管理所有开发所需的依赖项。要使用 Anaconda 创建虚拟环境,请运行以下命令:

conda create --name def_gui_3.8_env --override-channels --channel https://repo.anaconda.com/pkgs/snowflake python=3.8

此命令创建了一个名为 def_gui_3.8_env 的新 Python 虚拟环境,并使用 Python 3.8 安装了必要的包,例如 numpypandas,来自 Anaconda 的 Snowflake 通道。

安装 Snowpark Python 包

在安装包之前,让我们使用以下命令激活我们的 Python 虚拟环境:

conda activate def_gui_3.8_env

您可以使用以下命令从 Anaconda 的 Snowflake 通道安装 Snowpark 包:

conda install --channel https://repo.anaconda.com/pkgs/snowflake Snowflake-snowpark-python

接下来,让我们安装一些额外的包。

安装额外的 Python 包

要安装开发所需的额外包,例如 pandasnumpy,您可以使用相同的 Anaconda Snowflake 通道:

conda install --channel https://repo.anaconda.com/pkgs/snowflake numpy pandas

虚拟环境现在已准备好用于开发,并连接到您喜欢的 IDE、Jupyter Notebook 或 VS Code 开发环境。同样,我们也可以安装 IPython Notebook。

配置 Jupyter Notebook

Jupyter Notebook 是开发者中最受欢迎的 IDE 之一。在本节中,我们将介绍如何配置 Jupyter IDE 以支持 Snowpark,因为本书的示例使用 Jupyter。Jupyter Notebook 需要安装在本地的环境中。Jupyter 环境与 Anaconda 一起安装。因此,让我们打开 Jupyter Notebook:

  1. 如果在上一节中没有激活,则必须激活 def_gui_3.8_env 虚拟环境以进行开发。要激活虚拟环境,请运行以下命令:

    conda activate def_gui_3.8_env
    
  2. 通过运行以下命令启动 Jupyter Notebook:

    jupyter notebook
    
  3. 在 Jupyter Notebook 网页上,点击右上角的 New 按钮。从下拉菜单中,在 Notebooks 部分下选择 Python 3。这将打开一个新的笔记本,其中包含一个空单元格,准备好执行代码。

重要提示

本书将使用 Jupyter Notebook 来展示所有示例。

导入 Snowpark 模块

Snowpark API 的 Python 类是 snowflake.snowpark 模块的一部分。您可以通过指定它们的名称从模块中导入特定的类。例如,要导入 average 函数,您可以使用以下代码:

from snowflake.snowpark.functions import avg

现在开发环境已经设置好了,让我们学习如何使用 Snowpark 进行操作。

使用 Snowpark 操作

Snowpark for Python 由直接在 Python 引擎上执行的客户端 API、UDF 和存储过程组成。以下截图显示了您可以选择的各种 Snowpark 对象:

图 2.32 – Snowpark Python 对象

图 2.32 – Snowpark Python 对象

Snowpark 使用 DataFrame 对象来查询和处理数据。使用 Snowpark 操作的指导原则是将数据保持在 Snowflake 中,并使用各种 Snowflake 对象在 Snowflake 内部进行处理。以下图显示了 Snowpark 的架构:

图 2.33 – Snowpark Python 架构

图 2.33 – Snowpark Python 架构

在下一节中,我们将介绍 Python 引擎。

Python 引擎

Python 引擎是一个由 Anaconda 驱动的安全沙盒 Python 环境,它在 Snowflake 的虚拟仓库上执行,并托管在 Snowflake 的计算基础设施上。这允许您使用 Python 处理数据,而无需将数据从环境中提取出来。Python 引擎由 UDF 引擎和存储过程引擎组成。UDF 引擎是一个受限的引擎,不能在 Snowflake 外部读取或写入数据,而存储过程引擎则更为宽容,并包含一个用于与 Snowflake 数据库交互的会话对象。

客户端 API

客户端 API 是snowflake-snowpark-python库,它可以在任何 Python 环境中安装。它提供会话和 DataFrame API 作为方法来支持在 Snowflake 的 Python 引擎中下推查询。客户端 API 包括会话、DataFrame 等显著对象。让我们详细看看这些。

与会话一起工作

Snowpark 会话是 Snowpark API 的一部分,连接到 Snowflake 以与之交互并使用 Snowpark 对象执行操作。Snowpark API 中的Session函数负责会话操作。要导入 Snowpark 的Session函数,请运行以下命令:

from snowflake.snowpark import Session

Snowpark 会话包含一个 Python 字典,其中包含建立 Snowflake 连接所需的参数值:

connection_parameters = {
    "account": "<your snowflake account identifier>",
    "user": "<your snowflake username>",
    "password": "<your snowflake password>",
    "role": "<your snowflake role>", # optional
    "warehouse": "<your snowflake warehouse>",  # optional
    "database": "<your snowflake database>",  # optional
    "schema": "<your snowflake schema>" # optional
}

连接包含以下必填参数:

  • 账户: Snowflake 账户标识符为-<account_name>。无需指定snowflakecomputing.com后缀。

  • 用户: Snowflake 用户的用户名。

  • 密码: 用于验证 Snowflake 的密码。

以下是可以传递给连接的可选参数:

  • 角色: 在 Snowpark 会话中使用的角色。如果留空,将使用用户的默认角色。

  • 仓库: 用于执行进程的仓库。如果留空,将使用用户的默认仓库。

  • 数据库: 执行上下文中的数据库。如果留空,将使用用户的默认数据库。

  • 模式: 执行上下文中的模式。如果留空,将使用用户的默认模式。

您可以通过将此字典传递给会话构建器来创建session对象,然后使用它来建立会话:

session = Session.builder.configs(connection_parameters).create()

会话创建后,session对象充当与 Snowflake 交互的句柄,通过各种方法执行读取和操作数据等操作。以下是一些可用的会话方法:

print("Session Current Account:", session.get_current_account())

最后,您可以使用close()方法关闭会话。这将终止会话及其关联的持续查询:

session.close()

Snowflake 建议在进程完成后关闭会话。可以使用Session方法来建立会话并与Session对象交互。

高级认证

Snowpark 会话也支持高级认证,如密钥对认证,如果已为连接到 Snowflake 的用户配置,则可以使用。私钥必须序列化然后传递给会话构建器的连接对象。我们将使用 hazmat 加密包来序列化私钥。此私钥作为变量提供:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
private_key_plain_text = '''-----BEGIN PRIVATE KEY-----
< your private key >
-----END PRIVATE KEY-----'''
private_key_passphrase = '<your private key passphase>'

我们使用口令对私钥进行编码,然后将其序列化以分配给传递给 Snowflake 连接对象的变量:

private_key_encoded = private_key_plain_text.encode()
private_key_passphrase_encoded = private_key_passphrase.encode()
private_key_loaded = serialization.load_pem_private_key(
    private_key_encoded,
    password = private_key_passphrase_encoded,
    backend = default_backend()
)
private_key_serialized = private_key_loaded.private_bytes(
    encoding = serialization.Encoding.DER,
    format = serialization.PrivateFormat.PKCS8,
    encryption_algorithm = serialization.NoEncryption()
)

连接参数将以序列化的形式传递私钥,并且可以使用会话构建器建立会话:

connection_parameters = {
    "account": "<your snowflake account identifier>",
    "user": "< your snowflake username>",
    "private_key": private_key_serialized,
    "warehouse": "<your snowflake warehouse>",
    "database": "<your snowflake database>",
    "schema": "<your snowflake schema>"
}
session = Session.builder.configs(connection_parameters).create()

接下来,让我们讨论 Snowpark DataFrames。

Snowpark DataFrames

Snowpark for Python 由一个客户端 API 组成,它提供了一个基于 DataFrame 的方法,使用 DataFrame 对象查询和处理数据。以下图表解释了所使用的代码块:

图 2.34 – Snowpark DataFrame API

图 2.34 – Snowpark DataFrame API

Snowpark DataFrame API 提供了更高的效率,开发者所需的努力更少,因为它具有简洁且易于理解和调试的语法。以下图表比较了 DataFrame 和 SQL 查询:

图 2.35 – Snowpark DataFrame 与查询

图 2.35 – Snowpark DataFrame 与查询

此外,Snowpark DataFrames 可以直接从表、视图和SELECT语句中读取数据,这些语句支持下推,以便它们可以在 Snowflake 中执行。Snowpark 客户端 API 还支持将 pandas DataFrame 转换为 Snowflake DataFrame,以便数据可以写回 Snowflake。最后,Snowpark DataFrames 支持懒加载,因为数据计算是在动作调用时执行的。

注意

Snowpark 中的懒加载意味着数据操作不会在定义时立即执行。相反,Snowpark 会构建一系列转换,直到你明确请求结果时才执行。这种方法优化了性能和资源使用,允许你高效且交互式地构建复杂的数据工作流。懒加载是处理大型数据集和优化 Snowpark 中的数据处理任务的关键特性。

使用 DataFrames

DataFrames 是 Snowpark 中的数据集对象,用于查询和处理数据。它们代表提供懒加载的关系数据集。DataFrame 以下推方式执行 SQL,并可以执行创建对象、从 Python 代码中读取、写入和操作数据等操作。Session对象中的各种方法用于处理 DataFrames。

让我们创建一个名为SAMPLE_EMPLOYEE_DATA的员工数据表:

session.sql('CREATE OR REPLACE TABLE SAMPLE_EMPLOYEE_DATA (id INT,name VARCHAR, age INT, email VARCHAR, city VARCHAR,country VARCHAR)').collect()

上一段代码将创建一个包含员工数据所需字段的表格。为了操作目的,让我们向表格中插入一些数据:

session.sql("""
    INSERT INTO SAMPLE_EMPLOYEE_DATA VALUES
    (1,'John Doe',25,'johndoe@example.com','New York','USA'),
    (2,'Jane Smith',30,'janesmith@example.com','Los Angeles','USA'),
    (3,'Michael Johnson',35,'michaeljohnson@example.com','London', 
       'UK'),
    (4,'Sarah Williams',28,'sarahwilliams@example.com','Leeds', 
       'UK'),
    (5,'David Brown',32,'davidbrown@example.com','Tokyo','Japan'),
    (6,'Emily Davis',29,'emilydavis@example.com','Sydney',
       'Australia'),
    (7,'James Miller',27,'jamesmiller@example.com','Dallas','USA'),
    (8,'Emma Wilson',33,'emmawilson@example.com','Berlin','Germany'),
    (9,'Alexander Taylor',31,'alexandertaylor@example.com',
       'Rome','Italy'),
    (10,'Olivia Anderson',26,'oliviaanderson@example.com',
        'Melbourne','Australia')
""").collect()

上一段代码将填充表格,我们可以查询这些数据。要查询数据,我们可以直接传递 SQL 语句:

session.sql("SELECT count(*) FROM SAMPLE_EMPLOYEE_DATA").collect()

上一段代码在 Snowflake 中执行后会返回结果。我们还可以将这些结果存储在 DataFrame 中,以便在 Python 中操作:

from snowflake.snowpark.functions import col
df_subset_row = session.table(
    "SAMPLE_EMPLOYEE_DATA").filter(col("id") == 1)

以下代码将结果保存到df_subset_row DataFrame 中,可以使用show()显示:

df_subset_row.show()

这是输出:

图 2.36 – DataFrame 数据

图 2.36 – DataFrame 数据

在下一节中,我们将探讨 Snowpark UDFs 和存储过程。

关于代码片段的说明

下一个部分中提供的示例已经有意简化。我们的主要目标是掌握和区分概念,而不是深入研究复杂场景。然而,在接下来的章节中,我们将深入研究更复杂的示例。

UDFs

Snowpark for Python 支持 UDF,允许开发者编写可重用的自定义 lambda 和函数,通过 DataFrames 处理数据。像内置函数一样,UDF 可以从 SQL 中调用,这增强了 SQL 的功能,使其能够执行它原本不具备或执行不佳的操作。UDF 还提供了一种封装功能的方法,以便您可以从代码的多个地方重复调用它。例如,您可以编写一个返回单个值(称为标量函数)的 UDF,也称为 UDF,或者返回一组值(称为表函数)的 UDF,也称为用户自定义表函数UDTF)。这些 UDF 可以从 Python 工作表内部开发,或者通过在本地开发环境中使用 Snowpark 来开发。

标量 UDF

标量 UDF 对每一行调用一次,并为每个输入行返回一个输出行。这些 UDF 的调用方式与标准 SQL 函数相同,以列或表达式作为参数。它产生一个由单个列/值组成的行作为输出。数据在多节点虚拟仓库的每个节点内并行处理。

使用 UDF

一旦创建了一个 Snowpark 会话,UDF 可以被转换成一个标准函数,该函数可以在 Snowflake 中注册:

def <main Python function name>(<arguments>):
    return <function output>
from snowflake.snowpark.types \
    import <specific Snowpark DataType object>
snowpark_session.add_packages(
    'List of native packages in Anaconda Channel')
snowpark_session.add_import('Path to Local File')
snowpark_session.udf.register(
    func = <Main Function Name>
  , return_type = <Return Type of Snowpark DataType object >
  , input_types = <[Input Types of Snowflake DataType object]>
  , is_permanent = True
  , name = '<UDF name>'
  , replace = True
  , stage_location = '@<UDF stage name>'
)

以下模板定义了在 Snowflake 中使用 Python 创建 UDF 的步骤。它涉及定义 UDF 将使用的首选 Python 函数,并在 Snowflake 中注册它。接下来,指定函数和 UDF 的名称,以及 UDF 文件将上传的 Snowflake 阶段。最后,导入 UDF 返回值的所需 Snowpark DataType 对象,并确定其特定对象。这不是我们唯一可以遵循的模板——我们还可以利用装饰器来完成这项工作。但对于初学者来说,这可以非常有帮助,以便模板化和组织 UDFS、UDTFs 和存储过程。

此外,还需要导入和确定 UDF 输入参数所需的任何必要的 Snowpark DataType 对象。模板还允许您在 UDF 中包含额外的包和导入。我们还可以指定 UDF 是否应该是临时的,以及是否应该覆盖具有相同名称的现有 UDF:

def last_name_finder(input_name:str):
    last_name = input_name.split()[1]
    return last_name
from snowflake.snowpark.types \
    import StringType,IntegerType,ArrayType
test = session.udf.register(
    func = last_name_finder
  , return_type = StringType()
  , input_types = [StringType()]
  , is_permanent = True
  , name = 'LAST_NAME_FINDER'
  , replace = True
  , stage_location = '@MY_STAGE'
)

这个简单的函数获取输入名称并将其分割,以便返回最后一个名称。UDF 被注册到内部的 My_Stage 阶段,并部署到 Snowflake。UDF 可以直接在 SQL 中作为函数调用:

session.sql('''SELECT
    NAME,
    LAST_NAME_FINDER(NAME) AS LAST_NAME
    FROM SAMPLE_EMPLOYEE_DATA
''').show()

输出如下:

图 2.37 – UDF Snowpark 执行

图 2.37 – UDF Snowpark 执行

在此示例中,我们使用Name列调用了LAST_NAME_FINDER函数,该函数通过分割返回了姓氏。该函数也可以在DataFrame函数内部调用,如下所示:

from snowflake.snowpark.functions import col, call_udf
df = session.table("SAMPLE_EMPLOYEE_DATA")
df.with_column(
    "last_name",call_udf("LAST_NAME_FINDER", col("name"))).show()

上述代码生成了以下输出:

图 2.38 – UDF DataFrame 执行

图 2.38 – UDF DataFrame 执行

接下来,让我们看看 UDTF。

UDTF

表格 UDF,也称为 UDTF,需要在数据批次上执行有状态的操作,并且对每行数据调用一次,就像标量 UDF 一样,但它们可以为每个输入行返回多行输出。UDTF 处理方法包含一个额外的可选参数,该参数有助于为每个分区初始化处理程序并最终处理每个部分。UDTF 是一种类似于 UDF 但具有表格输出的 UDF。因此,它们可以在 Python 工作表和 Snowpark 开发环境中开发。

使用 UDTF

在 Snowpark 中创建 UDTF 与创建 UDF 类似,即在创建 Snowpark 会话之后,可以直接在标准命令中创建 UDTF,该命令可以在 Snowflake 中注册。

以下 Snowpark UDTF 模板提供了使用 Python 在 Snowpark 中创建 UDTF 的基本轮廓。以下代码显示了此模板中的关键元素:

# Define Python class locally
'''
Define main Python class which is
leveraged to process partitions.
Executes in the following order:
- __init__ | Executes once per partition
- process | Executes once per input row within the partition
- end_partition | Executes once per partition
'''
class <name of main Python class> :
    '''
    Optional __init__ method to
    execute logic for a partition
    before breaking out into rows
    '''
    def __init__(self) :

此模板在 Snowflake 中创建 UDTF。首先,为 UDTF 定义一个主要的 Python 处理程序类,该类可以利用脚本中的其他函数或从外部源导入。需要注意的是,只能将一个主要的 Python 处理程序类分配给 UDTF。

在此模板中,您需要将<主 Python 类名>替换为对您的 UDTF 类有意义的名称。这是您将定义处理 UDTF 内部数据逻辑的主要类。

__init__方法被标记为可选,这意味着您可以选择是否将其包含在您的 UDTF 实现中。如果您选择包含它,此方法将在每个分区之前执行一次,然后才会进入单个行。

您可以使用__init__方法执行任何针对您的 UDTF 的分区级设置或初始化。例如,您可能用它来初始化变量、打开连接或设置在整个 UDTF 执行过程中将使用的数据结构:

'''
Method to process each input row
within a partition, returning a
tabular value as tuples.
'''
def process(self, <arguments>) :
    '''
    Enter Python code here that
    executes for each input row.
    This likely ends with a set of yield
    clauses that output tuples,
    for example:
    '''
    yield (<field_1_value_1>, <field_2_value_1>, ...)
    yield (<field_1_value_2>, <field_2_value_2>, ...)
    '''
    Alternatively, this may end with
    a single return clause containing
    an iterable of tuples, for example:
    '''
    return [
        (<field_1_value_1>, <field_2_value_1>, ...)
      , (<field_1_value_2>, <field_2_value_2>, ...)
    ]

此方法负责处理分区内的每个输入行并生成作为元组的表格数据。在process方法内部,您可以编写为分区中的每个输入行执行的定制 Python 代码。此方法的关键部分是使用yield语句或return语句生成元组作为输出。

yield 语句方面,您可以针对每个输入行输出一个或多个元组,从而在生成表格结果方面提供灵活性。或者,您可以使用带有元组列表的 return 语句来实现相同的结果。本质上,process 方法是您 UDTF 的核心逻辑,其中您将每个输入行中的数据操作和转换成表格格式,使其适合进一步处理或分析:

'''
Optional end_partition method to
execute logic for a partition
after processing all input rows
'''
def end_partition(self) :
    # Python code at the partition level
    '''
    This ends with a set of yield
    clauses that output tuples,
    for example:
    '''
    yield (<field_1_value_1>, <field_2_value_1>, ...)
    yield (<field_1_value_2>, <field_2_value_2>, ...)
    '''
    Alternatively, this ends with
    a single return clause containing
    an iterable of tuples, for example:
    '''
    return [
        (<field_1_value_1>, <field_2_value_1>, ...)
      , (<field_1_value_2>, <field_2_value_2>, ...)
    ]

此方法用于在处理完该分区的所有输入行后执行特定于分区的逻辑。在 end_partition 方法内部,您可以编写自定义的 Python 代码,根据在该分区中处理的数据执行计算或生成结果。此方法还可以用于像 process 方法一样产生或返回作为元组的表格数据,但此数据通常代表整个分区的聚合或汇总信息。

您可以选择使用 yield 语句输出一个或多个元组,或者可以使用带有元组列表的 return 语句来提供分区级别的结果。这允许您执行特定于分区的操作并以结构化格式返回结果。

Snowpark UDTF 模板中的 end_partition 方法用于执行分区级别的逻辑,并在处理完该分区内的所有输入行后返回表格数据或特定于该分区的结果。这对于需要整个分区数据的聚合或计算等任务特别有用。

以下代码模板提供了如何在 Snowflake 中注册 UDTF 以及定义 UDTF 的相应选项的详细信息:

from snowflake.snowpark.types import StructType, StructField
from snowflake.snowpark.types \
    import <specific Snowpark DataType objects>
snowpark_session.add_packages(
    '<list of required packages natively available in Snowflake( 
        i.e. included in Anaconda Snowpark channel)>')
snowpark_session.add_import('<path\\to\\local\\directory\\or\\file>')
snowpark_session.udtf.register(
    handler = <name of main Python class>
  , output_schema = StructType(
        <list of StructField objects with specific field \
        name and Snowpark DataType objects>)
  , input_types = <list of input DataType() \
        objects for input parameters>
  , is_permanent = True
  , name = '<UDTF name>'
  , replace = True
  , stage_location = '@<UDTF stage name>'
)

Snowflake Snowpark Session 对象的 udtf() 方法用于在 Snowflake 中创建 UDTF。这个过程涉及几个步骤:确定 UDTF 将使用的 Python 类,指定 Snowflake 中 UDTF 的名称(可以是完全限定名称或与 Snowpark Session 对象在同一命名空间中创建),以及提供将上传 UDTF 文件的 Snowflake 阶段名称。

特定的 Snowpark DataType 对象被导入以定义 UDTF 的结构。这包括导入用于定义表格结构的对象,如表模式(使用 StructType)和表内的字段(使用 StructField)。此外,还导入了一个特定的 Snowpark DataType 对象,用于传递给 UDTF 并由 UDTF 返回的值。UDTF 的输出模式使用导入的 Snowpark DataType 对象定义,将它们嵌入到 StructFieldStructType 对象中。此外,还定义了一个特定 Snowpark DataType 对象的列表,用于 UDTF 的输入参数。在导入时包括所有这些 DataType 对象并确保它们与在 handler 类中传递给 process 方法的预期参数相匹配至关重要。

模板允许创建一个临时的 UDTF,它仅在特定的 Snowflake Snowpark Session 对象中存在。此外,还有一个选项可以覆盖具有相同名称的现有 UDTF;如果设置为 False 且已存在 UDTF,则将返回错误。最后,模板简要提到了向 UDTF 添加额外的包和导入,这是可选的,可以使用提供的行来完成。

以下示例说明了如何使用 Snowpark UDTF 来计算 Snowflake 表中数值数据的平均值。这展示了 UDTF 在 Snowpark 中用于自定义数据处理任务的实用应用:

class CalculateAverage:
    def __init__(self) :
        self._values = []
    def process(self, input_measure: int) :
        self._values.append(input_measure)
    def end_partition(self) :
        values_list = self._values
        average = sum(values_list) / len(values_list)
        yield(average ,)

CalculateAverage Snowpark UDTF 的设计是为了计算 Snowflake 表中数值列的平均值。它是通过累积每个数据分区的输入值,然后在分区结束时计算平均值来实现的。

process 方法逐个收集输入值并将它们存储在列表中。当分区结束时(在 end_partition 方法中),它通过将所有收集到的值相加并除以值的数量来计算平均值。最后,它将计算出的平均值作为 UDTF 的输出产生。这个 UDTF 简化了在 Snowflake SQL 查询中计算平均值的流程,尤其是在处理大型数据集时:

from snowflake.snowpark.types import StructType, StructField
from snowflake.snowpark.types \
    import FloatType,IntegerType,StringType
output_schema = StructType([
    StructField("Avg_Age", FloatType())
])
session.udtf.register(
    handler = CalculateAverage
  , output_schema = output_schema
  , input_types = [IntegerType()]
  , is_permanent = True
  , name = 'AVERAGE_AGE'
  , replace = True
  , stage_location = '@MY_STAGE'
)

在这个例子中,我们创建了一个名为 Average_Age 的 UDTF 函数,该函数通过获取年龄作为输入来计算平均年龄。该函数被上传到 MY_STAGE 并在 Snowflake 中注册。

该函数可以执行以从样本员工数据中获取每个国家的平均年龄:

session.sql('''
    SELECT
        COUNTRY, Avg_Age
    FROM
        SAMPLE_EMPLOYEE_DATA,
        table(AVERAGE_AGE(AGE) over (partition by COUNTRY))
''').show()

这将显示以下输出:

图 2.39 – UDTF Snowpark 执行

图 2.39 – UDTF Snowpark 执行

输出显示了 UDTF 的执行输出。在下一节中,我们将介绍矢量化 UDF。

矢量化 UDF

矢量化 UDF 与标量 UDF 类似,允许您定义接收输入行批次的 Python 函数作为 pandas DataFrame,并返回结果集合作为 pandas 数组或序列。

矢量化 UDF 将数据批次的操作并行化,并在与逐行序列处理相比的行集上提供显著的性能优势。此外,它们减少了使用在 pandas DataFrame 和数组上操作的库的复杂性。

与矢量化 UDF 一起工作

在通过将矢量化 DataFrame 作为输入传递给标准 UDF 建立会话后,之前查看的相同示例可以在 Snowpark 环境中执行:

import pandas as pd
from snowflake.snowpark.functions import pandas_udf
from snowflake.snowpark.types \
    import IntegerType, PandasSeriesType,StringType
@pandas_udf(
    name='column_adder'
  , stage_location = '@MY_STAGE'
  , input_types=[PandasSeriesType(StringType()), \
        PandasSeriesType(StringType())]
  , return_type=PandasSeriesType(StringType())
  , is_permanent=True
  , replace=True)
def column_adder(
    column1: pd.Series, column2: pd.Series) -> pd.Series:
    return column1 + "," + column2
df = session.table("SAMPLE_EMPLOYEE_DATA")
df.withColumn('City_Country', column_adder(col('CITY'), \
    col('COUNTRY'))).show()

示例 UDF 返回 Sample Employee 表中每行的 CITY_COUNTRY 列中的城市和国家:

图 2.40 – Snowpark 中的矢量化 UDF

图 2.40 – Snowpark 中的矢量化 UDF

输出显示了矢量化 UDF 的执行输出。在下一节中,我们将介绍存储过程。

存储过程

Python 存储过程是一系列可以参数化和按需执行的代码语句。它们在比 UDF 更不受限制的环境中运行,支持与 Snowflake 对象交互,以及在对表执行 DDL 和 DML 操作。

Snowpark 中的存储过程用于在 Snowflake 数据处理框架中执行任务和流。这些存储过程封装了特定的逻辑或功能,使用户能够无缝地对数据进行各种操作。任务通常与批量处理相关联,涉及在预定时间间隔内对数据集执行预定义的操作或工作流。存储过程使用户能够自动化这些任务,确保数据处理的持续性和效率。

另一方面,流是持续的数据管道,实时捕获数据源的变化。存储过程通过定义如何处理和集成到目标目的地来在管理和处理流中发挥关键作用。使用 Snowpark,用户可以创建存储过程来处理这些流数据场景,包括数据转换、过滤以及将数据加载到 Snowflake 表中。

使用存储过程

可以使用以下模板在 Snowpark 中创建存储过程:

# Define Python function locally
def <Python Function Name>(
    snowpark_session: snowflake.snowpark.Session, <arguments>):
    return <Output>
# Imports Required For Stored Procedure
from snowflake.snowpark.types \
    import <specific Snowpark DataType object>
# Optional: Import additional packages or files
snowpark_session.add_packages(
    'List of native packages in Anaconda Channel')
snowpark_session.add_import('Path to Local File')
# Upload Stored Procedure to Snowflake
snowpark_session.sproc.register(
    func = <Function name to register>
  , return_type = <Return Type of Snowpark DataType object>
  , input_types = <[Input Types of Snowflake DataType object]>
  , is_permanent = True
  , name = '<Stored Procedure name>'
  , replace = True
  , stage_location = '@<Stored Procedure stage name>'
    <optional: , execute_as = 'CALLER'>
)

在这里,我们定义了将在存储过程中使用的 Python 主函数以及一个名为snowpark_session的附加参数,它允许我们与 Snowflake 对象交互。接下来,我们使用sproc.register()方法创建存储过程,指定 Python 函数、存储过程名称和文件上传的 Snowflake 阶段。最后,我们导入存储过程的返回值和输入参数的特定 Snowpark DataType对象。

snowflake_session参数是隐含理解的,不包括在输入参数中。可选行允许额外的包和导入。在这里,我们可以确定存储过程是否为临时。我们还可以决定是否覆盖同名现有存储过程,并指定它将以调用者或所有者的身份执行:

def subset_table(snowpark_session:Session):
    df = snowpark_session.table(
        'SAMPLE_EMPLOYEE_DATA').select("NAME","AGE")
    return df.collect()
from snowflake.snowpark.types import StringType
session.add_packages('snowflake-snowpark-python')
session.sproc.register(
    func = subset_table
  , return_type = StringType()
  , input_types = []
  , is_permanent = True
  , name = 'SPROC_SUBSET_TABLE'
  , replace = True
  , stage_location = '@MY_STAGE'
)

该存储过程从Employee Data表中返回列名和年龄。它注册为SPROC_SUBSET_TABLE并通过My_Stage上传:

session.sql(''' CALL SPROC_SUBSET_TABLE()''').show()

下面是输出结果:

图 2.41 – 存储过程执行

图 2.41 – 存储过程执行

可以通过运行CALL命令来执行存储过程。

UDF 与存储过程之间的区别

UDF 和存储过程在功能和用途方面有显著差异。以下图显示了 UDF 和存储过程的基本区别以及它们的使用目的:

图 2.42 – UDF 与存储过程对比

图 2.42 – UDF 与存储过程对比

以下表格显示了 UDF 和存储过程基于其属性之间的差异和相似性:

UDF 存储过程
目的 执行计算并返回结果。UDF 需要返回一个值。 通过执行 SQL 语句执行复杂操作。它们不需要显式返回一个值。
使用 当需要将逻辑作为返回值的 SQL 语句的一部分调用时,可以使用 UDF。 当需要执行数据库操作或管理任务时。
输出 UDF 总是需要返回一个结果。 存储过程不需要返回结果。
上下文 UDF 的返回值可以直接在 SQL 上下文中访问。 存储过程的返回值在 SQL 上下文中不可访问。
执行 UDF 可以在另一个 SQL 语句的上下文中调用。此外,可以在单个 SQL 语句中调用多个 UDF。 存储过程是独立调用的。因此,SQL 语句中只调用一个存储过程。
安全性 UDF 不能直接访问数据库或在其上执行操作。 存储过程可以访问数据库并在其上执行数据操作。

表 2.1 – UDF 和存储过程的比较

存储过程和 UDF 都可以一起使用,以扩展 Python 在 Snowflake 中的执行功能。

为 Snowpark 建立项目结构

为了帮助在 Python 中开发 Snowpark 并使其易于创建 Snowpark 项目,Snowflake 已发布了 Python Snowpark 项目模板。这些模板包含您开发、测试和部署 Snowpark 所需的一切——它们提供了开发 UDF 和存储过程所需的所有样板代码,以及单元测试,甚至还有用于 CI/CD 的 GitHub Actions 工作流程文件。

该项目模板已作为开源发布在 GitHub 上,这使得开发者可以轻松克隆和使用项目。要克隆项目,请按照以下步骤操作:

  1. github.com/Snowflake-Labs/snowpark-python-template下载文件或克隆仓库。可以使用 GitHub CLI 从模板创建一个新的 GitHub 仓库,如下所示:

    gh repo create <new-repo-name> --template="Snowflake-Labs/snowpark-python-template"
    

    需要指定新仓库的名称。该仓库将与 Snowpark 项目模板类似。

  2. 设置以下环境变量,以便您可以配置必要的 Snowflake 详细信息:

    SNOWSQL_ACCOUNT=<replace with your account identifier>
    SNOWSQL_USER=<replace with your username>
    SNOWSQL_PWD=<replace with your password>
    SNOWSQL_DATABASE=<replace with your database>
    SNOWSQL_SCHEMA=<replace with your schema>
    SNOWSQL_WAREHOUSE=<replace with your warehouse>
    

    这些环境变量是连接到 Snowflake 环境以及 Snowpark 建立会话所必需的。

  3. 创建一个 Anaconda 虚拟环境,并从environment.yml文件安装依赖项:

    conda env create -f environment.yml
    snowpark that can be used for development.
    
  4. 您可以通过执行app.py存储过程来测试连接并检查环境是否已设置。导航到项目文件夹并运行以下命令:

    python src/procs/app.py
    

    这将生成一个名为Hello World的输出,用于建立与 Snowflake 的连接。

Snowpark 项目支持函数和存储过程。项目结构包括用于存储过程的procs目录,用于用户定义函数(UDFs)的udf目录,以及用于 UDFs 和存储过程之间共享的实用方法和类的util目录。

test目录包含可以通过pytest测试的测试用例。还有一个可以通过 GitHub Actions 部署的 GitHub 工作流程。我们将在接下来的章节中详细介绍这一点。

摘要

Snowpark 非常灵活,支持复杂的发展模式。在本章中,我们学习了如何配置 Snowpark 开发环境以及不同的 Snowpark 对象,例如会话、UDFs 和存储过程,以及如何使用它们。我们还学习了在查看一些我们可以用来开始开发的示例代码之前,如何在本地和 Python 工作表中设置 Snowpark 开发项目。

在下一章中,我们将介绍如何使用 Snowpark 进行数据处理,以及如何摄取、准备和分析数据。

第二部分:Snowpark 数据工作负载

本部分主要关注在 Snowpark 中部署数据工作负载,例如数据工程、数据科学以及机器学习ML)模型。

本部分包括以下章节:

  • 第三章, 使用 Snowpark 简化数据处理

  • 第四章, 使用 Snowpark 构建数据工程管道

  • 第五章, 使用 Snowpark 开发数据科学项目

  • 第六章, 使用 Snowpark 部署和管理 ML 模型

第三章:使用 Snowpark 简化数据处理

在上一章中,我们学习了如何为 Snowpark 设置开发环境,以及各种 Snowpark 组件,如 DataFrames、UDFs 和存储过程。我们还介绍了如何操作这些对象并在 Snowflake 中运行它们。在本章中,我们将介绍使用 Snowpark 进行数据处理,并学习如何使用 Snowpark 加载数据、准备、分析和转换数据。

在本章中,我们将涵盖以下主要内容:

  • 数据摄取

  • 数据探索和转换

  • 数据分组和分析

技术要求

对于本章,您需要一个有效的 Snowflake 账户,并且需要在本地安装了 Anaconda 的 Python。您可以参考以下文档获取安装说明:

本章的支持材料可在本书的 GitHub 仓库中找到,网址为 github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark

数据摄取

数据工程流程的第一部分是数据摄取——将所有不同的数据以可用的格式放入 Snowflake 进行分析至关重要。在上一章中,我们学习了 Snowpark 如何通过 DataFrame 访问数据。如果对这个 DataFrame 执行查询,它可以从 Snowflake 表、视图以及流等对象中访问数据。Snowpark 支持多种格式的结构化数据,如 Excel 和 CSV,以及半结构化数据,如 JSON、XML、Parquet、Avro 和 ORC;特殊格式,如 HL7 和 DICOM,以及非结构化数据,如图像和媒体,也可以在 Snowpark 中摄取和处理。Snowpark 使您能够安全且程序化地访问 Snowflake 阶段中的文件。

Snowpark Python 的灵活性允许您轻松适应不断变化的数据需求。假设您最初以 CSV 文件作为数据源;您可以在稍后阶段切换到 JSON 或数据包格式。使用 Snowpark,您无需重写整个代码库。相反,您可以通过进行少量调整或配置更改来适应新的结构,同时保持核心逻辑不变。这种灵活性为您节省了宝贵的时间和精力,使您能够根据需求快速演变在不同数据格式之间切换。

通过利用 Snowpark 的功能,您可以更多地专注于分析和利用数据,而不是担心数据格式处理的复杂性。这种简化的方法使您能够对不同数据源进行实验,适应不断变化的数据需求,并高效地将数据加载到 Snowflake 表中,所有这些只需进行最少的代码更改和最大的灵活性。

因此,让我们深入探讨 Snowpark Python 的力量及其轻松处理不同数据格式的能力,让您能够与各种来源协同工作而无需进行繁琐的代码修改。您将体验到探索、分析和从数据中提取见解的自由,同时享受无缝和灵活的集成过程。

数据摄取脚本提供在本书的 GitHub 仓库中:github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark。这些脚本将简化上传任何新数据集的过程,确保工作流程的顺畅和高效。遵循前几章中概述的类似方法,您可以轻松上传新数据集并探索 Snowflake 的数据工程和机器学习功能。提供的数据摄取脚本将作为您的指南,使过程无缝且无烦恼。

重要数据集注意事项

本章中我们将使用的数据集提供了对客户行为、活动响应和投诉的独特见解,从而支持数据驱动的决策和客户满意度提升。原始数据集来自 Kaggle 平台(www.kaggle.com/datasets/rodsaldanha/arketing-campaign)。然而,本节中讨论的数据集不能直接通过 Kaggle 链接访问。相反,我们从一个基础数据集开始,并生成新的数据格式,以展示如何使用 Snowpark 加载各种数据集格式。这些数据集可以在本书的 GitHub 仓库的datasets文件夹中找到。

数据集包括 CSV 格式的购买历史,JSON 格式的活动信息,以及 Parquet 格式的投诉信息。这些数据集提供了关于客户行为、活动响应和投诉的有价值信息:

  • 购买历史(CSV):此文件包含客户信息,如 ID、教育程度、婚姻状况和购买指标。该数据集提供了对客户购买习惯的见解,并可进一步分析以支持数据驱动的决策。

  • 活动信息(JSON):JSON 数据集包括关于活动接受度和客户响应的数据。分析此数据集将帮助您优化营销策略并了解活动效果。

  • 投诉信息(Parquet):此文件包含有关客户投诉的详细信息,包括联系和收入指标。此数据集有助于跟踪和解决客户投诉,以改善满意度。

注意

在接下来的工作中,我们将利用本地开发环境来执行所有 Snowpark 代码,而不是依赖于 Snowflake 工作表。这种方法提供了更大的灵活性和对 Snowpark 脚本开发和测试的控制。当使用工作表执行特定任务时,我们将明确指出其使用情况,以确保清晰和上下文。

将 CSV 文件导入 Snowflake

Snowflake 支持使用 CSV 文件轻松导入数据。我们将以 CSV 文件的形式将购买历史数据加载到 PURCHASE_HISTORY 表中。我们将使用 Snowpark 会话将 purchase_history.csv 上传到内部阶段,如下所示:

session.file.put('./datasets/purchase_history.csv', 'MY_STAGE')

因此,文件已上传到内部阶段。我们将在 Snowpark 中直接引用此文件。营销表的数据模式也可以直接定义为 Snowpark 类型。以下代码提供了创建 Snowflake 表所需的必要列和数据类型:

import snowflake.snowpark.types as T
purchase_history_schema = T.StructType([
    T.StructField("ID", T.IntegerType()),
    T.StructField("Year_Birth", T.IntegerType()),
    T.StructField("Education", T.StringType()),
    T.StructField("Marital_Status", T.StringType()),
    T.StructField("Income", T.IntegerType()),
    T.StructField("Kidhome", T.IntegerType()),
    T.StructField("Teenhome", T.IntegerType()),
    T.StructField("Dt_Customer", T.DateType()),
    T.StructField("Recency", T.IntegerType()),
    T.StructField("MntWines", T.IntegerType()),
    T.StructField("MntFruits", T.IntegerType()),
    T.StructField("MntMeatProducts", T.IntegerType()),
    T.StructField("MntFishProducts", T.IntegerType()),
    T.StructField("MntSweetProducts", T.IntegerType()),
    T.StructField("MntGoldProds", T.IntegerType()),
    T.StructField("NumDealsPurchases", T.IntegerType()),
    T.StructField("NumWebPurchases", T.IntegerType()),
    T.StructField("NumCatalogPurchases", T.IntegerType()),
    T.StructField("NumStorePurchases", T.IntegerType()),
    T.StructField("NumWebVisitsMonth", T.IntegerType())
])

在此代码片段中,我们通过为购买历史数据集定义一个模式来迈出理解我们数据结构的第一步。使用 Snowflake Snowpark 库,我们建立字段和对应的数据类型,为我们数据分析之旅奠定基础。此代码作为起点,指导我们定义和使用结构化数据。这不是我们使用 Snowpark 加载数据集的唯一方法。随着我们的进展,我们将继续探索不同的方法来加载其他表格数据集。

此代码从 Snowflake Snowpark 库导入必要的类型。它创建了一个名为 purchase_history_schema 的变量,并将其分配给一个 StructType 对象,该对象代表数据集的结构化模式。StructType 对象包含多个 StructField 对象,每个对象代表数据集中的一个字段。每个 StructField 对象使用 Snowflake Snowpark 提供的类型指定区域的名称及其对应的数据类型。以下代码读取文件:

purchase_history = session.read\
        .option("FIELD_DELIMITER", ',')\
        .option("SKIP_HEADER", 1)\
        .option("ON_ERROR", "CONTINUE")\
        .schema(purchase_history_schema).csv(
            "@MY_Stage/purchase_history.csv.gz")\
        .copy_into_table("PURCHASE_HISTORY")

CSV 文件使用文件格式选项(如 FIELD_DELIMITERSKIP_HEADER 等)进行读取,所有这些选项都与前面定义中定义的模式一起指定。使用来自 CSV 文件的数據创建了 PURCHASE_HISTORY 表,现在该表已准备好进行处理:

session.table("PURCHASE_HISTORY").show()

上述代码显示了 PURCHASE_HISTORY 表的输出:

图 3.1 – PURCHASE_HISTORY 表

图 3.1 – PURCHASE_HISTORY 表

CSV 文件易于加载,因为它使用了 Snowflake 中可用的文件格式选项。现在,让我们看看如何将 JSON 文件加载到 Snowflake 中。

将 JSON 数据导入 Snowflake

Snowflake 允许通过 Variant 数据类型摄取和处理 JSON 结构。我们可以像摄取 CSV 文件一样摄取 JSON 文件——通过将其上传到内部阶段。campaign_info.json 文件包含有关营销活动的数据。我们可以使用以下代码将其加载到 CAMPAIGN_INFO 表中:

session.file.put('./datasets/campaign_info.json', 'MY_STAGE')

由此,文件已上传到内部阶段;我们将在 Snowpark 中引用它。Snowpark 可以访问该文件并将其加载到表中:

df_from_json = session.read.json("@My_Stage/campaign_info.json.gz")

JSON 文件的内容被读入 DataFrame 作为 JSON 对象。这个 DataFrame 可以作为一个变体写入到表中:

df_from_json.write.save_as_table("CAMPAIGN_INFO_TEMP", 
    mode = "overwrite")

CAMPAIGN_INFO_TEMP 表包含 JSON 数据。我们可以查询该表来查看数据:

df_from_json.show()

上述命令显示 DataFrame 中的 JSON 数据:

图 3.2 – 营销信息表

图 3.2 – 营销信息表

以下代码片段利用 Snowflake 中的 Snowpark 库来操作 DataFrame:

from snowflake.snowpark.functions import col
df_flatten = df_from_json.select(col("$1")["ID"].as_("ID"),\
    col("$1")["AcceptedCmp1"].as_("AcceptedCmp1"),\
    col("$1")["AcceptedCmp2"].as_("AcceptedCmp2"),\
    col("$1")["AcceptedCmp3"].as_("AcceptedCmp3"),\
    col("$1")["AcceptedCmp4"].as_("AcceptedCmp4"),\
    col("$1")["AcceptedCmp5"].as_("AcceptedCmp5"),\
    col("$1")["Response"].as_("Response"))
df_flatten.write.save_as_table("CAMPAIGN_INFO")

上述代码从现有的 DataFrame 中选择特定的列,并使用 col 函数重命名它们。转换后的 DataFrame 然后作为新表保存到 Snowflake 中。该代码通过在 DataFrame 中选择和重命名列来执行数据 提取、转换和加载ETL)操作,并将结果保存为新表。

CAMPAIGN_INFO 表现在包含扁平化数据,数据分别存储在单独的列中,以便更容易处理。让我们看一下数据:

session.table("CAMPAIGN_INFO").show()

上述代码显示了 CAMPAIGN_INFO 表的输出:

图 3.3 –  表

图 3.3 – CAMPAIGN_INFO

使用 Variant 列,在 Snowpark 中加载和处理 JSON 文件变得更容易。接下来,我们将介绍如何使用 Snowpark 将 Parquet 文件加载到 Snowflake 中。

将 Parquet 文件摄取到 Snowflake

Parquet 是一种流行的开源数据存储格式,由 Apache 许可。列导向的格式存储更轻便,处理更快。Parquet 还支持复杂的数据类型,因为数据和列信息都存储在 Parquet 格式中。COMPLAINT_INFO 表由客户投诉信息组成。让我们将其加载到 Snowflake 中:

session.file.put('./datasets/complain_info.parquet', 'MY_STAGE')

文件将被上传到内部阶段。Snowpark 可以访问它以处理并将其加载到表中:

df_raw = session.read.parquet("@My_Stage/complain_info.parquet")
df_raw.copy_into_table("COMPLAINT_INFO")

将 Parquet 文件读入 DataFrame,然后复制到 COMPLAINT_INFO 表中。由于 Parquet 文件已经包含表元数据信息,它定义了表结构。我们可以查询该表来查看数据:

session.table("COMPLAINT_INFO").show()

这将输出以下 COMPLAINT_INFO 表:

图 3.4 –  表

图 3.4 – COMPLAINT_INFO

Parquet 是 Snowflake 的一种首选格式,因为它是由 Apache Iceberg 使用的格式。Parquet 在数据工程和数据分析中因其列式存储而脱颖而出,这优化了压缩和查询性能。其对模式演变和分区的支持确保了在处理不断变化的数据结构时的灵活性和效率。由于其与各种数据处理框架的广泛兼容性,Parquet 能够无缝集成到现有工作流程中,成为现代数据管道中的基石格式。在下一节中,我们将介绍如何轻松地将非结构化数据,如图片,加载到 Snowflake 中。

重要提示

我们选择为处理图片和文本维护单独的阶段,尽管这样做不是强制性的。MY_TEXTMY_IMAGES 阶段可以使用我们之前概述的方法准备。

将图片导入 Snowpark

Snowflake 支持多种数据类型,如图片,可以直接上传到阶段并在 Snowpark 中执行,无需管理依赖项。

平台如 Amazon S3、Google Cloud Storage 和 Azure Blob Storage 常常被用于管理和存储图片数据,因为它们具有可扩展性和可靠性。然而,值得注意的是,Snowpark 也提供了在数据工程和数据分析工作流程中处理图片数据的灵活性,使其成为一种多功能的选项。我们将加载一些样本图片,这些图片可以用于处理:

session.file.put("./datasets/sample_images/*.png", "@My_Images")

上一段代码展示了如何将图片从本地文件夹加载到内部阶段。路径可以支持通配符输入,以便上传特定文件夹中的所有图片。可以在阶段中查询文件夹以获取已上传图片的列表:

Session.sql("LS @My_Images").show()

上一段代码展示了阶段中所有现有图片的列表:

图 3.5 – 图片列表

图 3.5 – 图片列表

一旦图片上传成功,就可以通过 Snowpark 直接访问。Snowpark 支持使用 get_stream 函数从阶段以字节形式流式传输文件的內容。我们可以使用像 Pillow 这样的库从字节流中读取文件:

import PIL.Image
bytes_object = session.file.get_stream(
    "@My_Images/101.png.gz", decompress=True)
image = PIL.Image.open(bytes_object)
image.resize((150,150))

这将输出以下图片:

图 3.6 – 渲染图片

图 3.6 – 渲染图片

图片直接在笔记本中显示。Snowpark 对图片的原生支持支持图像分类、图像处理和图像识别等用例。Snowpark 还支持动态渲染图片。我们将在下一节中介绍这一点。

使用 Snowpark 动态读取文件

Snowpark 包含 files 模块和 SnowflakeFile 类,这两个都提供了动态访问文件并流式传输以进行处理的接口。这些动态文件对于读取多个文件也很有帮助,因为我们能够遍历它们。open() 扩展了 IOBase 文件对象,并提供了打开文件的函数。SnowflakeFile 对象也支持其他 IOBase 方法来处理文件。以下代码展示了如何使用内部阶段的相对路径读取多个文件的示例:

import snowflake.snowpark as snowpark
from snowflake.snowpark.functions import udf
from snowflake.snowpark.files import SnowflakeFile
from snowflake.snowpark.types import StringType, IntegerType
@udf(
    name="get_bytes_length",
    replace=True,
    input_types=[StringType()],
    return_type=IntegerType(),
    packages=['snowflake-snowpark-python']
)
def get_file_length(file_path):
    with SnowflakeFile.open(file_path) as f:
        s = f.read()
        return len(s)

上述代码遍历 @MY_TEXTS 阶段位置,并使用 SnowflakeFile 方法计算每个文件的长度。路径作为输入传递给 UDF。我们可以执行该函数以获取输出:

session.sql("SELECT RELATIVE_PATH, \
    get_bytes_length(build_scoped_file_url( \
        @MY_TEXTS,RELATIVE_PATH)) \
             as SIZE from DIRECTORY(@MY_TEXTS);").collect()

上述代码产生以下结果:

图 3.7 – Snowpark 中的动态文件

图 3.7 – Snowpark 中的动态文件

阶段中的文件以输出形式显示。在本节中,我们介绍了如何使用 Snowpark 将不同类型的文件导入 Snowflake。在下一节中,我们将学习如何使用 Snowpark 进行数据准备和转换。

数据探索和转换

数据加载完成后,下一步是准备数据,以便进行转换。在本节中,我们将介绍如何进行数据探索,以便我们了解如何根据需要修改数据。

数据探索

数据探索 是数据分析中的关键步骤,因为它为成功的见解和明智的决策奠定了基础。通过深入研究数据,分析师可以深入了解其特征,揭示潜在的规律,并识别潜在的问题或异常。探索数据提供了关于其结构、分布和关系的宝贵见解,使分析师能够选择适当的数据转换技术。

理解数据的特征和模式有助于分析师确定所需的适当转换和操作,以清理、重塑或从数据中派生新变量。此外,数据探索有助于识别与分析相关的数据子集,便于进行特定分析目标所需的过滤和子集操作。

在开始数据转换之前,我们必须了解我们现有的数据。通过全面理解数据,我们可以有效地识别其结构、质量和模式。这种理解是数据转换过程中做出明智决策的坚实基础,使我们能够提取有意义的见解并从数据中获得最大价值。请看以下代码:

purchase_history = session.table("PURCHASE_HISTORY")
campaign_info = session.table("CAMPAIGN_INFO")
complain_info = session.table("COMPLAINT_INFO")

在这里,我们将必要的表加载到会话中。这些表现在在 Snowpark 会话中可用,以便进行进一步的数据准备。我们将从准备 PURCHASE_HISTORY 表开始:

purchase_history.show(n=5)

show()方法返回 DataFrame 中的数据。前面的代码生成了PURCHASE_HISTORY表的前 5 行:

图 3.8 – 购买历史 – 前 5 行

图 3.8 – 购买历史 – 前 5 行

我们可以使用collect()方法在笔记本中显示数据:

purchase_history.collect()

PURCHASE_HISTORY表中的记录以 JSON 数组的形式显示:

图 3.9 – 购买历史 – 完整表

图 3.9 – 购买历史 – 完整表

collect()和 show()之间的区别

在 Snowpark Python 中,有两个基本函数:collect()show()。这些函数在数据处理和显示数据时具有不同的用途。Snowpark Python 中的collect()函数用于从指定的源(如表、文件或 API)收集或检索数据。它允许您执行查询、应用过滤器并从数据源中提取所需信息。收集到的数据存储在变量或结构(如 DataFrame)中,以便进行进一步的分析或操作。

另一方面,Snowpark Python 中的show()函数主要用于以表格格式显示 DataFrame 或任何其他数据结构的内 容。它提供了一个方便的方式来可视化并检查数据处理管道不同阶段的数据。show()函数以人类可读的方式呈现数据,以结构化表格格式显示行和列。它对于调试、理解数据结构或进行数据探索分析可能很有帮助。

简而言之,collect()函数专注于从源中收集和检索数据,而show()函数以可读的格式显示数据。这两个函数在 Snowpark Python 处理数据时都发挥着重要作用,但在数据处理工作流程中它们具有不同的用途。

接下来,我们将使用count()方法来获取表中行的总数:

purchase_history.count()

从结果输出中,我们可以看到PURCHASE_HISTORY表包含大约 2,000 行数据。

现在,我们可以检查表的列以了解有关这些数据的更多信息:

purchase_history.columns

这将返回列信息,有助于我们更好地理解数据。列信息包含与客户购买历史相关的数据:

图 3.10 – 购买历史 – 列

图 3.10 – 购买历史 – 列

现在,我们可以过滤数据以进行切片和切块。我们可以使用以下代码来过滤特定行或单行:

from snowflake.snowpark.functions import col
purchase_history.filter(col("id") == 1).show()

这将返回id设置为1的列。我们可以在列过滤器中传递多个值以执行额外的行级操作:

图 3.11 – 购买历史 ID 过滤

图 3.11 – 购买历史 ID 过滤

如果我们需要添加多个过滤器值,可以使用&操作符将多个列过滤器值传递给方法:

purchase_history.filter((col("MARITAL_STATUS") == "Married") & 
                        (col("KIDHOME") == 1)).show()

前面的代码提供了将MARITAL_STATUS设置为Married并且有孩子在家(KIDHOME)的人的数据:

图 3.12 – 购买历史过滤器

图 3.12 – 购买历史过滤器

这有助于我们了解有孩子的已婚客户的购买历史模式。我们还可以通过传递 1964 年至 1980 年的出生年份范围来过滤到出生年份:

purchase_history.filter((col("YEAR_BIRTH") >= 1964) & 
                        (col("YEAR_BIRTH") <= 1980)).show()

这显示了 1964 年至 1980 年出生的客户的购买数据:

图 3.13 – 购买历史过滤器

图 3.13 – 购买历史过滤器

这项数据有助于我们了解他们的购买情况。我们还可以使用select()方法仅选择分析所需的列:

purchase_history.select(col("ID"), col("YEAR_BIRTH"), 
                        col("EDUCATION")).show()

前面的代码仅返回客户的 ID、年份和教育状况:

图 3.14 – 购买历史列

图 3.14 – 购买历史列

在接下来的章节中,我们将更深入地探讨数据探索,揭示更多从数据中获取洞察的技术。

在这些基本探索步骤的基础上,我们将深入数据转换操作领域。通过结合我们对数据的理解以及转换技术的力量,我们将释放数据的全部潜力,并为明智的决策提取有价值的见解。

在下一节中,我们将讨论如何使用这些数据执行数据转换。

数据转换

数据转换是一个基本过程,涉及修改和重塑数据,使其更适合分析或其他下游任务,例如机器学习模型构建过程。它包括对数据应用一系列操作,如清理、过滤、聚合和重新格式化,以确保其质量、一致性和可用性。数据转换使我们能够将原始数据转换为易于解释和分析的结构化和组织化格式。

数据需要最小的转换,我们将在接下来的章节中对其进行详细阐述。本节的目标是将来自不同来源的数据结合起来,创建一个用于进一步处理的统一表格,我们将在下一章中使用它。我们将利用 Snowpark 强大的连接和合并功能来完成这项工作。通过使用连接,我们可以根据标准列或条件合并数据。另一方面,合并允许我们垂直地追加来自多个来源的数据。这些技术将使我们能够有效地整合和合并我们的数据,为全面分析和洞察奠定基础。让我们探索 Snowpark 的连接和合并功能如何帮助我们实现这种数据组合:

purchase_campaign = purchase_history.join(
    campaign_info,
    purchase_history.ID == campaign_info.ID ,
    lsuffix="_left", rsuffix="_right"
)

在这里,我们将购买历史与活动信息连接起来,以建立购买与活动之间的关系。标准 ID 列用于选择连接,默认为内部连接:

purchase_campaign = purchase_campaign.drop("ID_RIGHT")

我们正在从合并的结果中删除额外的 ID 列。DataFrame 现在只包含一个 ID 列:

purchase_campaign.show()

这显示了购买活动数据与购买历史和活动信息相结合的数据:

图 3.15 – 购买活动数据

图 3.15 – 购买活动数据

让我们将这些数据与投诉信息结合起来以获得完整的数据:

final_combined = purchase_campaign.join(
    complain_info,
    purchase_campaign["ID_LEFT"] == complain_info.ID
)
final_combined = final_combined.drop("ID_LEFT")

在这里,我们通过使用标准的 ID 列将购买活动的结果与投诉信息结合起来。结果 DataFrame 包含了数据分析所需的完整数据。我们正在从合并的结果中删除额外的 ID 列。DataFrame 现在只有一个 ID 列:

final_combined.show()

这显示了从所有三个表中综合得到的最终数据。我们现在可以将这些数据写入表格以进行进一步分析:

final_combined.write.save_as_table("MARKETING_DATA")

在这里,数据被写入 MARKETING_DATA 表,此时它将在 Snowflake 中可用。我们需要将此数据与必须加载到该表中的附加营销数据一起追加。

连接和联合的区别

连接基于共享列或条件从两个或多个表中合并数据。在 Snowflake Snowpark 中,您可以执行不同类型的连接,如内连接、左连接、右连接和全外连接。连接允许您通过根据指定列中的匹配值对齐行来水平合并数据。这使得您能够将来自多个表的相关数据合并,从而生成包含所有连接表信息的合并数据集。

另一方面,联合用于垂直追加来自多个表的数据,或将结果集追加到单个数据集中。与连接不同,联合不需要任何特定条件或匹配列。相反,它们将行堆叠在一起,垂直连接数据。这在您有相同结构且希望将它们合并到单个数据集中的类似数据集时非常有用。在 Snowflake Snowpark 中可以执行联合,以创建包含输入表或结果集中所有行的新的数据集。

总结来说,在 Snowflake Snowpark 中,连接用于通过匹配列水平地组合数据,而联合用于垂直堆叠数据,而不需要任何特定条件。连接将来自多个表的相关数据合并,而联合将类似的数据集追加到单个数据集中。

追加数据

Snowflake Snowpark 的 UNION 函数在将新数据合并和集成到 Snowflake 数据库中起着至关重要的作用。UNION 函数的重要性在于其能够垂直追加来自不同数据源或结果集的行,或将结果集合并到单个综合数据集中。当向数据库添加新数据时,通常需要将其与现有数据合并或结合,以便进行综合分析。UNION 函数使我们能够无缝地将新添加的数据与现有数据集集成,创建一个包含所有相关信息的统一视图。

UNION函数的这一功能在数据定期接收或更新的场景中非常宝贵。例如,假设我们接收每日的销售数据或日志文件。在这种情况下,UNION函数使我们能够轻松地将新记录附加到现有数据集,确保我们的分析反映最新的信息。此外,它确保数据一致性,并允许数据分析的无缝连续性,使我们能够从完整和统一的数据集中得出准确的见解并做出明智的决策。

额外的营销数据可在MARKETING_ADDITIONAL表中找到。让我们看看我们如何利用 Snowpark 的UNION函数来处理这些额外数据:

marketing_additional = session.table("MARKETING_ADDITIONAL")
marketing_additional.show()

上述代码显示了MARKETING_ADDITIONAL表中的数据:

图 3.16 – MARKETING_ADDITIONAL 表

图 3.16 – MARKETING_ADDITIONAL 表

因此,该表已加载到 DataFrame 中。让我们看看原始表和附加表的行数:

print("No of rows in MARKETING_ADDITIONAL table: \
    ",marketing_additional.count())
print("No of rows in PURCHASE_HISTORY table: \
    ",final_combined.count())

此代码显示了MARKETING_ADDITIONALPURCHASE_HISTORY表中的总行数:

图 3.17 – 数据行数

图 3.17 – 数据行数

MARKETING_ADDITIONAL表包含 240 行新数据,这些数据必须与包含 2,000 行数据的PURCHASE_HISTORY表合并。由于列名相同,可以使用union_by_name进行合并:

final_appended = final_combined.union_by_name(marketing_additional)

现在,DataFrame 包含附加的数据。让我们看看这个 DataFrame 中的行数:

print("No of rows in UPDATED table: ",final_appended.count())
final_appended.show()

上述代码显示了 DataFrame 中的最终数据:

图 3.18 – MARKETING_FINAL 表

图 3.18 – MARKETING_FINAL 表

总行数为 2,240 行。这样,新数据已经附加。现在,我们将这些数据写入 Snowflake 中的MARKETING_FINAL表:

final_appended.write.save_as_table("MARKETING_FINAL")

MARKETING_DATA表现在在 Snowflake 中可用并可被消费。

unionunion_by_name之间的区别

有两种方法可以合并数据:union_by_nameunion。这两种方法都允许合并多个数据集,但它们在方法和功能上有所不同。

Snowpark Python 中的union_by_name方法专门设计用于通过匹配和合并列名来组合数据集。此方法确保来自不同数据集的具有相同名称的列被合并,创建一个统一的数据集。当您有具有相似列结构的数据集并希望合并它们同时保留列名时,这非常有用。

另一方面,Snowpark Python 中的 union 方法通过简单地垂直追加数据集来合并数据集,而不考虑列名或结构。此方法将一个数据集的行与另一个数据集的行连接起来,从而生成包含两个来源所有行的单个数据集。union 方法适用于垂直堆叠数据集,而不考虑列名或匹配结构。然而,请注意,在某些情况下,列类型很重要,例如在将字符串列转换为数值类型时。

数据分组和分析

现在数据已经准备好并已转换,下一步是看看我们如何分组数据以了解重要的模式和进行分析。在本节中,我们将汇总这些数据并进行分析。

数据分组

在数据分析中,理解数据集中的模式对于获得见解和做出明智的决策至关重要。在这个过程中,一个强大的工具是 Snowpark Python 中的 group_by 函数。此函数允许我们根据特定标准对数据进行分组,使我们能够以结构化的方式剖析和分析数据集。

通过利用 group_by 函数,我们可以揭示数据在不同类别或属性中的分布和相关性方面的宝贵见解。例如,我们可以按产品类别对销售数据进行分组,以分析销售趋势,或按人口统计对客户数据进行分组,以了解购买行为。

此外,group_by 函数可以与其他数据处理和可视化技术结合使用,以获得更深入的见解。例如,我们可以创建条形图或热图等可视化,以直观地表示汇总数据,使其更容易发现模式和趋势。

为了便于分组和进行更深入的分析,我们将利用之前建立的 MARKETING_FINAL 表:

marketing_final = session.table("MARKETING_FINAL")

在这里,我们将从 MARKETING_FINAL 表中加载数据到 DataFrame 中。我们将使用此 DataFrame 来执行聚合:

marketing_final.group_by("EDUCATION").mean("INCOME").show()

这返回了按 EDUCATION 的平均收入。拥有博士学位的人平均收入最高,而拥有初等教育的人平均收入最低:

图 3.19 – 按教育水平的平均收入

图 3.19 – 按教育水平的平均收入

现在,我们可以为列创建一个别名:

marketing_final.group_by("EDUCATION").agg(avg("INCOME").alias( \
    "Avg_Income")).show()

平均收入以别名显示 – AVG_INCOME

图 3.20 – AVG_INCOME 别名

图 3.20 – AVG_INCOME 别名

我们还可以通过使用 function() 方法传递 Snowpark 函数中的相应操作来达到类似的结果:

marketing_final.group_by("MARITAL_STATUS").function("sum")( \
    "Z_REVENUE").show()

这将打印以下输出:

图 3.21 – 按婚姻状况的收益总和

图 3.21 – 按婚姻状况的收益总和

在这里,我们可以看到已婚客户产生了最高的收入。我们还可以使用 agg() 来执行这种特定的聚合。让我们按婚姻状况计算最大收入:

marketing_final.group_by("MARITAL_STATUS").agg(max("INCOME")).show()

这将生成以下输出:

图 3.22 – 按婚姻状况划分的收入

图 3.22 – 按婚姻状况划分的收入

这里,我们可以看到一起作为家庭并已婚的客户有最高的收入用于消费,因此他们产生了最大的收入。接下来,我们将找出不同类型毕业生的数量及其最高收入:

marketing_final.group_by("EDUCATION").agg((col("*"), "count"), 
    max("INCOME")).show()

之前的代码产生了以下输出:

图 3.23 – 类别计数

图 3.23 – 类别计数

这里,我们可以看到 PhD 的最高收入为 162397,而 Basic 收入的人最高收入最低,即 34445

我们还可以在 Snowpark 中执行复杂的多层次聚合。让我们找出不同教育水平和婚姻状况的人如何消费:

marketing_final.group_by(["EDUCATION","MARITAL_STATUS"]).agg(
    avg("INCOME").alias("Avg_Income"),
    sum("NUMSTOREPURCHASES").alias("Sum_Purchase")
).show()

这是输出结果:

图 3.24 – 多层次聚合

图 3.24 – 多层次聚合

让我们确定 EDUCATIONMARITAL_STATUSSUM_PURCHASE 之间的关系。与单身人士相比,已婚的毕业生花费最多。我们还可以使用 sort() 函数对结果进行排序:

aggregate_result = marketing_final.group_by(["EDUCATION","MARITAL_STATUS"]).agg(
    avg("INCOME").alias("Avg_Income"),
    sum("NUMSTOREPURCHASES").alias("Sum_Purchase")
)
aggregate_result.sort(
    col("EDUCATION").asc(), col("Sum_Purchase").asc()
).show()

这是输出结果:

图 3.25 – 排序结果

图 3.25 – 排序结果

这里,我们在聚合完成后按购买金额升序排序结果。下一节将介绍可以在这些数据上执行的一些标准数据分析。

数据分析

在前面的章节中,我们深入探讨了数据探索、转换和聚合,学习了我们可以使用的各种技术来了解我们的数据是什么以及我们如何结合不同的数据集。在掌握了坚实的通用数据集探索基础后,我们准备更深入地使用 Snowpark Python 进行数据分析。

本节重点介绍利用统计函数、采样技术、交叉操作以及将数据转换为 pandas DataFrame 进行高级分析的能力。我们将探讨如何应用统计函数从我们的数据中提取有意义的信息。然后,我们将了解不同的采样技术以高效地处理大型数据集。此外,我们将发现如何使用交叉操作重塑我们的数据,以促进深入分析。

此外,我们将探讨 Snowpark Python 与广泛使用的数据处理库 pandas 的无缝集成。我们将了解如何将 Snowpark 数据转换为 pandas DataFrame,从而利用 pandas 的广泛分析和可视化功能。

以下部分提供了 Snowpark Python 在数据分析方面的能力概述;我们将在下一章中深入探讨每个主题。在这里,我们旨在提供使用 Snowpark Python 分析数据的关键概念和技术的基础理解。在下一章中,我们将更详细地探讨这些主题,揭示 Snowpark Python 在数据分析方面的全部潜力。

描述数据

我们分析的第一步是理解我们的数据是如何分布的。pandas 中的 describe() 函数是一个非常有用的工具,它帮助我们深入了解数值数据的统计特性。当我们对 DataFrame 应用 describe() 时,它会计算各种描述性统计量,包括每个数值列的计数、平均值、标准差、最小值、四分位数和最大值。

此总结全面概述了我们的数据分布和中心趋势。通过检查这些统计量,我们可以快速识别关键特征,例如值的范围、数据的分布以及任何潜在的异常值。这种初步探索为更高级的分析技术奠定了基础,并允许我们根据对数据集分布的深入了解做出明智的决策:

marketing_final.describe().show()

上一段代码显示了 MARKETING_FINAL 表的数据:

图 3.26 – MARKETING_FINAL DataFrame

图 3.26 – MARKETING_FINAL DataFrame

结果显示了 MARKETING_FINAL 表的不同列和数据。

查找独特数据

在 Snowpark DataFrames 中,distinct() 函数对于在列或列集中识别唯一值至关重要。当应用于 Snowpark DataFrame 时,distinct() 会消除重复记录,从而生成一个新的 DataFrame,其中只包含唯一值。此函数特别适用于处理大型数据集或提取用于分析或数据处理的分析记录:

marketing_final.distinct().count()

上一段代码显示了 MARKETING_FINAL 表的总计数:

图 3.27 – MARKETING_FINAL 计数

图 3.27 – MARKETING_FINAL 计数

在我们的案例中,由于我们没有重复行,整个数据集被返回。distinct() 函数保留 DataFrame 的原始行,并且只过滤掉指定列中的重复值。

删除重复项

drop_duplicates() 函数从 Snowpark DataFrame 中删除重复行。它分析整行并与 DataFrame 中的其他行进行比较。如果发现某行是另一行的完全重复,drop_duplicates() 将会删除它,只保留第一次出现。默认情况下,此函数将 DataFrame 中的所有列都考虑为重复检测的依据:

marketing_final.select(["Education","Marital_Status"]).drop_duplicates().show()

这将显示以下输出:

图 3.28 – 已移除的营销重复项

图 3.28 – 已移除的营销重复项

注意,您可以使用 subset 参数指定特定的列,仅基于这些列检查重复项。drop_duplicates() 方法通过删除重复行来修改原始 DataFrame。

列联分析

一旦我们确定了数据集中 EDUCATIONMARITAL_STATUS 列的唯一组合,我们可能会对每个组合出现的频率仍然感到好奇。我们可以利用 crosstab 函数来确定这些唯一组合的出现次数。通过将 crosstab 函数应用于我们的数据集,我们可以生成一个交叉表或列联表,显示 EDUCATIONMARITAL_STATUS 的唯一组合的频率分布:

marketing_final.stat.crosstab(col1="Education",col2="Marital_Status").show()

上述代码显示了 DataFrame 中的列联表数据:

图 3.29 – 列联表数据

图 3.29 – 列联表数据

此表提供了关于数据集中每个唯一组合出现频率的全面概述,使我们能够深入了解这些变量之间的关系。crosstab 函数帮助我们理解唯一组合的分布和出现模式,进一步增强了我们的数据分析能力。

旋转分析

在使用 crosstab 函数检查我们的数据集中 EDUCATIONMARITAL_STATUS 列的唯一组合时,我们可能会遇到某些零次出现的组合。我们可以构建一个透视表来更全面地了解数据,并进一步研究这些变量之间的关系。

构建透视表使我们能够更动态和灵活地总结和分析数据。与仅提供唯一组合频率分布的 crosstab 函数不同,透视表允许我们探索额外的聚合函数,如总和、平均值或最大值。这使得我们能够更深入地研究数据集,并获得有意义的见解:

market_subset = marketing_final.select(
    "EDUCATION","MARITAL_STATUS","INCOME"
)
market_pivot = market_subset.pivot(
    "EDUCATION",
    ["Graduation","PhD","Master","Basic","2n Cycle"]
).sum("INCOME")
market_pivot.show()

上述代码显示了 DataFrame 中的数据:

图 3.30 – 透视表

图 3.30 – 透视表

通过为 EDUCATIONMARITAL_STATUS 列构建透视表,我们可以揭示每个组合的出现次数以及与每个组合相关的各种统计度量或计算。这种扩展分析提供了对数据的更全面视图,并允许进行更细致和详细的研究。

注意

列联函数显示某些变量的组合为零次出现时,需要注意的是,在构建透视表时,这些组合将用NULL值表示,而不是零。

列联不同,后者明确突出显示数据集中不存在的组合的零计数,透视表考虑了变量的所有可能组合。因此,如果数据集中不存在某种组合,透视表中的相应单元格将用NULL值表示,而不是零。

数据透视表中NULL值的存在突出了那些特定组合数据缺失的情况。在后续数据分析过程中,如数据清理、插补或进一步的统计计算中,适当地解释和处理这些NULL值是至关重要的。

删除缺失值

pandas 中的dropna()函数是处理 DataFrame 中缺失值的有力工具。在这种情况下,我们将利用 Snowpark 的dropna()功能,它允许我们删除包含缺失或NULL值的行或列,有助于确保数据的完整性和准确性。dropna()函数提供了几个参数,提供了在控制操作行为方面的灵活性:

market_pivot.dropna(how="all").show()

上一段代码显示了 DataFrame 中应用了过滤器的数据:

图 3.31 – 数据透视表 – dropna()

图 3.31 – 数据透视表 – dropna()

how参数决定了用于删除行或列的准则。它接受anyall作为输入:any会在行或列包含任何缺失值时删除该行或列,而all只有在所有值都缺失时才会删除行或列。

thresh参数指定了保留行或列所需的最小非空值数量。如果非空值超过阈值,则删除行或列:

market_pivot.dropna(thresh=5).show()

上一段代码显示了 DataFrame 中应用了过滤器的数据:

图 3.32 – 数据透视阈值

图 3.32 – 数据透视阈值

subset参数允许我们指定用于缺失值删除的列或行的子集。它接受列或行标签的列表。默认情况下,dropna()检查所有列或行中的缺失值。然而,使用子集,我们可以专注于特定列或行进行操作:

market_pivot.dropna(subset="'Graduation'").show()

上一段代码从market_pivot DataFrame 中删除了Graduation列有缺失值的任何行,然后显示了结果 DataFrame:

图 3.33 – 数据透视子集

图 3.33 – 数据透视子集

这显示了应用了过滤器的 DataFrame 中的数据。

注意

当使用数据透视表时,适当地处理NULL值至关重要,因为它们可能会影响后续分析的准确性和可靠性。这使我们能够确保我们有完整的数据用于进一步的分析和计算。

在数据透视结果中存在NULL值可能会导致错误的解释或计算,因为NULL值可能会在分析中传播并影响后续的聚合、统计或可视化。通过将NULL值替换为特定值,例如 0,我们可以在数据透视表中提供有意义的数值表示,从而允许我们进行可靠的分析并基于完整信息做出明智的决策。

填充缺失值

fillna() 函数允许我们用特定值替换空值或应用各种插补技术。它还允许我们在 DataFrame 中填充缺失值,确保我们保持数据结构的完整性。我们可以指定用于填充空值的值,例如一个常数,或者从统计计算中得出的值,如平均值、中位数或众数。当我们在考虑数据的性质和期望的分析时处理空值时,fillna() 函数非常有用:

market_pivot.fillna(0).show()

以下代码将 market_pivot DataFrame 中的任何空值填充为 0,然后显示结果 DataFrame:

图 3.34 – 缺失值

图 3.34 – 缺失值

这是一个方便的功能,用于填充需要用于计算的缺失值。

变量交互

corr() 函数计算相关系数,它衡量两个变量之间线性关系的强度和方向。它返回一个介于 -1 和 1 之间的值,其中 -1 表示完美的负相关,1 表示完美的正相关,0 表示没有线性相关:

marketing_final.stat.corr("INCOME", "NUMSTOREPURCHASES")

执行此代码后,我们获得 INCOMENUMSTOREPURCHASES 列之间的相关系数,从而提供了关于收入水平和数据集中商店购买数量之间潜在关系的见解:

图 3.35 – 相关值

图 3.35 – 相关值

另一方面,cov() 函数计算协方差,它衡量两个变量之间关联的程度,而不对规模进行归一化:

marketing_final.stat.cov("INCOME", "NUMSTOREPURCHASES")

这是输出结果:

图 3.36 – 协方差值

图 3.36 – 协方差值

INCOMENUMSTOREPURCHASES 列之间的协方差帮助我们了解收入水平的变化如何与数据集中商店购买数量的变化相对应。

注意

虽然 corr()cov() 都有助于分析变量之间的关系,但重要的是要注意,在 Snowpark Python 中,这些函数仅支持同时分析两个变量。这种限制意味着我们只能计算 DataFrame 中两列之间的相关性或协方差,而不能跨多个变量同时进行。可能需要额外的技术或函数来克服这种限制,并执行多个变量的相关性或协方差分析。

使用 pandas DataFrame 操作

将 Snowpark DataFrame 转换为 pandas DataFrame 是一个有价值的步骤,它为分析能力打开了广泛的大门。Snowpark 提供了与 pandas 的无缝集成,使我们能够利用 pandas 的广泛的数据操作、分析和可视化功能。通过将 Snowpark DataFrame 转换为 pandas DataFrame,我们获得了专门为数据分析设计的工具和库的庞大生态系统。

这种转换使我们能够利用 pandas 丰富的函数和方法,例如统计计算、高级过滤、分组操作和时间序列分析。pandas 还提供了许多可视化选项,例如生成有洞察力的图表、图表和图形,使数据可视化更加容易访问。使用 pandas,我们可以创建有意义的可视化数据表示,便于探索模式、趋势和关系。此外,使用 pandas 允许我们利用其广泛的社区支持和资源。pandas 库拥有庞大的用户社区,这使得查找特定数据分析任务的文档、教程和有帮助的讨论变得更加容易。

pandas DataFrame 的局限性

将 Snowpark DataFrame 转换为 pandas DataFrame 可能存在局限性,尤其是在处理大型数据集时。主要约束是内存消耗,因为同时转换整个数据集可能会超过可用的内存资源。这可能会阻碍分析过程,并可能导致系统崩溃或性能问题。

然而,这些局限性可以通过将 DataFrame 分批处理和采样数据来缓解。我们将在稍后讨论这一点。

使用 pandas 进行数据分析

将 Snowpark DataFrame 转换为 pandas DataFrame 使我们能够无缝地从 Snowpark 强大的数据处理能力过渡到 pandas 丰富的环境。这种互操作性扩展了我们的分析可能性,并使我们能够执行高级分析,从数据中获得更深入的见解:

pandas_df = marketing_final.to_pandas()
pandas_df.head()

以下代码将 marketing_final Snowpark DataFrame 转换为 pandas DataFrame,使我们能够使用 pandas 的广泛数据分析和操作功能来处理数据。它将输出以下内容:

图 3.37 – 结果 pandas DataFrame

图 3.37 – 结果 pandas DataFrame

这显示了已转换为 pandas DataFrame 的数据。

pandas 中的相关性

在 pandas 中,计算多个列之间的相关性很简单:涉及选择所需的列并应用 corr() 函数。它生成一个相关性矩阵,使我们能够同时检查每一对列之间的关系:

pandas_df[["INCOME","KIDHOME","RECENCY"]].corr()

以下代码计算 pandas_df pandas DataFrame 中 INCOMEKIDHOMERECENCY 列之间的相关性矩阵。它计算这些列之间的成对相关系数,提供对这些关系见解。输出如下:

图 3.38 – Pandas 相关性

图 3.38 – Pandas 相关性

接下来,我们将查看频率分布。

频率分布

在 pandas 中计算单列的值频率比在 Snowpark Python 中简单。我们可以通过在特定列上使用 value_counts() 函数快速获取 pandas 中的频率分布。它返回一个 Series,其中唯一值作为索引,其对应的计数作为值。这种简洁的方法使我们能够快速了解列中每个唯一值的分布和普遍性。另一方面,在 Snowpark Python 中,获取单列的值频率需要更多步骤和额外的编码。我们通常需要按所需的列对 DataFrame 进行分组,然后执行聚合操作以计算每个唯一值的出现次数。尽管在 Snowpark Python 中可以实现这一点,但它涉及更复杂的语法和多个转换,使得与 pandas 相比,这个过程更加繁琐:

frequency = pandas_df.EDUCATION.value_counts()
frequency

frequency = pandas_df.EDUCATION.value_counts() 计算了 pandas DataFrame 中 pandas_dfEDUCATION 列中唯一值的频率分布,并将结果赋值给 frequency 变量。输出如下:

图 3.39 – Pandas 数据频率

图 3.39 – Pandas 数据频率

这显示了 pandas DataFrame 中的数据频率值。

pandas 中的可视化

由于与流行的可视化库(如 Matplotlib 和 Seaborn)的无缝集成,使用 pandas 创建可视化变得非常简单。pandas 提供了一个简单直观的界面来生成各种可视化,包括线图、条形图、直方图、散点图等等。

通过利用 pandas 内置的绘图函数,我们可以轻松地将我们的数据转换为有洞察力的视觉表示,使我们能够探索数据集中的模式、趋势和关系。只需几行代码,pandas 就能让我们产生视觉上吸引人且信息丰富的图表,从而促进我们数据的沟通和解读:

frequency.plot(kind="barh",figsize=(8,3))

上述代码从存储在 frequency 变量中的频率分布数据创建了一个水平条形图,其中每个唯一值由一个长度与其计数成比例的条形表示,该图宽度为 8 英寸,高度为 3 英寸的自定义尺寸:

图 3.40 – 频率图

图 3.40 – 频率图

同样,我们可以通过将 kind 改为 hexbin 生成 Hexbin 图:

pandas_df.plot(
    kind="hexbin",
    x="INCOME",y="MNTGOLDPRODS",
    xlim=[0,100000],ylim=[0,100],
    figsize=(8,3)
)

上述代码创建了一个 Hexbin 图,用于可视化 pandas_df pandas DataFrame 中 INCOMEMNTGOLDPRODS 列之间的关系:

图 3.41 – Hexbin 图

图 3.41 – Hexbin 图

在这里,X 轴表示收入值,Y 轴表示黄金产品的数量。该图限制在 X 轴范围为 0 到 100,000,Y 轴范围为 0 到 100,宽度为 8 英寸,高度为 3 英寸的自定义尺寸。

将 DataFrame 分成批次

to_pandas_batches() 函数将 Snowpark DataFrame 转换为多个较小的 pandas DataFrame,以便批量处理。这种方法通过将数据转换为可管理的部分来减少内存使用,从而能够高效地分析大型数据集:

for batch in marketing_final.to_pandas_batches(): print(batch.shape)

这里是输出:

图 3.42 – DataFrame 批量

图 3.42 – DataFrame 批量

以下代码演示了如何使用 Snowpark Python 中的 to_pandas_batches() 函数批量分析大型数据集。通过遍历 to_pandas_batches() 函数,代码以可管理的批量处理数据集,而不是一次性将整个数据集加载到内存中。在每次迭代中,数据集的一个批次被转换为 pandas DataFrame 并存储在 batch 变量中。print(batch.shape) 语句提供了每个批次的形状,指示该特定批次的行数和列数。

以批量的方式分析数据集可以更有效地利用内存,使我们能够处理可能超出可用内存资源的大型数据集。这种方法通过将数据集分解为更小、更易于管理的部分,从而便于快速计算并降低与内存相关问题的风险。

采样 DataFrame

Snowpark Python 中的 sample() 函数允许我们从 Snowpark DataFrame 中检索随机子集的数据。通过指定所需的分数或行数,我们可以有效地提取用于分析的代表性样本。这种技术减少了转换和后续分析所需的内存占用,同时提供了有意义的见解:

sample_df = marketing_final.sample(frac=0.50)
sample_df.count()

这里是输出:

图 3.43 – 采样数据

图 3.43 – 采样数据

以下代码从 marketing_final DataFrame 中选择 50% 的随机样本并将其分配给 sample_df DataFrame。每次运行代码段时,最终计数步骤都会产生略微不同的输出,因为它涉及到对原始表的采样。随后的 sample_df.count() 函数计算 sample_df DataFrame 中每列的非空值计数。

通过在 Snowpark Python 中使用我们介绍的方法,我们可以克服将大型 Snowpark DataFrame 转换为 pandas DataFrame 的限制,从而在有效管理内存资源的同时进行实际分析。这些函数提供了灵活性和控制力,使我们能够以可管理和优化的方式处理大型数据集。

摘要

Snowpark 提供了不同的数据处理能力并支持各种技术。它为我们提供了一种简单且通用的方式来摄取不同的结构化和非结构化文件格式,并且 Snowpark 的 DataFrame 支持各种数据转换和分析操作。我们介绍了 Snowpark 会话变量以及可以使用 Snowpark 执行的不同数据操作。

在下一章中,我们将介绍如何使用 Snowpark 构建数据工程管道。

第四章:使用 Snowpark 构建数据工程管道

数据是每个组织的脉搏,数据工程是确保各种消费有最新、准确数据流动的生命线。数据工程师的角色是开发和管理工作流程,该流程收集、转换并将数据交付到不同的业务线LOB)。正如 Gartner 的研究正确提到的,“数据的日益多样化以及需要在正确的时间向正确的人提供正确数据的需求,已经产生了对数据工程实践的需求。数据和分析领导者必须将数据工程学科整合到他们的数据管理策略中。”本章讨论了使用 Snowpark 构建高效数据工程管道的实用方法。

在本章中,我们将涵盖以下主要主题:

  • 使用 Snowpark 开发具有弹性的数据管道

  • 在 Snowpark 中部署高效的数据操作

  • Snowflake 中任务概述

  • 在 Snowpark 中实现日志记录和跟踪

技术要求

本章需要有效的 Snowflake 账户,并且需要安装 Anaconda 中的 Python 并在本地配置。您可以在 signup.snowflake.com/ 注册 Snowflake 试用账户。

环境设置的技术要求与前面章节相同。如果您还没有设置环境,请参考前面的章节。支持材料可在 github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark 获取。

使用 Snowpark 开发具有弹性的数据管道

一个强大且具有弹性的数据管道将使组织能够获取、收集、分析和有效地使用洞察力以增长业务并交付节省成本的业务流程。传统的数据管道难以管理,且不支持组织不断变化的数据需求。Snowpark 通过在 Snowflake 数据云中本地运行来解决问题,使得从数据云中提取信息变得更加容易和快速。本节将涵盖具有弹性的数据管道的各种特性、如何在 Snowpark 中开发它们以及它们的益处。

传统数据管道与现代数据管道的比较

传统数据管道的一个重大挑战是,开发和管理工作流程需要相当的时间和成本,并且有很高的技术债务。它还包含多个工具,需要花费大量时间进行集成。由于解决方案的复杂性,可能会因为延迟和流数据支持而出现数据延迟。以下图表突出了传统方法:

图 4.1 – 传统数据管道

图 4.1 – 传统数据管道

该架构展示了各种技术和系统被拼接在一起,以从源到消费者传递数据,每个阶段都有多个故障点。由于存在数据孤岛,并且有大量相同数据的副本,因此还存在数据治理和安全问题。

现代数据管道基于统一平台和多工作负载模型运行。它们通过集成数据源,如批量处理和流式处理,通过启用各种商业智能BI)和分析工作负载以及支持内部和外部用户,通过简化的架构提高生产力。统一平台架构支持连续、可扩展的数据处理管道,具有可扩展的性能。以下图表突出了现代 Snowflake 方法:

图 4.2 – 现代数据管道

图 4.2 – 现代数据管道

Snowpark 由于与领先的云数据仓库 Snowflake 的原生集成而脱颖而出,使其能够在 Spark 应用程序内直接进行无缝数据处理。它提供了一种统一的开发体验,使用熟悉的编程语言,如 Scala 和 Java,消除了传统 Spark 设置的复杂性,从而简化了数据管道的开发和维护。Snowpark 对 Snowflake 架构的优化性能确保了高效的数据处理和降低延迟,使得对大型数据集的快速分析成为可能。此外,其高级分析能力、可扩展性和成本效益使其成为寻求构建敏捷、云原生数据管道的组织的一个有吸引力的选择,与传统的 Spark 设置相比,它提高了生产力和灵活性。

使用 Snowpark 进行数据工程

Snowpark 拥有许多数据工程能力,使其成为一个快速灵活的平台,使开发者能够使用 Python 进行数据工程。有了提取、转换和加载ETL)和提取、加载和转换ELT)的支持,开发者可以使用 Snowpark 客户端进行开发,并使用他们喜欢的开发环境与 Snowflake 引擎进行交互以处理数据。有了 Anaconda 的支持,你可以确保所需的包和依赖项对 Snowpark 脚本来说是现成的。这使得加速产品管道的增长变得更容易。Snowpark 中的数据管道可以是批量或实时,利用可扩展、高性能的多集群仓库,能够处理复杂的数据转换而不会影响性能:

图 4.3 – Snowpark 数据工程

图 4.3 – Snowpark 数据工程

Snowpark 通过一个能够为开发者提供数据云操作简单性的引擎,增强了整个数据工程的生命周期,使开发者能够拥有表达性和灵活性。Snowflake 可以帮助实现数据的集中化,包括结构化、半结构化和非结构化数据,这些数据被加载到 Snowflake 中进行处理。可以通过基于 Python 的强大 Snowpark 函数来执行转换。在整个开发生命周期中,Snowpark 的数据工程工作负载可以与其他 Snowflake 对象一起完全管理,内置的监控和编排能力支持由数据云驱动的复杂数据管道的扩展。高级数据转换的结果存储在 Snowflake 内部,可用于不同的数据消费者。Snowpark 数据管道通过移除用于计算的数据移动步骤,减少了数据需要移动到可操作见解的阶段数量。

使用 Snowpark 实现程序化 ELT

Snowflake 支持和推荐使用现代 ELT 实现模式进行数据工程,而不是传统的 ETL 过程。ETL 是一种模式,其中数据从各种来源提取,在数据管道中进行转换,然后将转换后的数据加载到目的地,如数据仓库或数据集市。以下图表显示了 ETL 与 ELT 的比较:

图 4.4 – ETL 与 ELT 的比较

图 4.4 – ETL 与 ELT 的比较

ELT 是一种更适合 Snowflake 数据云的模式,其中数据从源提取并加载到 Snowflake 中。然后使用 Snowpark 在 Snowflake 内部对数据进行转换。Snowpark 管道被设计为首先提取和加载数据,然后在目的地进行转换,因为转换是在 Snowflake 内部完成的,这提供了更好的可扩展性和弹性。ELT 还提高了性能,并减少了使用 Snowpark 在 Snowflake 中摄取、转换和分析数据所需的时间。以下图表显示了 Snowflake 内部数据的不同层:

图 4.5 – Snowflake 中的数据阶段

图 4.5 – Snowflake 中的数据阶段

数据管道在 Snowflake 中构建不同的数据阶段。这些阶段包含数据库和模式,其中包含诸如表和视图等对象。原始数据从源系统直接导入 Snowflake,没有进行转换,因为 Snowflake 支持多种数据格式。然后,使用 Snowpark 对这些数据进行转换,形成包含去重和标准化数据的规范阶段。这些数据成为数据管道下一步的输入。参考阶段包含业务定义和数据映射,以及层次结构和主数据。最终阶段包含建模数据,它包含清洁和转换后的数据。Snowpark 拥有许多功能,有助于进行增值转换,帮助将数据转换为用户和应用可访问的、业务就绪的格式,从而为组织增加价值。

Snowpark 中的 ETL 与 ELT

Snowpark 支持 ETL 和 ELT 工作负载。虽然 ELT 因现代管道而闻名,但在某些场景中也会使用 ETL 模式。ETL 通常用于结构化数据,其中数据总量较小。它还用于将传统数据库迁移到数据云的系统,其中源数据和目标数据类型不同。与传统的 ETL 过程相比,ELT 提供了显著的优势。ELT 支持大量结构化、非结构化和半结构化数据,可以使用 Snowflake 进行处理。它还允许开发人员和分析师在数据加载到 Snowflake 时进行数据实验。ELT 还最大限度地提高了他们转换数据以获取潜在见解的选项。它还支持低延迟和实时分析。由于这些原因,ELT 比传统的 ETL 更适合 Snowflake。以下部分将介绍如何在 Snowpark 中开发高效的数据操作。

在 Snowpark 中部署高效的数据操作

通过为数据管道的开发和管理带来更多严谨性,数据操作帮助数据团队减少开发时间,提高数据质量,并最大化数据的商业价值。它还确保数据在数据治理的简化环境中保持清洁、准确和最新。数据工程引入了有效开发、管理和部署数据工程管道所需的过程和能力。以下图表突出了数据操作方法:

图 4.6 – 数据操作流程

图 4.6 – 数据操作流程

数据操作流程侧重于通过迭代开发、测试和部署流程在循环中引入敏捷开发到数据工程管道。它还包括数据的持续集成和持续部署CI/CD)、模式更改、数据版本以及数据模型和工件自动化的数据版本。本节将展示在 Snowpark 中执行的数据工程管道的示例。

开发数据工程管道

在 Snowpark 框架内创建一个有弹性的数据工程管道需要无缝集成三个核心组件:

  1. 首先且最重要的是,数据工程师必须掌握将数据加载到 Snowflake 中的艺术,为后续处理奠定基础。这一初始步骤为整个管道的建设奠定了基础。

  2. 其次,Snowpark 数据函数的变革力量开始发挥作用,使工程师能够塑造和塑造数据以满足特定的分析需求。第三章**使用 Snowpark 简化数据处理提供了对 DataFrame 操作的详细探索,为这一关键转换阶段奠定了基础。

  3. 最后,数据之旅以将这些操作捆绑为 Snowpark 存储过程而告终,这提供了处理数据的效率和可重复性。

在深入本节之前,我们将基于从第二章**建立 Snowpark 基础第三章**使用 Snowpark 简化数据处理所获得的知识,这两章中我们详细阐述了 DataFrame 操作及其转换为用户定义函数UDFs)和存储过程的转换,我们将揭示将这些元素统一到一个有弹性的数据工程管道中的复杂过程。本章是对理论与实践结合的证明,赋予数据专业人士无缝连接加载、转换和捆绑阶段的能力,从而在 Snowpark 生态系统中构建一个强大的数据处理和分析框架。

在前几章中我们对数据加载有了全面的理解之后,我们的旅程现在转向战略性地利用这些数据。这一关键过渡将我们的重点放在三个核心步骤上:

  1. 数据准备

  2. 数据转换

  3. 数据清理

这些阶段构成了我们数据工程之旅的基石,我们将塑造、整合和精炼我们的数据,揭示其分析潜力和宝贵见解。现在,我们将将这些概念转化为实际的数据工程管道,利用我们之前关于存储过程模板和转换步骤的讨论中的宝贵见解。我们的重点将集中在我们的营销活动数据上,其中在第三章**使用 Snowpark 简化数据处理中已经仔细概述了基础加载步骤,为我们的数据准备提供了一个坚实的起点。

数据准备

在我们的数据工程管道进展过程中,下一关键阶段是数据准备,涉及不同表的集成。在本节中,我们将探讨使用针对任务定制的各种函数合并这些不同的数据表的技术。此外,我们将阐明将这些函数注册为存储过程的过程,以确保数据工作流程的流畅和高效。第一步是将购买历史与活动信息连接起来。两个表都使用ID列输入,并保留单个 ID:

def combine_campaign_table(purchase_history,campaign_info):
    purchase_campaign = purchase_history.join(
        campaign_info, \
        purchase_history.ID == campaign_info.ID, \
        lsuffix="_left", rsuffix="_right"
    )
    purchase_campaign = purchase_campaign.drop("ID_RIGHT")
    return purchase_campaign

结果的purchase_campaign DataFrame 包含数据,并在下一步中使用。在下一步中,我们将使用相同的ID列将购买活动与投诉信息连接起来,然后创建一个purchase_campaign_complain DataFrame:

def combine_complain_table(purchase_campaign,complain_info):
    purchase_campaign_complain = purchase_campaign.join(
        complain_info, \
        purchase_campaign["ID_LEFT"] == complain_info.ID
    )
    purchase_campaign_complain = \
        purchase_campaign_complain.drop("ID_LEFT")
    return purchase_campaign_complain

上述代码将列连接起来创建一个purchase_campaign_complain DataFrame,其中包含映射的购买数据和投诉信息。在最终步骤中,通过购买投诉和营销表之间的数据合并创建一个营销表:

def union_marketing_additional_table(
    purchase_campaign_complain,marketing_additional):
    final_marketing_table = \
        purchase_campaign_complain.union_by_name(
            marketing_additional
        )
    return final_marketing_table

上述代码生成一个包含所有合并数据的表,这是管道的最终结果,将被写入为表。代表每个步骤的 Python 函数作为 Snowpark 存储过程的一部分执行。存储过程可以依次执行,也可以作为 Snowflake 任务进行调度。数据准备过程调用三个 Python 方法,并将最终表写入 Snowflake:

from snowflake.snowpark.functions import sproc
import snowflake
def data_prep(session: Session):
    #### Loading Required Tables
    purchase_history = session.table("PURCHASE_HISTORY")
    campaign_info = session.table("CAMPAIGN_INFO")
    complain_info = session.table("COMPLAINT_INFO")
    marketing_additional = session.table("MARKETING_ADDITIONAL")

上述代码通过将所需数据加载到 DataFrame 中来进行数据准备。我们现在将调用每个步骤来执行它,就像一个管道:

    #### Calling Step 1
    purchase_campaign = combine_campaign_table(
        purchase_history, campaign_info)
    #### Calling Step 2
    purchase_campaign_complain = combine_campaign_table(
        purchase_campaign, complain_info)
    #### Calling Step 3
    final_marketing_data = union_marketing_additional_table(
        purchase_campaign_complain, marketing_additional)

执行了之前定义的三个步骤函数。结果数据被加载到新的final_marketing_data DataFrame 中,然后将被加载到 Snowflake 表中:

    #### Writing Combined Data To New Table
    final_marketing_data.write.save_as_table( \
        "FINAL_MARKETING_DATA")
    return "LOADED FINAL MARKETING DATA TABLE"

现在,我们将创建并执行一个包含上述逻辑的存储过程。该过程称为data_prep_sproc,是数据工程管道的第一部分——数据准备:

# Create an instance of StoredProcedure using the sproc() function
from snowflake.snowpark.types import IntegerType,StringType
data_prep_sproc = sproc(
                        func= data_prep,\
                        replace=True,\
                        return_type = StringType(),\
                        stage_location="@my_stage",\
                        packages=["snowflake-snowpark-python"]
                        )

上述存储过程将数据写入Final_Marketing_Data表,该表将在数据转换的下一步中使用。

数据转换

此过程中的下一阶段涉及数据转换,基于之前步骤准备的数据。在这里,我们将在最后阶段之后注册另一个存储过程。此过程应用转换逻辑,将数据塑造成适合分析的形式。利用 Snowpark 的一系列有价值的聚合和汇总函数,我们将利用这些功能来塑造和增强我们的数据,为严格的分析奠定坚实的基础。以下代码转换数据:

def data_transform(session: Session):
    #### Loading Required Tables
    marketing_final = session.table("FINAL_MARKETING_DATA")
    market_subset = marketing_final.select("EDUCATION", \
        "MARITAL_STATUS","INCOME")
    market_pivot = market_subset.pivot("EDUCATION", \
        ["Graduation","PhD","Master","Basic","2n Cycle"]
    ).sum("INCOME")
    #### Writing Transformed Data To New Table
    market_pivot.write.save_as_table("MARKETING_PIVOT")
    return "CREATED MARKETING PIVOT TABLE"
data_transform_sproc = sproc(
                        func= data_transform,\
                        replace=True,\
                        return_type = StringType(),\
                        stage_location="@my_stage",\
                        packages=["snowflake-snowpark-python"]
                        )

创建了一个 data_transform_sproc 存储过程,该过程读取 Final_Marketing_Data 表并创建一个包含客户教育和总收入的数据透视表。当存储过程执行时,这些数据随后被写入到 Marketing_Pivot 表中。

数据清理

在我们数据工程流程的最后一步,我们专注于一个关键任务:清理 Marketing_Pivot 表中的数据。类似于艺术家完善杰作一样,我们仔细检查我们的数据,移除对分析不重要的表格中的任何空值。为此,我们依赖于多功能的 dropna() 函数,它就像一把精确的工具,可以切除不必要的数据:

def data_cleanup(session: Session):
    #### Loading Required Tables
    market_pivot = session.table("MARKETING_PIVOT")
    market_drop_null = market_pivot.dropna(thresh=5)
    #### Writing Cleaned Data To New Table
    market_drop_null.write.save_as_table("MARKET_PIVOT_CLEANED")
    return "CREATED CLEANED TABLE"
data_cleanup_sproc = sproc(
                        func= data_cleanup,\
                        replace=True,\
                        return_type = StringType(),\
                        stage_location="@my_stage",\
                        packages=["snowflake-snowpark-python"]
                        )

market_drop_null DataFrame 清理后的数据随后被保存到 Market_Pivot_Cleaned 表中。这些数据是管道的最后阶段,用于分析。

编排管道

通过调用三个 Snowpark 过程来编排数据管道,这些过程调用了数据工程管道的三个不同步骤。这些过程按照 data_prep_sprocdata_transform_sprocdata_cleanup_sproc 的顺序执行:

#### Calling Data Preparation Stored Procedure
data_prep_sproc()
#### Calling Data Transformation Stored Procedure
data_transform_sproc()
#### Calling Data Cleanup Stored Procedure
data_cleanup_sproc()

Snowpark 过程被执行,并且每执行一步后,最终数据都会写入到 Market_Pivot_Cleaned 表中。Snowflake 通过任务支持调度和编排。任务可以通过 Python API 和工作表进行调度,并且可以按顺序触发过程:

图 4.7 – 存储过程执行

图 4.7 – 存储过程执行

在下一节中,我们将探讨如何利用 Snowflake 任务和任务图来执行前面的管道。

Snowflake 中任务概述

Snowflake 中的任务是有力的工具,旨在简化数据处理工作流程并在 Snowflake 环境中自动化各种任务。提供一系列功能,任务执行不同类型的 SQL 代码,使用户能够对其数据进行各种操作。

Snowflake 中的任务可以执行三种主要的 SQL 代码类型:

  • 单个 SQL 语句:允许执行单个 SQL 语句

  • 调用存储过程:允许调用存储过程

  • 使用 Snowflake 脚本实现的程序逻辑:支持使用 Snowflake 脚本实现程序逻辑

任务可以与表流集成,以创建连续的 ELT 工作流。通过处理最近更改的表行,任务确保数据完整性的维护,并为新数据或更改的数据提供一次且仅一次的语义。Snowflake 中的任务可以安排在指定的时间间隔运行。Snowflake 确保同一时间只执行一个计划任务的实例,如果任务仍在运行,则跳过计划执行。

为任务计算模型

在无服务器计算模型中,任务依赖于 Snowflake 管理的计算资源。这些资源会根据工作负载需求自动调整大小和扩展,确保最佳性能和资源利用率。Snowflake 会根据历史统计数据动态确定每个任务运行所需的适当计算大小。

或者,用户可以选择用户管理的虚拟仓库模型,为单个任务指定一个现有的虚拟仓库。此模型使用户能够更好地控制计算资源管理,但需要仔细规划以确保任务执行效率。

任务图

任务图,也称为有向无环图DAGs),允许根据依赖关系组织任务。任务图中的每个任务都有前驱任务和后续任务,便于复杂工作流程管理。

任务图受到某些限制,包括总任务数最多为 1,000 个,包括根任务。任务图中的单个任务可以有最多 100 个前驱任务和 100 个子任务。

用户可以使用 SQL 或 Snowsight(Snowflake 的集成开发环境)查看和监控他们的任务图,从而了解任务依赖关系和执行状态。

总结来说,Snowflake 中的任务提供了强大的数据处理、自动化和工作流程管理能力,对于寻求在 Snowflake 生态系统中优化数据操作的用户来说,它们是不可或缺的工具。

使用 Python 管理任务和任务图

我们的主要关注点是 Snowpark。现在,我们将探讨如何使用 Python Snowpark 以编程方式执行任务图操作,而不是使用 SQL 语句。

现在,Python 可以管理 Snowflake 任务,使用户能够运行 SQL 语句、过程调用和 Snowflake 脚本逻辑。Snowflake Python API 引入了两种类型:

  • Task:此类型表示任务的属性,如其调度、参数和依赖关系

  • TaskResource:此类型提供与Task对象交互的方法,使任务执行和修改成为可能。

任务可以被分组到任务图中,这些图基于任务依赖关系相互连接。要创建任务图,用户首先定义一个 DAG 对象,指定其名称和可选属性,如其调度。任务图的调度可以使用timedelta值或 cron 表达式进行自定义,从而实现灵活的任务执行时间和重复模式。

让我们先设置必要的函数来实现我们的 DAG。本节中提供的示例假设您已经编写了代码来与 Snowflake 建立连接,以利用 Snowflake Python API:

from snowflake.core import Root
from snowflake.core.task import StoredProcedureCall
from snowflake.core.task.dagv1 import DAG, DAGTask, DAGOperation
from snowflake.snowpark import Session
from datetime import timedelta
root = Root(session)

上述代码初始化 Snowflake Python API,创建一个 root 对象以利用其类型和方法。此外,它为任务计划设置了一个 1 小时的 timedelta 值。你可以使用 timedelta 值或 Cron 表达式来定义计划。对于一次性运行,你可以省略 DAG 对象的计划参数,无需担心它在不必要的情况下在后台运行。

让我们定义一个简单的 DAG,我们将使用它来执行我们的管道:

dag = DAG("Task_Demo",
          warehouse="COMPUTE_WH",
          schedule=timedelta(days=1),
          stage_location= \
              "SNOWPARK_DEFINITIVE_GUIDE.MY_SCHEMA.MY_STAGE",
          packages=["snowflake-snowpark-python"]
          )

在此 DAG 设置中,以下适用:

  • 我们将我们的 DAG 命名为 Task_Demo,它默认在指定的仓库上运行。

  • 使用 timedelta 定义了一个每日执行的调度计划。

  • 存储任务的序列化版本需要 stage_location 属性通过 Python API 进行。

  • 该 DAG 下的所有任务都将使用默认的包列表和指定的仓库运行。然而,DAG 内部各个任务的仓库和包可以使用不同的值进行覆盖。

  • 此外,use_func_return_value 属性表示 Python 函数的返回值将被视为任务的返回值,尽管在我们的案例中,我们没有使用 return 对象。

我们已经定义了一系列代表三个任务管道或 DAG 的 Python 函数。然而,我们尚未创建并将 DAG 推送到 Snowflake。现在让我们使用 Snowflake Python API 来完成这项工作:

with dag:
    data_prep_task = DAGTask("Data_Prep", definition=data_prep)
    data_transform_task = DAGTask("Data_Transform", \
        definition=data_transform)
    data_cleanup_task = DAGTask("Data_Cleanup", \
        definition=data_cleanup)
    data_prep_task >> data_transform_task >> data_cleanup_task
DAGTask objects for each task in our pipeline: data_prep, data_transform, and data_cleanup. These tasks are then linked together using the >> operator to specify their execution order.

对于一次性测试或运行 DAG,用户可以省略指定计划,并使用 dag_op.run(dag) 手动触发运行:

schema = root.databases["SNOWPARK_DEFINITIVE_GUIDE"].schemas[ \
    "MY_SCHEMA"]
dag_op = DAGOperation(schema)
dag_op.deploy(dag,mode="orReplace")
dag_op.run(dag)

提供的代码使用 Snowflake 的 Snowpark 库执行多个操作。首先,它使用 root 对象从 SNOWPARK_DEFINITIVE_GUIDE 数据库检索名为 MY_SCHEMA 的模式。然后,它初始化一个名为 dag_opDAGOperation 对象,指定 DAG 将部署到的模式。然后,在 dag_op 上调用 deploy() 方法来部署指定模式中指定的 DAG(名为 dag)。

orReplace 模式参数表示如果模式中已存在具有相同名称的 DAG,它将被替换。最后,在 dag_op 上调用 run() 方法来执行已部署的 DAG。此代码实际上是在 Snowflake 中使用 Snowpark 设置和执行 DAG。现在,你可以在 Snowsight 中检查图以查看图是如何设置的:

图 4.8 – Snowsight 图

图 4.8 – Snowsight 图

此外,请注意,你可以将原始函数传递给依赖定义,而无需显式创建 DAGTask 实例,库会自动为你创建一个具有相同名称的任务。然而,有一些例外,例如需要显式创建 DAGTask 实例的第一个任务或在使用 DAGTaskBranch 或在多个任务中重复某些函数时。

一旦部署并运行了 DAG,你可以使用以下代码轻松检查其状态:

current_runs = dag_op.get_current_dag_runs(dag)
for r in current_runs:
    print(f"RunId={r.run_id} State={r.state}")

如截图所示,我们的任务被安排为每天运行。此外,我们已经执行了一次以验证任务部署:

图 4.9 – 部署的任务

图 4.9 – 部署的任务

随着我们构建更复杂的管道,管理和调试变得越来越具有挑战性。建立强大的日志机制对于维护和简化错误解决至关重要。在下一节中,我们将深入了解在 Snowpark 中实现日志和回溯功能,以增强我们管道的可管理性和简化故障排除。

在 Snowpark 中实现日志和跟踪

日志和跟踪对于 DataOps 至关重要,并且对于监控和修复数据工程管道中的故障是必要的。Snowpark 内置了日志和跟踪功能,可以帮助记录 Snowpark 函数和过程的活动,并在 Snowflake 内部的易于访问的中心表中捕获这些信息。日志消息是独立、详细的字符串形式的信息消息,提供了关于代码片段的详细信息,而跟踪事件是有结构的我们可以用来获取跨越和分组我们代码多个部分的信息。一旦收集了日志,就可以通过 SQL 或通过 Snowpark 容易地查询或访问。以下图表突出了事件表和警报:

图 4.10 – 事件表

图 4.10 – 事件表

Snowpark 在事件表中存储日志和跟踪消息,这是一个具有预定义列集的唯一表。日志和跟踪在代码执行时捕获在这个表中。让我们看看事件表的结构以及如何创建它们。

事件表

事件表是 Snowflake 的本地表,需要创建。一个 Snowflake 账户只能有一个事件表来捕获所有信息,但可以创建多个视图进行分析。事件表包含以下列:

数据类型 描述
TIMESTAMP TIMESTAMP_NTZ 事件创建时的 UTC 时间戳。这是表示一段时间的事件的结束。
START_TIMESTAMP TIMESTAMP_NTZ 对于表示一段时间的事件,例如作为该时间段开始的时间戳的跟踪事件。
OBSERVED_TIMESTAMP TIMESTAMP_NTZ 用于日志的 UTC 时间戳。目前,它具有与 TIMESTAMP 相同的值。
TRACE OBJECT 所有信号类型的跟踪上下文。包含 trace_idspan_id 字符串值。
RESOURCE OBJECT 保留供将来使用。
RESOURCE_ATTRIBUTES OBJECT 识别事件来源的属性,例如数据库、模式、用户、仓库等。
SCOPE OBJECT 事件的作用域;例如,日志的类名。
SCOPE_ATTRIBUTES OBJECT 保留供将来使用。
RECORD_TYPE STRING 事件类型。以下之一:LOG 表示日志消息。SPAN 表示在同一线程上顺序执行的 UDF 调用。SPAN_EVENT 表示单个跟踪事件。单个查询可以发出多个 SPAN_EVENT 事件类型。
RECORD OBJECT 每种记录类型的固定值。
RECORD_ATTRIBUTES OBJECT 每种记录类型的可变属性。
VALUE VARIANT 主要事件值。
EXEMPLARS ARRAY 保留供将来使用。

表 4.1 – 事件表列

每个列都可以被查询或组合,根据日志和跟踪分析不同的结果。日志类型描述了作为日志一部分分配的日志级别,可以在对象和会话中设置。日志级别可以是以下之一:

LOG_LEVEL 参数设置 消息摄入级别
TRACE TRACE DEBUG INFO WARN ERROR FATAL
DEBUG DEBUG INFO WARN ERROR FATAL
INFO INFO WARN ERROR FATAL
WARN WARN ERROR FATAL
ERROR ERROR FATAL
FATAL ERROR FATAL

表 4.2 – 事件表日志级别

日志级别是按照 表 4.2 中呈现的顺序应用的层次结构。在下一节中,我们将探讨创建事件表。

备注

最佳实践是根据次要级别(如 FATAL 和 ERROR)设置必要的日志级别,以便日志消息的数量更少。在记录 INFO 的情况下,通常在生产中开启以捕获日志,关闭以避免捕获过多的记录。

创建和配置事件表

第一步是为 Snowflake 账户创建一个事件表。事件表的名字可以指定,创建表时不需要设置事件表的列,因为 Snowflake 会自动使用标准列创建它。事件表分配给账户,并且需要在一个没有启用 Snowflake 复制的单独数据库中使用 ACCOUNTADMIN 角色创建。

要创建事件表,请使用 Snowpark 以 ACCOUNTADMIN 角色运行以下命令:

session.sql('''CREATE EVENT TABLE MY_EVENTS;''').show()

创建了一个名为 MY_EVENTS 的事件表,具有默认的列结构。下一步是将事件表分配为特定 Snowflake 账户的活动事件表。可以通过使用 ACCOUNTADMIN 角色执行以下代码将事件表分配给账户:

session.sql('''ALTER ACCOUNT SET EVENT_TABLE = \
    SNOWPARK_DEFINITIVE_GUIDE.MY_SCHEMA.MY_EVENTS;
''').show()

参数在账户级别应用,特定 Snowflake 账户的所有事件都被捕获在这个事件表中。这完成了事件表的设置。

查询事件表

可以像访问任何其他 Snowflake 表一样访问事件表。要从事件表中获取记录,可以使用以下代码查询事件表:

session.sql('''SELECT *
    FROM SNOWPARK_DEFINITIVE_GUIDE.MY_SCHEMA.MY_EVENTS;
''').show()

它返回一个空记录的结果,因为没有捕获任何信息。可以通过特定列过滤事件表中的记录以获取详细信息。可以在事件表之上设置 Snowflake 流以仅捕获新事件。可以通过运行以下查询来创建流:

session.sql('''CREATE STREAM EVENT_APPEND ON EVENT TABLE MY_EVENTS APPEND_ONLY=TRUE;''').show()

EVENT_APPEND 流捕获事件表中最新插入的记录。在下一节中,我们将设置日志记录和跟踪以捕获事件表中的记录。

在 Snowpark 中设置日志记录

将日志记录和跟踪功能引入我们的管道类似于将弹性注入到我们的标准数据工程流程中。本节将深入探讨将日志功能集成到我们现有的数据工程管道中。通过这样做,我们不仅获得了监控和跟踪数据流的能力,还增强了代码的健壮性,使其能够抵御潜在的陷阱。随着我们探索这些日志功能如何提升我们的数据工程实践,使它们更加可靠和容错,请加入我们。

可以为 Snowpark 函数和过程启用日志记录。捕获日志的第一步是在 Snowpark 会话中设置日志级别。可以通过运行以下代码来设置会话中的日志级别:

session.sql('''alter session set log_level = INFO;''').show()

这将特定会话的日志级别设置为 INFO,因此所有针对特定会话发生的 Snowpark 执行都将捕获为信息级别的日志。Snowpark 支持从处理程序直接记录消息的 API。

捕获信息日志

现在,我们将修改数据管道中的数据准备程序以捕获信息日志。我们从准备数据开始:

from snowflake.snowpark.functions import sproc
import logging
def data_prep(session: Session):
    ## Initializing Logger
    logger = logging.getLogger("My_Logger")
    logger.info("Data Preparation Pipeline Starts")
    #### Loading Required Tables
    logger.info("Loading Required Tables")
    purchase_history = session.table("PURCHASE_HISTORY")
    campaign_info = session.table("CAMPAIGN_INFO")
    complain_info = session.table("COMPLAINT_INFO")
    marketing_additional = session.table("MARKETING_ADDITIONAL")

来自四个表的数据被加载到 DataFrame 中。然后使用此 DataFrame 执行每个步骤。接下来,我们将按顺序调用每个步骤以处理数据:

    #### Calling Step 1
    purchase_campaign = combine_campaign_table(
        purchase_history,campaign_info)
    logger.info("Joined Purchase and Campaign Tables")
    #### Calling Step 2
    purchase_campaign_complain = combine_complain_table(
        purchase_campaign,complain_info)
    logger.info("Joined Complain Table")
    #### Calling Step 3
    final_marketing_data = union_marketing_additional_table(
        purchase_campaign_complain,marketing_additional)
    logger.info("Final Marketing Data Created")

执行所有三个步骤后,我们将获得最终的市场数据,准备将其加载到 Snowflake 表中进行消费。以下代码将数据加载到 Snowflake 表中:

    #### Writing Combined Data To New Table
    final_marketing_data.write.save_as_table( \
        "FINAL_MARKETING_DATA")
    logger.info("Final Marketing Data Table Created")
    return "LOADED FINAL MARKETING DATA TABLE"

数据被加载到一个名为 FINAL_MARKETING_DATA 的表中。该表由 Snowpark DataFrame 中的数据自动创建。现在我们将将其注册为 Snowpark 存储过程:

## Register Stored Procedure in Snowflake
### Add packages and data types
from snowflake.snowpark.types import StringType
session.add_packages('snowflake-snowpark-python')
### Upload Stored Procedure to Snowflake
session.sproc.register(
    func = data_prep
  , return_type = StringType()
  , input_types = []
  , is_permanent = True
  , name = 'DATA_PREP_SPROC_LOG'
  , replace = True
  , stage_location = '@MY_STAGE'
)

Python 标准库中的 logging 模块用于日志记录。该包被导入,并且记录器的名称被指定为 My_Logger。可以为不同的进程设置不同的记录器名称,以帮助识别特定的日志应用。我们可以执行存储过程以将日志捕获到事件表中。可以通过运行以下命令来执行存储过程:

session.sql(''' Call DATA_PREP_SPROC_LOG()''').show()

存储过程被执行,我们可以看到执行的输出结果:

图 4.11 – 存储过程执行

图 4.11 – 存储过程执行

以下部分将介绍如何查询存储过程生成的日志。

查询信息日志

由存储过程生成的日志可以从事件表中访问。通过运行以下查询可以访问从先前存储过程捕获的记录:

session.sql("""
    SELECT RECORD['severity_text'] AS SEVERITY,
        VALUE AS MESSAGE
    FROM MY_EVENTS
    WHERE SCOPE['name'] = 'My_Logger'
    AND RECORD_TYPE = 'LOG'
""").show()

我们通过在作用域中指定它来仅过滤从My_Logger捕获的日志。查询返回以下记录,这些记录是从执行存储过程生成的:

图 4.12 – 查询日志

图 4.12 – 查询日志

在下一节中,我们将设置日志以捕获错误消息并处理 Snowpark 中的异常。

在 Snowpark 中处理异常

异常处理对于数据管道至关重要,因为它有助于识别和处理问题。可以通过捕获try块内部抛出的异常并捕获这些错误日志来完成异常处理。在捕获异常时,通常使用ERRORWARN日志级别,而致命问题则记录在FATAL级别。本节将探讨捕获错误日志和在管道上处理异常。

捕获错误日志

我们将修改数据转换存储过程以捕获错误日志并添加异常处理:

def data_transform(session: Session):
    try:
        ## Initializing Logger
        logger = logging.getLogger("Data_Transform_Logger")
        logger.info("Data Transformation Pipeline Starts")
        ## Pivoting Process
        marketing_final = session.table("FINAL_MARKETING_DATA")
        market_subset = marketing_final.select("EDUCATION", \
            "MARITAL_STATUS","INCOME")
        market_pivot = market_subset.pivot("EDUCATION", \
            ["Graduation","PhD","Master","Basic","2n Cycle"]
        ).sum("INCOME")
        #### Writing Transformed Data To New Table
        market_pivot.write.save_as_table("MAREKTING_PIVOT")
        logger.log("MARKETING PIVOT TABLE CREATED")
        return "CREATED MARKETING PIVOT TABLE"
    except Exception as err:
        logger.error("Logging an error from Python handler: ")
        logger.error(err)
        return "ERROR"

数据转换逻辑被移动到try块中,并启动了一个名为Data_Transform_Logger的记录器。代码引发的异常被捕获在定义为err的异常对象中。然后通过ERROR日志级别将这些记录在事件表中。现在我们将在此存储过程中注册 Snowpark:

## Register Stored Procedure in Snowflake
### Add packages and data types
from snowflake.snowpark.types import StringType
session.add_packages('snowflake-snowpark-python')
### Upload Stored Procedure to Snowflake
session.sproc.register(
    func = data_transform
  , return_type = StringType()
  , input_types = []
  , is_permanent = True
  , name = 'DATA_TRANSFORM_SPROC_LOG_ERROR'
  , replace = True   , stage_locations = "@MY_STAGE" )

我们将故意执行存储过程以记录错误。可以通过运行以下命令来触发存储过程:

session.sql(''' Call DATA_TRANSFORM_SPROC_LOG_ERROR()''').show()

存储过程已引发异常,错误已记录在事件表中:

图 4.13 – 错误执行

图 4.13 – 错误执行

以下部分将涵盖查询由存储过程生成的错误日志。

查询错误日志

前一个存储过程生成的错误日志记录在Data_Transform_Logger记录器下,可以通过过滤查询以返回特定于记录器的日志来访问。以下查询可以执行以从事件表中获取记录:

session.sql("""
    SELECT RECORD['severity_text'] AS SEVERITY,VALUE AS MESSAGE
    FROM MY_EVENTS
    WHERE SCOPE['name'] = 'Data_Transform_Logger'
    AND RECORD_TYPE = 'LOG'
""").collect()

作用域名称被过滤为Data_Transform_Logger以获取结果,由异常引起的错误在SEVERITY下记录为ERROR

图 4.14 – 错误日志消息

图 4.14 – 错误日志消息

Snowpark 通过支持通过事件表记录错误和异常处理来简化调试。以下部分将涵盖事件跟踪以及如何在 Snowpark 中捕获跟踪信息。

在 Snowpark 中设置跟踪

跟踪事件是在代码中发生某些事情时捕获的一种遥测数据类型。它有一个结构化有效负载,有助于通过聚合这些信息来分析跟踪,从而理解代码在高级别上的行为。当程序或函数执行时,会发出跟踪事件,这些事件在活动事件表中可用。

捕获事件的第一步是通过在 Snowpark 会话中设置跟踪级别来启用跟踪功能。会话中的跟踪级别可以通过运行以下代码来设置:

session.sql("ALTER SESSION SET TRACE_LEVEL = ALWAYS;").show()

在下一节中,我们将探讨如何捕获跟踪信息。

捕获跟踪信息

可以使用开源 Snowflake telemetry Python 包捕获跟踪,该包可在 Anaconda Snowflake 频道中找到。需要将包导入到代码中,并在 Snowpark 中执行。可以通过在 Snowpark 处理器中包含以下代码来导入 telemetry 包:

from snowflake import telemetry

telemetry 包帮助捕获在代码上生成并记录到事件表中的跟踪信息。我们将通过添加遥测事件来修改数据清理程序以捕获跟踪:

def data_cleanup(session: Session):
    #### Loading Telemetry Package
    from snowflake import telemetry
    #### Loading Required Tables
    market_pivot = session.table("MARKETING_PIVOT")
    #### Adding Trace Event
    telemetry.add_event("data_cleanup", \
        {"table_name": "MARKETING_PIVOT", \
         "count": market_pivot.count()})
    #### Dropping Null
    market_drop_null = market_pivot.dropna(thresh=5)
    #### Writing Cleaned Data To New Table
    market_drop_null.write.save_as_table("MARKET_PIVOT_CLEANED")
    #### Adding Trace Event
    telemetry.add_event("data_cleanup", \
        {"table_name": "MARKET_PIVOT_CLEANED", \
         "count": market_drop_null.count()})
    return "CREATED CLEANED TABLE"
###########################################################
## Register Stored Procedure in Snowflake
### Add packages and data types
from snowflake.snowpark.types import StringType
session.add_packages('snowflake-snowpark-python', \
    'snowflake-telemetry-python')
### Upload Stored Procedure to Snowflake
session.sproc.register(
    func = data_cleanup
  , return_type = StringType()
  , input_types = []
  , is_permanent = True
  , name = 'DATA_CLEANUP_SPROC_TRACE'
  , replace = True
  , stage_location = '@MY_STAGE'
)

程序现在已准备好执行。我们正在传递捕获在跟踪上的属性。可以通过运行以下代码来执行程序:

session.sql(''' Call DATA_CLEANUP_SPROC_TRACE()''').show()

程序执行,数据从表中清理:

图 4.15 – 数据清理程序执行

图 4.15 – 数据清理程序执行

跟踪信息现在在事件表中生成。我们可以直接查询事件表以获取跟踪信息。

查询跟踪

生成的跟踪可以从事件表中访问。可以通过运行以下查询访问从先前存储过程捕获的跟踪:

session.sql("""
    SELECT
        TIMESTAMP as time,
        RESOURCE_ATTRIBUTES['snow.executable.name']
            as handler_name,
        RESOURCE_ATTRIBUTES['snow.executable.type']
            as handler_type,
        RECORD['name'] as event_name,
        RECORD_ATTRIBUTES as attributes
    FROM
        MY_EVENTS
    WHERE
        EVENT_NAME ='data_cleanup'
""").show(2)

我们使用 data_cleanup 事件名称进行查询。这返回了代码执行时捕获的两个跟踪:

图 4.16 – 跟踪捕获信息

图 4.16 – 跟踪捕获信息

我们可以看到从存储过程的执行中捕获的属性。在清理了 NULL 值之后,详细信息返回了总数据计数,包括 NULLcount 值。

日志和跟踪的比较

以下表格比较了日志和跟踪,并列出了使用每种情况的场景:

特性 日志条目 跟踪事件
预期用途 记录关于代码状态的详细但非结构化信息。使用这些信息来了解函数或过程特定调用期间发生了什么。 记录每次代码调用的简短但结构化摘要。聚合这些信息以了解代码在高级别上的行为。
结构作为有效载荷 无。日志条目只是一个字符串。 结构化,可以附加到跟踪事件上的属性。属性是键值对,可以很容易地通过 SQL 查询进行查询。
支持分组 否。每个日志条目都是一个独立的事件。 是。跟踪事件组织成跨度。一个跨度可以有自己的属性。
数量限制 无限制。你代码发出的所有日志条目都会被摄入到事件表中。 每个跨度中的跟踪事件数量限制为 128。跨度属性的数量也有限制。
对记录数据的查询复杂度 相对较高。您的查询必须解析每个日志条目以从中提取有意义的信息。 相对较低。您的查询可以利用跟踪事件的有序性质。

表 4.3 – 日志和跟踪之间的差异

日志和跟踪有助于调试 Snowpark,并且是高效 DataOps 的实用功能。

摘要

在本章中,我们详细介绍了在 Snowpark 中构建和部署具有弹性的数据管道,以及如何使用事件表启用日志记录和跟踪。通过程序化 ELT 方法以及日志记录和跟踪等特性,Snowpark 支持现代数据管道的开发,这使得开发者能够轻松实现 DataOps。我们还介绍了如何使用任务和任务图进行调度和部署管道。

在下一章中,我们将介绍如何使用 Snowpark 开发数据科学工作负载。

第五章:使用 Snowpark 开发数据科学项目

云技术的出现引领了一个新的可能性时代。随着数据云的出现,一个统一数据存储、处理和分析的强大平台,数据科学家有许多机会探索、分析和从大量数据集中提取有意义的见解。在这个错综复杂的数字领域,Snowpark 这一前沿数据处理框架的作用变得至关重要。本章作为一本指导性的指南,深入探讨了使用 Snowpark 开发数据科学项目,揭示了其复杂性,并最大限度地利用了其潜力。

在本章中,我们将涵盖以下主要主题:

  • 数据科学在数据云中

  • 探索和准备数据

  • 在 Snowpark 中训练机器学习ML)模型

技术要求

为了本章,你需要一个活跃的 Snowflake 账户,并在本地安装了 Anaconda 的 Python。你可以在signup.snowflake.com/注册 Snowflake 试用账户。

要配置 Anaconda,请遵循conda.io/projects/conda/en/latest/user-guide/getting-started.html

此外,要安装和设置 VS Code 中的 Python,请遵循code.visualstudio.com/docs/python/python-tutorial

要了解如何在 VS Code 中操作 Jupyter Notebook,请访问code.visualstudio.com/docs/datascience/jupyter-notebooks

本章的支持材料可在本书的 GitHub 存储库中找到,网址为github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark

数据科学在数据云中

数据科学在数据云生态系统中超越了传统边界,提供了一个数据科学家可以利用分布式计算和高级分析能力的动态环境。通过能够无缝集成各种数据源,包括结构化和非结构化数据,数据云提供了一个数据探索和实验环境。我们将从这个部分开始,简要回顾数据科学和机器学习。

数据科学和机器学习概念

数据科学和机器学习已经迅速成为技术创新和商业创新的先锋,成为几乎所有行业中决策、战略规划和产品开发的核心组成部分。它们目前受欢迎和影响力的旅程是对多个因素的证明,包括技术的进步、数据的爆炸性增长以及可用计算能力的增加。本节将简要讨论数据科学和机器学习概念。

数据科学

数据科学是一个多学科领域,它依赖于各种软件工具、算法和机器学习(ML)原则,从大量数据集中提取有价值的见解。数据科学家在收集、转换和将数据转化为预测性和规范性见解方面发挥着关键作用。通过采用复杂技术,数据科学揭示了数据中的隐藏模式和有意义的关联,使企业能够基于经验证据做出明智的决策。

人工智能

人工智能AI)包括创建能够自主处理信息和生成结果的智能机器和卓越计算机程序的科学和工程。人工智能系统旨在使用逻辑和推理解决复杂问题,类似于人类的认知过程。这些系统自主运行,旨在在决策和问题解决任务中模仿人类智能。

机器学习

机器学习(ML),作为人工智能(AI)的一个子集,涉及将专用算法集成到数据科学工作流程中。这些算法是精心设计的软件程序,旨在检测数据中的模式、识别相关性以及定位异常。机器学习算法擅长根据现有数据进行预测,并在遇到新数据和情境时不断学习和提高其准确性。与人类不同,机器学习算法可以处理数千个属性和特征,从而在庞大的数据集中发现独特的组合和相关性。这种能力使得机器学习在从大量数据集中提取有价值的见解和预测方面变得不可或缺。

现在我们已经明确了术语,我们将讨论数据云范式如何帮助组织的数据科学和机器学习(ML)的发展。

数据云范式

云端数据科学代表了数据驱动见解的获取和应用方式的范式转变。在这种创新方法中,数据科学流程、工具和技术无缝集成到云端,使组织能够利用可扩展基础设施和高级分析的力量。

这一范式的核心是数据云,这是一个超越传统数据存储和处理限制的动态生态系统。数据云范式代表了从传统数据孤岛到统一平台的地震式转变,其中结构化和非结构化数据无缝融合。通过分布式计算、并行处理和强大的数据管理,数据云为数据科学革命奠定了基础。赋予数据科学家能力的功能和工具无缝集成,并设计用于处理数据云内各种数据类型和分析工作负载。因此,数据云为运行数据科学和机器学习工作负载提供了各种优势。

数据云对数据科学的优势

Snowflake 的 Data Cloud 的一个关键优势是能够在没有硬件限制的约束下存储和处理大量数据。它提供了一个可扩展的解决方案来处理大量数据,使数据科学家能够在不担心计算或存储限制的情况下处理大量数据集。基于云的界面为数据科学家和分析师提供了一个协作和灵活的环境,并内置了协作功能、版本控制和通过 Snowpark 支持流行的数据科学库和框架。

此外,Data Cloud 通过管理服务提供了一系列针对数据科学任务定制的服务和资源,简化了这些流程,从数据摄取和准备到机器学习模型训练和部署。例如,可以使用无服务器计算自动化数据管道,机器学习模型可以在强大的 GPU 实例上训练,从而实现更快的实验和迭代。数据安全和合规性在数据科学中至关重要,尤其是在处理敏感信息时,Data Cloud 提供了不同的安全措施,包括加密、访问控制和行级策略,确保数据科学家可以以安全和合规的方式处理敏感数据,遵守行业法规和组织政策。Snowpark 框架是 Snowflake Data Cloud 的核心,以支持这些功能。下一节将讨论为什么使用 Snowpark 进行数据科学和机器学习。

为什么选择 Snowpark 进行数据科学和机器学习?

Snowpark 为数据工程师提供了无与伦比的集成能力,使他们能够无缝地与存储在大量和不同格式中的数据交互。其多功能的 API 简化了数据探索、转换和处理,为数据科学模型和机器学习开发奠定了坚实的基础,并赋予数据科学家充分利用其分析工作流程的潜力。数据科学团队现在可以专注于他们的核心任务,而无需烦恼于基础设施或环境维护。

Snowpark 在可扩展性和性能方面表现出色,这对于企业数据科学和机器学习工作负载至关重要;利用 Snowflake 的分布式架构以非凡的效率处理大量数据集和复杂计算,以及并行化处理任务和将工作负载分配到多个节点的能力,确保了闪电般的执行速度,即使在处理 PB 级数据时也是如此。这些功能与 Snowflake 的自动优化功能相结合,使数据科学家能够专注于他们的分析,而不受基础设施限制的负担。

Snowpark 提供了一系列高级分析能力,对于数据科学和 ML 任务是不可或缺的。从统计分析到预测建模、地理空间分析,甚至数据挖掘,它为数据科学家提供了一套全面的工具包,以探索复杂模式并提取宝贵见解。其对 ML 库和算法的支持进一步增强了其效用,使得能够开发用于分类、回归和聚类的复杂模型。凭借之前提到的丰富特性和功能,Snowpark 为数据科学和 ML 工作负载提供了许多好处。在下一节中,我们将探索 Snowpark ML 库及其不同的功能。

Snowpark ML 简介

Snowpark 是 Snowflake 内部各种库和运行时的汇编,通过包含 Python 等语言,并在 Snowflake 基础设施中服务器端执行的代码执行,同时利用虚拟仓库,简化了非 SQL 代码的安全部署和处理。Snowpark 库的最新增补是 Snowpark ML。Snowpark ML 代表了 Snowflake 强大的数据处理能力与 ML 变革潜力的突破性融合。随着数据科学的前沿不断扩展,Snowpark ML 作为一个前沿框架出现,旨在赋予数据专业人士在 Snowflake 云环境中充分利用其数据的权力。

在其核心,Snowpark ML 被设计用来促进 Snowflake 的数据处理能力与高级 ML 技术之间的无缝集成。借助 Snowpark ML,数据科学家、分析师和工程师可以直接在 Snowflake 中利用熟悉的编程语言和库来开发复杂的 ML 模型。这种集成消除了数据存储、处理和建模之间的障碍,简化了端到端的数据科学工作流程。Snowpark ML 催化创新,使数据专业人士能够高效地探索、转换和建模数据。通过弥合数据处理与 ML 之间的差距,Snowpark ML 赋予组织在数字时代基于数据做出决策、发现宝贵见解并推动业务增长的权力。以下图显示了 Snowpark ML 框架:

图 5.1 – Snowpark ML

图 5.1 – Snowpark ML

上述架构由各种协同工作的组件组成。我们将在下一节中更详细地查看这些组件。

Snowpark ML API

与帮助操作数据的 Snowpark DataFrame 类似,Snowpark ML 提供 API 作为名为snowflake-ml的 Python 库,以支持 ML 开发与部署的每个阶段,允许在 Snowflake 中支持预处理数据、训练、管理和部署 ML 模型:

图 5.2 – Snowpark ML API

图 5.2 – Snowpark ML API

Snowpark 机器学习 API 包括用于开发和训练模型的 Snowpark 机器学习建模以及用于监控和操作模型的 Snowpark 机器学习操作。snowflake.ml.modeling模块提供了基于熟悉库(如 scikit-learn 和 XGBoost)的 API,用于预处理、特征工程和模型训练。完整的端到端机器学习体验可以使用 Snowpark 完成。我们将在下一节中介绍这一点。

使用 Snowpark 的端到端机器学习

寻求无缝、端到端的机器学习解决方案已成为当务之急,Snowpark 为端到端机器学习提供了一个全面的生态系统。本节将深入探讨利用 Snowpark 构建端到端机器学习管道的复杂世界,从数据摄取和预处理到模型开发、训练和部署,揭示在 Snowflake 强大框架内开发机器学习的无缝流程。

机器学习过程涉及通过数据处理解决复杂问题的系统方法。这通常包括定义问题、收集和准备数据、探索性数据分析(EDA)、特征工程、模型选择、训练、评估和部署等阶段,每个操作都至关重要且是迭代的。它允许数据科学家根据沿途获得的见解来完善他们的方法。这个过程通常是循环的,不断迭代以改进模型和预测:

图 5.3 – 端到端机器学习流程

图 5.3 – 端到端机器学习流程

我们可以将机器学习阶段大致分为准备和转换、训练和构建模型以及干扰模型以获得预测结果。我们将在下面讨论这些步骤。

准备和转换数据

原始数据通常很杂乱,包含缺失值、异常值和不一致性。从多个系统中获取正确数据通常消耗了数据科学家的大部分时间。Snowflake 通过提供支持所有类型数据的受管数据云范式来解决此问题,该范式提供了一个统一的地方来即时存储和消费相关数据,以释放机器学习模型的力量。数据准备和转换过程包括数据探索分析(EDA)、清理和处理,并以特征工程结束。这一步骤还包括数据工程管道,它有助于应用数据转换,为下一步准备数据。对于数据预处理,可以使用snowflake.ml.modeling、预处理和 Snowpark 函数来转换数据。

数据探索分析(EDA)

EDA(探索性数据分析)是一个关键步骤,涉及初步调查以了解数据的结构、模式和趋势,因为它有助于揭示隐藏的模式并指导特征选择。数据科学家和分析师与业务利益相关者紧密合作,定义需要回答的具体问题或需要解决的问题,这指导他们选择相关数据。通过图表、图形和统计摘要,数据科学家可以在数据集中识别模式、趋势、相关性和异常值,所有这些都为数据的分布提供了宝贵的见解,并帮助他们更好地理解数据以进行特征选择。

数据清洗和预处理

数据清洗涉及处理缺失数据、纠正错误和确保一致性。通过预处理技术,如归一化、缩放和变换,以及各种采样技术,数据适用于训练模型,这些采样技术应用于评估数据子集,提供对其丰富性和变异性的见解。

特征工程

特征工程涉及创建新特征或修改现有特征以增强机器学习模型的性能。这需要领域专业知识来识别可以提高预测准确性的相关特征。在 Snowflake 中对集中数据进行特征工程可以加速模型开发,降低成本,并使新特征得以重复使用。一些技术,如创建交互项和变换变量,可以从原始数据中提取有意义的信息,使其对建模更具信息量。

训练和构建模型

一旦数据准备就绪且特征已构建,下一步就是训练和开发模型。在这个阶段,数据科学家根据问题的性质,通过将数据子集或训练集传递给建模函数来推导预测函数,训练各种模型,如回归、分类、聚类或深度学习。模型的开发使用假设检验和推断统计的统计方法。根据数据,还使用了高级技术,如集成方法、神经网络和自然语言处理。

一旦模型已经开发,它将在训练集之外的数据上进行测试,以确定其有效性,这通常以预测强度和鲁棒性来衡量,并通过超参数调整对模型进行优化。交叉验证技术优化模型性能,确保准确预测和有价值的见解。

这些步骤的组合使数据科学家能够进行深入的特征工程,调整超参数,并迭代创建和评估机器学习模型。随着数据科学家对各种算法进行实验,评估每个模型的性能,并在所选模型上调整参数以优化针对其特定数据集的代码,直觉变成了准确的预测。snowflake.ml.modeling 可以通过使用 XGBoost 等算法的 fit() 方法进行训练。

推理

一旦模型被训练,Snowpark ML 支持它们的无缝部署和推理。模型可以用于推理,使组织能够根据预测洞察力做出数据驱动的决策。Snowpark ML 有一个模型注册表来管理和组织 Snowpark 模型在其生命周期中的所有阶段。模型注册表支持模型的版本控制,并存储有关模型、超参数和评估指标元数据信息,从而促进实验和模型比较。它还支持模型监控和审计,并有助于在从事模型工作的数据科学家之间进行协作。模型注册表是 Snowpark MLOps 的一部分,可以通过 snowpark.ml.registry 访问。可以使用 Snowflake Tasks 来编排管道。

现在我们已经建立了 Snowpark ML 的基础、其在机器学习中的位置以及 Snowpark 如何支持数据科学工作负载,我们将深入探讨 Snowpark 的完整数据科学场景。下一节将专注于探索和准备数据。

关于数据工程的说明

在下一节中,我们将使用 Snowpark Python 和 pandas 进行探索、转换和特征工程。随着我们使用 SnowparkML 构建模型,我们将结合本节前面讨论的一些步骤。

探索和准备数据

在机器学习过程的第一个步骤中,我们必须使用 Snowpark 在 Snowflake 中探索和准备数据,使其可用于训练机器学习模型。我们将使用 Kaggle 的共享单车数据集,该数据集提供了两年内租赁数据的每小时记录。主要目标是仅基于租赁期之前的信息,预测特定时间段内每小时租赁的自行车数量。本质上,该模型将利用历史数据,通过 Snowpark 预测未来的自行车租赁模式。有关特定数据集的更多信息已在相应的 GitHub 仓库中提供(github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark)。

数据探索使我们能够剖析数据,揭示可能否则隐藏的复杂细节,这为我们整个分析奠定了基础。我们将通过将数据集加载到 Snowpark DataFrame 中开始这个过程:

df_table=session.table("BSD_TRAINING")

一旦数据成功加载,接下来的关键任务是全面了解数据集的规模:

number_of_rows = df_table.count()
number_of_columns = len(df_table.columns)

幸运的是,Snowpark 提供了专门设计来简化这一关键任务的函数:

图 5.4 – 列总数

图 5.4 – 列总数

现在我们知道了数据的大小,让我们通过查看数据集的几行来对其有一个直观的了解:

df_table.sample(n=2).show()

这将返回用于分析的数据的两行:

图 5.5 – 两行数据

图 5.5 – 两行数据

如前图所示,COUNT列是CASUALREGISTERED的直接聚合。在数据科学中,这类变量通常被称为“泄漏变量”。当我们构建模型时,我们将深入探讨管理这些变量的策略。日期列始终是一个引人入胜且复杂的类别,需要我们应对。在此数据集中,我们可以从DATETIME列中创建有价值的新的特征,这可能会显著影响我们的响应变量。在我们开始数据清洗和特征工程过程之前,让我们查看列类型,以便更好地理解和做出更明智的决策:

import pprint
data_types = df_table.schema
data_types = df_table.schema.fields
pprint.pprint(data_types)

这将给出每个字段的架构和数据类型,以便我们更好地理解数据:

图 5.6 – 架构信息

图 5.6 – 架构信息

现在我们已经具备了关于数据的基本信息,让我们开始寻找数据中的缺失值。

缺失值分析

在机器学习中,处理缺失值是一个基本的预处理步骤。不完整的数据可能会干扰模型训练并阻碍预测准确性,可能导致错误的结论或次优性能。通过系统地填充或填补这些空白,我们可以增强数据集的完整性,为机器学习算法提供更全面和一致的数据集,以便进行更稳健和可靠的分析和预测。这种做法类似于为我们提供模型所需的信息,以便做出明智的数据驱动决策。让我们检查数据集中是否存在任何缺失值:

from snowflake.snowpark.functions import count, col
data_types = df_table.schema
print(data_types)
for column in df_table.columns:
    print(f"Null values in {column} is {number_of_rows - df_table.agg(count(col(column))).collect()[0][0]}")

上述代码帮助我们找出是否有任何值为空或缺失:

图 5.7 – 缺失值分析

图 5.7 – 缺失值分析

我们对列中缺失值的初步检查显示,我们的数据集中没有缺失值。然而,更仔细的检查揭示了WINDSPEED列中存在许多 0,这表明可能存在缺失值。从逻辑上讲,风速不可能等于零,这意味着列中的每个0都表示一个缺失值:

print(f"Zero Values in windspeed column is {df_table.filter(df_table['WINDSPEED']==0).count()}")

这将输出以下内容:

图 5.8 – 输出值

图 5.8 – 输出值

我们可以看到 WINDSPEED 列中有 1313 个值。由于该列包含缺失数据,接下来的挑战是确定一个有效的策略来填充这些缺失值。正如广泛认可的,存在各种方法来解决列中的缺失数据。在我们的案例中,我们将采用一种简单的填充方法,用列的平均值替换 0:

from snowflake.snowpark.functions import iff, avg
wind_speed_mean = df_train.select(mean("windspeed")).collect()[0][0]
df_train = df_train.replace({0:wind_speed_mean}, subset=["windspeed"])
df_train.show()
df_train.write.mode("overwrite").save_as_table("model_data")

上一段代码用列的平均值替换了 0:

图 5.9 – 预处理数据

图 5.9 – 预处理数据

这标志着我们的预处理之旅结束。接下来,我们将执行异常值分析。

异常值分析

检测和移除异常值是提高模型准确性和鲁棒性的关键步骤。异常值是与大多数数据集显著偏离的数据点,通常源于错误、异常或罕见事件。这些偏差可能会不当地影响模型训练,导致预测偏差或降低泛化能力。通过识别和消除异常值,我们可以提高模型的质量和可靠性,并确保它们能够更好地从数据中辨别出有意义的模式。这种做法促进了更准确的预测和更高的弹性,最终有助于机器学习项目的整体成功。

我们将把 DataFrame 转换为 pandas DataFrame,以便我们可以进行有洞察力的分析,包括构建可视化来提取有意义的模式。我们的初步重点是 COUNT 列作为响应变量。在模型开发之前,确定 COUNT 列是否包含任何异常值至关重要:

import seaborn as sns
import matplotlib.pyplot as plt
f, axes = plt.subplots(1, 2)
sns.boxplot(x=df_table.to_pandas()['COUNT'], ax=axes[0])
sns.boxplot(x=df_without_outlier.to_pandas()['COUNT'], ax=axes[1])
plt.show()

上一段代码使用 seabornmatplotlib 库生成一个图表,帮助我们找到异常值:

图 5.10 – 异常值图

图 5.10 – 异常值图

如我们所见,COUNT 列显示出可能对模型性能产生负面影响且未得到充分处理的异常数据点。缓解异常值是一个关键的前处理步骤。一种广泛采用的方法是移除位于预定义阈值或允许范围之外的数据点,如下所述:

from snowflake.snowpark.functions \
    import mean, stddev, abs, date_part
mean_value = df_table.select(mean("count")).collect()[0][0]
print(mean_value)
std_value = df_table.select(stddev("count")).collect()[0][0]
print(std_value)
df_without_outlier = df_table.filter(
    (abs(df_table["count"] - mean_value)) >= (3 * std_value))
df_without_outlier.show()

上一段代码使用 Snowpark 库分析存储在 df_table 中的数据集。它计算数据集中 'count' 列的平均值(平均)和标准差(数据分散程度的度量)。然后,它识别并从数据集中移除异常值。异常值是与平均值显著不同的数据点。在这种情况下,它将异常值定义为与平均值相差超过三个标准差的数据点。在识别这些异常值之后,它使用 df_without_outlier.show() 显示没有异常值的数据集,以帮助进行进一步分析:

图 5.11 – 移除异常值

图 5.11 – 移除异常值

现在我们已经处理了异常值,我们可以进行相关性分析。

相关性分析

识别变量之间的相关性对于几个至关重要的原因至关重要。相关性提供了关于数据集中不同特征如何相互关联的宝贵见解。通过理解这些关系,机器学习模型可以做出更明智的预测,因为它们利用相关性的强度和方向来揭示模式和依赖关系。此外,识别和量化相关性有助于特征选择,其中不相关或高度相关的特征可以被排除,以提高模型效率和可解释性。它还有助于识别潜在的共线性问题,其中两个或更多特征高度相关,导致模型系数不稳定。认识和利用相关性使机器学习模型能够做出更好的预测并产生更稳健的结果,使其成为建模的基本方面。

Chapter_5.ipynb:
corr_matrix = df_without_outlier.to_pandas().corr()
plt.figure(figsize=(12, 6))
sns.heatmap(corr_matrix, cmap='coolwarm', annot=True)

上一段代码生成了相关矩阵的热图可视化:

图 5.12 – 相关矩阵热图

图 5.12 – 相关矩阵热图

此热图可视化揭示了TEMPATEMP变量之间存在显著的关联性,这表明了一种称为共线性的条件。当模型中的两个或多个预测变量高度相关时,就会发生共线性,这会扭曲模型的解释性和稳定性。为了减轻这个问题并确保我们分析的可信度,我们选择保留TEMP变量,同时在后续的建模工作中排除ATEMP的考虑。这个战略决策是为了保持模型稳健性并有效地捕捉数据的本质,而不受共线性干扰的影响。

泄露变量

数据科学中的泄露变量无意中包含了在现实世界的预测或决策过程中不可用的信息。消除它们至关重要,因为使用泄露变量可能导致模型性能过于乐观和结果不可靠。在数据预处理期间检测和排除这些变量是至关重要的,以确保我们的模型基于可访问的相同信息进行预测。通过这样做,我们防止了构建在历史数据上表现良好但在现实世界情况下无法执行的模型的风险,这是数据科学的一个关键目标。

如前所述,CASUALREGISTEREDCOUNT列表现出高度共线性,其中COUNTCASUALREGISTERED的显式求和。这种冗余使得包含所有三个变量不可取,导致泄漏变量情况。为了保持我们的模型构建过程的完整性,我们将从特征集中消除CASUALREGISTERED,从而减轻任何潜在的混淆效应,并确保模型能够基于最相关和非冗余的信息进行预测。下一步是对准备好的数据进行特征工程。

特征工程

在机器学习中,特征工程就像为特定工作制作完美的工具。它涉及从可用数据中选择、转换或创建新的特征(变量),使其更适合机器学习算法。这个过程至关重要,因为它有助于模型更好地理解数据中的模式和关系,从而提高预测和洞察力。通过精心设计特征,我们可以揭示隐藏信息,减少噪声,并提高模型性能,使其在构建有效和准确的机器学习系统中成为关键步骤。

分析数据表明,DATETIME列是此数据集中特征工程的有希望的候选者。鉴于预测结果依赖于时间因素,如一天中的时间和一周中的某一天,推导出时间相关特征具有至关重要的意义。提取这些时间特征至关重要,因为它可以增强模型性能,并通过捕捉数据集材料特性的关键细微差别来提高整体预测准确性:

from snowflake.snowpark.functions import hour, month,to_date,dayofweek
df_table = df_table.with_column("hour", hour("DATETIME"))
df_table = df_table.with_column("month", month("DATETIME"))
df_table = df_table.with_column("date", to_date("DATETIME"))
df_table = df_table.with_column("weekday", dayofweek("DATETIME"))
df_table.show()

之前的代码通过从DATETIME列中提取特定的时间和日期细节来丰富 DataFrame,创建新的列:

图 5.13 – DATETIME 数据

图 5.13 – DATETIME 数据

hour列告诉我们一天中的小时,month列识别月份,date列提取日期本身,而weekday列表示一周中的某一天。这些额外的列提供了对数据集中时间相关信息的更全面视角,增强了其深入分析和机器学习应用潜力。

这一步结束了我们的数据准备和探索之旅。下一节将使用这些准备好的数据,使用 Snowpark 构建和训练我们的模型。

关于模型构建过程的说明

在我们的模型构建过程中,我们不会包含迄今为止讨论的所有步骤。相反,我们将专注于两个重要的转换,以展示 Snowpark ML 管道。此外,附带的笔记本(chapter_5.ipynb)展示了使用 Python 的 scikit-learn 库进行模型构建以及如何将其作为存储过程调用。这将使您能够比较和对比通过 Snowpark ML 简化的模型构建过程。为了继续本章内容,您可以跳过使用 scikit-learn 部分进行模型构建的过程,直接进入笔记本中的 Snowpark ML 部分。

在 Snowpark 中训练 ML 模型

现在我们已经准备好了我们的数据集,我们旅程的巅峰就是模型构建过程,我们将利用 Snowpark ML 的力量。Snowpark ML 作为 Snowpark 工具箱中的最新成员,战略性地部署以简化模型构建过程的复杂性。当我们通过新颖的 ML 库进行模型构建过程的比较探索时,其优雅性变得显而易见。我们将首先开发我们将使用之前准备的数据来训练模型的管道:

import snowflake.ml.modeling.preprocessing as snowml
from snowflake.ml.modeling.pipeline import Pipeline
import joblib
df = session.table("BSD_TRAINING")
df = df.drop("DATETIME","DATE")
CATEGORICAL_COLUMNS = ["SEASON","WEATHER"]
CATEGORICAL_COLUMNS_OHE = ["SEASON_OE","WEATHER_OE"]
MIN_MAX_COLUMNS = ["TEMP"]
import numpy as np
categories = {
    "SEASON": np.array([1,2,3,4]),
    "WEATHER": np.array([1,2,3,4]),
}
preprocessing_pipeline = Pipeline(
    steps=[
        (
            "OE",
            snowml.OrdinalEncoder(
                input_cols=CATEGORICAL_COLUMNS,
                output_cols=CATEGORICAL_COLUMNS_OHE,
                categories=categories
            )
        ),
        (
            "MMS",
            snowml.MinMaxScaler(
                clip=True,
                input_cols=MIN_MAX_COLUMNS,
                output_cols=MIN_MAX_COLUMNS,
            )
        )
    ]
)
PIPELINE_FILE = 'preprocessing_pipeline.joblib'
joblib.dump(preprocessing_pipeline, PIPELINE_FILE)
transformed_df = preprocessing_pipeline.fit(df).transform(df)
transformed_df.show()
session.file.put(PIPELINE_FILE,"@snowpark_test_stage",overwrite=True)

上述代码通过使用各种 Snowpark ML 函数为数据集创建预处理管道。导入preprocessingpipeline模块,因为这些是开发和训练模型所必需的:

图 5.14 – 转换后的数据

图 5.14 – 转换后的数据

管道包括对分类列(SEASONWEATHER)进行顺序编码,对数值列(TEMP)进行 min-max 缩放。该管道使用joblib库保存到阶段,可用于未来分析的持续预处理。现在我们已经准备好了管道代码,我们将构建模型所需的特征:

CATEGORICAL_COLUMNS = ["SEASON","WEATHER"]
CATEGORICAL_COLUMNS_OHE = ["SEASON_OE","WEATHER_OE"]
MIN_MAX_COLUMNS = ["TEMP","ATEMP"]
FEATURE_LIST = \
    ["HOLIDAY","WORKINGDAY","HUMIDITY","TEMP","ATEMP","WINDSPEED"]
LABEL_COLUMNS = ['COUNT']
OUTPUT_COLUMNS = ['PREDICTED_COUNT']
PIPELINE_FILE = 'preprocessing_pipeline.joblib'
preprocessing_pipeline = joblib.load(PIPELINE_FILE)

上述代码定义了表示分类列的列表、单热编码的分类列和用于 min-max 缩放的列。它还指定了 ML 模型的特征列表、标签列和输出列。加载了preprocessing_pipeline.joblib文件,并假设它包含之前保存的预处理管道。这些元素共同准备后续 ML 任务所需的数据和配置,确保对分类变量、特征缩放和基于预先建立的管道进行模型预测的一致处理。我们现在将数据分为训练集和测试集:

bsd_train_df, bsd_test_df = df.random_split(
    weights=[0.7,0.3], seed=0)
train_df = preprocessing_pipeline.fit(
    bsd_train_df).transform(bsd_train_df)
test_df = preprocessing_pipeline.transform(bsd_test_df)
train_df.show()
test_df.show()

上述代码使用随机分割将数据集分为训练集(70%)和测试集(30%)。它应用之前定义的预处理管道来转换这两个集合,显示转换后的训练集和测试集,并确保模型训练和评估的预处理一致性。输出显示了不同的训练和测试数据:

图 5.15 – 训练和测试数据集

图 5.15 – 训练和测试数据集

接下来,我们将使用训练数据训练模型:

from snowflake.ml.modeling.linear_model import LinearRegression
regressor = LinearRegression(
    input_cols=CATEGORICAL_COLUMNS_OHE+FEATURE_LIST,
    label_cols=LABEL_COLUMNS,
    output_cols=OUTPUT_COLUMNS
)
# Train
regressor.fit(train_df)
# Predict
result = regressor.predict(test_df)
result.show()

LinearRegression 类定义了模型,指定了输入列(一热编码后的分类列和附加特征)、标签列(目标变量,即 COUNT)和预测输出列。模型使用 fit 在转换后的训练数据集上训练,然后使用 predict 对转换后的测试数据集生成预测。显示的结果预测了模型在测试数据上的性能:

图 5.16 – 预测输出

图 5.16 – 预测输出

下一步是计算各种性能指标以评估线性回归模型预测的准确性:

from snowflake.ml.modeling.metrics import mean_squared_error, explained_variance_score, mean_absolute_error, mean_absolute_percentage_error, d2_absolute_error_score, d2_pinball_score
mse = mean_squared_error(df=result,
    y_true_col_names="COUNT",
    y_pred_col_names="PREDICTED_COUNT")
evs = explained_variance_score(df=result,
    y_true_col_names="COUNT",
    y_pred_col_names="PREDICTED_COUNT")
mae = mean_absolute_error(df=result,
    y_true_col_names="COUNT",
    y_pred_col_names="PREDICTED_COUNT")
mape = mean_absolute_percentage_error(df=result,
    y_true_col_names="COUNT",
    y_pred_col_names="PREDICTED_COUNT")
d2aes = d2_absolute_error_score(df=result,
    y_true_col_names="COUNT",
    y_pred_col_names="PREDICTED_COUNT")
d2ps = d2_pinball_score(df=result,
    y_true_col_names="COUNT",
    y_pred_col_names="PREDICTED_COUNT")
print(f"Mean squared error: {mse}")
print(f"explained_variance_score: {evs}")
print(f"mean_absolute_error: {mae}")
print(f"mean_absolute_percentage_error: {mape}")
print(f"d2_absolute_error_score: {d2aes}")
print(f"d2_pinball_score: {d2ps}")

上述代码计算了各种性能指标以评估线性回归模型预测的准确性。基于存储在 result DataFrame 中的实际(COUNT)和预测(PREDICTED_COUNT)值,计算了均方误差、解释方差得分、平均绝对误差、平均基本百分比误差、d2 确定性误差得分和 d2 针球得分等指标:

图 5.17 – 性能指标

图 5.17 – 性能指标

这些性能指标全面评估了模型在预测准确性不同方面的性能。

模型结果和效率

呈现的模型指标可能需要展示更出色的结果。强调本案例研究的主要目标是阐明模型构建过程并突出 Snowpark ML 的促进作用。本章的重点在于说明线性回归模型的构建。

Snowpark ML 的效率

在深入探究由 Snowpark ML 便利的模型构建过程的复杂性时,其精心设计的初始特点尤为突出。与传统方法的一个显著不同之处在于 Snowpark ML 与 scikit-learn 中发现的简化方法非常相似。一个显著的优势是消除了创建单独的用户定义函数UDFs)和存储过程的必要性,从而简化了整个模型构建工作流程。

认识到 Snowpark ML 与 scikit-learn 无缝集成,同时在模型构建过程中遵循类似的传统规范至关重要。一个值得注意的区别是 scikit-learn 中数据必须以 pandas DataFrame 的形式传递。因此,在启动模型构建阶段之前,必须将 Snowflake 表转换为 pandas DataFrame。然而,务必注意潜在的内存限制,尤其是在处理大量数据集时。将大型表转换为 pandas DataFrame 需要大量的内存,因为整个数据集都加载到内存中。

相比之下,Snowpark ML 为模型构建过程提供了一个更原生且内存效率更高的方法。这种与 Snowflake 环境的原生集成不仅提高了工作流程的效率,还减轻了与大数据集相关的内存挑战。Snowpark ML 的应用成为在 Snowflake 生态系统中执行复杂模型构建任务的战略性和无缝选择。

摘要

Snowpark ML 成为数据科学家的一个多才多艺且强大的工具,使他们能够在 Snowflake 统一的数据库平台上处理复杂的数据科学任务。它与流行编程语言的集成、可扩展性和实时处理能力使其在从预测建模到实时分析和高级人工智能任务的各种应用中变得极其宝贵。借助 Snowpark ML,组织可以利用其数据的全部潜力,推动创新,并在当今以数据驱动为导向的竞争环境中获得优势。

在下一章中,我们将继续通过在 Snowflake 中部署模型并实现其运营来推进。

第六章:使用 Snowpark 部署和管理 ML 模型

在 Snowpark 中开发数据科学时,模型的无缝部署和有效管理已成为关键组成部分。上一章介绍了如何准备数据和训练模型。本章深入探讨了利用 Snowpark 高效部署和管理机器学习ML)模型的复杂性,从部署到与特征存储和模型注册表的集成,探讨了在 Snowpark 中简化 ML 模型的基本要素。

在本章中,我们将涵盖以下主要主题:

  • 在 Snowpark 中部署 ML 模型

  • 管理 Snowpark 模型数据

技术要求

请参考上一章的技术要求部分以设置环境。

支持材料可在github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark找到。

在 Snowpark 中部署 ML 模型

在上一章中,我们学习了如何开发 ML 模型。现在模型已经准备好了,我们必须将它们部署到 Snowpark 中。为了使开发者更容易部署模型,Snowpark ML 库包含了一系列函数,这些函数涵盖了引入新的开发接口以及旨在安全地促进特征和模型部署的附加功能。Snowpark MLOps 通过提供高级模型管理能力和 Snowflake 生态系统内的集成部署功能,无缝地补充了 Snowpark ML 开发 API。在以下小节中,我们将探讨模型注册表并将模型部署用于推理以获取预测。

Snowpark ML 模型注册表

模型注册表是一个集中式存储库,使模型开发者能够高效地组织、共享和发布 ML 模型。它简化了团队和利益相关者之间的协作,促进了组织内所有模型生命周期的协作管理。组织模型对于跟踪各种版本、快速识别最新版本以及深入了解每个模型的超参数至关重要。一个结构良好的模型注册表可以增强结果的复现性和有说服力的比较。它还允许跟踪和分析模型准确度指标,使决策更加明智并实现持续改进。以下图表显示了将模型部署到模型注册表的过程:

图 6.1 – 将模型部署到模型注册表

图 6.1 – 将模型部署到模型注册表

模型注册表是一个 Python API,它管理 Snowflake 环境中的模型,为 Snowflake 中的模型提供可扩展、安全的部署和管理能力。Snowpark 模型注册表建立在原生的 Snowflake 模型实体之上,集成了内置的版本支持,以实现更流畅的模型管理。

准备模型

为了说明模型注册过程,我们将高效地构建一个使用最少参数的 XGBoost 模型,利用 共享自行车 数据集上的网格搜索。上一章中准备的 BSD_TRAINING 表是我们构建 XGBoost 模型的基础数据集。

在这里,我们正在创建一个特征列表并找到标签和输出列:

FEATURE_LIST = [ "HOLIDAY", "WORKINGDAY", "HUMIDITY", "TEMP", "ATEMP", 
    "WINDSPEED", "SEASON", "WEATHER"]
LABEL_COLUMNS = ['COUNT']
OUTPUT_COLUMNS = ['PREDICTED_COUNT']
df = session.table("BSD_TRAINING")
df = df.drop("DATETIME","DATE")
df.show(2)

这将打印出以下 DataFrame:

图 6.2 – 模型 DataFrame

图 6.2 – 模型 DataFrame

为了简化,我们将专注于优化 XGBoost 中的两个参数:

from snowflake.ml.modeling.model_selection import GridSearchCV
from snowflake.ml.modeling.xgboost import XGBRegressor
param_grid = {
    "max_depth":[3, 4, 5, 6, 7, 8],
    "min_child_weight":[1, 2, 3, 4],
}
grid_search = GridSearchCV(
    estimator=XGBRegressor(),
    param_grid=param_grid,
    n_jobs = -1,
    scoring="neg_root_mean_squared_error",
    input_cols=FEATURE_LIST,
    label_cols=LABEL_COLUMNS,
    output_cols=OUTPUT_COLUMNS
)

此代码使用 Snowflake 的 ML 模块执行梯度提升回归器的超参数调优网格搜索。它探索了 max_depthmin_child_weight 在指定范围内的组合,旨在根据提供的输入和标签列优化模型。

下一步的逻辑进展是将数据集划分为训练集和测试集:

train_df, test_df = df.random_split(weights=[0.7, 0.3], seed=0)
grid_search.fit(train_df)

这种划分对于促进模型拟合至关重要,使我们能够在指定的训练数据集上训练模型。

提取最佳参数

成功使用 XGBoost 模型训练我们的数据集后,下一步是确定通过网格搜索定义的最佳参数值。Snowpark ML 提供了一种与 scikit-learn 包中类似的方法。接下来的代码反映了提取这些最佳参数的步骤,并随后可视化它们,使过程易于理解:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
gs_results = grid_search.to_sklearn().cv_results_
max_depth_val = []
min_child_weight_val = []
for param_dict in gs_results["params"]:
    max_depth_val.append(param_dict["max_depth"])
    min_child_weight_val.append(param_dict["min_child_weight"])
mape_val = gs_results["mean_test_score"]*-1

上一段代码使用了 pandasseabornmatplotlib 来分析并可视化 Snowpark ML 模型的网格搜索结果。它提取了参数,例如 max_depthmin_child_weight,以及相应的 平均绝对百分比误差MAPE)值以进行评估。以下代码展示了这些值:

gs_results_df = pd.DataFrame(data={
    "max_depth":max_depth_val,
    "min_child_weight":min_child_weight_val,
    "mape":mape_val})
sns.relplot(data=gs_results_df, x="min_child_weight",
    y="mape", hue="max_depth", kind="line")

上一段代码从列出的 max_depthmin_child_weightmape 值创建了一个名为 gs_results_dfpandas DataFrame。然后它使用 seaborn 生成线图,可视化学习率、MAPE 分数和不同估计器数量之间的关系。最后,matplotlibplt.show() 命令显示了以下图表:

图 6.3 – 数据图

图 6.3 – 数据图

仔细观察前面的图表,可以明显看出 max_depth 值为 8min_child_weight 学习率为 2 的组合产生了最佳结果。值得注意的是,与 scikit-learn 类似,Snowpark ML 提供了提取这些最佳参数的简化方法,简化了过程,提高了用户便利性:

grid_search.to_sklearn().best_estimator_

代码将 Snowpark ML 网格搜索结果转换为与 scikit-learn 兼容的格式,然后检索最佳估计器,代表具有最佳超参数的模型:

图 6.4 – Snowpark ML 网格搜索结果

图 6.4 – Snowpark ML 网格搜索结果

在下一节中,我们将使用 Snowpark 模型注册表来记录模型。

记录最佳模型

拥有我们的最佳模型在手,模型注册表的关键阶段展开。与之前创建的模型非常相似,我们可以扩展此过程以涵盖多个模型,通过模型注册表逐一注册。在这种情况下,我们将只注册我们的最佳模型。我们将深入探讨如何逐步注册和无缝部署模型。

关于模型注册表和特征存储的说明

当我们编写本章时,模型注册表和特征存储都处于私有预览阶段。一旦它们对所有用户开放,API 方法可能与我们在这本书中看到的不同。

接下来,我们需要创建一个注册表来记录我们的模型:

from snowflake.ml.registry import model_registry
registry = model_registry.ModelRegistry(session=session,
    database_name="SNOWPARK_DEFINITIVE_GUIDE",
    schema_name="MY_SCHEMA", create_if_not_exists=True)
ModelRegistry instance with session information, specified database, and schema names. If not existing, it creates a registry in the SNOWPARK_DEFINITIVE_GUIDE database and MY_SCHEMA schema.

以下代码准备记录模型到模型注册表所需的基本详细信息:

optimal_model = grid_search.to_sklearn().best_estimator_
optimal_max_depth = \
    grid_search.to_sklearn().best_estimator_.max_depth
optimal_min_child_weight = \
    grid_search.to_sklearn().best_estimator_.min_child_weight
optimal_mape = gs_results_df.loc[
    (gs_results_df['max_depth']==optimal_max_depth) &
    (gs_results_df['min_child_weight']== \
        optimal_min_child_weight), 'mape'].values[0]

它提取了网格搜索确定的最佳模型,并检索了特定的超参数,如 max_depthmin_child_weight 和最佳参数值。

完成所有必要的模型注册步骤后,前面的代码无缝地将收集到的信息整合,以正式将我们的最佳 XGBoost 模型注册到模型注册表中:

model_name = "bike_model_xg_boost"
model_version = 1
X = train_df.select(FEATURE_LIST).limit(100)
registry.log_model( model_name=model_name,
                    model_version=model_version,
                    model=optimal_model,
                    sample_input_data=X,
                    options={"embed_local_ml_library": True, \
                             "relax": True})
registry.set_metric(model_name=model_name,
                    model_version=model_version,
                    metric_name="mean_abs_pct_err",
                    metric_value=optimal_mape)

代码为模型分配了一个名称(bike_model_xg_boost)和一个版本(1),并将其与相关的详细信息(包括样本输入数据和特定选项)一起记录到注册表中。此外,它为注册的模型设置了一个自定义指标,MAPE(mean_abs_pct_err),以及其对应的值(optimal_mape)。为了验证注册成功,执行以下代码:

registry.list_models().to_pandas()

这将确认我们的 XGBoost 模型和梯度提升模型(此处仅展示 XGBoost 模型以避免不必要的重复)是否适当地列在模型注册表中:

图 6.5 – 已注册到模型注册表的模型

图 6.5 – 已注册到模型注册表的模型

在使用各种模型和参数配置进行实验的迭代旅程中,我们通过一种结构化的方法,勤奋地将每个模型在模型注册表中注册,确保每个经过微调和优化的模型都能高效地存储以供将来使用。在下一节中,我们将使用 Snowpark MLOps 从注册表中部署模型并预测其结果。

模型部署

在前面的章节中,我们 navigated the intricate landscape of deploying models through complex 用户定义函数UDFs)或存储过程。然而,新的 Snowpark 模型注册表简化了繁琐的过程。它通过提供一个简化和标准化的框架来处理生产环境中的预测模型,从而增强了模型的可维护性。这种方法论的转变优化了运营效率,并与数据科学动态领域的当代实践无缝对接。标准的模型部署将遵循以下命名约定:

model_deployment_name = model_name + f"{model_version}" + "_UDF"
registry.deploy(model_name=model_name,
                model_version=model_version,
                deployment_name=model_deployment_name,
                target_method="predict",
                permanent=True,
                options={"relax_version": True})
predict target method, ensuring permanence in the deployment. Additionally, it includes an option to relax version constraints during deployment. Just as we’ve showcased the catalog of registered models, an equivalent insight into deployed models can be obtained using the following line of code:
registry.list_deployments(model_name, model_version).to_pandas()

此功能提供了系统内从注册到部署的模型全面视图:

图 6.6 – 自行车模型部署

图 6.6 – 自行车模型部署

现在,让我们利用我们的部署模型来推断测试数据的预测,并评估我们的预测与实际结果之间的准确性:

model_ref = model_registry.ModelReference(
    registry=registry,
    model_name=model_name,
    model_version=model_version)
result_sdf = model_ref.predict(
    deployment_name=model_deployment_name,
    data=test_df)
result_sdf.show()

代码初始化一个ModelReference对象,通过引用其名称和版本链接到注册表中的特定模型。随后,它利用此引用使用指定的部署预测提供的测试数据,结果生成一个 Snowpark DataFrame(result_sdf)。最后,它通过show()方法显示预期结果,如下面的截图所示:

图 6.7 – 模型结果 DataFrame

图 6.7 – 模型结果 DataFrame

在观察了涵盖模型开发、注册和部署的全面周期后,值得注意的是,此过程可以通过模型注册表复制到任何模型构建任务中。在下一节中,我们将阐明模型注册表中固有的几个有益方法,提高其可用性并增强整体建模体验。现在我们已经部署了模型,我们将探讨其他模型注册表方法。

模型注册表方法

除了概述的模型部署功能外,模型注册表通过几个旨在有效维护和日常管理活动的有益方法扩展了其效用。在本节中,我们将探讨这些方法中的一些,以增强我们对其实际应用的了解。我们将从模型指标开始。

模型指标

将指标链接到您的模型版本是模型注册库中的一个关键功能。此功能作为基本方面,提供了一种系统性的方法来区分测量每个模型版本的性能。通过关联指标,用户可以获得关于不同迭代有效性的宝贵见解,便于根据跨各个版本的模型性能的定量评估做出明智的决策。它还有助于自动化流程,如果模型指标低于阈值值,则重新训练。这种故意的指标集成丰富了全面的模型管理功能,并建立了一个结构化的框架,用于持续模型评估和改进:

registry.set_metric(model_name=model_name,
                    model_version=model_version,
                    metric_name="mean_abs_pct_err",
                    metric_value=optimal_mape)

上一行设置了一个自定义指标 mean_abs_pct_err,用于模型注册库中特定模型版本,将计算出的 MAPE 值分配给该模型以量化其性能。这增强了模型注册库跟踪和评估不同模型版本有效性的能力:

registry.get_metric_value(model_name=model_name,
                          model_version=model_version,
                          metric_name="mean_abs_pct_err")

这将打印以下输出:

图 6.8 – mean_abs_pct_err 的 MAPE 值

图 6.8 – mean_abs_pct_err 的 MAPE 值

除了设置之外,我们还可以从模型注册库中检索与特定模型版本关联的特定自定义指标 mean_abs_pct_err 的值。它允许用户访问和分析定量性能指标,以进行实际模型评估和不同版本之间的比较:

registry.get_metrics(model_name=model_name, 
    model_version=model_version)

类似于检索已部署模型的特定指标,类似的方法允许我们访问给定已部署模型的所有相关指标的综合列表。这有助于全面理解模型性能,提供有关其评估的各种指标的详细概述,有助于对其有效性进行全面分析:

图 6.9 – mean_abs_pct_err 的指标

图 6.9 – mean_abs_pct_err 的指标

我们可以在注册库中找到模型的指标值。在下一节中,我们将介绍模型标签和描述。

模型标签和描述

为已部署模型设置标签名称和描述对于有效的实验跟踪和文档至关重要。标签和描述提供了关于模型目的、配置和显著特性的背景和见解。这有助于维护结构化和信息化的记录,提高可重复性,并促进对实验结果的更全面分析:

registry.set_tag(model_name=model_name,
                 model_version=model_version,
                 tag_name="usage",
                 tag_value="experiment")
registry.list_models().to_pandas()[["NAME", "TAGS"]]

提供的代码首先为模型注册库中特定模型版本设置了一个名为 stage 的标签,值为 experiment_1。这种标记是模型目的或使用的上下文标记。随后的行检索并显示所有模型的名称及其相关标签,展示了每个模型的标记信息:

图 6.10 – 模型标签

图 6.10 – 模型标签

另一个值得注意的方面是,可以根据需要修改和删除标签,允许动态调整我们的实验设计。这种能力使用户能够迭代地改进与模型关联的上下文信息,提供有意义的、不断发展的标签。修改和删除标签的能力增强了实验设计的灵活性。它确保了模型周围的文档和上下文能够适应实验生命周期中不断变化的见解和要求:

registry.remove_tag(model_name=model_name,
                    model_version=model_version,
                    tag_name="usage")
registry.list_models().to_pandas()[["NAME", "TAGS"]]

提供的代码启动从模型注册表中删除特定标签的操作,该标签名为usage,针对特定模型版本。在此操作之后,下一行检索并以表格格式显示所有模型的名称及其关联的标签。这展示了删除指定标签后的更新信息,提供了模型及其更改的标签配置的全面视图:

图 6.11 – 移除的模型标签

图 6.11 – 移除的模型标签

我们还可以为部署的模型提供描述性信息,提供有价值的背景知识并有助于未来的参考。提供有意义的描述增强了模型目的、配置或其他相关细节的可理解性。以下代码块是自我解释的,并反映了设置标签的过程,它允许将描述性叙述分配给部署的模型,确保关键信息在后续分析或实验中得以封装:

registry.set_model_description(model_name=model_name,
    model_version=model_version,
    description="this is a test model")
print(registry.get_model_description(model_name=model_name,
    model_version=model_version))

模型描述已设置并可检索以在屏幕上显示:

图 6.12 – 模型描述

图 6.12 – 模型描述

现在我们已经设置了模型标签和描述,我们将探讨如何访问注册表的历史记录。

注册表历史

访问注册表历史是一项非常有价值的特性,它提供了模型版本、相关指标、标签和描述的按时间顺序的记录。这种历史视角增强了模型开发中的透明度,并使数据科学家能够做出明智的决定,跟踪性能趋势,并精确地迭代改进模型。因此,ML 注册表及其历史跟踪功能成为数据科学工具箱中的关键资产,促进了模型开发、部署和持续改进的结构化和高效方法:

registry.get_history().to_pandas()

代码检索并将模型注册表的整个历史记录转换为pandas DataFrame,以表格形式全面展示所有记录的事件和变更:

图 6.13 – 注册表历史

图 6.13 – 注册表历史

在注册表历史记录中缩小搜索范围是一种常见做法,可以通过指定模型名称和版本来实现。这种有针对性的过滤允许进行更深入的探索,与在模型注册表历史记录中导航时的典型偏好相一致:

registry.get_model_history(model_name=model_name,
    model_version=model_version).to_pandas()

此代码获取并转换特定模型版本的特定历史记录,通过其名称和版本标识,将其转换为pandas DataFrame:

图 6.14 – 注册表历史过滤器

图 6.14 – 注册表历史过滤器

生成的 DataFrame 提供了与该特定模型版本在注册表中相关的所有事件和变化的详细时间记录。在下一节中,我们将学习关于模型注册表的操作。

模型注册表操作

在当代机器学习领域,模型的生命周期正在不断缩短,导致部署模型的持续时间变短。同时,具有不同参数的实验产生了许多模型,它们的后续部署被注册。这种激增需要一种深思熟虑的方法来管理模型,包括定期的清理过程,以保持模型注册表的流畅和高效:

registry.delete_deployment(model_name=model_name,
    model_version=model_version,
    deployment_name=model_deployment_name)
    registry.list_deployments(model_name, model_version).to_pandas()

上一段代码通过模型名称、版本和部署名称从模型注册表中删除特定的部署实例,确保部署模型的清理和管理高效:

图 6.15 – 删除特定部署

图 6.15 – 删除特定部署

它作为删除过时或不希望部署的方法。我们还可以使用以下代码从注册表中删除整个模型:

registry.delete_model(model_name=model_name,
    model_version=model_version)
registry.list_models().to_pandas()

与删除部署类似,此代码将从模型注册表中删除一个模型:

图 6.16 – 删除模型

图 6.16 – 删除模型

我们可以看到整个模型已从注册表中删除。在下一节中,我们将探讨模型注册表的好处。

模型生命周期中模型注册表的好处

Snowpark 模型注册表简化了整个生命周期中机器学习模型的管理。让我们深入了解 Snowpark 中的模型注册表如何协助机器学习模型生命周期的各个阶段:

  1. 模型开发:在开发阶段,数据科学家可以使用 Snowpark 在 Snowflake 中直接构建、训练和验证机器学习模型。模型注册表提供了一个集中位置来存储和版本控制这些模型,使得跟踪更改、比较性能和与团队成员协作变得更加容易。

  2. 模型部署:一旦模型经过训练和验证,就需要将其部署到生产环境中进行推理。模型注册表通过提供跨不同环境的标准化接口来促进无缝部署,确保模型部署过程的连贯性和可靠性。

  3. 模型监控:监控已部署模型的性能对于检测漂移并确保随着时间的推移持续准确性至关重要。模型注册表可以与监控工具集成,以跟踪模型性能指标,如准确率、精确率、召回率和 F1 分数,从而实现主动维护和优化。

  4. 模型治理:确保符合监管要求和组织政策对于负责任的 AI 部署至关重要。模型注册表通过提供访问控制、审计日志和版本控制等功能来支持治理,这有助于组织在整个模型生命周期中保持可见性和控制力。

  5. 模型再训练和更新:机器学习模型需要定期再训练和更新以适应不断变化的数据分布和业务需求。模型注册表通过允许数据科学家使用更新的数据和算法无缝再训练模型,同时保留模型版本的世系和历史,简化了这一过程。

  6. 模型退役:随着模型变得过时或被更新的版本所取代,它们需要优雅地退役。模型注册表通过存档过时模型、记录退役原因并确保相关利益相关者得到变更通知来简化退役过程。

模型注册表提供了一个有组织的框架用于模型管理,并提供了高效维护的功能,包括设置和跟踪指标、标签和描述。注册表的历史跟踪功能已成为一个有价值的特性,使用户能够深入了解模型随时间演化的过程。标签和描述提供了上下文,并促进了实验跟踪,以便访问和过滤注册表的历史记录,从而实现对模型相关活动的全面视图。总的来说,模型注册表成为 Snowpark ML 的一个强大补充,集中管理模型,促进实验,并确保模型开发和部署的流程流畅且有序。

总体而言,Snowpark 中的模型注册表在简化机器学习模型的生命周期方面发挥着关键作用,从开发、部署到监控、治理、再训练和退役。通过提供一个集中平台来管理模型,它帮助组织最大化其机器学习投资的效益,同时最小化运营成本和风险。

管理 Snowpark 模型数据

在上一节中,我们介绍了使用模型注册表部署机器学习模型。本节将探讨使用特征存储管理 Snowpark 模型。Snowpark ML 特征存储简化了特征工程过程,对于机器学习至关重要,它显著影响了基于所采用特征的质量的模型性能。本章将帮助我们了解使用特征存储和管理 Snowpark 模型。

Snowpark 特征存储

Snowpark 特征存储库是为数据科学家和机器学习工程师提供的一体化解决方案。它简化了机器学习特征在模型训练和推理过程中的创建、存储、管理和提供,并通过 Snowpark 机器学习库进行访问。特征存储库定义、管理和检索特征,由一个用于特征元数据管理和持续特征处理的托管基础设施支持。其主要功能是使这些特征能够方便地用于未来机器学习模型的持续开发。特征存储库在机器学习特征工程领域的数据输入、跟踪和管理中发挥着关键作用:

图 6.17 – 特征存储库

图 6.17 – 特征存储库

通过利用 Snowpark 特征存储库,该库旨在通过提供更高的效率来简化并增强这一过程,数据科学家和机器学习从业者可以保持模型训练、版本控制和推理特征的单一、更新源。我们将使用共享单车数据集和前一小节中开发的机器学习模型来展示特征存储库如何增强模型开发和部署周期。

特征存储库的好处

利用特征存储库为机器学习项目提供了几个好处。首先,它们通过保存已开发的特征,允许快速访问和重新用于新的机器学习模型,从而节省时间和精力。其次,它们通过提供一个集中注册所有机器学习特征的注册表,确保跨团队保持一致的定义和文档。第三,特征存储库通过集中管理特征管道,确保训练和推理之间的一致性,并持续监控数据管道中的任何差异,帮助维持模型的最佳性能。

此外,特征存储库通过提供有关每个机器学习模型训练数据和部署数据的详细信息,增强了安全和数据治理,促进了迭代和调试。将特征存储库与云数据仓库集成增强了数据安全性,确保模型和训练数据都得到保护。最后,特征存储库通过提供一个集中平台,用于开发、存储、修改和共享机器学习特征,促进了团队间的协作和思想共享,适用于多种商业应用。

特征存储库与数据仓库的比较

深入探讨特征存储库与数据仓库之间的区别,有助于了解它们在增强机器学习项目价值中的协作作用。

相似性 – 共同特性和功能

特征存储库和数据仓库在运营方法上表现出相似之处。它们都依赖于 提取、转换、加载ETL)管道来促进数据管理和可访问性。此外,它们作为具有元数据的存储库,促进了组织团队之间的无缝数据共享和利用。

最终用户 – 定制化效用

一个显著的差异在于它们的主要用户群体。数据仓库传统上服务于深陷于生成全面业务报告的分析师,深入历史数据以获取战略洞察。相反,特征存储库专门服务于沉浸在预测 ML 模型开发中的数据科学家。虽然后者可能从数据仓库中获取补充洞察,但他们的核心功能是利用特征存储库以简化模型开发和推理。

数据类型 – 结构差异

结构上,数据仓库在具有良好定义模式的数据库中存储特定领域的数据。这种结构化格式简化了相关信息的查询和检索,非常适合分析工作。相反,特征存储库包含用于 ML 模型训练的关键特征值集合。这些值包括定量和分类变量,为模型开发过程提供了细粒度的洞察。

ETL 管道 – 分歧的轨迹

ETL 管道的运营动态进一步突出了特征存储库和数据仓库之间的差异。数据仓库中的 ETL 流程主要关注数据清洗和转换,确保在定义的方案中数据的准确性和一致性。相比之下,特征存储库的管道则涉及更复杂的旅程,包括数据提取、转换和特征工程。特征存储库中的转换通常涉及复杂的计算和聚合,以提炼对模型训练和推理至关重要的复杂洞察,强调了它们在 ML 生命周期中的关键作用。

现在我们已经掌握了特征存储库的本质,理解了它们与数据仓库的区别和重要性,让我们更深入地探讨构成特征存储库的各个组成部分。

在下一节中,我们将开始创建一个针对 共享单车 数据集的基本特征存储库,专注于与天气相关的特征。这个过程包括以下步骤:

  1. 创建特征存储库

  2. 创建特征实体

  3. 选择和转换天气特征

  4. 创建一个特征视图

  5. 生成包含特征视图的丰富数据集

  6. 构建一个由丰富数据集支持的 ML 模型

  7. 促进基于训练模型的预测

让我们详细讨论每一个。

创建一个特征存储库

开始使用 Snowflake 特征存储涉及创建一个新的特征存储或连接到现有的一个。这是通过向FeatureStore构造函数提供特定细节来完成的,包括 Snowpark 会话、数据库名称、特征存储名称和默认仓库名称。creation_mode参数对于确定是否应该创建一个新特征存储(如果不存在)至关重要。为了实现这一功能,我们将使用以下代码:

from snowflake.ml.feature_store import (
    FeatureStore, FeatureView, Entity, CreationMode)
fs = FeatureStore(
    session=session,
    database="SNOWPARK_DEFINITIVE_GUIDE",
    name="BIKE_SHARING_FEATURES",
    default_warehouse="COMPUTE_WH",
    creation_mode=CreationMode.CREATE_IF_NOT_EXIST,
)

这将打开一个到特征存储的会话,并允许在 Snowpark 会话中访问它。下一步将是设置此特征存储上的特征实体。

创建特征实体

实体是与特征和特征视图相关的基本元素,通过定义连接键为特征查找提供基石。用户可以生成新的实体并在特征存储中正式注册它们,从而促进各种特征之间的连接和关系。此代码创建了一个名为WEATHER的实体,具有ID连接键,将其注册在特征存储(fs)中,然后显示特征存储中的实体列表:

entity = Entity(name="ENTITY_WEATHER", join_keys=["ID"])
fs.register_entity(entity)
fs.list_entities().show()

这将生成以下输出:

图 6.18 – 特征实体

图 6.18 – 特征实体

ENTITY_WEATHER实体已创建,ID 作为连接键。下一步是设置特征视图。

创建特征视图

在特征存储中,特征视图充当综合管道,系统性地将原始数据在固定时间间隔内转换为相互关联的特征。这些特征视图从指定的源表实例化,确保在引入新数据时增量且高效地更新。在我们之前的章节中,我们探索了一个包含各种天气相关特征的数据集。为了有效地预处理这些数据,我们使用了 Snowpark 管道。

通过这个管道,我们对SEASONWEATHER列使用了独热编码技术进行转换。此外,我们对TEMP列进行了归一化,以确保一致性并便于模型训练。鉴于我们在之前的章节中详细讨论了此管道的每个步骤,我们将简要回顾它,更多地关注高级概述而不是深入解释:

import snowflake.ml.modeling.preprocessing as snowml
from snowflake.ml.modeling.pipeline import Pipeline
from snowflake.snowpark.types import IntegerType
# CREATING ID COLUMN
from snowflake.snowpark.functions \
    import monotonically_increasing_id
df = df.withColumn("ID", monotonically_increasing_id())
df = df.drop("DATETIME","DATE")
CATEGORICAL_COLUMNS = ["SEASON","WEATHER"]
CATEGORICAL_COLUMNS_OHE = ["SEASON_OE","WEATHER_OE"]
MIN_MAX_COLUMNS = ["TEMP"]
import numpy as np
categories = {
    "SEASON": np.array([1,2,3,4]),
    "WEATHER": np.array([1,2,3,4]),
}

这个代码块利用了 Snowflake 的机器学习能力进行数据预处理。它导入了必要的模块,例如预处理函数和Pipeline类。代码创建了一个新的ID列,为每一行提供唯一的标识符,并删除了不必要的列。它定义了分类列及其经过独热编码后的版本列表,以及需要归一化的列。此外,它还指定了每个分类列的类别,可能用于编码目的,从而便于有效的机器学习模型处理:

preprocessing_pipeline = Pipeline(
    steps=[
        (
            "OE",
            snowml.OrdinalEncoder(
                input_cols=CATEGORICAL_COLUMNS,
                output_cols=CATEGORICAL_COLUMNS_OHE,
                categories=categories
            )
        ),
        (
            "MMS",
            snowml.MinMaxScaler(
                clip=True,
                input_cols=MIN_MAX_COLUMNS,
                output_cols=MIN_MAX_COLUMNS,
            )
        )
    ]
)
transformed_df = preprocessing_pipeline.fit(df).transform(df)
transformed_df.show()

在第一步中,对 CATEGORICAL_COLUMNS 列表中指定的类别列应用有序编码器 (OE),将其转换为 CATEGORICAL_COLUMNS_OHE 列表中定义的独热编码版本。categories 参数指定每个类别列的类别,可能用于编码目的。

在第二步中,使用 min-max 缩放器 (MMS) 对 MIN_MAX_COLUMNS 列表中指定的列进行归一化。此缩放器确保这些列中的值缩放到特定的范围,通常是 01 之间,同时保留它们的相对比例。

预处理管道随后应用于 df DataFrame,使用 fit-transform 模式,其中管道首先拟合到数据以学习参数(例如,用于有序编码的类别映射),然后应用于转换 DataFrame。转换后的 DataFrame 然后使用 show() 方法显示。总体而言,此代码通过使用指定的管道预处理其列,为进一步分析或模型训练准备 DataFrame。结果 DataFrame 如下所示:

图 6.19 – 转换后的 DataFrame

图 6.19 – 转换后的 DataFrame

在整个模型构建过程中,使用特征子集构建了各种模型,例如天气特征和时间相关特征。此外,通过组合数据开发模型以确保卓越的性能。为了加速模型构建过程并减少数据工程开销,我们将与天气相关的特征组织到一个专用的特征视图中。随后,我们将利用这个特征视图在接下来的部分生成数据集并构建 XGBoost 模型:

feature_df = transformed_df.select(["SEASON_OE",
    "WEATHER_OE", "TEMP", "ATEMP", "HUMIDITY",
    "WINDSPEED", "ID"])
fv = FeatureView(
    name="WEATHER_FEATURES",
    entities=[entity],
    feature_df=feature_df,
    desc="weather features"
)
fv = fs.register_feature_view(
    feature_view=fv,
    version="V1",
    block=True
)
fs.read_feature_view(fv).show()

代码从 DataFrame 中选择特定列以创建特征 DataFrame (feature_df)。然后,它构建一个名为 WEATHER_FEATURES 的特征视图,与之前定义的实体相关联,并在特征存储库 (fs) 中以版本 V1 注册。结果 DataFrame 如下所示:

图 6.20 – 特征 DataFrame

图 6.20 – 特征 DataFrame

一旦开发完成,特征可以系统地存储在特征存储库中,从而促进它们在各种机器学习模型和团队之间的重用或无缝共享。这一功能显著加速了新机器学习模型的创建,消除了从头开始构建每个特征的冗余。同样,我们可以通过组合相似的特征来创建另一个特征视图,作为租赁特征。

准备数据集

一旦我们的特征管道被精心配置并准备就绪,我们可以启动它们的部署以生成训练数据。随后,这些特征管道成为促进模型预测的关键工具,标志着从特征工程到机器学习模型实际应用的顺利过渡:

#GENERATING TRAINING DATA
spine_df = session.table("BSD_TRAINING")
spine_df = spine_df.withColumn("ID",
    monotonically_increasing_id())
spine_df = spine_df.select("ID", "COUNT")
spine_df.show()
train_data = fs.generate_dataset(
    spine_df=spine_df,
    features=[
        fv.slice([
            "HUMIDITY","SEASON_OE","TEMP",
            "WEATHER_OE","WINDSPEED"
        ])
    ],
    materialized_table=None,
    spine_timestamp_col=None,
    spine_label_cols=["COUNT"],
    save_mode="merge",
    exclude_columns=['ID']
)
train_data.df.show()

创建训练数据变得简单,因为物化特征视图本身包含关键元数据,如用于点时间PIT)查找的连接键和时间戳:

图 6.21 – 训练数据

图 6.21 – 训练数据

该过程主要涉及提供脊柱数据——之所以称为脊柱数据,是因为它作为由特征连接丰富的基础结构。在我们的案例中,脊柱数据包括要预测的特征——COUNT——以及连接键列 ID。此外,通过切片,可以灵活地生成包含特征视图内特征子集的数据集。现在我们已经准备好了训练数据,我们将使用它来训练模型并使用特征存储库预测数据输出。

所有数据的准备——无论是用于训练还是运营使用——都需要通过特征管道进行细致的处理。这些管道类似于传统数据管道,它们汇总、验证和转换数据输出,使其适合输入到 ML 模型中。适当的特征管道编排确保在数据被输入到模型之前对其进行精炼,从而保持从训练过程中提取的特征的完整性和相关性。

模型训练

在前一章中,我们详细介绍了模型构建过程,因此在本节中,我们将专注于使用从特征存储库的特征视图生成的训练数据集来构建模型。我们在训练梯度提升模型时使用了与上一章类似的方法,但只是使用了特征视图:

from snowflake.ml.modeling.model_selection import GridSearchCV
from snowflake.ml.modeling.ensemble \
    import GradientBoostingRegressor
FEATURE_LIST = ["TEMP", "WINDSPEED", "SEASON_OE", "WEATHER_OE"]
LABEL_COLUMNS = ['COUNT']
OUTPUT_COLUMNS = ['PREDICTED_COUNT']
param_grid = {
    "n_estimators":[100, 200, 300, 400, 500],
    "learning_rate":[0.1, 0.2, 0.3, 0.4, 0.5],
}
grid_search = GridSearchCV(
    estimator=GradientBoostingRegressor(),
    param_grid=param_grid,
    n_jobs = -1,
    scoring="neg_root_mean_squared_error",
    input_cols=FEATURE_LIST,
    label_cols=LABEL_COLUMNS,
    output_cols=OUTPUT_COLUMNS
)
train_df = train_data.df.drop(["ID"])
grid_search.fit(train_df)

Snowpark 的优雅之处体现在这种简单性上,因为使用特征视图无缝训练模型不需要进行任何重大修改。我们将创建测试数据,使用以下代码测试模型的准确性:

test_df = spine_df.limit(3).select("ID")
enriched_df = fs.retrieve_feature_values(
    test_df, train_data.load_features())
enriched_df = enriched_df.drop('ID')
enriched_df.show()

这通过从 spine_df 的前三行中选择 ID 列创建了一个测试 DataFrame (test_df)。然后,它使用特征存储库和从特征视图生成的训练数据检索并显示测试数据框的特征值:

图 6.22 – 测试数据

图 6.22 – 测试数据

现在测试数据已经准备好了,我们可以使用这些数据来预测模型并获取结果。

模型预测

在本节中,我们将使用从特征存储库生成的测试数据来进行预测:

pred = grid_search.predict(enriched_df.to_pandas())
pred.head()

预测结果显示了预测的计数值,显示了在给定小时使用共享自行车的客户数量:

图 6.23 – 模型预测

图 6.23 – 模型预测

这展示了使用特征存储库构建 Snowpark ML 模型的简便性和改进。在下一节中,我们将强调使用特征存储库的一些好处。

何时使用特征存储库以及何时避免使用特征存储库

特征存储库在需要高效管理特征并在多个模型或团队之间重用的情况下特别有利。它们在以下场景中表现出色:

  • 特征重用:特征需要在不同的机器学习模型或团队之间重用或共享,以减少特征工程中的重复工作

  • 一致性和治理:确保在多样化的机器学习项目或团队中,特征的定义、文档和治理保持一致至关重要

  • 模型性能:通过确保训练和推理管道中特征定义的一致性来维持模型性能的峰值,从而避免由于差异导致的性能下降

  • 数据协作:通过提供一个集中平台用于特征开发、存储、修改和共享,促进涉及机器学习项目的不同团队或利益相关者的协作

  • 可扩展性:高效处理大量特征和数据,尤其是在数据持续演变或更新的环境中

然而,在以下场景中,特征存储可能并非必要:

  • 简单模型:对于具有少量特征和最小复杂性的简单模型,设置和维护特征存储的开销可能超过其带来的好处

  • 静态数据:在数据相对静态且不需要频繁更新或特征工程的情况下,对特征存储的需求可能有限

  • 有限的协作:当机器学习项目涉及一个规模小、紧密合作的团队,且不需要广泛协作或特征共享时,使用特征存储可能是不必要的

  • 资源限制:资源或基础设施有限的组织可能发现有效实施和维护特征存储具有挑战性

总结来说,虽然特征存储为机器学习项目中的高效特征管理提供了众多好处,但其采用应根据每个项目或组织的具体需求和限制进行仔细考虑。

摘要

本章讨论了模型注册表以及有意义的标签和描述的重要性,提供了上下文并促进了实验跟踪。我们还强调了操作模型注册表的不同方法。我们探讨了 Snowflake Feature Store 在 Snowpark ML 生态系统中的功能,以及如何利用它来管理 Snowflake 模型。

在下一章中,我们将学习如何使用 Snowpark 框架开发原生应用程序。

第三部分:Snowpark 应用

本部分重点介绍如何使用原生应用框架容器服务在 Snowflake 上开发和部署应用程序和 LLMs。

本部分包括以下章节:

  • 第七章, 使用 Snowpark 开发原生应用

  • 第八章, Snowpark 容器服务简介

第七章:使用 Snowpark 开发原生应用

在今天以数据驱动的世界中,对可操作洞察的需求达到了前所未有的高度。然而,传统的数据分析工作流程往往需要解决缓慢的处理时间、孤岛数据和复杂的开发程序等问题。这正是 Snowpark 凭借其原生应用的独特特性来拯救的地方。通过提供强大而创新的解决方案,Snowpark 克服了这些挑战,并通过在 Snowflake 市场中的分发为组织开辟了新的收入渠道。

在本章中,我们将涵盖以下主要主题:

  • 原生应用框架简介

  • 开发原生应用

  • 发布原生应用

  • 管理原生应用

技术要求

要设置环境,请参考上一章中的技术要求。支持材料可在github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark找到。

原生应用框架简介

数据分析已经超越了静态报告。在当今的商业环境中,实时洞察、交互式视觉和与操作工具的无缝集成需求极高。虽然使用 SQL 或预构建仪表板的传统方法有其位置,但它们需要更多的敏捷性和灵活性来满足现代数据探索的需求。这正是原生应用介入的地方,它们提供了即时和可操作的洞察的巨大潜力,并为你的数据分析开启了新的可能性。

Snowflake 原生应用通过允许你在数据云中直接开发应用程序,提供了一个独特的优势。这些应用程序与你的数据并存,消除了数据移动或外部连接器的需求。这消除了系统之间的数据传输瓶颈,并显著降低了网络延迟。Snowflake 的架构,针对并行处理和分布式存储进行了优化,比传统模型能更快地分析复杂查询和大数据集。此外,原生应用还受益于 Snowflake 的安全基础,它提供了一个多层安全架构,包括加密、访问控制和审计跟踪等特性,无需单独的基础设施。

Snowflake 原生应用景观

Snowflake 原生应用框架是构建利用 Snowflake 核心功能的数据应用的有力工具。使用此框架,你可以通过与其他 Snowflake 账户共享数据和相关的业务逻辑来扩展 Snowflake 功能。这可以包括使用 Streamlit 应用、存储过程和用 Snowpark API、JavaScript 和 SQL 编写的函数。通过利用 Snowflake 的能力,你可以创建针对你独特需求的高级应用。

除了扩展 Snowflake 的功能外,Snowflake Native App Framework 还允许您与他人共享您的应用程序。您可以通过 Snowflake 市场的免费或付费列表分发您的应用程序,或者私下将它们分发给特定的消费者。此外,此框架还允许您使用 Streamlit 在应用程序中创建丰富的可视化。借助 Snowflake Native App Framework,您拥有创建强大、可扩展和灵活的数据应用程序所需的所有工具,以满足您组织的需要。

总结来说,Snowflake Native App Framework 为构建复杂的数据应用程序提供了一个全面的解决方案。无论您需要扩展 Snowflake 的功能、与他人共享应用程序,还是创建丰富的可视化,此框架都能满足您的需求。凭借其强大的功能和可扩展性,Snowflake Native App Framework 是任何希望利用 Snowflake 全部潜能构建数据应用程序的组织不可或缺的工具。

Snowflake 原生应用程序由定义应用程序核心功能的企业逻辑组成,包括数据转换、计算和模型预测等任务。数据模型可以使用 Snowpark 开发,原生应用程序利用 Snowflake 强大的引擎高效地与您的数据交互。Snowpark 使您能够操作表、查询数据集并提取有意义的见解。用户界面可以使用 Streamlit 开发,用户可以在 Snowflake 中以原生方式与应用程序交互。

Snowpark 通过提供几个关键特性来弥合传统数据分析与现代应用程序开发之间的差距,使其成为构建原生应用程序的理想引擎。应用程序可以无缝地与您的数据量一起扩展,消除了手动基础设施配置的需求。

在深入开发 Snowpark 应用程序之前,让我们了解 Native Apps 提供的优势。

Native App Framework 组件

Snowflake Native App Framework的组件包括提供者,应用程序的开发者和分发者,以及消费者,他们访问提供者分发的数据和应用程序。Native App Framework 的高级组件如下图所示:

图 7.1 – Native App Framework 组件

图 7.1 – Native App Framework 组件

提供者账户是应用程序开发和打包以供分发的地方。该包包括应用程序代码、Snowpark 模型和相关数据。它还包括应用程序包所需的各项许可证和权益。然后,应用程序被打包并可供分发。

提供者可以将包作为公共市场列表分发,供其他组织消费,或者通过私人列表分发,这也可以在同一个组织内部作为内部应用分发。消费者账户部署应用程序,并可以使用本地数据访问外部系统。此类应用可以使用 Python 中的 Streamlit 构建。

Snowflake 中的 Streamlit

Streamlit 是一个开源的 Python 库,它使得创建和分享用于机器学习和数据科学的自定义 Web 应用变得简单。它允许你快速构建和部署强大的数据应用。Snowflake 中的 Streamlit 帮助你超越纯粹的数据,进入应用逻辑的领域,使应用变得生动,并将见解转化为行动和反应。使用 Snowflake 中的 Streamlit,你可以构建在 Snowflake 中处理和使用数据的应用,而无需将数据或应用代码移动到外部系统。原生应用可以直接将交互式数据可视化和用户界面嵌入到应用中,实现动态图表和图形,这些图表和图形在用户与数据交互时更新,并为用户提供交互式数据探索工具,以便深入挖掘并发现更深刻的见解。

让我们讨论原生应用提供的各种优势。

原生应用的优势

在 Snowflake 内部构建的原生应用为数据分析与决策提供了无与伦比的优势。原生应用提供了多项好处;通过提供 Snowpark 的简化开发,它们消除了与开发框架相关联的陡峭学习曲线。开发者可以无缝过渡到 Snowflake 强大云环境中的数据操作,从而实现快速创新。这使开发者能够专注于构建有影响力的应用和提取有价值的见解,而无需克服语法障碍。

Snowflake 与 Streamlit 的集成使得开发者能够在 Snowflake 内部直接释放他们现有的 Python 专长。开发者可以构建用于数据探索、机器学习和交互式仪表板的高级应用。这消除了环境切换,并在 Snowflake 安全且可扩展的环境中最大化了生产力。Snowflake 的数据驻留原则确保有价值的信息保持在安全范围内,显著降低了数据泄露的风险。

原生应用能够无缝地与现有的 Snowflake 功能和工具,如任务和流,进行接口交互,创建一个集成的分析环境。利用 Snowflake 任务进行自动执行和调度,结合 Snowflake 流或 Snowpipe 进行实时数据摄取和交付,确保持续更新并提升性能。Snowflake 用户定义函数UDFs)和存储过程也提高了灵活性并最小化了数据移动。我们将在下一节中探讨 Snowflake 中的 Streamlit。

随着我们深入本章内容,我们将利用熟悉的共享单车数据集及其在上一章中看到的关联 Snowpark 模型,并使用 Snowsight 开发 Streamlit 应用程序。

开发本地应用程序

第一步涉及开发一个 Streamlit 应用程序来展示 Snowpark 模型的开发。该应用程序提供了一个用户友好的界面,简化了对 Snowpark 底层数据和建模技术的探索和理解。在 Snowflake 中,可以通过 Snowsight 中的专用 Python 编辑器开发 Streamlit 应用程序。此编辑器使用户能够轻松地编写、编辑和执行 Streamlit 应用程序的代码。值得注意的是,它提供了自动完成和全面文档等宝贵功能,在利用 Streamlit 和 Snowpark 功能的同时,提供了一个顺畅直观的开发体验。我们将使用此编辑器来开发 Streamlit 应用程序。

让我们深入了解在 Snowflake 环境中创建 Streamlit 应用程序的详细步骤:

注意

用户在模式级别需要CREATE STREAMLIT权限来开发应用程序,并且此应用程序将利用仓库计算来运行,因此请适当选择仓库大小。X-Small 将足以用于本章中开发的 Streamlit 和本地应用程序。

  1. 登录成功后,在 Snowsight 左侧导航栏中选择项目,然后选择Streamlit以启动 Streamlit 应用程序:

图 7.2 – Streamlit Snowsight

图 7.2 – Streamlit Snowsight

  1. 在 Streamlit 项目部分,点击+ Streamlit 应用程序按钮以创建一个新的 Streamlit 应用程序:

图 7.3 – Streamlit 应用程序创建

图 7.3 – Streamlit 应用程序创建

  1. 点击按钮后,将出现创建 Streamlit 应用程序窗口。提供应用程序名称,如此处所示:

图 7.4 – 配置应用程序详情

图 7.4 – 配置应用程序详情

  1. 从下拉菜单中选择您希望执行应用程序查询的适当仓库。从相应的下拉菜单中指定应用程序将托管的数据库和模式:

图 7.5 – 定义执行环境

图 7.5 – 定义执行环境

  1. 通过点击创建按钮完成创建过程,这将启动 Streamlit 应用程序的设置。创建成功后,Streamlit 将在您的 Snowflake 编辑器中启动,以查看器模式展示 Streamlit 应用程序的示例。此模式提供了应用程序将如何呈现给最终用户的预览,有助于可视化评估其用户界面:

图 7.6 – Streamlit Snowflake 编辑器

图 7.6 – Streamlit Snowflake 编辑器

在下一节中,我们将发现并熟悉 Streamlit 编辑器。

Streamlit 编辑器

Snowflake 中的 Streamlit 编辑器有助于在 Snowflake 中开发 Streamlit 应用程序。编辑器集成在 Snowsight 界面中,使得创建应用程序更加容易,无需外部环境。Snowflake 中的 Streamlit 界面分为三个主要窗格。

对象浏览器可以查看你权限下可访问的数据库、模式和视图,有助于数据探索和访问。Streamlit 编辑器包含一个定制的 Python 编辑器,专门用于编写 Streamlit 应用程序代码,便于开发和定制。Streamlit 预览实时显示运行中的 Streamlit 应用程序,允许你在修改时观察其行为和外观。

默认情况下,只有 Streamlit 编辑器和预览窗格可见。然而,你可以根据个人喜好调整界面布局。使用 Snowflake 编辑器左下角的 显示/隐藏 按钮根据工作流程需求切换不同窗格的可见性。

现在我们已经熟悉了界面,接下来将在下一节创建我们的第一个 Streamlit 应用程序。

运行 Streamlit 应用程序

在本节中,我们将使用 GitHub 仓库中 chapter_7 文件夹下的 Streamlit 代码构建一个应用程序(github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark/tree/main/chapter_7)。SnowparkML 代码在前几章中已有详细说明,并在仓库中提供。将仓库中的 streamlit_bike_share_analysis.py 文件复制粘贴到 Streamlit 编辑器中。该代码需要使用之前使用的 Bike Sharing 数据集创建的 BSD_TRAIN 表。可以通过运行 chapter_7_data_load.ipynb 文件来创建所需的表。

现在,通过选择 运行 选项来刷新应用程序,结果将在 Streamlit 预览窗格中更新:

图 7.7 – Streamlit 应用预览

图 7.7 – Streamlit 应用预览

该代码需要在你的 Streamlit 环境中安装几个包。在代码编辑器中点击 按钮并选择以下包:

  • Matplotlib

  • Seaborn

  • SciPy

  • Snowflake-ml-python

你已在 Snowflake 中成功运行了第一个 Streamlit 应用程序。你可以在 Snowsight 环境中选择应用程序来执行:

图 7.8 – Streamlit 应用

图 7.8 – Streamlit 应用

注意

Snowsight 为创建单页 Streamlit 应用程序提供了一个方便的平台。然而,部署多页 Streamlit 应用程序需要稍微不同的方法,需要使用 SQL 命令。一旦部署过程完成,您就可以在 Snowsight 的界面中无缝导航和交互多页应用程序。

在下一节中,我们将使用原生应用程序框架开发和推送 Streamlit 应用程序。

使用原生应用程序框架进行开发

现在,我们的 Streamlit 应用程序已经运行,让我们探索将其转换为 Snowflake 生态系统中的原生应用程序的过程。在本节中,我们将详细介绍如何利用原生应用程序框架开发应用程序,以便与其他 Snowflake 账户共享数据和相关的业务逻辑。我们将主要使用 Snowsight 网络界面来演示这一点,该界面也可以使用 VS Code 等客户端创建。

以下顺序步骤将把您的现有 Streamlit 应用程序转换为 Snowflake 中的原生应用程序:

  1. 创建应用程序文件

  2. 创建应用程序包

  3. 上传应用程序文件到命名阶段

  4. 安装应用程序

  5. 版本控制应用程序

  6. 测试应用程序

我们将从第一步开始,创建应用程序文件。

创建应用程序文件

创建应用程序文件包括创建设置脚本和清单文件,这些是原生应用程序框架必需的组件。

创建设置脚本

设置脚本是一个 SQL 文件,当消费者在其账户中安装应用程序时自动执行。它简化了配置和设置过程,并包含配置应用程序所需的初始化指令。要启动创建设置脚本,请按照以下步骤操作:

  1. 我们首先将在您的本地文件系统中创建一个目录结构。创建一个名为 native_apps 的文件夹,作为应用程序外部文件的根目录。

  2. 在此文件夹中,创建一个名为 scripts 的子文件夹来存储脚本文件。此文件夹将包含应用程序所需的所有脚本文件。

  3. scripts 文件夹中,创建一个名为 setup.sql 的新文件,并添加以下语句:

    -- Setup script for the Bike Share Streamlit application.
    CREATE APPLICATION ROLE app_public;
    CREATE OR ALTER VERSIONED SCHEMA code_schema;
    GRANT USAGE ON SCHEMA code_schema 
      TO APPLICATION ROLE app_public;
    CREATE STREAMLIT code_schema.bike_share_streamlit
      FROM '/streamlit'
      MAIN_FILE = '/streamlit_bike_share_analysis.py';
    GRANT USAGE ON STREAMLIT code_schema.bike_share_streamlit
      TO APPLICATION ROLE app_public;
    

随着我们进入本节,我们将看到之前代码的详细解释。

创建 Streamlit 应用程序文件

我们将结合应用程序逻辑和 Streamlit 应用程序,利用其在数据科学和机器学习任务中的功能。利用 Streamlit 应用程序增强了用户交互并促进了数据可视化。我们将通过创建 Streamlit 应用程序开始这个过程:

  1. native_apps 目录中创建一个名为 streamlit 的子文件夹。

  2. 在文件夹中,创建一个名为 streamlit_bike_share_analysis.py 的文件。该代码位于 chapter_7 文件夹内的存储库中。

  3. 将以下代码复制并粘贴到该文件中并保存:

    # Set page config
    st.set_page_config(layout="wide")
    # Get current session
    session = get_active_session()
    @st.cache_data()
    def load_data():
        # Load Bike Share data
        snow_df = session.table(
            "SNOWPARK_DEFINITIVE_GUIDE.MY_SCHEMA.BSD_TRAIN")
    
streamlit_bike_share_analysis.py file under the chapter_7 folder in the book GitHub repository for the complete code.

创建 README 文件

README 文件作为描述性指南,概述了应用程序的功能。在 Snowsight 中查看应用程序时可以轻松访问。要为应用程序生成 README 文件,请按照以下步骤操作:

  1. 导航到native_apps目录并创建一个名为readme.md的文件。

  2. 将以下内容填充到readme.md文件中,详细说明应用程序的目的和功能:

    # Bike Sharing Analysis - Through Native Apps.
    This native app presents the analysis we performed on the Kaggle dataset - Bike sharing demand as an interactive dashboard
    

现在我们可以在下一节创建清单文件。

创建清单文件

使用 YAML 语法的清单文件包含有关应用程序的基本配置细节。它是概述适当功能关键规范的基石文档,并且每个在原生应用程序框架中开发的应用程序都需要一个相应的清单文件。

要生成清单文件,请按以下步骤操作:

  1. native_apps文件夹中,创建一个名为manifest.yml的新文件。

  2. 将以下内容填充到manifest.yml文件中:

    manifest_version: 1
    artifacts:
      setup_script: scripts/setup.sql
      readme: readme.md
    

setup_script属性指定了设置脚本相对于清单文件位置的路径。请确保指定的路径和文件名与之前创建的设置脚本完全一致。

注意

清单文件对您的项目至关重要。它必须命名为manifest.yml并放置在根目录。所有文件路径,包括设置脚本,都是相对于此文件位置的相对路径。manifest_versionartifactssetup_script等基本属性是强制性的,以确保正确运行。您可以可选地包含readme属性以提供额外的文档。

在下一节中,我们将为原生应用程序创建应用程序包。

创建应用程序包

在本节中,您将创建一个应用程序包,它是应用程序资源的容器。应用程序包扩展 Snowflake 数据库,包括额外的应用程序相关信息,作为一个综合容器,包含共享数据内容和应用程序。要创建应用程序包,您的角色需要具有创建应用程序包的权限。执行以下命令以授予权限:

GRANT CREATE APPLICATION PACKAGE ON ACCOUNT
  TO ROLE accountadmin;

执行后,您应该看到以下输出:

图 7.9 – 角色创建

图 7.9 – 角色创建

现在,运行以下命令以创建SNOWFLAKE_NATIVE_APPS_PACKAGE应用程序包:

CREATE APPLICATION PACKAGE snowflake_native_apps_package;

您将收到以下输出,确认已创建包:

图 7.10 – 包创建

图 7.10 – 包创建

应用程序包将被创建,当前上下文将切换到SNOWFLAKE_NATIVE_APPS_PACKAGE。我们可以通过运行以下命令来确认包列表:

SHOW APPLICATION PACKAGES;

这将显示以下输出:

图 7.11 – 显示包

图 7.11 – 显示包

现在应用包已经创建,下一步是创建一个用于上传应用文件的命名阶段。通过运行以下命令切换到应用包的上下文:

USE APPLICATION PACKAGE snowflake_native_apps_package;

这将显示以下输出:

图 7.12 – 使用 snowflake_native_apps_package

图 7.12 – 使用 snowflake_native_apps_package

我们需要一个架构来托管阶段。通过运行以下命令创建一个架构:

CREATE SCHEMA my_schema;

架构创建后,您将看到以下消息:

图 7.13 – 创建新架构

图 7.13 – 创建新架构

下一步是在此架构中创建阶段。通过运行以下代码创建一个阶段:

CREATE OR REPLACE STAGE snowflake_native_apps_package.my_schema.my_stage FILE_FORMAT = (TYPE = 'csv' FIELD_DELIMITER = '|' SKIP_HEADER = 1);

通过这一步,您在应用包内建立了一个命名阶段,为上传构建应用所需的关键文件提供了一个指定的空间。

关于 setup.sql 的说明

以下 SQL 命令集是 setup.sql 的一部分,并打包在 chapter_7 中的原生应用文件夹内。此文件将被上传到阶段区域,并在我们创建原生应用时使用,因此不需要在 Snowflake 工作表中执行此代码。

让我们看看 set.sql 文件包含什么内容以创建一个原生应用应用。我们还将创建一个必需的应用角色。要创建应用角色,请运行以下命令:

CREATE APPLICATION ROLE app_public;
CREATE OR ALTER VERSIONED SCHEMA code_schema;
GRANT USAGE ON SCHEMA code_schema TO APPLICATION ROLE app_public;

此代码初始化一个名为 app_public 的应用角色。然后创建或更新一个名为 code_schema 的版本化架构。最后,它授予 app_public 应用角色使用 code_schema 架构的权限。这种设置通常用于在数据库应用中管理访问控制和架构组织。

运行以下代码以集成 Streamlit 应用:

CREATE STREAMLIT code_schema.bike_share_streamlit
  FROM '/streamlit'
  MAIN_FILE = '/streamlit_bike_share_analysis.py';
GRANT USAGE ON STREAMLIT code_schema.bike_share_streamlit
  TO APPLICATION ROLE app_public;

此代码在 code_schema 架构内创建一个名为 bike_share_streamlit 的 Streamlit 应用,其内容来自 /streamlit 目录,主文件来自 /streamlit_bike_share_analysis.py。然后它授予此 Streamlit 应用对 app_public 应用角色的使用权限,允许在应用范围内授权用户访问。这有助于将基于 Streamlit 的自行车共享分析工具集成到数据库设置中,并通过应用角色管理受控访问。

上传应用文件到命名阶段

应用文件需要上传到命名阶段。它们可以通过 Snowsight 或通过如 第二章 中所述的 Snowpark Python 代码上传,为使用 SnowSQL 客户端上传文件到阶段的读者,执行以下步骤:

  1. 导航到您本地机器上的 native_apps 文件夹。

  2. 运行以下 SnowSQL 命令上传所有文件夹和文件:

    PUT file:///<path_to_your_root_folder>/native_apps/manifest.yml @snowflake_native_apps_package.my_schema.my_stage overwrite=true auto_compress=false;
    PUT file:///<path_to_your_root_folder>/native_apps/scripts/setup.sql @snowflake_native_apps_package.my_schema.my_stage/scripts overwrite=true auto_compress=false;
    PUT file:///<path_to_your_root_folder>/native_apps/streamlit/streamlit_bike_share_analysis.py @snowflake_native_apps_package.my_schema.my_stage/streamlit overwrite=true auto_compress=false;
    PUT file:///<path_to_your_root_folder>/native_apps/readme.md @snowflake_native_apps_package.my_schema.my_stage overwrite=true auto_compress=false;
    
  3. 在工作表中运行以下命令以验证文件上传是否成功:

    List @snowflake_native_apps_package.my_schema.my_stage;
    

    运行命令后,上传的文件将在输出中显示:

图 7.14 – 通过 SnowSQL 检查应用程序文件

图 7.14 – 通过 SnowSQL 检查应用程序文件

我们也可以在 Snowsight 中检查应用程序文件:

图 7.15 – 通过 Snowsight 检查应用程序文件

图 7.15 – 通过 Snowsight 检查应用程序文件

现在我们已经将文件上传到阶段,我们将安装应用程序包到 Snowflake。

安装应用程序

执行以下命令来安装应用程序:

CREATE APPLICATION bike_share_native_app 
  FROM APPLICATION PACKAGE snowflake_native_apps_package 
  USING '@snowflake_native_apps_package.my_schema.my_stage';

这将在 Snowflake 内部自动创建一个新的当前版本的应用程序。原生应用程序还提供了对应用程序进行版本控制和更新版本的能力,我们将在下一节中介绍。

图 7.16 – 应用程序已安装

图 7.16 – 应用程序已安装

现在应用程序已安装,让我们更新应用程序的版本。

应用程序版本控制

在本节中,我们将更新应用程序的版本并将其安装到 Snowflake。我们将使用ALTER APPLICATION PACKAGE命令来更新先前创建的应用程序。要更新新版本,请按照以下步骤操作:

  1. 运行以下命令将版本追加到SNOWFLAKE_NATIVE_APPS_PACKAGE

    ALTER APPLICATION PACKAGE snowflake_native_apps_package 
      ADD VERSION v1_0 
      USING '@ snowflake_native_apps_package.my_schema.my_stage';
    

    这将显示以下输出:

    图 7.17 – 修改应用程序版本

    图 7.17 – 修改应用程序版本

    此命令通过结合从先前部分上传到指定阶段的文件中派生的版本来修改应用程序包。

  2. 接下来,通过执行以下命令确认版本更新的成功:

    SHOW VERSIONS IN APPLICATION PACKAGE snowflake_native_apps_package;
    

    这将显示更新后的版本作为输出:

    图 7.18 – 应用程序的更新版本

    图 7.18 – 应用程序的更新版本

    此命令提供了关于版本的额外见解,包括创建时间戳和审查状态。

  3. 在您安装新版本之前,通过运行以下命令删除现有应用程序:

    DROP APPLICATION bike_share_native_app;
    

    您应该看到以下确认消息:

    图 7.19 – 应用程序已删除

    图 7.19 – 应用程序已删除

    现在现有应用程序已被删除,我们可以安装新的应用程序。

  4. 最后,要安装应用程序的新版本,请运行以下代码:

    CREATE APPLICATION bike_share_native_app 
      FROM APPLICATION PACKAGE snowflake_native_apps_package 
      USING VERSION V1_0;
    

    这将基于指定的版本创建一个新的应用程序,确保您的应用程序包含所有最新的增强功能和特性。

注意

版本提供的值作为标签,而不是数值或字符串值。需要注意的是,在添加新版本时,补丁号会自动设置为0。随后添加到该版本的补丁将自动递增。然而,在引入新版本,如V1_1时,该特定版本的补丁号将重置为0

我们已成功部署新版本的应用程序。在下一节中,我们将在 Snowflake 环境中运行应用程序以测试它。

测试应用程序

在本节中,我们将在 Snowpark 中运行我们的本地应用程序以进行测试。虽然前面的章节主要使用 SQL 语句进行测试和信息检索,但 Snowsight 提供了另一种与您的应用程序交互和检查的界面。此外,您可以检查您已部署的 Streamlit 应用程序。要访问和探索 Snowsight 中的应用程序,请按照以下步骤操作:

  1. 在 Snowsight 中,切换到ACCOUNTADMIN角色,该角色授予您在 Snowsight 中有效查看和管理应用程序所需的权限:

图 7.20 – 切换到 ACCOUNTADMIN 角色

图 7.20 – 切换到 ACCOUNTADMIN 角色

  1. 在 Snowsight 的直观导航菜单中,找到并选择数据产品选项,然后选择应用程序。此操作将您引导到 Snowflake 环境中已安装应用程序的仓库:

图 7.21 – 本地应用程序仓库

图 7.21 – 本地应用程序仓库

  1. 已安装应用程序列表中找到您的应用程序,并选择BIKE_SHARE_NATIVE_APP。此操作提供了您应用程序的全面视图,让您深入了解其各种组件和功能。Readme标签提供了访问您之前添加到 README 文件的内容,为您提供了关于应用程序目的和功能的见解:

图 7.22 – 本地应用程序 README 文件

图 7.22 – 本地应用程序 README 文件

  1. 定位并选择BIKE_SHARE_STREAMLIT以访问您部署的 Streamlit 应用程序。选择后,SNOWFLAKE_NATIVE_APPS_PACKAGE数据库的内容将在 Streamlit DataFrame 中展示,便于动态交互和数据可视化:

图 7.23 – 本地应用程序

图 7.23 – 本地应用程序

步骤 4 的注意事项

在执行步骤 4 之前,请确保chapter_7.sqlCREATE VIEW ON BSD TRAIN TABLE部分下的所有命令都已执行。这是访问位于SNOWPARK_DEFINITIVE_GUIDE数据库中的BSD_TRAIN表所必需的。

Streamlit 应用程序在 Snowflake 环境中成功运行,突出了数据表示和交互功能。

在下一节中,我们将探讨如何发布本地应用程序。

发布本地应用程序

在本节中,我们将通过创建一个使用应用程序包作为核心数据内容的私密列表来检查应用程序的发布。此过程使您能够与其他 Snowflake 用户无缝共享应用程序,并使他们能够在自己的账户中安装和使用它。我们将首先设置发布指令。

设置默认发布指令

在继续创建列表之前,建立指定消费者可访问的应用程序版本的默认发布指令至关重要。要将版本v1_0和补丁0指定为默认发布指令,请执行以下命令:

SHOW VERSIONS IN APPLICATION PACKAGE snowflake_native_apps_package;

这将显示以下输出:

图 7.24 – 检查应用程序版本

图 7.24 – 检查应用程序版本

输出确认了默认发布指令的成功建立,确保了版本管理的清晰和一致性。

在下一节中,我们将创建应用程序的列表。

为您的应用程序创建列表

在默认发布指令就绪的情况下,我们将为您的应用程序创建一个列表,将应用程序包作为其共享数据内容。按照以下步骤创建列表:

  1. 通过导航到数据产品 | 提供者工作室来打开提供者工作室,以访问列表创建界面:

图 7.25 – 提供者工作室

图 7.25 – 提供者工作室

  1. 接下来,通过选择+ 列表来启动列表创建,打开创建列表窗口,提示您指定列表的名称和其他必要细节:

图 7.26 – 创建列表对话框

图 7.26 – 创建列表对话框

  1. 为您的列表输入一个描述性的名称,并选择仅指定消费者可见性选项,以确保仅与特定账户进行私密共享。选择与列表关联的应用程序包,有效地将其链接为核心数据内容。提供您列表的全面描述,以便让潜在用户了解其功能和目的:

图 7.27 – 创建列表描述

图 7.27 – 创建列表描述

  1. 通过包含用于测试从列表安装应用程序的消费者体验的账户标识符来添加消费者账户:

图 7.28 – 添加消费者账户

图 7.28 – 添加消费者账户

现在,您已成功创建了一个包含应用程序包作为共享数据内容的私密列表,便于在指定用户之间简化应用程序的传播和安装。

以下部分将讨论如何管理原生应用程序。

管理原生应用程序

配置和管理使用原生应用程序框架创建的已安装应用程序涉及各种任务,包括查看已安装应用程序、访问 README 信息、将应用程序角色授予账户角色以及卸载应用程序。让我们更详细地探讨每个方面。

查看已安装的应用程序

要访问和查看与您的账户关联的已安装应用程序或 Streamlit 应用程序,请按照以下步骤操作:

  1. 在导航菜单中,选择数据产品应用程序以访问已安装应用程序的列表:

图 7.29 – 已安装应用程序列表

图 7.29 – 已安装应用程序列表

  1. 查看已安装应用程序的列表,然后选择所需的应用程序。如果您选择一个应用程序,将显示应用程序界面:

图 7.30 – Streamlit 应用程序

图 7.30 – Streamlit 应用程序

接下来,我们将介绍如何查看应用程序的 README。

查看应用程序的 README

README 提供了有意义的描述以及其他与该应用程序相关的详细信息。从列表中选择应用程序以访问 README,然后在工具栏中点击README图标以查看与应用程序关联的 README:

图 7.31 – Streamlit README 信息

图 7.31 – Streamlit 的 README 信息

接下来,我们将介绍通过授予角色来管理应用程序访问权限。

管理应用程序访问

要授予应用程序角色访问权限,请选择应用程序并选择管理访问。点击添加角色,然后选择您想授予应用程序角色访问权限的账户角色。点击完成以确认:

图 7.32 – 应用程序访问

图 7.32 – 应用程序访问

或者,您可以在消费者账户中通过运行以下命令来将应用程序角色授予账户角色:

GRANT APPLICATION ROLE bike_share_native_app.app_public
  TO ROLE <ACCOUNT ROLE>;

在最后一节,我们将学习如何卸载已安装的应用程序。

删除已安装的应用程序

应用程序可以使用 Snowsight UI 或命令行进行删除。要使用 Snowsight 删除应用程序,请点击应用程序旁边的省略号(...),然后选择卸载。通过选择卸载来确认操作:

图 7.33 – 卸载应用程序

图 7.33 – 卸载应用程序

我们也可以通过在工作表中执行以下命令来通过命令行卸载它:

DROP APPLICATION BIKE_SHARE_NATIVE_APP;

这就完成了原生应用程序的管理。Snowflake 提供了一种简单的方法来做这件事。我们将以总结结束本章。

总结

在本章中,我们探讨了 Snowflake 生态系统中的原生应用,揭示了其动态的景观和内在价值。本章从对 Snowflake 中原生应用的深入介绍开始,我们深入探讨了其基础方面,包括强大的安全特性和在更广泛的原生应用景观中的重要性。我们探讨了 Snowflake 中的 Streamlit,这是一个用于开发沉浸式数据科学和机器学习应用的强大工具。我们提供了 Streamlit 的全面概述,强调了其直观性和与 Snowflake 的无缝集成。我们指导读者通过在 Snowflake 中部署第一个 Streamlit 应用程序的过程,使他们能够利用这种强大的组合来获取数据驱动的洞察。

总体而言,本章提供了 Snowflake 中原生应用的全面概述,为您提供了利用这种创新技术在数据工作流程中充分发挥其潜能所需的知识和工具。在下一章中,我们将讨论 Snowpark 容器服务。

第八章:雪花容器服务简介

容器是使用多种语言打包代码的当代方法,确保在各种环境中实现无缝的可移植性和一致性。这对于高级 AI/ML 模型和全面的数据中心应用尤其如此。这些现代数据产品通常处理大量专有数据,在高效创建、开发和扩展工作负载方面提出了挑战。

开发人员和数据科学家通常花费更多的时间来管理计算资源和集群,而不是解决核心业务挑战。凭借其独特功能,Snowpark 容器服务为解决这个问题提供了一个无缝的解决方案。它允许应用程序和大型语言模型(LLMs)直接在 Snowflake 数据云中执行,从而减少了在资源管理上花费的时间和精力。本章将帮助您了解如何在 Snowpark 中部署应用程序和 LLMs。

在本章中,我们将涵盖以下主题:

  • 雪花容器服务简介

  • 设置 Snowpark 容器服务

  • 设置 Snowpark 容器服务作业

  • 使用 Snowpark 部署 LLMs

技术要求

要设置环境,请参考前一章中的技术要求。还需要 Docker 客户端和桌面;您可以从docs.docker.com/get-docker/安装 Docker。

我们还将使用 Hugging Face API。要获取 Hugging Face API 令牌,请在huggingface.co/注册。

支持材料可在github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark获取。

雪花容器服务简介

雪花容器服务代表了一种全面的托管容器解决方案,旨在简化在 Snowflake 环境中部署、管理和扩展容器化应用程序。用户可以直接在 Snowflake 中执行容器化工作负载,从而无需将数据转移到 Snowflake 生态系统之外进行处理。Snowpark 容器服务引入了一个经过精心优化的开放容器倡议(OCI)运行时执行环境,适用于 Snowflake,使用户能够完美执行 OCI 镜像,同时利用 Snowflake 数据平台的强大功能。

Snowpark 容器服务扩展了 Snowpark 的功能,为开发者提供了一个可信且熟悉的环境,使他们能够在 Snowflake 受管数据域内无缝处理非 SQL 代码。这使得应用程序能够轻松执行诸如连接到 Snowflake、在 Snowflake 虚拟仓库中执行 SQL 查询、访问 Snowflake 阶段的文件以及使用 Snowpark 模型处理数据等任务。这种简化的集成促进了团队内部高效协作和专注的开发工作环境。

开发者可以创建满足他们需求的容器,提供可配置的硬件选项,包括 GPU 支持,通过 Snowpark 在 Snowflake 内实现广泛的 AI/ML 和应用工作负载。例如,数据科学团队可以通过利用 Python 库进行训练和推理来加速 ML 任务,同时执行资源密集型的生成式 AI 模型,如 LLM。应用开发者可以使用流行的框架设计和部署用户界面,而数据工程师可以在处理 SQL 或 Python DataFrame 操作的同一处理引擎中执行优化的逻辑。

在下一节中,我们将了解 Snowpark 容器服务中的数据安全是如何工作的。

关于 Snowpark 容器服务的注意事项

在撰写本章时,Snowpark 容器服务目前处于私有预览阶段。请注意,一旦它们对所有用户开放,API 方法可能与本书中描述的略有不同。我们鼓励您监控本书的 GitHub 存储库,以获取任何新的代码内容更改和更新:github.com/PacktPublishing/The-Ultimate-Guide-To-Snowpark

Snowpark 容器服务中的数据安全

Snowpark 容器服务简化了全栈应用程序、LLM 和其他高级数据产品在数据环境中的安全部署。在 Snowpark 下,这种新的运行时选项简化了容器化工作负载(包括作业、服务和服务函数)的部署、管理和扩展,利用 Snowflake 管理的硬件配置基础设施,如 GPU。通过采用这种创新运行时,用户可以绕过管理计算资源和容器集群的复杂性,在不影响数据安全的前提下,无缝集成复杂的 AI/ML 模型和应用。在 Snowflake 环境中运行的容器无需将受管数据转移到 Snowflake 之外,从而最小化潜在的安全风险。这确保了利用内部开发解决方案或第三方产品(如通过 Snowflake Marketplace 可访问的 Snowflake Native Apps)的可靠和强大生态系统。

在下一节中,我们将探讨 Snowpark 容器的组成部分。

Snowpark 容器的组成部分

Snowpark 容器服务为容器化应用程序和 AI/ML 模型的生命周期管理提供了一种简化和完全管理的途径。与其他解决方案不同,它提供了一个统一的解决方案,需要将容器注册库、管理服务和计算平台等不同组件组合在一起。整合这些元素消除了管理计算资源和集群的负担,从而加速了数据应用程序的开发和部署。

此外,Snowpark 容器服务通过提供简单性和可扩展性的结合简化了容器托管和部署。开发者只需提供他们的容器,Snowflake 就会处理托管和扩展,无需深入了解 Kubernetes。开发者可以使用 SQL、CLI 或 Python 接口与服务交互,满足不同的偏好和工作负载。Snowpark 容器提供两种不同的执行选项,以适应各种应用程序需求:通过使用服务函数的服务作业和计算池。以下图表显示了不同的组件:

图 8.1 – Snowpark 容器组件

图 8.1 – Snowpark 容器组件

让我们来看看每个选项:

  • 服务:在 Snowflake 中,服务持续运行,就像一个网络服务一样,直到明确终止。这些服务托管在安全的入口端点,通常托管应用程序前端或 API。它们持续可用,以处理按需请求。

  • 作业:这些是具有特定时间限制的进程,通常由人工启动或定期安排。它们包括各种任务,例如在 GPU 上启动用于机器学习训练的容器镜像,或使用封装在容器中的不同语言、框架或库执行数据管道中的步骤。

  • 服务函数:函数是时间有限的进程,旨在接收输入、执行特定操作,并通过事件反复触发,利用您的容器化环境。

  • 计算池:由一个或多个 虚拟机(VM)节点组成的计算池是 Snowflake 执行您的作业和服务的底层基础设施。

Snowpark 容器服务还允许开发者在他们的最终客户 Snowflake 账户中使用上述组件直接部署应用程序。这使得他们能够在 Snowflake 环境中安全地安装和运行最先进的产品,例如托管笔记本和大型语言模型(LLMs),从而保护提供商的知识产权。

在下一节中,我们将介绍如何设置 Snowpark 容器服务。

设置 Snowpark 容器服务

在本节中,我们将阐述探索 Snowpark 容器服务所需的基础知识。我们将使用 Docker 创建一个符合 OCI 标准的镜像以部署到 Snowpark。我们将从创建 Snowflake 对象开始。

创建 Snowflake 对象

要创建 Snowflake 对象,请按照以下步骤在 Snowsight 中使用 ACCOUNTADMIN 角色执行:

  1. 使用以下命令创建一个名为 test_role 的角色。此角色将用于我们的 Snowpark 应用程序:

    USE ROLE ACCOUNTADMIN;
    CREATE ROLE test_role;
    

    这将打印以下输出:

图 8.2 – Snowflake 角色

图 8.2 – Snowflake 角色

  1. 通过运行以下命令创建一个数据库并授予数据库角色的访问权限:

    CREATE DATABASE IF NOT EXISTS SNOWPARK_DEFINITIVE_GUIDE;
    GRANT OWNERSHIP ON DATABASE SNOWPARK_DEFINITIVE_GUIDE
      TO ROLE test_role COPY CURRENT GRANTS;
    

    这将显示以下输出:

图 8.3 – 授予权限

图 8.3 – 授予权限

  1. 我们将通过执行以下命令为该角色授予仓库的访问权限:

    GRANT USAGE ON WAREHOUSE COMPUTE_WH TO ROLE test_role;
    
  2. 接下来,我们将通过运行以下命令为 Snowflake 服务创建一个安全集成,以便安全地访问资源:

    CREATE SECURITY INTEGRATION IF NOT EXISTS snowservices_ingress_oauth
      TYPE=oauth
      OAUTH_CLIENT=snowservices_ingress
      ENABLED=true;
    

    输出如下:

图 8.4 – 安全集成

图 8.4 – 安全集成

  1. 接下来,我们将通过运行以下命令将账户上的服务端点绑定到该角色。这允许从公共入口访问服务端点:

    GRANT BIND SERVICE ENDPOINT ON ACCOUNT TO ROLE test_role;
    

    这将显示以下输出:

图 8.5 – 绑定服务端点

图 8.5 – 绑定服务端点

  1. 最后,我们将通过运行以下命令创建一个计算池并将其分配给角色:

    CREATE COMPUTE POOL snowpark_cs_compute_pool
    MIN_NODES = 1
    MAX_NODES = 1
    INSTANCE_FAMILY = CPU_X64_XS;
    GRANT USAGE, MONITOR ON
      COMPUTE POOL snowpark_cs_compute_pool TO ROLE test_role;
    

    这将显示以下输出:

图 8.6 – 计算池

图 8.6 – 计算池

  1. 现在,我们已经创建了一个角色,test_role,以及我们将用于容器服务的必要 Snowflake 对象。现在,通过运行以下命令将角色授予你登录的用户:

    GRANT ROLE test_role TO USER <user_name>;
    
  2. 现在我们已经配置并准备好了角色,让我们创建必要的数据库范围对象:

    • 通过运行以下命令选择数据库:

      USE DATABASE SNOWPARK_DEFINITIVE_GUIDE;
      
    • 通过运行以下代码创建一个名为 MY_SCHEMA 的模式:

      CREATE SCHEMA IF NOT EXISTS MY_SCHEMA;
      
  3. 通过运行以下命令创建一个存储容器镜像的图像存储库:

    CREATE IMAGE REPOSITORY IF NOT EXISTS snowpark_cs_repository;
    

    一旦创建图像,你将看到以下输出:

图 8.7 – 图像存储库

图 8.7 – 图像存储库

  1. 最后,通过运行以下命令创建一个用于上传文件的阶段:

    CREATE STAGE IF NOT EXISTS snowpark_cs_stage
      DIRECTORY = ( ENABLE = true );
    

    你将看到以下输出:

图 8.8 – 阶段创建

图 8.8 – 阶段创建

我们将使用 HuffPost 数据集,该数据集可在 Kaggle 上找到。数据集包含在我们的代码存储库中。该数据集界定了从 2012 年到 2018 年 5 月的约 200,000 个标题,以及从 2018 年 5 月到 2022 年的额外 10,000 个标题,反映了网站运营动态的调整。

在下一节中,我们将设置服务。

设置服务

Flask 是一个轻量级的 Web 框架,它允许开发者轻松地在 Python 中创建 Web 应用程序。它被设计成灵活、模块化和易于使用,使其成为构建各种规模 Web 应用的流行选择。Flask 特别适合构建小型到中型 Web 应用程序,因为它提供了完成任务所需的功能,而不增加不必要的复杂性。

Flask 被用于广泛的用途,包括构建 Web API、开发微服务和创建简单的 Web 应用程序。它的灵活性和简单性使其成为希望快速原型设计和部署 Web 应用的开发者的热门选择。此外,Flask 可以轻松地通过各种第三方库和工具进行扩展,使其成为构建 Python 中 Web 应用程序的有力且多功能的工具。

我们将使用 Flask 来编写我们的服务代码,该代码运行一个持久化的服务以接收 HTTPS 请求。

关于过滤器服务的说明

在下一节中我们将讨论的过滤器服务只是一个简单的例子,因为我们的重点更多地在于解释如何设置 Snowpark 容器服务,而不是构建一个复杂的应用程序。通过遵循类似的步骤,可以开发出任何其他用例。

设置过滤器服务

在本节中,我们将设置一个名为 filter_service 的服务,该服务根据唯一 ID 过滤表格。我们将执行以下步骤来设置服务。

服务代码

你可以在代码仓库中找到一个包含创建过滤器服务代码的 Python 应用程序。首先,将提供的 zip 文件下载到指定的目录。下载完成后,继续提取其内容。你将遇到一个包含提取文件中的服务代码的 service 目录。该目录包括 Docker 文件、filter_service.py 和 UI 模板。

Python 中的过滤器服务

以下 Python 脚本包含了我们服务的核心逻辑,封装了一个基于 Flask 的最小化 HTTP 服务器,并设计用于根据输入过滤表格。它具有双重用途:处理来自 Snowflake 服务函数的过滤请求,并为提交过滤请求提供 Web UI。

@app.post("/filter")
def udf_calling_function():
    message = request.json
    logger.debug(f'Received request: {message}')
    if message is None or not message['data']:
        logger.info('Received empty message')
        return {}
    unique_id = message['data']

过滤函数促进了 Snowflake 服务函数与服务之间的通信。这个函数被 @app.post() 装饰器所装饰,表明它能够处理指向 /filter 路径的 HTTP POST 请求。在接收到此类请求后,该函数处理并返回请求体中封装的过滤结果:

def ui():
    '''
    Main handler for providing a web UI.
    '''
    if request.method == "POST":
        # Getting input in HTML form
        input_text = request.form.get("input")

UI 函数段负责协调网页表单的展示和管理通过网页表单提交的筛选请求。使用 @app.route() 装饰器装饰的此函数被指定处理针对 /ui 路径的请求。当服务器接收到针对此路径的 HTTP GET 请求时,它将提供一个简单的 HTML 表单,提示用户输入一个字符串。随后,在表单提交后,将发送一个 HTTP POST 请求,服务器处理它,并在 HTTP 响应中返回原始字符串:

@app.get("/healthcheck")
def readiness_probe():
    return "I'm ready!"

带有 @app.get() 装饰器的 readiness_probe 函数准备处理指向 /healthcheck 的请求。这个函数对于 Snowflake 验证服务的就绪状态至关重要。当 Snowflake 启动一个容器时,它将向此路径发送一个 HTTP GET 请求作为健康检查,确保只有健康的容器处理传入的流量。函数的实现是灵活的,可以适应各种操作以确保服务的就绪状态。

接下来,我们将查看目录中的 Dockerfile。

Dockerfile

Dockerfile 作为使用 Docker 构建镜像的蓝图。它包括在 Docker 容器中安装 Flask 库的指令。Dockerfile 包含以下内容:

ARG BASE_IMAGE=python:3.10-slim-buster
FROM $BASE_IMAGE
COPY filter_service.py ./
COPY templates/ ./templates/
RUN pip install --upgrade pip && \
    pip install flask && \
    pip install snowflake-snowpark-python[pandas]
CMD ["python3", "filter_service.py"]

filter_service.py 中的代码依赖于 Flask 来高效处理 HTTP 请求。

接下来,我们将检查 UI 模板。

UI 模板

UI 模板文件位于 /template/basic_ui.html。它们渲染一个用于筛选服务公开端点的网页表单。当在带有 /ui 后缀的网页浏览器中加载公共端点 URL 时,将显示此表单。用户可以通过此表单输入一个字符串,并在提交后,服务将使用提交的字符串筛选表格,并在 HTTP 响应中返回。

在下一节中,我们将介绍服务函数。

服务函数

服务函数作为与您的服务通信的通道。一个带有指定参数的 CREATE FUNCTION 命令,例如 filter_doc_udf 函数:

CREATE FUNCTION filter_doc_udf (InputText varchar)
  RETURNS varchar
  SERVICE=filter_service
  ENDPOINT=filterendpoint
  AS '/filter';

例如,这个函数接受一个字符串作为输入,并返回一个字符串,其中 SERVICE 属性指定服务(filter_service)和 ENDPOINT 属性指定用户友好的端点名称(filterendpoint)。AS '/filter' 指定服务路径,将其与 filter_service.py 内的相应函数关联。因此,调用此函数将触发 Snowflake 向服务容器内的指定路径发送请求。

在下一节中,我们将构建 Docker 镜像。

构建 Docker 镜像

在本节中,我们将使用与 Snowpark 兼容的 Linux/AMD64 基础构建镜像,并将其发送到您的账户镜像仓库。要构建 Docker 镜像,请执行以下步骤:

  1. 通过执行以下 SQL 命令获取仓库 URL:

    SHOW IMAGE REPOSITORIES;
    

    这将显示所有图像仓库:

图 8.9 – 镜像仓库

图 8.9 – 镜像仓库

输出中的 repository_url 列提供了必要的 URL,而仓库 URL 中指定的主机名表示注册表主机名。

  1. 以下命令需要在系统中安装 Docker Desktop。在执行命令之前,您可以从 www.docker.com/products/docker-desktop/ 安装它。接下来,在本地终端窗口中,切换到包含解压文件的 service 目录,并使用 Docker CLI 执行后续的 docker build 命令:

    .) as the path for building the latest image from the Docker file. The output will be as follows:
    

图 8.10 – Docker 构建命令

图 8.10 – Docker 构建命令

  1. 接下来,我们将使用 Snowflake 注册表对 Docker 进行身份验证。要使用 Snowflake 注册表对 Docker 进行身份验证,请执行以下命令:

    docker login <registry_hostname> -u <username>
    

    为用户名参数指定您的 Snowflake 用户名。Docker 将提示您输入密码。使用 Snowflake 密码进行身份验证:

    图 8.11 – 仓库登录

图 8.11 – 仓库登录

  1. 最后,通过执行以下命令将镜像上传到 Docker 注册表:

    docker push <orgname>-<acctname>.registry.snowflakecomputing.com/snowpark_definitive_guide/my_schema/snowpark_cs_repository/my_filter_service_image:latest
    

    您应该看到以下输出:

图 8.12 – 仓库推送

图 8.12 – 仓库推送

现在镜像已可在注册表中部署到容器服务。

在下一节中,我们将检查如何部署服务,但在将构建推送至 Snowflake 仓库之前进行本地测试始终是最佳实践。这部分内容超出了本书的范围,因此在本节中未进行解释。

部署服务

在本节中,我们将指导您部署服务并建立服务函数以方便与其通信。我们将首先部署服务,这需要现有的计算池。让我们先通过运行以下命令来检查计算池:

DESCRIBE COMPUTE POOL snowpark_cs_compute_pool;

图 8.13 – 计算池

图 8.13 – 计算池

如果它处于 启动 状态,您需要等待它过渡到 活动空闲 状态。

现在池已激活,我们可以在下一节创建服务。

创建服务

我们可以通过使用 test_role 运行服务来创建服务。为此,请运行以下命令:

USE ROLE test_role;
CREATE SERVICE filter_service
  IN COMPUTE POOL snowpark_cs_compute_pool
  FROM SPECIFICATION $$
    spec:
      containers:
      - name: filter
        image: /snowpark_definitive_guide/my_schema/snowpark_cs_repository/my_filter_service_image:latest
        env:
          SERVER_PORT: 8000
        readinessProbe:
          port: 8000
          path: /healthcheck
      endpoints:
      - name: filterendpoint
        port: 8000
        public: true
      $$
  MIN_INSTANCES=1
  MAX_INSTANCES=1;

我们使用已构建的镜像来部署服务。服务应在 Snowflake 中创建。

一旦创建服务,你可以执行以下 SQL 命令来检查其状态:

SELECT SYSTEM$GET_SERVICE_STATUS('filter_service');

输出应显示服务正在运行。可以通过运行以下命令来获取有关服务的详细信息:

DESCRIBE SERVICE filter_service;

这将显示以下截图中的详细信息:

图 8.14 – 服务信息

图 8.14 – 服务信息

在下一节中,我们将创建服务函数。

创建服务函数

服务函数执行过滤功能并将其与端点关联。要创建服务函数,执行以下命令:

CREATE FUNCTION filter_doc_udf (InputText varchar)
RETURNS varchar
SERVICE=filter_service
ENDPOINT=filterendpoint
AS '/filter';

在这里,SERVICE 属性将 UDF 与 filter_service 服务链接起来,而 ENDPOINT 属性将其与服务内的 filterendpoint 端点关联起来。AS '/filter' 规范表示通向过滤服务器的 HTTP 路径,该路径可以在服务代码中找到。

一旦正确执行了前面的 SQL 语句,您可以在 Snowsight 下的 函数 中看到您创建的服务函数。

图 8.15 – 服务函数

图 8.15 – 服务函数

现在函数已准备好执行。

执行函数

我们将通过运行以下命令切换到本章中创建的上下文:

USE ROLE test_role;
USE DATABASE SNOWPARK_DEFINITIVE_GUIDE;
USE SCHEMA MY_SCHEMA;
USE WAREHOUSE compute_wh;

您应该获得以下确认:

图 8.16 – 函数执行

图 8.16 – 函数执行

在设置好上下文后,您可以通过在查询中调用服务函数来启动与过滤服务的通信。要调用 filter_doc_udf 服务函数,执行以下 SELECT 语句,提供示例输入字符串 ('122880'):

SELECT filter_doc_udf('122880');

执行此查询后,Snowflake 将向服务端点 (filterendpoint) 发送一个 POST 请求。在收到请求后,服务使用输入字符串来过滤 UNIQUE_ID 表,并在响应中发送适当的行,如下所示:

图 8.17 – 过滤功能

图 8.17 – 过滤功能

服务公开其端点,但仍然在 Snowflake 身份验证机制的安全保护下,如 CREATE SERVICE 命令中提供的内联说明所述。因此,您可以访问服务公开到互联网的 Web UI,并通过 Web 浏览器向服务发送请求。要找到服务公开的公共端点 URL,执行以下命令:

SHOW ENDPOINTS IN SERVICE filter_service;

要访问 Web UI,将 /ui 添加到端点 URL,并将其粘贴到 Web 浏览器中。此操作将触发 filter_service.py 脚本中指定的 ui() 函数的执行:

图 8.18 – 服务 UI

图 8.18 – 服务 UI

请注意,您第一次访问端点 URL 时,系统会提示您登录到 Snowflake。请确保以创建服务的同一用户身份登录,以确保您拥有必要的权限。

我们已成功部署了服务和组件。在下一节中,我们将查看服务作业。

设置 Snowpark 容器服务作业

在本节中,我们将创建一个简单的作业,用于连接到 Snowflake 表,并通过生成新列执行一些特征工程任务。随后,我们将把结果数据保存到 Snowflake 环境中的同一表中。与服务不同,作业是短暂的,提供一次性的任务执行。

在下一节中,我们将设置容器作业。

设置作业

对于作业,我们不会使用服务的 Flask 服务器实现,而是将使用一个简单的main.py文件来执行作业操作。我们将执行以下步骤来设置作业。

作业代码

本节代码位于我们的 GitHub 仓库的chapter_8文件夹中。该文件夹包含以下文件,这些文件对于作业是必需的。

main.py文件

main.py文件是作业执行的核心 Python 脚本。其核心是以下run_job()函数,当脚本执行时会被调用。此函数在读取环境变量并利用它们为连接到 Snowflake 的各种重要参数设置默认值方面发挥着关键作用。

def run_job():
    """
    Main body of this job.
    """
    logger = get_logger()
    logger.info("Job started")
    # Parse input arguments
    args = get_arg_parser().parse_args()
    table = args.table
    column = args.date_column

当镜像在 Snowflake 环境中运行时,Snowflake 会自动填充一些参数,但在本地测试镜像时需要显式提供。run_job()函数从规范中获取一个表名和一个用于特征工程的列。

Dockerfile

Dockerfile 封装了构建镜像所需的所有必要命令。此文件类似于我们在服务部分中实现的内容,确保了在不同 Snowpark 容器服务环境组件之间的连贯性和一致性。

作业规范文件

以下作业规范文件为 Snowflake 提供了必要的容器配置信息。Snowflake 利用my_job_spec.yaml规范文件中提供的信息来无缝配置和执行您的作业。除了如container.namecontainer.image等必填字段外,此规范文件还包括可选字段,如container.args,它列出了作业执行所需的参数。

spec:
  container:
  - name: main
    image: /snowpark_definitive_guide/my_schema/snowpark_cs_repository/my_job_image:latest
    env:
      SNOWFLAKE_WAREHOUSE: compute_wh
    args:
    - "--table=NEWS_CATEGORY"
    - "--date_column=DATE"

值得注意的是,--query参数指定了作业运行时要执行的查询,而--result_table参数标识了查询结果将存储的表。

在下一节中,我们将部署作业。

部署作业

要将您的作业规范文件(my_job_spec.yaml)上传到 Snowflake 环境,您有几个选项将其上传到指定的阶段:

  • Snowsight 网页界面:使用 Snowsight 网页界面提供了一个用户友好的方法来上传您的作业规范文件。按照我们在前几章中介绍的操作说明,您可以轻松地导航此过程并确保成功集成。

  • SnowSQL 命令行界面 (CLI):或者,您可以使用 SnowSQL CLI 通过执行以下 PUT 命令语法来执行文件上传过程:

    PUT command, detailed information regarding the uploaded file will be displayed in Snowsight:
    

图 8.19 – 作业上传

图 8.19 – 作业上传

现在作业文件已上传,我们将在下一节中执行作业。

执行作业

要启动作业的执行,您将使用 EXECUTE SERVICE 命令,该命令作为启动指定任务的催化剂。运行以下命令以触发作业(由于我们当时处于私有预览阶段,此命令可能会更改):

EXECUTE SERVICE IN COMPUTE POOL snowpark_cs_compute_pool
  FROM @snowpark_cs_stage SPEC='my_job_spec.yaml';

或者,您可以使用以下方法:

EXECUTE JOB SERVICE
  IN COMPUTE POOL snowpark_cs_compute_pool
  NAME = test_job
  FROM @SNOWPARK_CS_STAGE
  SPECIFICATION_FILE='my_job_spec.yaml';

指定的计算池 snowpark_cs_compute_pool 决定了作业成功执行所需的计算资源分配。@snowpark_cs_stage 符号表示在 Snowflake 中存储作业规范文件的指定阶段,便于无缝访问所需的配置细节。my_job_spec.yaml 文件指的是包含执行作业所需指令和参数的特定配置文件。成功执行命令应显示以下输出:

图 8.20 – 作业执行

图 8.20 – 作业执行

执行后,作业将执行指定的 SQL 语句,并将结果数据保存到指定的表中,如作业规范文件(my_job_spec.yaml)中所述。需要注意的是,SQL 语句的执行并不在 Docker 容器内部进行。相反,容器连接到 Snowflake,利用 Snowflake 仓库高效地执行 SQL 语句。EXECUTE SERVICE 命令返回包含关键信息的输出,包括作业分配的 UUID(通用唯一标识符的缩写)。这个 UUID 作为执行作业的唯一标识符,有助于跟踪和监控其进度和状态。

在下一节中,我们将为 Snowpark 容器服务部署一个 LLM。

使用 Snowpark 部署 LLM

现代企业越来越要求将 LLM 与专有数据相结合。开源和专有模型在实现这一转型中发挥着关键作用。然而,主要挑战是找到一个能够有效利用 LLM 力量的强大平台。Snowflake 使组织能够将近乎神奇的生成式 AI 变换应用于其数据。通过在 Snowflake 内部利用高级 LLM 模型,组织可以高效地处理大量数据,实现生成式 AI 用例。在本节中,我们将讨论在 Snowpark 容器服务中部署 LLM 模型。

在本指南中,我们将探讨如何利用公开可访问的数据,通过部署来自 Hugging Face 存储库的 Llama 2 LLM,来展示 Snowflake 生态系统的变革能力。

注意

Meta 的 Llama 2,位于 Hugging Face 的库中,代表了先进的自然语言处理(NLP)技术。根据 Meta 的具体服务条款,你需要一个 Hugging Face 令牌才能通过 Hugging Face 访问 Llama 2。请访问huggingface.co/docs/hub/en/security-tokens了解更多信息。

准备 LLM

我们将首先通过利用我们对 Hugging Face Transformers API 的便捷包装来准备 LLM,并利用 Hugging Face 的 Llama 2 7B 的能力。为此,请运行以下代码:

HF_AUTH_TOKEN = " ************************* "
registry = model_registry.ModelRegistry(session=session, database_name="SNOWPARK_DEFINITIVE_GUIDE", schema_name="MY_SCHEMA", create_if_not_exists=True)
llama_model = huggingface_pipeline.HuggingFacePipelineModel(task="text-generation", model="meta-llama/Llama-2-7b-chat-hf", token=HF_AUTH_TOKEN, return_full_text=False, max_new_tokens=100)

确保将HF_AUTH_TOKEN替换为你在 Hugging Face 的令牌。代码创建模型注册库并将从 Hugging Face 注册库中获取的模型分配给模型。模型是从 Hugging Face 注册库中获取的,并直接导入到 Snowpark 中。

接下来,我们将在 Snowpark ML 中注册模型。

注册模型

接下来,我们将利用 Snowpark ML 中的模型注册库的log_model API 来注册模型。这包括指定一个模型名称和一个版本字符串,并提供上一步获得的模型:

MODEL_NAME = "LLAMA2_MODEL_7b_CHAT"
MODEL_VERSION = "1"
llama_model=registry.log_model(
    model_name=MODEL_NAME,
    model_version=MODEL_VERSION,
    model=llama_model
)

您应该会看到以下类似的输出:

图 8.21 – 模型注册

图 8.21 – 模型注册

模型现在已注册在注册库中。现在模型已准备就绪,我们将将其部署到容器服务。

将模型部署到 Snowpark 容器服务

现在,让我们将模型部署到我们的指定计算池。一旦部署过程开始,模型将作为 Snowpark 容器服务端点可用。运行以下代码将模型部署到容器服务。运行此步骤可能需要您将计算池更改为包含 GPU 实例,或者您可以创建一个新的包含 GPU 实例的计算池。

llama_model.deploy(
  deployment_name="llama_predict",
  platform=deploy_platforms.TargetPlatform.SNOWPARK_CONTAINER_SERVICES,
  options={
            "compute_pool": "snowpark_cs_compute_pool",
            "num_gpus": 1
  },
)
"external_access_integrations": ["ALLOW_ALL_ACCESS_INTEGRATION"]

这个简化的部署过程突出了 Snowpark ML 如何简化 LLM 的部署,包括创建相应的 Snowpark 容器服务SERVICE定义,将模型及其运行时依赖项打包到 Docker 镜像中,并在指定的计算池中启动服务。

执行代码后,你应该会看到类似的输出:

图 8.22 – 将 LLM 模型部署到 Snowpark 容器服务

图 8.22 – 将 LLM 模型部署到 Snowpark 容器服务

在下一节中,我们将在这个容器中执行此模型。

关于模型部署的注意事项

本节中仅显示了用于解释的代码片段。完整的代码可在 GitHub 中的chapter_8.ipynb笔记本中找到。您应该注意模型部署步骤,因为它需要相当多的时间和资源。

运行模型

通过提供包含提示的inputs列的NEWS_CATEGORY表子集来调用模型:

res = llama_model_ref.predict( deployment_name=DEPLOYMENT_NAME, data=input_df )

这将生成一个包含模型对每行响应的输出列的 Snowpark DataFrame。原始响应将文本与预期的 JSON 输出交织在一起,如下所示:

{
  "category": "Art",
  "keywords": [
    "Gertrude",
    "contemporary art",
    "democratization",
    "demystification"
  ],
  "importance": 9
}

使用 Snowpark 容器服务部署和执行 LLM 模型非常简单。

我们将以本章的总结结束。

摘要

在本章中,我们探讨了 Snowpark 容器服务,这是一个旨在简化 Snowflake 生态系统内容器化应用程序部署和管理的强大解决方案。我们讨论了 Snowpark 容器服务中作业和服务之间的区别,强调了它们各自的功能和用例。我们通过实际实施示例展示了如何有效地配置、部署和管理作业和服务。

此外,我们深入探讨了通过 Snowpark ML 的容器化,展示了 Snowflake 用户如何无缝地在他们的环境中利用高级 ML 模型。通过集成来自 Hugging Face 的语言模型,我们说明了 Snowpark ML 如何促进容器化模型的集成,使复杂的 NLP 任务能够直接在 Snowflake 中执行。总的来说,本章为您提供了利用 SCS 和 Snowpark ML 在数据驱动型项目中的变革性潜力的知识和工具。

总之,Snowpark 容器服务为寻求高效和可扩展数据处理解决方案的企业提供了一个有吸引力的价值主张。通过在 Snowflake 中直接执行容器化工作负载,Snowpark 消除了数据移动的需要,确保数据完整性并降低延迟。此外,Snowpark 简化了数据应用程序的开发和部署,使团队能够专注于创新而不是基础设施管理。自动化的容器管理进一步简化了运营任务,提高了整体的生产力和敏捷性。

有了这个,我们就结束了这本书。感谢您的阅读。

第九章:索引

由于此电子书版本没有固定的页码,以下页码仅为参考,基于本书的印刷版。

A

Anaconda 环境 4

Anaconda 包

管理,在 Python 工作表 19-22

应用程序文件

创建 185

清单文件。创建 187

README 文件,创建 187

设置脚本,创建 185, 186

Streamlit 应用程序文件,创建 186

上传,到命名阶段 191, 192

应用程序包

创建 188

应用程序编程接口 (API) 12

人工智能 (AI) 128

C

客户端 API,Snowpark Python 35

高级身份验证,Snowpark 会话 37, 38

会话,与 36 一起工作

Snowpark 数据帧 38, 39

Snowpark 数据帧,与 39, 40 一起工作

命令行界面 (CLI) 31

组件,Snowpark 容器服务

计算池 206

任务 206

服务函数 206

服务 206

持续集成和持续部署 (CI/CD) 102

自定义包

管理,在 Python 工作表 23-27

D

数据分析 82

列联分析 84

数据,描述 82

独特数据,查找 83

重复项,删除 83

缺失值,删除 86, 87

缺失值,填充 88

pandas 数据帧,操作 90

pandas 数据帧,使用 90, 91

中心分析 85

变量交互 89

数据云

优点 129

数据科学 128

范式 129

数据工程

使用 Snowpark 99

数据工程管道

数据清理 106, 107

数据准备 103-105

数据转换 106

开发 102, 103

协调 107

数据探索 69-73, 134-136

相关性分析 139, 140

特征工程 141, 142

泄露变量 141

缺失值分析 136, 137

异常值分析 137-139

数据分组 78-82

数据采集 60

CSV 文件,导入到 Snowflake 61, 62

动态读取文件,使用 Snowpark 68, 69

图片,导入到 Snowpark 66, 67

JSON,导入到 Snowflake 63-65

Parquet 文件,导入到 Snowflake 65, 66

DataOps 流程 102

数据科学 128

数据集 60

营销信息(JSON) 61

投诉信息(Parquet) 61

购买历史(CSV) 61

数据转换 73-75

数据追加 75, 76

有向无环图(DAGs) 108

E

端到端机器学习,Snowpark 132

数据清洗 133

数据准备 132

数据预处理 133

数据转换 133

探索性数据分析(EDA) 133

特征工程 133

推理 134

模型构建 133

模型训练 133

事件表 113

列 114, 115

配置 115

创建 115

查询 115

异常处理,在 Snowpark 中 119

错误日志,捕获 119, 120

错误日志,查询 121

探索性数据分析 (EDA) 132, 133

外部函数 4

提取、加载和转换 (ELT) 99-101

提取、转换和加载 (ETL) 99, 100

F

特征工程 141, 142

特征存储

优势 172

创建 165

数据集,准备 169, 170

特征实体,创建 165

特征视图,创建 166-169

模型预测 172

模型训练 170, 171

场景 172, 173

特征存储,与数据仓库对比

数据类型 164

最终用户 164

ETL 管道 164

共享特性和函数 164

过滤服务

代码 211

Dockerfile 212

在 Python 中 211, 212

服务函数 213

设置 211

UI 模板 212

Flask 210

G

一般数据保护条例 (GDPR) 5

H

健康保险可携带性和问责制法案 (HIPAA) 5

I

集成开发环境 (IDEs) 12

J

Jupyter Notebook 33

L

泄露变量 141

LLMs 部署,使用 Snowpark 222

大型语言模型(LLM),准备 223

模型,注册 223,224

模型,运行 225

到 Snowpark 容器服务 224

记录 112

记录,在 Snowpark

信息日志,捕获 116-118

信息日志,查询 118,119

设置 116

M

机器学习(ML) 128

模型 4,12

ML 模型部署,在 Snowpark 149

模型注册 150

N

原生应用程序,开发 180-182

Streamlit 应用程序,使用原生应用程序框架开发 185

Streamlit 应用程序,运行 183-185

Streamlit 编辑器 183

原生应用程序,管理

应用角色访问,授权 201

已安装应用程序,移除 202

已安装应用程序,查看 199,200

应用程序的 README,查看 200

原生应用程序,发布 196

默认发布指令,设置 197

列表,创建 197-199

原生应用程序框架 177,178

优势 180

组件 179

Streamlit,在 Snowflake 179,180

自然语言处理(NLP)技术 223

O

开放容器倡议(OCI) 204

P

pandas DataFrame

打破,分批 93,94

相关性 91

数据分析 90

频率分布 91

局限性 90

操作 90

抽样 94,95

可视化 92,93

个人可识别信息(PII)7

点时间(PIT)查找 170

编程式 ELT

实现,使用 Snowpark 100, 101

项目结构,Snowpark

建立 54, 55

Python 引擎 35

Snowpark 的 Python

特征 6

利用 4

Python 存储过程

部署 27, 28

Python 工作表

图表和数据探索 30

交互式 Python 环境 28

Python 库支持 29

Snowpark 协作 30

Snowpark 调试 29

R

弹性数据管道

开发,使用 Snowpark 98

传统,与现代数据管道对比 98

S

标量 UDF41

与 41-43 合作

安全运营中心(SOC)5

setup.sql 190

Snowflake

数据阶段 101

原生应用景观 178

Streamlit 179, 180

任务 108

Snowflake 市场 5

Snowflake 原生应用框架 181

消费者 179

提供商 179

Snowpark 3, 4

利益 7, 8

数据工程 99

数据操作 102

ELT,使用 100

端到端机器学习 132

ETL,与 ELT 对比 101

异常处理 119

用于数据应用 7

用于数据工程 6

用于数据治理和安全 7

用于数据科学和机器学习 6, 129, 130

对于不同的工作负载 6

记录 116

机器学习模型部署 149

操作 34

项目结构,建立 54, 55

跟踪 121

Snowpark 容器服务作业

代码 219

部署 221

Dockerfile 220

执行 221, 222

作业规范文件 220

main.py 文件 219

设置 219

Snowpark 容器服务 204

组件 205-207

数据安全 205

Docker 镜像,构建 213-215

过滤服务,设置 211

服务,创建 215, 216

服务,部署 215

服务功能,创建 217

服务功能,执行 217-219

服务,设置 210

设置 207

Snowflake 对象,创建 207-210

Snowpark 开发环境

配置 12

Snowpark Python 工作表 12

Snowpark 特征存储 163

利益 163

Snowpark for Python

能力 5

Snowpark 本地开发环境 31, 32

额外的 Python 包,安装 33

Jupyter Notebook,配置 33

Snowpark 模块,导入 33

Snowpark Python 包,安装 32

虚拟环境,创建 32

Snowpark 机器学习 130

API 131, 132

架构 131

效率 147

模型,训练 142-146

Snowpark 机器学习模型注册 150

优点,在模型生命周期中 161, 162

方法 157

模型,部署到 150

模型部署 155-157

模型度量 157, 158

模型,准备 151

模型标签和描述 158, 159

操作 160, 161

最佳模型,记录 154, 155

最佳参数,提取 152, 153

注册历史 160

Snowpark 模型数据

管理 162

Snowpark 功能存储 163

Snowpark Python 架构

客户端 API 35

Python 引擎 35

用户定义函数 (UDFs) 41

Snowpark Python 工作表 12

Anaconda 软件包,管理 19-22

自定义软件包,管理 23-27

数据库和模式创建,在 Snowflake Snowsight 中 14, 15

功能 28-30

局限性 31

先决条件 12-14

Python 存储过程,部署 27, 28

在 Snowflake 中的 Python 工作表 16-18

Snowsight 网页界面 221

SnowSQL 命令行界面 (CLI) 221

软件即服务 (SaaS) 平台 3

SQL 3

存储过程 4, 51

相比,用户定义函数 (UDFs) 53, 54

与 51-53 一起工作

Streamlit 7

在 Snowflake 中 179, 180

Streamlit 应用程序

运行 183-185

Streamlit 应用程序,使用原生应用程序框架开发 185

应用文件,上传到命名阶段 191,192

文件,创建 185,186

包,创建 188-190

包,安装 192

测试,执行 194-196

版本,安装 193,194

Streamlit 编辑器 183

T

任务,在 Snowflake 中 108

计算模型 108

管理,使用 Python 109-112

概述 108

SQL 代码,执行 108

任务图 108

跟踪 112

在 Snowpark 中的跟踪

设置 121

跟踪,捕获 121-123

跟踪,查询 123,124

U

UDFs,Snowpark 41

标量 UDFs 41

存储过程 51

UDTF 44

向量化 UDFs 50

相比,存储过程 53,54

UDTF 44

使用 44-49

通用唯一标识符(UUID) 222

用户定义函数(UDFs) 4,28,103,147,180,213

V

向量化 UDFs 50

使用 50,51

虚拟机(VM)节点 206

packtpub.com

订阅我们的在线数字图书馆,全面访问超过 7,000 本书籍和视频,以及领先的行业工具,帮助您规划个人发展并推进职业生涯。更多信息,请访问我们的网站。

第十章:为什么订阅?

  • 使用来自超过 4,000 位行业专业人士的实用电子书和视频,节省学习时间,更多时间用于编码

  • 通过为您量身定制的 Skill Plans 提高学习效果

  • 每月免费获得一本电子书或视频

  • 完全可搜索,便于轻松访问关键信息

  • 复制粘贴、打印和收藏内容

您知道 Packt 为每本书都提供电子书版本,包括 PDF 和 ePub 文件吗?您可以在packtpub.com升级到电子书版本,并且作为印刷版书籍的顾客,您有权获得电子书副本的折扣。如需了解更多详情,请联系我们 customercare@packtpub.com。

www.packtpub.com,您还可以阅读一系列免费技术文章,注册各种免费通讯,并享受 Packt 书籍和电子书的独家折扣和优惠。

您可能还会喜欢的其他书籍

如果您喜欢这本书,您可能对 Packt 的其他书籍也感兴趣:

Data Modeling with Snowflake

Serge Gershkovich

ISBN: 978-1-83763-445-3

  • 发现数据建模节省时间和应用的好处

  • 了解 Snowflake 的云原生架构及其功能

  • 理解并应用使用 Snowflake 对象的建模技术

  • 通过 Snowflake 对象实现通用建模概念和语言

  • 舒适地阅读和转换半结构化数据

  • 通过预构建的食谱和示例直接学习

  • 学习如何应用从 Star 到 Data Vault 的建模框架

Data Engineering with dbt

Roberto Zagni

ISBN: 978-1-80324-628-4

  • 创建 dbt Cloud 账户并了解 ELT 工作流程

  • 结合 Snowflake 和 dbt 构建现代数据工程管道

  • 使用 SQL 将原始数据转换为可用数据,并测试其准确性

  • 编写 dbt 宏并使用 Jinja 应用软件工程原则

  • 测试数据和转换以确保可靠性和数据质量

  • 使用经过验证的模式构建轻量级实用数据平台

  • 使用 dbt 材料化编写易于维护的幂等代码

Packt 正在寻找像您这样的作者

如果您有兴趣成为 Packt 的作者,请访问authors.packtpub.com并申请。我们已经与成千上万的开发人员和科技专业人士合作,就像您一样,帮助他们将见解分享给全球科技社区。您可以提交一般申请,申请我们正在招募作者的特定热门话题,或提交您自己的想法。

分享您的想法

现在,您已经完成了《Snowpark 终极指南》,我们非常想听听您的想法!如果您从亚马逊购买了这本书,请点击此处直接转到该书的亚马逊评论页面并分享您的反馈或在该购买网站上留下评论。

您的评论对我们和科技社区非常重要,并将帮助我们确保我们提供高质量的内容。

下载这本书的免费 PDF 副本

感谢您购买这本书!

您喜欢在路上阅读,但无法携带您的印刷书籍到处走?

您选择的设备是否不兼容您的电子书购买?

别担心,现在,您每购买一本 Packt 书籍,都可以免费获得该书的 DRM 免费 PDF 版本。

在任何地方、任何地点、任何设备上阅读。直接从您最喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。

优惠远不止于此,您还可以获得独家折扣、时事通讯和每日免费内容的每日邮箱访问权限

按照以下简单步骤获取好处:

  1. 扫描二维码或访问以下链接

packt.link/free-ebook/9781805123415

  1. 提交您的购买证明

  2. 就这些!我们将直接将您的免费 PDF 和其他好处发送到您的电子邮件

posted @ 2025-10-24 10:01  绝不原创的飞龙  阅读(28)  评论(0)    收藏  举报