TowardsDataScience 2023 博客中文翻译(二百七十九)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

在 CPU 上使用 Hugging Face Pipelines 运行 Falcon 推断

原文:towardsdatascience.com/running-falcon-on-a-cpu-with-hugging-face-pipelines-b60b3b8a32a3?source=collection_archive---------2-----------------------#2023-06-06

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源

了解如何在第 4 代 Xeon CPU 上使用 Hugging Face Pipelines 运行 7 亿和 40 亿 Falcon 模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 爱德华多·阿尔瓦雷斯

·

关注 发表在 Towards Data Science ·5 分钟阅读·2023 年 6 月 6 日

很容易认为我们只能通过 GPU 执行由数十亿参数构成的 LLM 推理。虽然 GPU 在深度学习中相较于 CPU 提供了显著的加速,但硬件的选择应始终基于具体的使用案例。例如,假设你的终端用户只需要每 30 秒得到一次响应。如果你在经济和后勤上都很困难去预留能在 < 30 秒内给出答案的加速器,那么你可能会遇到收益递减的问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1. 从终端用户到硬件和软件堆栈的反向思考——像“计算意识的 AI 开发者”一样思考——图像由作者提供

这一切都回到一个基本原则,即成为一个“计算意识的 AI 开发者”——从应用程序的目标反向推导出使用的正确软件和硬件。想象一下开始一个家庭项目,比如挂一个新架子,却直接使用大锤,而没有考虑到一个更小、更精确的锤子可能更适合这个项目。

在本文中,我们将使用 Hugging Face Pipelines4th Generation Xeon CPU 上对 Falcon-7bFalcon-40b 进行推理。Falcon-40b 是由阿布扎比技术创新研究院 (TII) 开发的一个 40 亿参数的解码器模型。它在多个模型如 LLaMA、StableLM、RedPajama 和 MPT 上表现优越,利用 FlashAttention 方法实现更快和优化的推理,显著提高了在不同任务中的速度。

环境设置

一旦访问到你的 Xeon 计算实例,你必须确保有足够的存储空间来下载 Falcon 的检查点和模型碎片。如果你想同时测试 7 亿和 40 亿的 Falcon 版本,我们建议至少确保 150 GB 的存储空间。你还必须提供足够的 RAM 以将模型加载到内存中,并提供足够的核心以高效运行工作负载。我们成功地在 Intel Developer Cloud 的 32 核心 64GB RAM 虚拟机(第 4 代 Xeon)上运行了 7 亿和 40 亿 Falcon 版本。然而,这只是众多有效计算规格中的一种,进一步的测试可能会提高性能。

  1. 安装 miniconda。你可以在他们的网站找到最新版本:docs.conda.io/en/latest/miniconda.html

  2. 创建一个 conda 环境 conda create -n falcon python==3.8.10

  3. 安装依赖项 pip install -r requirements.txt。你可以在下面找到 requirements.txt 文件的内容。

transformers==4.29.2
torch==2.0.1
accelerate==0.19.0
einops==0.6.1

# requirements.txt

4. 激活你的 conda 环境 conda activate falcon

使用 Hugging Face Pipelines 运行 Falcon

Hugging Face 管道提供了一个简单而高级的接口,用于将预训练模型应用于各种自然语言处理(NLP)任务,如文本分类、命名实体识别、文本生成等。这些管道抽象了模型加载、分词和推理的复杂性,使用户能够仅用几行代码快速利用最先进的 NLP 模型。

以下是一个方便的脚本,你可以在 cmd/terminal 中运行它来试验原始的预训练 Falcon 模型。

from transformers import AutoTokenizer, AutoModelForCausalLM
import transformers
import torch
import argparse
import time

def main(FLAGS):

    model = f"tiiuae/falcon-{FLAGS.falcon_version}"

    tokenizer = AutoTokenizer.from_pretrained(model, trust_remote_code=True)

    generator = transformers.pipeline(
        "text-generation",
        model=model,
        tokenizer=tokenizer,
        torch_dtype=torch.bfloat16,
        trust_remote_code=True,
        device_map="auto",
    )

    user_input = "start"

    while user_input != "stop":

        user_input = input(f"Provide Input to {model} parameter Falcon (not tuned): ")

        start = time.time()

        if user_input != "stop":
            sequences = generator( 
            f""" {user_input}""",
            max_length=FLAGS.max_length,
            do_sample=False,
            top_k=FLAGS.top_k,
            num_return_sequences=1,
            eos_token_id=tokenizer.eos_token_id,)

        inference_time = time.time() - start

        for seq in sequences:
         print(f"Result: {seq['generated_text']}")

        print(f'Total Inference Time: {inference_time} seconds')

if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument('-fv',
                        '--falcon_version',
                        type=str,
                        default="7b",
                        help="select 7b or 40b version of falcon")
    parser.add_argument('-ml',
                        '--max_length',
                        type=int,
                        default="25",
                        help="used to control the maximum length of the generated text in text generation tasks")
    parser.add_argument('-tk',
                        '--top_k',
                        type=int,
                        default="5",
                        help="specifies the number of highest probability tokens to consider at each step")

    FLAGS = parser.parse_args()
    main(FLAGS)

# falcon-demo.py

要运行脚本(falcon-demo.py),你必须提供脚本和各种参数:

python falcon-demo.py --falcon_version "7b" --max_length 25 --top_k 5

该脚本有 3 个可选参数,帮助控制 Hugging Face 管道的执行:

  • falcon_version: 允许你从 Falcon 的 70 亿或 400 亿参数版本中进行选择。

  • max_length: 用于控制文本生成任务中生成文本的最大长度。

  • top_k: 指定在每一步中考虑的最高概率标记数量。

你可以修改脚本以添加/删除/编辑参数。重要的是,你现在可以访问到有史以来最强大的开源模型之一!

玩转原始 Falcon

原始 Falcon 并未针对任何特定目的进行调整,因此可能会输出无意义的内容(图 2)。不过,这并不妨碍我们提出一些问题来进行测试。当脚本完成模型下载和创建管道后,你将被提示向模型提供输入。当你准备停止时,键入“stop”。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2. 在 Intel 第四代 Xeon 上使用默认脚本参数对 70 亿参数 Falcon 模型进行命令行接口推理测试 — 图片由作者提供

脚本会打印推理时间,让你了解模型在当前参数设置和你为该工作负载提供的计算资源下响应的时间。

提示:通过调整 max_length 参数,你可以显著改变推理时间。

本教程旨在分享如何在 CPU 上通过 Hugging Face Transformers 运行 Falcon,但不探索 Intel CPUs 上进一步优化的选项。像 Intel Extension for Transformers 这样的库提供了通过量化、蒸馏和剪枝等技术加速基于 Transformer 的模型的能力。量化是一种广泛使用的模型压缩技术,可以减少模型大小并提高推理延迟 — 这将是探索提升此工作流性能的宝贵下一步。

总结与讨论

基础的 LLMs 为开发者创造了构建令人兴奋的 AI 应用的机会。然而,通常一半的挑战是找到一个允许商业衍生的正确许可的模型。Falcon 提供了一个难得的机会,因为它兼具性能和许可证灵活性。

尽管 Falcon 从开源的角度来看相当民主化,但其规模给工程师/爱好者带来了新的挑战。本教程通过结合 Falcon 的“真正开放”许可证、Hugging Face Pipelines 和 CPU 的可用性/可及性,帮助解决了这些问题,使开发者可以更多地访问这个强大的模型。

尝试一些令人兴奋的事情包括:

别忘了关注 我的个人资料以获取更多类似的文章

在本地运行 Llama 2 进行文档问答的 CPU 推理

原文:towardsdatascience.com/running-llama-2-on-cpu-inference-for-document-q-a-3d636037a3d8

清晰解释了如何使用 Llama 2、C Transformers、GGML 和 LangChain 在 CPU 上运行量化开源 LLM 应用程序的指南

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Kenneth Leung

·发表于 Towards Data Science ·阅读时间 11 分钟·2023 年 7 月 18 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 NOAA 提供,照片来自 Unsplash

像 OpenAI 的 GPT4 这样的第三方商业大型语言模型 (LLM) 提供商通过简单的 API 调用使 LLM 的使用实现了民主化。然而,由于数据隐私和合规性的各种原因,团队可能仍需在企业边界内进行自我管理或私有部署以进行模型推理。

开源 LLM 的普及幸运地为我们提供了广泛的选择,从而减少了对这些第三方供应商的依赖。

当我们在本地或云端托管开源模型时,专用计算能力成为关键考虑因素。尽管 GPU 实例可能看起来是最方便的选择,但成本可能会迅速失控。

在这份易于跟随的指南中,我们将探讨如何在 Python 中为检索增强生成(即文档问答)运行开源 LLM 的量化版本。在这个项目中,我们特别利用了最新的高性能 Llama 2 聊天模型。

内容

(1) 量化简明指南***(2)*** 工具和数据***(3)*** 开源 LLM 选择***(4)*** 逐步指南***(5)*** 后续步骤

本文的 GitHub 仓库可以在 这里 找到。

(1) 快速 量化简明指南

大型语言模型(LLMs)展示了出色的能力,但它们的计算和内存需求却很高。为了管理这些缺点,我们可以使用量化来压缩这些模型,从而减少内存占用,加速计算推理,同时保持模型性能。

量化是减少表示一个数字或值所用位数的技术。在大型语言模型的背景下,它涉及通过将权重存储在低精度数据类型中来减少模型参数的精度。

由于量化减少了模型的大小,因此它有利于在资源受限的设备上部署模型,如 CPU 或嵌入式系统。

一种常见的方法是将模型权重从其原始的 16 位浮点值量化为较低精度的 8 位整数值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从 FP16 到 INT8 的权重量化 | 图片作者

(2) 工具和数据

下图展示了我们将在本项目中构建的文档知识问答应用程序的架构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

文档问答架构 | 图片作者

我们将要运行文档问答的文件是曼彻斯特联足球俱乐部的公共177 页 2022 年年度报告

数据来源: 曼彻斯特联队有限公司(2022)。2022 年 20-F 表格年度报告。ir.manutd.com/~/media/Files/M/Manutd-IR/documents/manu-20f-2022-09-24.pdf(CC0:公共领域,因为SEC 内容 是公共领域且可以自由使用)

本项目的本地机器配备了AMD Ryzen 5 5600X 6 核处理器16GB RAM(DDR4 3600)。虽然它还配备了 RTX 3060TI GPU(8GB VRAM),但在本项目中将不使用该 GPU,因为我们将专注于 CPU 的使用。

现在让我们探讨一下在构建这个后台应用程序时将使用的软件工具:

(i) LangChain

LangChain 是一个流行的框架,用于开发由语言模型驱动的应用程序。它提供了一整套集成和数据连接器,允许我们将不同的模块链式连接和编排,创建诸如聊天机器人、数据分析和文档问答等高级用例。

(ii) C Transformers

C Transformers 是一个 Python 库,提供了对使用GGML库在 C/C++ 中实现的变换模型的绑定。首先,让我们了解一下 GGML 的内容。

ggml.ai 团队构建的 GGML 库是一个用于机器学习的张量库,它使大型模型能够在消费级硬件上高效运行。这是通过整数量化支持和内置优化算法实现的。

结果是,LLM 的 GGML 版本(量化模型的二进制格式)可以在 CPU 上高效运行。鉴于我们在这个项目中使用 Python,我们将使用 C Transformers 库,它本质上提供了 GGML 模型的 Python 绑定。

C Transformers 支持一组选定的开源模型,包括像 Llama、GPT4All-J、MPT 和 Falcon 等流行模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

支持 C Transformers 的 LLM(及相应的模型类型名称) | 图片由作者提供

(iii) Sentence-Transformers 嵌入模型

sentence-transformers 是一个 Python 库,提供了计算句子、文本和图像的嵌入(稠密向量表示)的方法。

它使用户能够计算超过 100 种语言的嵌入,然后可以进行比较以找到具有相似含义的句子。

我们将使用开源的 all-MiniLM-L6-v2 模型,因为它提供了最佳的速度和出色的通用嵌入质量。

(iv) FAISS

Facebook AI 相似性搜索(FAISS) 是一个设计用于高效相似性搜索和密集向量聚类的库。

给定一组嵌入,我们可以使用 FAISS 对它们进行索引,然后利用其强大的语义搜索算法在索引中搜索最相似的向量。

虽然它不是传统意义上的完整向量存储(如数据库管理系统),但它以一种优化的方式处理向量存储,以便高效地进行最近邻搜索。

(v) Poetry

Poetry 被用于设置虚拟环境和处理 Python 包管理,因为它易于使用且一致性高。

之前使用过 venv 的我强烈推荐切换到 Poetry,因为它使依赖管理更高效、更无缝。

查看 这个视频 以开始使用 Poetry。

[## 通过 Kenneth 的推荐链接加入 Medium

作为 Medium 会员,你的一部分会员费将支付给你阅读的作者,并且你可以全面访问每一个故事。

kennethleungty.medium.com](https://kennethleungty.medium.com/membership?source=post_page-----3d636037a3d8--------------------------------)

(3) 开源 LLM 选择

在开源 LLM 领域已经取得了巨大的进展,许多 LLM 可以在HuggingFace 的 Open LLM 排行榜上找到。

我为这个项目选择了最新的开源Llama-2–7B-Chat 模型 (GGML 8-bit),这是基于以下考虑:

模型类型(Llama 2)

  • 这是一个在 C Transformers 库中支持的开源模型。

  • 根据 2023 年 7 月的 Open LLM 排行榜,它在多个指标上表现最佳。

  • 在原始 Llama 模型设定的基准上表现出巨大的改进。

  • 它在社区中被广泛提及和下载。

模型大小(7B)

  • 鉴于我们正在进行文档问答,LLM 将主要用于相对简单的任务——总结文档块。因此,7B 模型的大小符合我们的需求,因为我们在技术上并不需要过于庞大的模型(例如 65B 及以上)。

微调版本(Llama-2-7B-Chat)

  • Llama-2-7B 基础模型是为文本补全构建的,因此缺乏在文档问答用例中获得最佳性能所需的微调。

  • Llama-2–7B-Chat 模型是我们用例的理想选择,因为它是专为对话和问答设计的。

  • 该模型(部分)授权用于商业用途。这是因为经过微调的 Llama-2-Chat 模型利用了公开的指令数据集和超过 100 万条人工标注。

量化格式(8 位)

  • 鉴于 RAM 限制为 16GB,8 位 GGML 版本适合,因为它只需要 9.6GB 的内存。

  • 8 位格式也提供了与 16 位相当的响应质量。

  • 原始的未量化 16 位模型需要约 15 GB 的内存,这距离 16GB RAM 的限制非常接近。

  • 还有其他更小的量化格式(即 4 位和 5 位),但它们在准确性和响应质量上有所折衷。

(4) 步骤指南

现在我们知道了各种组件,让我们逐步了解如何构建文档问答应用程序。

本指南的配套代码可以在 这个 GitHub 仓库中找到,所有依赖项可以在 requirements.txt 文件中找到。

注意:由于已有许多教程,我们* 不会 深入探讨一般文档问答组件的复杂性和细节(例如,文本分块、向量存储设置)。我们将重点关注开源 LLM 和 CPU 推断方面的内容。

步骤 1 — 处理数据并构建向量存储

在这一步中,将执行三个子任务:

  • 数据摄取和将文本拆分成块

  • 加载嵌入模型(sentence-transformers)

  • 索引块并存储在 FAISS 向量存储中

运行上述 Python 脚本后,向量存储将被生成并保存在名为 'vectorstore/db_faiss' 的本地目录中,并且准备好进行语义搜索和检索。

步骤 2 — 设置提示模板

鉴于我们使用的是 Llama-2–7B-Chat 模型,我们必须注意这里使用的提示模板。

例如,OpenAI 的 GPT 模型设计为对话输入和消息输出。这意味着输入模板应该是类似聊天的 记录格式(例如,分开的系统和用户消息)。

但是,这些模板在这里无法使用,因为我们的 Llama 2 模型并未特别优化用于那种对话界面。相反,更经典的提示模板,如下所示,将更为合适。

注意: 较小的 LLM,如 7B 模型,似乎对格式特别敏感。例如,当我改变提示模板的空格和缩进时,输出略有不同。

步骤 3 — 下载 Llama-2–7B-Chat GGML 二进制文件

由于我们将在本地运行 LLM,因此需要下载量化的 Llama-2–7B-Chat 模型的二进制文件。

我们可以通过访问 TheBloke 的 Llama-2–7B-Chat GGML 页面,托管在 Hugging Face 上,然后下载名为 llama-2–7b-chat.ggmlv3.q8_0.bin 的 GGML 8 位量化文件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

HuggingFace 上 Llama-2–7B-Chat-GGML 页面中的文件和版本页面 | 作者提供的图片

下载的 .bin 文件用于 8 位量化模型,可以保存在类似 /models 的适当项目子文件夹中。

模型卡页面还展示了每种量化格式的更多信息和细节:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不同量化格式的详细信息 | 作者提供的图片

注意: 要下载 C Transformers 支持的其他 GGML 量化模型,请访问主 TheBloke 在 HuggingFace 的页面 以搜索您所需的模型,并查找名称以 ‘-GGML’ 结尾的链接。

步骤 4 — 设置 LLM

为了利用我们下载的 GGML 模型,我们将使用 C Transformers 和 LangChain 的集成。具体来说,我们将使用 LangChain 中的 CTransformers LLM 包装器,它为 GGML 模型提供了统一的接口。

我们可以为 LLM 定义一系列 配置设置,例如最大新令牌数、top k 值、温度和重复惩罚。

注意: 我将温度设置为 0.01,而不是 0,因为当温度恰好为零时,我得到了奇怪的响应(例如,一长串重复的字母 E)。

第 5 步 — 构建和初始化 RetrievalQA

准备好提示模板和 C Transformers LLM 后,我们编写了三个函数来构建 LangChain RetrievalQA 对象,使我们能够执行文档问答。

第 6 步 — 整合到主脚本中

下一步是将之前的组件整合到 main.py 脚本中。由于我们将通过命令行将用户查询传递到应用程序,因此使用了 argparse 模块。

鉴于我们将返回源文档,附加代码用于处理文档片段,以获得更好的视觉显示效果。

为了评估 CPU 推理的速度,还使用了 timeit 模块。

第 7 步 — 运行示例查询

现在是时候对我们的应用程序进行测试了。在从项目目录加载虚拟环境后,我们可以在命令行界面(CLI)中运行一个包含用户查询的命令。

例如,我们可以使用以下命令询问 Adidas(曼联的全球技术赞助商)最低保障金额的值:

poetry run python main.py "How much is the minimum guarantee payable by adidas?"

注意: 如果我们不使用 Poetry,我们可以省略前面的 poetry run

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从用户查询传递到文档问答应用程序的输出 | 图片作者

输出显示我们成功获取了用户查询的正确响应(即 7.5 亿英镑),以及与查询语义相似的相关文档片段。

启动应用程序并生成响应的总时间为 31 秒,考虑到我们在本地使用的是 AMD Ryzen 5600X(虽然是一款不错的 CPU,但绝非市场上最好的),这个时间还是相当不错的。

结果更为令人印象深刻,因为在 GPU 上运行 LLM 推理(例如,直接在 HuggingFace 上)也可能需要两位数的秒数。

你的使用情况可能会有所不同

根据你的 CPU,获取响应的时间可能会有所不同。例如,当我在我的笔记本电脑上测试时,可能会在几分钟的范围内。

需要注意的是,将 LLM 适配到消费级硬件仍处于早期阶段,因此我们不能期望达到与 OpenAI API(由大量计算能力驱动)相当的速度。

目前,确实可以考虑在更强大的 CPU 实例上运行此程序,或者切换到使用 GPU 实例(例如 Google Colab 上的免费实例)。

(5) 下一步

现在我们已经构建了一个运行在 CPU 推理上的文档问答后端 LLM 应用程序,我们可以采取许多令人兴奋的步骤来推进这个项目。

  • 使用 Streamlit 构建前端聊天界面,特别是考虑到它最近做出了两个重要公告:Streamlit 与 LangChain 的集成,以及 Streamlit ChatUI 的发布,以便轻松构建强大的聊天机器人界面。

  • 将应用程序容器化并部署到云 CPU 实例上。虽然我们已经探索了本地推理,但应用程序可以轻松移植到云端。我们还可以利用云端更强大的 CPU 实例来加速推理(例如,计算优化的 AWS EC2 实例,如 c5.4xlarge)

  • 尝试稍大的 LLMs,如Llama 13B Chat模型。由于我们已经处理了 7B 模型,评估稍大一些的模型的性能是个好主意,因为它们在理论上应该更准确,并且仍能适应内存。

  • 尝试使用较小的量化格式,如 4-bit 和 5-bit(包括使用新 k-quant 方法的格式),以客观评估推理速度和响应质量的差异。

  • 利用本地 GPU 加速推理。如果我们想测试在C Transformers 模型上使用 GPU,可以通过在 GPU 上运行一些模型层来实现。这是有用的,因为目前只有 Llama 模型类型支持 GPU。

  • 评估使用vLLM,这是一个高吞吐量且内存高效的 LLMs 推理和服务引擎。然而,使用 vLLM 需要使用 GPU。

我将在接下来的几周内处理有关上述想法的文章和项目,敬请关注更多有见地的生成式 AI 内容!

离开前

我欢迎你加入我数据科学发现之旅! 关注这个Medium页面,并访问我的GitHub以获取更多有趣且实用的内容。同时,享受在 CPU 推理上运行开源 LLMs 的乐趣吧!

[## arXiv 关键词提取与分析管道,使用 KeyBERT 和 Taipy]

在 Python 中构建一个包含前端用户界面和后台管道的关键词分析应用程序

towardsdatascience.com](/arxiv-keyword-extraction-and-analysis-pipeline-with-keybert-and-taipy-2972e81d9fa4?source=post_page-----3d636037a3d8--------------------------------) [## 如何 Docker 化构建于 H2O、MLflow、FastAPI 和 Streamlit 的机器学习应用程序]

一个简单易懂的 Docker 多服务 ML 应用程序容器化指南

towardsdatascience.com ## F1 分数的微平均、宏平均与加权平均,详细解释

理解多类分类中 F1 分数的微平均、宏平均和加权平均的概念。

[towardsdatascience.com

通过 Excel VBA 运行 Python —— 时间序列重采样的案例

原文:towardsdatascience.com/running-python-via-excel-vba-a-case-of-time-series-resampling-fe108610e4e4?source=collection_archive---------8-----------------------#2023-04-17

对使用 VBA、Python 和通过 Excel VBA 运行 Python 进行太阳辐射时间序列重采样的综合评估

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Himalaya Bir Shrestha

·

关注 发布于 Towards Data Science ·11 min read·2023 年 4 月 17 日

最近,我频繁使用基于 Excel 的界面和模型。在此期间,我对 Office 的 Visual Basic for Application (VBA) 进行了了解,它是一种强大的编程语言,用于扩展 Office 应用程序。VBA 用于自动化重复任务、扩展用户交互以及在不同的 Office 应用程序之间进行交互,从而使日常任务更加高效和有效。

由于我有一定的 Python 编程背景,开始学习 VBA 的不同功能和特性时经历了相对陡峭的学习曲线,但随着时间的推移,这种曲线逐渐变得平缓。我意识到学习 VBA 非常有用,因为它可以直接与 Excel 工作簿交互,并能够自动化本身需要用不同编程语言编码的任务。然而,如果能在 Excel VBA 中运行其他编程语言(如 Python)的脚本,这将对自动化我们的日常任务更为有帮助。

在这篇文章中,我将分享使用 Excel VBA 和 Python 执行一个简单任务的经验——对太阳辐射数据进行时间序列重采样。此外,我还将展示如何通过 Excel VBA 运行 Python 脚本来执行相同的任务。让我们开始吧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来自 Aron VisualsUnsplash

数据

使用的数据是 2020 年的每小时全空面短波下行辐射(ALLSKY_SFC_SW_DWN),时区为 UTC,从 NASA Power 网站 下载,覆盖四个城市:奇特旺(尼泊尔)、纽约(美国)、悉尼(澳大利亚)和波恩(德国)。这些数据通过 NASA Power 的 API 服务 使用 Python 脚本访问和下载,我打算在另一篇文章中对此进行详细说明。

太阳辐射是指从太阳获得的每单位面积的功率(W/m²),以电磁辐射的形式存在于测量仪器的波长范围内。太阳辐射在一段时间内的积分值称为太阳辐照量,或称为太阳辐射量(Wh/m²)。

根据 NASA Power 的 定义,所使用的参数全空面短波下行辐射(ALLSKY_SFC_SW_DWN)指的是在所有天空条件下,地球表面水平面上的总太阳辐射(直接辐射加散射辐射)。总太阳辐射的另一种术语是全球水平辐射(GHI)。由于数据是每小时的,其单位为 Wh/m²。

Excel 文件中的数据

由于 2020 年是闰年,我获得了四个城市的 8784 小时太阳辐射值。这些值被放置在如下 Excel 文件中的列 B、C、D 和 E 中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2020 年四个城市的小时太阳辐射值放置在 Excel 文件中。公式栏显示了在突出显示的单元格 A2 中使用的公式。作者插图。

为了以日期格式获取列 A 中的值,Excel 中使用了以下公式(例如,在单元格 A2 中):

=TEXT(DATE(2020,1,1)+ROW(A1)/24, “yyyy-mm-dd hh:mm:ss”)

ROW(A1)/24 用于获取一天内的小时值(0–23 小时)。

同样,我将单元格 A1 命名为 datetime

在 Excel 中绘制 2020 年四个城市的原始小时太阳辐射数据如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Excel 绘制小时太阳辐射值。作者插图。

时间序列重采样

时间序列重采样指的是转换时间序列数据的频率级别。简单来说,重采样 是一种基于时间的分组操作,随后对每个组应用减少方法。数据必须具有类似日期时间的索引才能实现此目的。

1. 使用 Excel VBA 进行时间序列重采样

Excel 中没有默认的时间序列重采样函数。因此,我编写了一些子例程来获取如下面 a 和 b 节所述的月度和小时平均值。

a. 将小时值转换为平均月频率的 VBA 代码

本节描述了用于将小时值转换为 VBA 中平均月值的代码片段(见下文)。

数组是 VBA 中的一组变量。VBA 中数组元素的默认下界是 0. 在子例程的顶部提到 Option Base 1 可以将数组元素的下界改为 1. 我定义了一个名为 columns(4) 的数组,作为包含 4 个变量的字符串组。我在这个数组中传递了字符串 B、C、D 和 E。

Excel 中的单元格 A1 被定义为名为 datetime 的命名单元格。为了在 VBA 中引用此单元格,我声明了 datetime 作为一个范围,并将其分配给 Excel 中具有相同名称的范围。

要从 Excel 中的日期时间列中引用月份,我使用了 MONTH() 函数并将其分配给名为 mnth 的整数变量。为了循环遍历每行的小时值,我声明了另一个整数变量 row。最后,我声明了 sumnum_hours 以计算月度平均值。

'Use Option Base 1 before this subroutine if you want to start the list from 1 instead of 0.
'https://excelchamps.com/vba/arrays/
Option Base 1
Sub GetMonthlyAverage()

'defining an array for 4 strings
Dim columns(4) As String
columns(1) = "B"
columns(2) = "C"
columns(3) = "D"
columns(4) = "E"

'Refer to cell A1
Dim datetime As Range
Set datetime = Range("datetime")

'Definining mnth because Month is a function
Dim mnth As Integer
Dim row As Integer

Dim sum As Double
Dim num_hours As Double 

接下来,我创建了一个 for 循环来遍历每个城市的值。在这个 for 循环中,还有两个嵌套循环分别遍历每个月和每年的小时。sum 聚合了每个月的小时太阳辐射值,而 num_hours 聚合了每个月的小时数。最后,通过将 sum 除以 num_hours,获得了每个月每个城市的太阳辐射月度平均值。

'Loop through column for each city
For Each column In columns

    'Loop through each month of the year
    For mnth = 1 To 12

        sum = 0
        num_hours = 0

        'Loop through each row
        For row = 2 To 8785

            If MONTH(Cells(row, datetime.column)) = mnth Then
                Range(column & row).Interior.Color = RGB(255, 255, 0)
                num_hours = num_hours + 1
                sum = sum + Range(column & row).Value

            End If

        Next row

        Range(column & mnth).Offset(1, 7).Value = sum / num_hours

    Next mnth

Next column

End Sub

作为说明,Range(column & row).Interior.Color = RGB(255, 255, 0) 在遍历行(城市)和列(月份)时,用黄色高亮显示每个单元格。

b. VBA 代码,用于将一年中的每小时值转换为 2020 年每天的 24 小时(0–23 小时)的平均小时值

用于将每小时值(一年)转换为 2020 年每天的 24 小时的平均小时值的代码。

在本节的代码中,首先,我提取了数据表中 last_row(8785)的值,使用

Cells(datetime.row, datetime.column).End(xlDown).row

用于遍历每一行以进行进一步处理。

我使用了 Excel 中的 HOUR() 函数来检索每一行中 A 列对应的小时:

Hour(Cells(row, datetime.column).column).Value

本节的完整代码如下所示:

Option Base 1
Sub GetHourlyAverage()

‘defining an array for 4 strings
Dim columns(4) As String
columns(1) = “B”
columns(2) = “C”
columns(3) = “D”
columns(4) = “E”

'Definining mnth because Month is a function
Dim mnth As Integer
Dim row As Integer

Dim sum As Double
Dim num_hours As Double

Dim wb As Workbook
Dim ws As Worksheet
Dim datetime As Range
Dim last_row As Integer

Set wb = ThisWorkbook
Set ws = ThisWorkbook.Sheets("Sheet1")
Set datetime = ws.Range("datetime")
last_row = Cells(datetime.row, datetime.column).End(xlDown).row

Debug.Print datetime.Value
Debug.Print "Row: " & datetime.row & " Column: " & datetime.column
Debug.Print "Last row: " & last_row

'Loop through column for each city
For Each column In columns

    'Loop through each hour of the day
    For hr = 0 To 23

        sum = 0
        num_hours = 0

        'Loop through each row
        For row = datetime.row + 1 To last_row

            If Hour(Cells(row, datetime.column).Value) = hr Then
                Range(column & row).Interior.Color = RGB(255, 255, 0)
                num_hours = num_hours + 1
                sum = sum + Range(column & row).Value

            End If

        Next row

        Range(column & hr + 2).Offset(0, 14).Value = sum / num_hours

    Next hr

Next column

End Sub

在上面的代码片段中,Debug.Print 命令用于在 VBA 开发空间中的中间窗口打印中间结果,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Debug.Print 的输出在即时窗口中可见。由作者插图。

2. 使用 Pandas 进行时间序列重采样

Python 中的 pandas 库提供了一个内置的 方法 用于时间序列重采样,方法是使用 df.resample() 并传递重采样规则。例如,“M” 代表按月,“W” 代表按周,“Q” 代表按季度,“D” 代表按日,“B” 代表按工作日等。有关不同频率级别重采样的完整规则集可以在 这里 找到。

时间序列重采样的前提条件是数据框索引需要使用 pd.to_datetime() 转换为 datetime 类型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据框索引需要是 DatetimeIndex 类型,这是进行时间序列重采样的前提条件。由作者插图

通过 Groupby 提供的任何内置方法都可以作为 df.resample() 返回对象的方法,包括 min()max()mean()median()std()first()last()ohlc()sem()。在这篇文章中,我只是评估了太阳辐射的平均值。

在下面的 Python 代码中,我允许用户输入他们希望返回和显示的太阳辐射值的频率。选项包括原始数据、月平均值、日平均值、周平均值、季度平均值、以上所有选项以及每小时平均值(每天的 24 小时内每小时)。

import pandas as pd
import matplotlib.pyplot as plt
import os
import sys

#Enter os system to current working directory
os.chdir(sys.path[0])
file = "solar_irradiance.xlsm"

#read all rows and first 5 columns
df = pd.read_excel(file).iloc[:, :5]
df["Datetime"] = pd.to_datetime(df["Datetime"])
df.set_index(["Datetime"], inplace = True)

frequency = input("Enter the frequency you want to display? \n1\. Original \n2\. Monthly average\n3\. Daily average \n4\. Weekly average\n 5.Quarterly average \n 6.All of the above \n 7\. Hourly average \n? ")

if frequency == "Original":
    print (df)
    df.plot()
    plt.title("Original solar irradiance in 2020")
    plt.ylabel("Wh/m$²$")
    plt.legend()
    plt.show()

elif frequency == "Monthly average":
    print (df.resample(rule = "M").mean())
    df.resample(rule = "M").mean().plot()
    plt.title("Monthly average solar irradiance in 2022")
    plt.ylabel("Wh/m$²$")
    plt.legend()
    plt.show()

elif frequency == "Daily average":
    print (df.resample(rule = "D").mean())
    df.resample(rule = "D").mean().plot()
    plt.title("Daily average solar irradiance in 2022")
    plt.ylabel("Wh/m$²$")
    plt.show()

elif frequency == "Weekly average":
    print (df.resample(rule = "W").mean())
    df.resample(rule = "W").mean().plot()
    plt.title("Weekly average solar irradiance in 2022")
    plt.ylabel("Wh/m$²$")
    plt.legend()
    plt.show()

elif frequency == "Quarterly average":
    print (df.resample(rule = "Q").mean())
    df.resample(rule = "Q").mean().plot()
    plt.title("Quarterly average solar irradiance in 2022")
    plt.ylabel("Wh/m$²$")
    plt.legend()
    plt.show()

elif frequency == "All of the above":
    fig, axs = plt.subplots(2, 2, figsize = (20, 10), sharex = True, sharey = True)
    df.resample(rule = "D").mean().plot(ax = axs[0, 0])
    axs[0, 0].set_title("Daily mean")
    axs[0, 0].set_ylabel("Wh/m$²$")
    df.resample(rule = "W").mean().plot(ax = axs[0, 1])
    axs[0, 1].set_title("Weekly mean")
    df.resample(rule = "M").mean().plot(ax = axs[1, 0])
    axs[1, 0].set_title("Monthly mean")
    axs[1, 0].set_ylabel("Wh/m$²$")
    df.resample(rule = "Q").mean().plot(ax = axs[1, 1])
    axs[1, 1].set_title("Quarterly mean")fig.suptitle("Mean solar irradiance in four locations converted to different temporal frequencies")
    plt.show()

elif frequency == "Hourly average":
    #average value in each hour within 24 hours of a day
    print (df.groupby(df.index.hour).mean())
    df.groupby(df.index.hour).mean().plot()
    plt.title("Hourly average solar irradiance in 2022")
    plt.ylabel("Wh/m$²$")
    plt.legend()
    plt.show()

else:
    print ("The frequency you entered is incorrect.")

可以通过进入终端/命令提示符并键入 python -m python_script.py 来运行此脚本,如果路径与脚本文件相同。要中断运行,可以键入 Ctrl+C

3. 通过 Excel VBA 运行 Python 脚本

上述 Python 脚本也可以通过 Excel VBA 运行。为此,我将上述脚本保存为python_script.py文件。

下面的代码片段给出了完整的 VBA 子程序,用于运行 Python 脚本。

Sub RunPythonScript()
Dim objShell As Object
Dim PythonExePath As String, PythonScriptPath As String
ActiveWorkbook.Save

'Enter into the path of given workbook
ChDir Application.ThisWorkbook.Path
Set objShell = VBA.CreateObject(“Wscript.Shell”)

'Enter into the path of given workbook
ChDir Application.ThisWorkbook.Path

    Set objShell = VBA.CreateObject("Wscript.Shell")

    'Goto cmd. Type where python to get this path. Note that there are three quotes below.
    ' The hash symbol # below needs to be filled with the path in your system.
    PythonExePath = """C:\Users\#######################\python.exe"""

    'Get the path of the file.
    PythonScriptPath = Application.ThisWorkbook.Path & "\python_script.py"

    objShell.Run PythonExePath & PythonScriptPath

End Sub

首先,我声明了objShell变量以引用对象的地址(Wscript.shell)。该对象允许访问 Windows 功能以运行外部程序(此处为 Python 脚本)。

PythonExePath指的是计算机系统中 Python 应用程序的路径。在 Windows 系统中,可以通过在 Windows 命令提示符中输入where python来找到该路径。

Python 脚本的路径被定义为PythonScriptPath中的字符串。请注意,此路径不应包含空格,以确保脚本正常运行。

最后,以下行用于通过 Excel VBA 接口使用 Python 应用程序运行 Python 脚本。

objShell.Run PythonExePath & PythonScriptPath

输出

我将上述子程序/宏(使用 VBA 语言编写的命令)分配给了 Excel 文件中的一个按钮,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将宏分配到按钮以运行 Python 脚本。作者插图。

点击按钮会运行 Python 脚本,如下所示,并要求用户输入希望显示输出的频率:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Python 会要求用户输入选项。作者插图。

选择“所有上述选项”后,我得到了 2020 年四个城市日、周、月和季度的太阳辐射平均值输出图。可以观察到,随着时间分辨率从右到左、从上到下增加,线条/曲线变得更加平滑,因为较低时间分辨率的变异性被平均化了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

选择所有上述选项时的输出显示了按日、周、月和季度水平平均化的小时太阳辐射值。作者插图。

结论

在这篇文章中,我介绍了三种 2020 年四个城市小时太阳辐射数据时间序列重采样的技术:

  1. 使用 Excel VBA

  2. 使用 Python 中的 pandas

  3. 通过 Excel VBA 接口运行 Python 脚本

Excel VBA 在处理 Excel 数据时非常有用,因为它允许我们执行各种操作,并与同一或不同 Excel 文件中的不同工作表中的数据进行直接交互。此外,通过编写宏并将其分配给交互式按钮/用户表单或其他小部件,可以轻松地与其他用户共享 Excel 文件,对他们而言,功能才是关键,而不是后台代码的具体内容。

Python 的主要优势之一是其包含多个内置功能的包,这使得为常规任务编写单独的代码变得多余。在这种情况下,结合 Excel VBA 和 Python 的优势可能非常有利。这体现在我通过在 Excel 文件上点击按钮来运行时间序列重采样的 Python 脚本的方式上,这与几行简单的 VBA 代码相关联。

包括宏的 Excel 文件和 Python 脚本都可以在这个仓库script文件夹中找到。感谢您的阅读!

在 Databricks 中使用自定义 Docker 容器运行 Python Wheel 任务

原文:towardsdatascience.com/running-python-wheel-tasks-in-custom-docker-containers-in-databricks-de3ff20f5c79

一步一步的教程,教你如何在 Databricks 中使用自定义 Docker 镜像构建和运行 Python Wheel 任务(配合 Poetry 和 Typer CLI)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Johannes Schmidt

·发表于 Towards Data Science ·13 分钟阅读·2023 年 6 月 29 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片来自 Lluvia MoralesUnsplash

数据工程师设计和构建管道以运行 ETL 工作负载,使数据能够在下游用于解决业务问题。在 Databricks 中,对于这样的管道,通常需要创建一个集群、一个笔记本/脚本并编写一些Spark 代码。一旦有了工作原型,就可以使其准备好生产环境,以便通过 Databricks 作业执行代码,例如使用 REST API。对于 Databricks,这意味着通常需要在 Databricks 文件系统中已有一个 Python 笔记本/脚本,或者已将远程 Git 存储库连接到工作区*。但如果你不想做这两种情况呢?还有另一种方法可以在不上传任何文件到 Databricks 工作区或 连接到远程 Git 存储库 的情况下将 Python 脚本作为 Databricks 作业运行:Python wheel 任务具有声明的入口点,Databricks 容器服务允许你启动作业运行,这些作业将使用来自容器注册表的 Docker 镜像。

因此,本教程将向你展示如何做到这一点:在 Databricks 中的自定义 Docker 镜像中运行Python 任务(Python wheel 任务)。

要么 同步过程将 Git 文件上传到 Databricks 工作区 在代码执行之前,要么提供远程 git 引用笔记本/脚本用于作业运行

为什么你会想这么做?

你可能有“构建、发布和随处运行”的理念,所以你可能对使用 DataBricks 的传统方式不满意。

让我解释一下。

Databricks 为其平台建议了一些 CI/CD 技术

持续集成和持续交付/持续部署(CI/CD)是指通过使用自动化管道,以短而频繁的周期开发和交付软件的过程。

通常,对默认分支或发布的提交会启动一个管道进行代码检查、测试等,并最终导致与 Databricks 交互的操作。这可能是一个 REST API 调用以触发作业运行,其中指定了笔记本/脚本,或者在 Databricks 的情况下,将部署包部署到目标环境,这可以是工作区。

第一种选择 通常需要 Databricks 连接到远程 Git 仓库,以便能够使用远程 Git 引用,例如,Github 仓库主分支中的特定笔记本,以触发作业运行。

第二种选择 将文件上传到其工作区,但不一定需要 Databricks 连接到远程 Git 仓库。此工作流选项的可视化摘要请见 此处

部署包 可以是笔记本、库、工作流等。通常使用 Databricks CLI 或 REST API 将包部署到 Databricks 工作区。实质上,自动化管道将远程 git 仓库中的更改与 Databricks 工作区同步。

我这篇博客的目标是探讨一种不同的 CI/CD 工作流,即不与 Databricks 交互的工作流(将代码与 Databricks 工作区解耦)。所建议的工作流只是创建一个 Docker 镜像,并将其推送到容器注册表,将作业运行的执行留给服务。这可以是任何东西,例如 Web 应用、函数、定时任务或 Apache Airflow

请记住,像这样做并不适用于所有用例,但我认为一些工作负载(例如 ETL)可以从中受益。用常识来决定最适合你的方法。尽管如此,值得探索 Databricks 提供的平台选项。那么,让我们开始吧。

TLDR

Databricks(标准层*)将在 Azure 上提供。将使用 Poetry 创建一个具有定义入口点和依赖项的单个 Python wheel 文件。该 wheel 文件将被安装在与 Databricks 兼容的 Docker 镜像 中,并推送到容器注册表。将创建并触发作业运行,使用 Databricks 工作区 UI 门户和 REST API。

标准层的 Azure Databricks 工作区不应产生任何费用

替代方案包括 AWS 或 GCP

前提条件

  • Poetry

  • Docker

  • Azure 或 AWS 账户

  • 容器注册表(例如 DockerHub、ACR、ECR)

结构

  • Apache Spark 和 Databricks

  • 在 Azure 上配置 Databricks

  • 启用 Databricks 容器服务

  • 创建个人访问令牌(PAT)

  • 执行作业运行的选项(Python)

  • 使用入口点创建 Python wheel(使用 Poetry 和 Typer CLI)

  • 构建一个与 Databricks 兼容的 Docker 镜像

  • 创建并触发一个作业运行(UI)

  • 创建并触发一个作业运行(REST API)

Apache Spark 和 Databricks

在介绍中,我已经谈到了 Databricks,并提到了数据工程师的常见用例。如果你需要一个关于 Apache Spark 和 Databricks 的简短定义,请看这里:

Spark 是一个用于处理大规模数据的开源引擎。通过将数据和计算分布到集群中的多个节点,它实现了并行性和可扩展性。

Databricks 是一个基于云的平台,利用 Spark 执行各种与数据相关的任务,如数据处理、数据分析、机器学习和 AI 工作负载。

在 Azure 上配置 Databricks

假设你已经有了Azure 账户订阅,如果没有,创建一个免费的 Azure 账户,或者继续跟随教程。

让我们在 Azure 上配置 Databricks 资源(工作区)。在此阶段不应产生费用。

我们创建一个资源组,在其中配置 Databricks 资源:databricks-job-run-rg

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建资源组 — 作者提供的图片

在此资源组内,我们可以创建Azure Databricks 工作区并为其命名:databricks-job-run

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建一个 Azure Databricks 工作区 — 作者提供的图片

对于定价层,选择标准。你可以保持其余部分为建议设置。托管资源组名称可以留空。

请注意,只有使用 Premium 版,我们才会拥有适当的基于角色的访问控制(RBAC)功能。但为了本教程的方便,我们不需要它。我们将使用个人访问令牌(PAT),允许我们通过 Databricks REST API 创建和触发作业运行。

部署后,我们的资源组现在包含 Azure Databricks 工作区:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

资源组中的 Azure Databricks 服务 — 作者提供的图片

然后我们可以启动工作区,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从 Azure 门户启动 Databricks 工作区 — 作者提供的图片

这将打开一个友好的用户界面(UI),如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Databricks UI — 作者提供的图片

到目前为止,一切顺利。

启用 Databricks 容器服务

Databricks 默认情况下不允许自定义 Databricks Docker 镜像,我们必须先启用此功能。有关步骤,请参阅这里

管理员设置(右上角下拉菜单)中,我们必须启用容器服务字段:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

工作区设置 — 图片由作者提供

还要确保个人访问令牌已启用,我们将在下一节中创建一个。

创建个人访问令牌(PAT)

用户设置(右上角下拉菜单)中,有一个按钮允许我们生成一个新令牌:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建 PAT — 图片由作者提供

将这个令牌保存在安全的地方,因为它可以用于对Databricks API进行安全认证。我们稍后将需要它。

请注意:我们使用 PAT,因为标准层不提供 RBAC 功能。为此,我们需要升级到高级版。

执行作业运行的选项(Python)

在我们为 Databricks 作业创建Python wheel之前,我想重点关注我们用于创建和运行Databricks 作业(Python)的选项。有不同的方法可以在集群中执行脚本。

工作流/作业工作流/作业运行面板中创建任务,显示了我们的选项。或者,我们可以通过阅读文档来了解它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Databricks 作业中运行脚本的选项 — 图片由作者提供

如前所述,我们基本上可以指定2种来源从中获取要执行的笔记本/脚本:Databricks 工作区远程 Git 仓库。对于Python wheels,我们不能选择来源,而是必须输入包名称入口点。wheel 包应存在于 DBFS(Databricks 文件系统)或如 PyPi 等索引上。我认为文档中的教程对来源选项的解释不够清晰(2023 年 5 月),或者可能是我无法找到这些信息。但有一篇很好的博客文章展示了如何做:如何将你的 Python 项目部署到 Databricks

不过,实际上并没有提到(尽管这是合理的),如果你提供一个已经安装了你的 Python wheel 任务的自定义 docker 镜像,你也可以指定它并且它会被执行。这就是我们要做的。

*对于 Python 脚本,还有一个选项:DBFS(Databricks 文件系统)

使用入口点创建一个 Python wheel(配有 Poetry 和Typer CLI

我已经在src 布局中设置了一个使用Poetry的项目,其中包含了构建 wheel 的代码和命令。你可以在这里找到完整的代码:

github.com/johschmidt42/databricks-python-wheel-job-run

./src
├── dbscript1
│   ├── __init__.py
│   └── script.py
└── dbscript2
    ├── __init__.py
    └── __main__.py

pyproject.toml中,这些脚本定义如下:

[tool.poetry]
name = "dbscripts"
version = "1.0.0"

...

packages = [
    {include = "dbscript1", from = "src"},
    {include = "dbscript2", from = "src"},
]

[tool.poetry.scripts]
dbscript1 = "dbscript1.script:main"
dbscript2 = "dbscript2.__main__:main"

...

在两个包dbscript1dbscript2中,我们找到了一些代码:

src/dbscript1/script.py

src/dbscript2/main.py

它们基本上做了相同的事情。它们之间唯一明显的区别是script1 的名字只是“script”,而另一个的名字是“main”。你会很快看到这个区别的影响。这两个脚本都使用 typer**库来为 script1script2 函数创建一个命令行界面(CLI)。

script1script2 函数被带有参数(必需)调用时,它会打印当前文件的名称 (__file__) 和传入的 argument 参数的值。

从这些文件中,我们可以使用 Poetry 创建一个包:

> **poetry build --format wheel**

Building dbscripts (1.0.0)
  - Building wheel
  - Built dbscripts-1.0.0-py3-none-any.whl

这将创建一个wheel文件在dist目录中

dist
└── dbscripts-1.0.0-py3-none-any.whl

我们可以使用 pip 将这个wheel安装到虚拟环境中,看看会发生什么:

**> pip install dist/dbscripts-1.0.0-py3-none-any.whl**

Successfully installed dbscripts-1.0.0

如果我们重新启动 shell(bash、zsh 等),我们现在可以调用两个新函数:

(dbscripts-py3.9) databricks-python-wheel-job-run % **dbscript [TAB]**

**dbscript1**  **dbscript2**

两者都可以作为函数使用,如 blackisort

> **dbscript1 Databricks**

/Users/johannes/learnspace/databricks-python-wheel-job-run/.venv/lib/python3.9/site-packages/dbscript1/script.py
Your argument is: Databricks
> **dbscript2 Databricks**

/Users/johannes/learnspace/databricks-python-wheel-job-run/.venv/lib/python3.9/site-packages/dbscript2/__main__.py
Your argument is: Databricks

这里是将入口点函数命名为“main”的好处:

> **python -m dbscript1 abc**

/Users/johannes/learnspace/databricks-python-wheel-job-run/.venv/bin/python: No module named dbscript1.__main__; 'dbscript1' is a package and cannot be directly executed

与之相比

> **python -m dbscript2 abc**

/Users/johannes/learnspace/databricks-python-wheel-job-run/.venv/lib/python3.9/site-packages/dbscript2/__main__.py
Your argument is: abc

如果你对两个脚本如何在 shell 中作为函数执行感到困惑,只需查看你的虚拟环境的 bin 目录:.venv/bin

# .venv/bin

.
├── activate
...
├── black
├── blackd
├── **dbscript1**
├── **dbscript2**
├── dotenv
├── flake8
...

代码看起来像这样:

这些被称为consol_scripts 入口点。你可以在Python 文档中阅读相关内容。

Poetry 是一个依赖、环境和包管理器

Typer 是一个基于流行 Python 库 click 的封装器。它允许我们仅通过 Python 类型提示构建 CLI!

构建一个兼容 Databricks 的 Docker 镜像

我们现在有一个 Python wheel 文件,在安装时带有两个控制台脚本入口点。让我们用 docker 容器化它:

Dockerfile

这个 Dockerfile 定义了一个多阶段构建用于我们的 Python 应用程序。各个阶段通过 Dockerfile 中的 #----# 分隔。

第一个阶段是基础镜像,它使用Databricks Runtime Python 12.2-LTS*作为基础镜像,并将工作目录设置为 /app。它还会更新 pip。

我们也可以构建我们自己的基础镜像,只要我们安装了某些库: 构建你自己的 Docker 基础镜像

第二阶段是构建镜像,它安装 Poetry,复制应用程序文件(包括 pyproject.tomlpoetry.lockREADME.md),并使用 Poetry 构建一个 wheel。

第三阶段是生产镜像,它从构建阶段复制 wheel 并使用 pip 安装(我们不希望在生产镜像中使用 Poetry!)。

我们可以通过以下命令从中构建一个 docker 容器:

> **docker build --file Dockerfile --tag databricks-wheel-scripts:latest --target production .**

进入(bash)后,我们可以像之前一样执行我们的console_script

> **docker run -it --rm databricks-wheel-scripts:latest /bin/bash**

> root@bca549fdcb50:/app# **dbscript1 some_value**

/databricks/python3/lib/python3.9/site-packages/dbscript1/script.py
Your argument is: some_value

或者我们可以一行完成:

> **docker run -it --rm databricks-wheel-scripts:latest /bin/bash -c "dbscript1 hello_world"**

请注意,docker 的入口点是 /bin/bash,因为这个 shell 包含了 $PATH 变量中的 dbscript1 和 dbscript2。

这个 docker 镜像现在可以推送到我们选择的容器注册表。这可以是 DockerHub、ACR、ECR 等。例如,在我的情况下,我选择 Azure Container Registry(ACR),因为 Databricks 工作区也在 Azure 上。

要推送镜像,我运行这些命令:

> **az login** > **az acr login --name databricksjobrunacr**
> **docker tag databricks-wheel-scripts:latest databricksjobrunacr.azurecr.io/databricks-wheel-scripts:latest**
> **docker push databricksjobrunacr.azurecr.io/databricks-wheel-scripts:latest**

创建并触发一个 Databricks 作业运行(UI)

在 Databricks 工作区(UI)中,我们可以创建一个作业(工作流选项卡)并为其定义一个新的集群:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建一个新的 Databricks 集群 — 作者图片

在这里,我创建了最小的单节点集群,Standard_F4,其消耗0.5 DBU/h。在高级 选项 部分,我们可以指定 Docker 设置:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用你自己的 Docker 容器 — 作者图片

这样集群就可以从容器注册表中拉取镜像。我们提供一个容器注册表用户名密码,但我们也可以使用“默认身份验证”方法(例如 Azure)。

在作业 UI 中,我们可以创建一个作业:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作业创建 — 作者图片

在这里我们定义包名入口点

请注意,在 UI 中,我们先创建一个作业,然后触发它进行作业运行。REST API 允许我们通过一个调用创建并触发一次性作业运行!我们将在下一部分中看到这一点。

作业运行 选项卡中,我们可以看到作业运行的状态:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

待处理的作业运行 — 作者图片

几分钟内,集群就准备好了,并在我们选择的 docker 镜像中运行了我们的控制台脚本:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作业运行成功 — 作者图片

我们可以通过点击运行来获取日志(stdout、stderr):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作业运行的输出 — 作者图片

太棒了!

创建并触发作业运行(REST API)

像 Azure Function 这样的服务不能使用 UI 来启动作业运行。相反,它必须使用 Databricks REST API(Jobs API 2.1)。我们可以使用 Airflow 的 Databricks 连接器 来完成,但编写一些发送单个请求到 REST API 的 Python 代码可能设置更快。因此,让我们编写一些允许我们创建和触发作业运行的 Python 代码。我将把这些代码封装在一个名为Databricks 服务的类中:

脚本有两个组件:一个 SecretsConfig 类和一个 DatabricksService 类。

SecretsConfig类用于读取和存储配置设置和秘密,例如 Databricks URL、Databricks 个人访问令牌(PAT)*、Azure 容器注册表(ACR)用户名和密码。这些是我们通过 UI 需要指定的基本参数。

*如果你使用的是 Premium Tier 部署的 Databricks,你无需使用 PAT,而可以通过 OAuth 获取令牌。

DatabricksService类用于与 Databricks API 交互。它允许使用现有集群或新集群创建和触发一次性作业运行。API 文档可以在作业 API 2.1中找到。该服务本身只有对同一 submit 端点的两种变体调用:create_job_run_on_existing_cluster()方法用于在现有集群上创建作业运行,而create_job_run_on_new_cluster()方法用于在新集群上创建作业运行。

我们简要查看一下create_job_run_on_new_cluster()函数:

该方法接受多个参数,例如image_urlpackage_nameentrypoint 等,并调用 submit 端点以在新集群上创建并启动作业运行。

python_wheel_task_payload字典用于指定要使用的 Python 包的包名称和入口点。如果提供了位置参数和命名参数,也可以在此字典中指定。

cluster字典用于指定新集群的设置。设置包括工作节点数量、Spark 版本、运行时引擎、节点类型 ID 和驱动程序节点类型 ID。

有了这些,我们现在需要一些代码来使用我们的 DatabricksService 调用 Databricks REST API:

示例脚本用于通过 Databricks REST API Jobs2.1 创建作业运行

运行脚本后,我们观察到返回了状态码 200,并且作业运行在一段时间后成功完成:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成功的作业运行(通过 REST API 调用) — 图片由作者提供

然后我们看到输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作业运行输出 — 图片由作者提供

很简单!

如果我们想要使用身份访问 Azure 中的资源,我们需要在调用提交端点时(新集群!)提供服务主体的凭据作为环境变量

结论

在 Databricks 中运行自定义 Docker 镜像中的 Python 作业不仅是可能的,而且是实用且高效的。这为你的代码和工作流程提供了更多的灵活性、控制权和可移植性。你可以使用这种技术运行任何 Python 脚本(或任何其他代码)作为 Databricks 作业,而无需将任何文件上传到 Databricks 工作区或连接到远程 Git 存储库。

在这个简短的教程中,你已经学习了如何在自定义 Docker 镜像中创建Python wheel 任务,并使用 Databricks UI 或 REST API 触发作业运行。

你对这个工作流程怎么看?你在 Databricks 中是如何运行你的 Python 作业的?请在评论中告诉我。

在 Jupyter Notebook 中使用 JupySQL、DuckDB 和 MySQL 运行 SQL 查询

原文:towardsdatascience.com/running-sql-queries-in-jupyter-notebook-using-jupysql-duckdb-and-mysql-3c53fbe40f8d

学习如何在你的 Jupyter Notebooks 中运行 SQL

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 魏梦龙

·发表于 数据科学的前沿 ·8 分钟阅读·2023 年 2 月 24 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Wafer WAN 提供,来自 Unsplash

传统上,数据科学家使用 Jupyter Notebook 从数据库服务器或外部数据集(如 CSV、JSON 文件等)中提取数据,并将其存储到 Pandas 数据框中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

除非另有说明,否则所有图片均由作者提供

然后,他们使用数据框进行可视化。这种方法有几个缺点:

  • 查询数据库服务器可能会降低数据库服务器的性能,因为数据库服务器可能未针对分析工作负载进行优化。

  • 将数据加载到数据框中会占用宝贵的资源。例如,如果目的是可视化数据集的某些方面,则需要先将整个数据集加载到内存中,然后才能进行可视化操作。

为了提高上述操作的性能,理想情况下,应将数据处理(所有的数据清理和过滤)卸载到能够高效执行数据分析的客户端,并返回结果用于可视化。这就是本文的主题 — JupySQL

JupySQL 是一个 Jupyter Notebook 的 SQL 客户端,允许你使用 SQL 直接访问数据集。JupySQL 的主要思想是在 Jupyter Notebook 中运行 SQL,因此得名。

JupySQL 允许你使用 SQL 查询数据集,而无需维护存储数据集的数据框。例如,你可以使用 JupySQL 连接到数据库服务器(如 MySQL 或 PostgreSQL),或通过 DuckDB 引擎访问你的 CSV 文件。查询结果可以直接用于可视化。下图展示了 JupySQL 的工作原理:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你可以使用以下魔法命令在 Jupyter Notebooks 中使用 JupySQL:

  • %sql — 这是一个行魔法命令,用于执行 SQL 语句

  • %%sql — 这是一个单元魔法命令,用于执行多行 SQL 语句

  • %sqlplot — 这是一个行魔法命令,用于绘制图表

我们的数据集

对于本文,我将使用一些数据集:

安装 JupySQL

要安装 JupySQL,你可以使用 pip 命令:

!pip install jupysql duckdb-engine --quiet

上述语句安装了 jupysql 包以及 duckdb-engine

下一步是使用 %load_ext 行魔法命令加载 sql 扩展:

%load_ext sql

与 DuckDB 集成

加载 sql 扩展后,你需要加载一个数据库引擎来处理数据。对于本节,我将使用 DuckDB。以下语句启动一个 DuckDB 内存数据库:

%sql duckdb://

执行查询

启动 DuckDB 数据库后,让我们使用 airports.csv 文件执行查询:

%sql SELECT * FROM airports.csv ORDER by STATE

你将看到以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果你的 SQL 查询很长,请使用 %%sql 单元魔法命令:

%%sql
SELECT 
    count(*) as Count, STATE 
FROM airports.csv 
GROUP BY STATE 
ORDER BY Count 
DESC LIMIT 5

上述 SQL 语句生成了以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

保存查询

你还可以使用--save选项保存查询,以便后续使用:

%%sql --save boston 
SELECT 
    * 
FROM boston.csv

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果你想保存一个查询但不执行它,请使用--no-execute选项:

%%sql --save boston --no-execute
SELECT 
    * 
FROM boston.csv

上述语句将查询结果保存为名为boston的表。你将看到以下输出:

*  duckdb://
Skipping execution...

绘图

JupySQL 允许你使用 %sqlplot 行魔术命令绘制图表。

直方图

使用前面部分保存的查询,你现在可以绘制一个直方图,显示agemedv字段的分布:

%sqlplot histogram --column age medv --table boston --with boston

这是显示agemedv字段值分布的直方图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是另一个示例。这一次,我们将使用 titanic_train.csv 文件:

%%sql --save titanic 
SELECT 
   *
FROM titanic_train.csv WHERE age NOT NULL AND embarked NOT NULL

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你现在可以绘制所有乘客的年龄分布:

%sqlplot histogram --column age --bins 10 --table titanic --with titanic

你可以使用--bin选项来指定你想要的箱数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你还可以通过将绘图分配给一个变量来定制绘图,该变量的类型为matplotlib.axes._subplots.AxesSubplot

ax = %sqlplot histogram --column age --bins 10 --table titanic --with titanic
ax.grid()
ax.set_title("Distribution of Age on Titanic")
_ = ax.set_xlabel("Age")

使用matplotlib.axes._subplots.AxesSubplot对象,你可以开启网格、设置标题以及为绘图设置 x 轴标签:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

箱形图

除了直方图,你还可以绘制箱形图:

%sqlplot boxplot --column age fare --table titanic --with titanic

结果箱形图显示了agefare字段的中位数、最小值和最大值以及异常值:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你还可以查看sibspparch字段的箱形图:

%sqlplot boxplot --column sibsp parch --table titanic --with titanic

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

饼图

你还可以使用 JupySQL 的遗留绘图 API 绘制饼图。对于这个示例,我将使用 airports.csv 文件来找出每个州的机场数量。

首先,我使用 SQL 统计每个州的所有机场并筛选出前五名:

airports_states = %sql SELECT count(*) as Count, STATE FROM airports.csv GROUP BY STATE ORDER BY Count DESC LIMIT 5
print(type(airports_states))

%sql语句的结果是一个sql.run.ResultSet对象。从这个对象中,如果需要,我可以获得数据框:

airports_states.DataFrame()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我还可以使用它调用pie() API 来绘制饼图:

import seaborn
# https://seaborn.pydata.org/generated/seaborn.color_palette.html
palette_color = seaborn.color_palette('pastel')

total = airports_states.DataFrame()['Count'].sum()
def fmt(x):    
    return '{:.4f}%\n({:.0f} airports)'.format(x, total * x / 100)

airports_states.pie(colors=palette_color, autopct=fmt)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绘图 API 还支持条形图:

palette_color = seaborn.color_palette('husl')
airports_states.bar(color=palette_color)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

并使用plot()函数绘制折线图(这里我使用 AAPL.csv 文件):

apple = %sql SELECT Date, High, Low FROM AAPL.csv

# apple.plot() is of type matplotlib.axes._subplots.AxesSubplot
apple.plot().legend(['High','Low'])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

集成 MySQL

迄今为止,前面几个部分的所有示例都是使用 DuckDB。现在让我们尝试连接到数据库服务器。对于我的示例,我将使用 MySQL 服务器,具体信息如下:

  • 数据库 — 保险

  • 表格 — 保险(从 insurance.csv 文件导入)

  • 用户账户user1

要连接到 MySQL 服务器,请创建一个SQLAlchemy URL 标准连接字符串,格式如下:mysql://*username*:*password*@*host*/*db*

运行以下代码片段时,将提示你输入 user1 账户的密码:

from getpass import getpass

password = getpass()
username = 'user1'
host = 'localhost'
db = 'Insurance'

# Connection strings are SQLAlchemy URL standard
connection_string = f"mysql://{username}:{password}@{host}/{db}"

输入 user1 账户的密码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

要将 JupySQL 连接到 MySQL 服务器,请使用 %sql 行魔法,并附上连接字符串:

%sql $connection_string

如果你使用 %sql 行魔法而没有任何输入,你将会看到当前的连接(即 DuckDB 和 MySQL):

%sql

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们选择保险表来检查其内容:

%sql SELECT * FROM Insurance

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接下来,让我们使用 bar() API 绘制一个条形图:

regions_count = %sql SELECT region, count(*) FROM Insurance GROUP BY region
regions_count.bar(color=palette_color)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果你喜欢阅读我的文章,并且它对你的职业/学习有所帮助,请考虑注册成为 Medium 会员。每月 $5,它可以让你无限访问 Medium 上的所有文章(包括我的文章)。如果你使用以下链接注册,我将获得一小笔佣金(对你没有额外费用)。你的支持意味着我将能投入更多时间写类似的文章。

[## 使用我的推荐链接加入 Medium - Wei-Meng Lee

阅读 Wei-Meng Lee(以及 Medium 上成千上万其他作家的)每一个故事。你的会员费用直接支持……

weimenglee.medium.com

概要

我希望这篇文章让你更好地了解如何使用 JupySQL 以及连接到不同数据源(如 MySQL 和 DuckDB)的各种方法。此外,除了连接到我们的数据集,我还展示了如何使用 JupySQL 直接对查询结果进行可视化。像往常一样,请务必尝试一下,并告诉我效果如何!

使用 Pandas AI 跑步:对波士顿马拉松的探索

原文:towardsdatascience.com/running-with-pandas-ai-an-exploration-of-the-boston-marathon-ad9516b34d8a

从开始到终点线,深入探索波士顿马拉松获胜者数据集

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Sejal Dua

·发表于 Towards Data Science ·9 分钟阅读·2023 年 5 月 22 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Miguel A Amutio 拍摄,来自 Unsplash

当我站在今年波士顿马拉松激动人心的氛围中,兴奋地为跑者加油时,我开始理解波士顿几乎难以言喻的魔力。马拉松展示了通过意志力、纪律和决心,普通人可以完成非凡的壮举。更重要的是,马拉松是一种对人类进步和潜力的庆祝。波士顿作为世界上最古老的年度马拉松之一,是最具挑战性的赛道之一,也是最具象征意义的事件之一,尤其是在 2013 年爆炸事件之后,我决定通过数据的视角来探索它。

我找到了一份 Kaggle 数据集,其中包含了波士顿马拉松的获胜者(男女)及其获胜时间。该数据集的列如下:

  • 年份 (int): 波士顿马拉松举行的年份及获胜者获得冠军的年份

  • 获胜者 (str): 获得该年度马拉松冠军的运动员(男/女)的名字

  • 国家 (str): 运动员所代表的或来自的国家

  • 时间 (time): 运动员的完成时间(小时、分钟和秒)

  • 距离 (英里): 运动员所跑的距离(英里)

  • 距离 (公里): 运动员所跑的距离(公里)

在早期,我了解到没有公开可用的数据集包含所有精英完赛者的时间,这限制了我能够探索的问题类型。然而,由于这个波士顿马拉松冠军数据集相对紧凑(126 行 x 6 列),我认为尝试更快捷的方法会很有趣。随着 ChatGPT 以及其他大型语言模型(LLMs)的出现和普及,我想探索如何利用 AI 提升我们的分析水平和/或将其带给技术水平较低的观众。

恰巧在我的 GitHub 探索页面上,我发现了Pandas AI,这是一个将生成性人工智能功能集成到 Pandas 中的 Python 库,使数据框可以进行对话。基本上,我们是否可以仅通过与数据集对话来了解波士顿马拉松冠军的一些信息?这将是一个颠覆性的改变。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由 DALL-E 生成的图像

Pandas AI 提供了有关如何设置你的 Open AI 令牌、选择 LLM 等的详细文档,因此我将直接进入分析,但值得一提的是,用户在调用 Pandas AI 时可以利用几个方便的参数。你可以请求对话式响应,也可以要求 Pandas AI“显示代码”。在本次演示中,我将展示这两种提示类型的示例。

分析

首先,我从 Kaggle 下载了数据集,用 pandas 读取数据,并设置了我的 LLM 实例。

import pandas as pd
from pandasai import PandasAI
from pandasai.llm.openai import OpenAI
import matplotlib.pyplot as plt
from dotenv import load_dotenv
load_dotenv()
import os

# Instantiate a LLM
llm = OpenAI(api_token=os.environ.get('OPENAI_KEY'))
pandas_ai = PandasAI(llm, conversational=True)

# Read in the data
df = pd.read_csv('./data/Mens_Boston_Marathon_Winners.csv')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

接下来,我让 Pandas AI 为我进行数据清理,因为我注意到有些空行。即 2020 年由于 COVID-19 疫情没有波士顿马拉松冠军。我还注意到一些数据类型可能没有被正确解释。

pandas_ai.run(df, prompt='''
Clean the dataset for me, please. Drop rows with empty, not a number, 
or null values. Cast Year as integers. Interepret Time as a pandas 
datetime object where the format is hours, minutes, then seconds. 
Include comments in the code.
''', 
show_code=True, is_conversational_answer=True)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

它轻松完成了这个任务,仅用了大约 30 秒!我最喜欢它的响应的是,它准确地展示了我的英文提示如何通过每一行代码得到回应。这使得这个包作为一个教育工具非常有吸引力,特别是对于那些希望开始使用 Python 进行数据分析的人。

然后,我对 Pandas AI 的事实检索能力进行了一个小测试。我问了它这个问题:

“2022 年谁赢得了波士顿马拉松?他们的时间与 1922 年的时间相比快了还是慢了?请用分钟报告答案。”

经过一些磕磕绊绊后,它能够提供一些有用的代码:

winner_2022 = df.loc[df['Year'] == 2022, 'Winner'].values[0]
time_2022 = df.loc[df['Year'] == 2022, 'Time'].values[0]
time_1922 = df.loc[df['Year'] == 1922, 'Time'].values[0]
time_diff = (time_2022 - time_1922).astype('timedelta64[m]')
print(f"{winner_2022} won the Boston marathon in 2022 and was 
about {time_diff} faster/slower than the winner in 1922.")

埃文斯·切贝特(Evans Chebet)在 2022 年赢得了波士顿马拉松,比 1922 年的冠军快了约-12 分钟/慢了约-12 分钟。

Pandas AI 提供了这样的见解非常酷,但让我们花点时间消化一下这个声明有多么不可思议。克拉伦斯·德玛在 1922 年波士顿马拉松中完成时间为 2:18:10。快进 100 年,埃文斯·切贝特在 2022 年波士顿马拉松中完成时间为 2:06:51。现在,重点来了:实际上,波士顿马拉松在 1922 年仅仅是 24.5 英里的比赛。后来为了符合奥林匹克标准,比赛距离增加到了超过 26 英里,这在我们的数据集中,可以用 1927 年的比赛标记。一个世纪间 12 分钟以上的提升,标志着人类进步、体能训练以及鞋类创新的巨大成就。

可视化

转到一些可视化提示,我们现在可以探索 Country 列,揭示哪些国家生产了最多的波士顿马拉松获胜者。

pandas_ai.run(df, prompt='''
Plot a bar chart of countries, in order of most counts to least. 
Use fivethirtyeight as the matplotlib style. Make the font size 10.0
''', is_conversational_answer=True)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

上面的条形图展示了美国在波士顿马拉松的长期历史中占据主导地位,其次是肯尼亚、加拿大、日本、芬兰和埃塞俄比亚。这个见解需要保持一定的保留态度,因为我们这里只展示了获胜者,而且我想早期 1900 年代比赛的国际参与者可能并不多。也许我们可以通过理解代表肯尼亚、加拿大、日本和其他国家的跑者赢得比赛的时间来为这些见解提供背景。

pandas_ai.run(df, prompt='''
Use the data frame to plot a scatter plot of Time and Year. 
Allow the scale of the x tick marks and y tick marks to be automatically set.
''', show_code=True)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

上面的散点图展示了在 127 年波士顿马拉松历史中,获胜时间是如何剧烈下降的。尽管个人跑者可以通过显著的体能提升来提高他们的马拉松个人最佳时间,但获胜时间减少超过 45 分钟则显示了真正的长期表现演变。如前所述,需要记住的是波士顿马拉松的赛道起初为 24.5 英里,仅在 1920 年代增加到 26 英里以上,这一点可以通过图中那个时期点的上升尖峰来证明。在 1990 年至 2000 年代初期间,最 consistently 快的完成时间似乎集中在这一时期。自 2005 年左右以来,获胜时间的变异性增加,这可能值得将来进一步研究!

接下来,让我们看看是否可以通过获胜跑者的国家对这个散点图进行颜色编码。如果我们用每个跑者的国家来表示,颜色将会太多,因此我们只对主要国家进行颜色编码。我们可以用“其他”标签来表示那些不代表主要国家的跑者的点。我们将分两个步骤来实现这一可视化:

  1. 让 Pandas AI 创建我们的主要国家数组

  2. 让 Pandas AI 为之前展示的散点图按主要国家进行颜色编码

对于第 1 步,这是提供给 Pandas AI 的提示:

pandas_ai.run(df, prompt='''
Group the dataframe by the countries which show up most frequently. 
Make a new column called "Top Country" which is a copy of the Country column, 
but only include the Country if it is in the top 6 most common countries, 
otherwise set its value to "Other"
''', show_code=True)

它返回了以下代码:

top_countries = df['Country'].value_counts().nlargest(6).index.tolist()
df['Top Country'] = df['Country'].apply(lambda x: x if x in top_countries else 'Other')
grouped_df = df.groupby('Top Country')

对于第二步,这是给 Pandas AI 的提示:

pandas_ai.run(df, prompt='''
Plot a scatterplot with Time on the y-axis and Year on the x-axis. 
Make the xlabel "Year" and the ylabel "Time". 
Color each point by top_countries, which is a list that already exists. 
Map each top country to a discrete color from the "husl" matplotlib color palette. 
Place the legend below the plot. Make the font size 10.0.
''', is_conversational_answer=True, show_code=True)

我们收到了以下相当复杂的代码:

import matplotlib.pyplot as plt
import seaborn as sns

# create a dictionary to map each top country to a color from the "Set1" palette
color_dict = {country: sns.color_palette("husl", n_colors=len(df["Top Country"].unique()))[i] for i, country in enumerate(df["Top Country"].unique())}

# create the scatterplot
sns.scatterplot(x="Year", y="Time", hue="Top Country", data=df, palette=color_dict)

# set the xlabel and ylabel
plt.xlabel("Year")
plt.ylabel("Time")

# move the legend below the plot and set the font size
plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.2), ncol=5, fontsize=10.0)

# show the plot
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

如上所示,早期的假设,即国际选手直到后来才开始累积波士顿马拉松冠军,实际上是通过散点图得到了验证。在 1940 年代之前,美国和加拿大选手似乎主导了波士顿马拉松。从 1950 年起,我们看到更多的绿色点,表明来自其他地方的选手增多。我们还看到 1950 年代芬兰选手的短暂主导地位。然后,从 1970 年到现在,我们看到从美国选手主宰波士顿山地到肯尼亚和埃塞俄比亚选手争夺金牌的转变。

结论

这仅仅是个开始…

在这篇文章中,我提到了数据清理、分组和可视化任务,这些任务可以借助 Pandas AI 加速完成。能够在这个项目中尝试 Pandas AI 是非常有成就感的。我个人很兴奋的是,虽然它刚刚发布几周,但已经能够增强和加速 EDA 工作。我还想指出,利用 AI 并非一切都是美好的。如果你是数据分析师,不用担心——Pandas AI 并不会取代你…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者截图

事实上,有时它在回应某些提示时可能会有点幼稚或讽刺(见上例)。话虽如此,作为提高你自己生产力的工具,我绝对会鼓励使用它(前提是你已经考虑过数据的隐私问题)。

它的使用案例和功能只会从这里开始不断改进。特别是,我很期待测试不同的 LLM,将其与不同的数据用户界面(如 Google Sheets)集成,轻松合并数据框,并且希望有一天能够提出预测性问题,比如 谁将赢得明年的波士顿马拉松?

链接与参考

Rust Polars:解锁高性能数据分析 — 第一部分

原文:towardsdatascience.com/rust-polars-unlocking-high-performance-data-analysis-part-1-ce42af370ece?source=collection_archive---------0-----------------------#2023-05-11

探索 Rust Polars 的世界,系列及更多

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Mahmoud Harmouch

·

跟进 发表在 Towards Data Science ·32 min 阅读·2023 年 5 月 11 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Kaiwen SunUnsplash 上的照片

简而言之

在快速发展的数据科学领域,跟上新进展可能令人望而生畏。然而,Rust 已被证明是这一行业中可靠且至关重要的工具。目前,Polars 是 Rust 中最受欢迎的数据操作库之一。

这篇文章作为一个关于在 Polars 中对 Series 进行数据分析相关任务的密集课程。通过实际操作和代码片段,您将掌握关键技能,如在 Series 上执行各种操作以及处理时间序列等。最终目标是为像您这样的读者提供足够的知识,使得执行预处理活动成为一种本能!

注意: 本文假定您对 Rust 编程语言有基本的了解。

名为 3-polars-tutorial-part-1.ipynb 的笔记本专门为本文撰写,您可以在以下存储库中找到:

[## GitHub - wiseaidev/rust-data-analysis: The ultimate data analysis with Rust course.

这个存储库是由 Rust 内核支持的 Jupyter 笔记本的集合。有了这些笔记本,您将…

github.com](https://github.com/wiseaidev/rust-data-analysis?source=post_page-----ce42af370ece--------------------------------)

目录表格(TOC)

∘ 介绍

∘ 历史背景

∘ 核心组件

∘ 应用场景

∘ 架构

∘ 核心对象

∘ 系列对象

∘ 描述性统计

∘ 中心趋势测量

∘ 传播测量

∘ 结论

∘ 结束语

∘ 资源

介绍

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Raimond KlavinsUnsplash

Polars 是数据科学和机器学习社区中非常流行的开源库,是进行数据操作、准备和分析的强大工具。它不仅不依赖于特定的编程语言(例如可以与 Python, Rust, Node.JS 甚至 R 配合使用),而且还具有加载不同格式数据以及操作复杂数据集的各种功能,使其成为进行任何形式复杂分析任务时的首选标准。为了充分理解它在今天的重要性,我们必须深入了解其起源,以理解它如何随着时间的推移而发展。

历史背景

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Polars 星历史(由作者生成的图像,来自 star-history.com

在 2020 年,有远见的Ritchie Vink从结构工程转行到数据工程,发现市场上对更好数据科学工具的需求未得到满足。对现有工具不满,他决定创建一些真正卓越的东西: Polars

这个出色的工具不仅优雅直观,而且代码量少却性能极高。自诞生以来,由于其无与伦比的处理复杂分析任务和轻松操作大量数据的能力,它在数据科学社区内成为了一场革命。

难怪 Polars 迅速成为今天全球最受欢迎的数据科学工具之一。我们可以说,这个库在某种程度上促使 Rust 编程语言获得了更多的关注!

Polars 在处理各种数据类型方面表现出色,如“表格数据”、“有序数据”、“观察统计数据”等等。

表格数据作为一种数据类型,其中包含不同类型的数值、文本或分类值填充的行和列。另一种形式是有序或无序的系列数据,本质上是包含按顺序或随机排列的数字的列表。

多维矩阵是另一种需要考虑的类型;这些矩阵可以有三维、四维……甚至更多!它们通常用于科学计算和图像处理。最后但同样重要的是,来自多个来源如 SQL 数据库的观察统计数据;它们为我们社会中的复杂问题提供了独特的视角。

核心组件

Polars 是数据操作和分析的有效工具。为了高效使用它,理解其结构至关重要。因此,让我们深入了解构成这个 Polars 库的各种组件。

  • [**Polars/algo**](https://github.com/pola-rs/polars/tree/main/polars/polars-algo):这个模块提供的功能确实很了不起,包括 qcut、cut 和 hist。这些函数使用户能够轻松进行复杂计算,同时生成详细的统计数据,无论是对系列还是数据框。

  • [**Polars/arrow**](https://github.com/pola-rs/polars/tree/main/polars/polars-arrow):Polars 的 arrow 组件包含数组转换、数据类型和内核的实现。这个组件使得在不同格式之间转换数据变得更加容易,例如 Polars 数据框和 Apache Arrow 数组。

  • [**Polars/core**](https://github.com/pola-rs/polars/tree/main/polars/polars-core):此组件实现了 Polars 的基础数据结构,包括 Series 和 DataFrames。这些数据结构对于有效地处理数据至关重要,通过提供过滤、分组和合并操作等多种功能来支持各种分析任务。

  • [**Polars/io**](https://github.com/pola-rs/polars/tree/main/polars/polars-io):此模块包含文件和数据的输入输出组件,简化了过程。它们使我们能够轻松从各种来源导入数据,包括 CSV 和文本文件,同时允许轻松导出为文本或 CSV 等格式。这种功能简化了在多个平台之间读取和写入数据的过程,从而提高了处理大量数据时的效率。

  • [**Polars/lazy**](https://github.com/pola-rs/polars/tree/main/polars/polars-lazy):Polars 中的懒惰 API 提供了一种高效的数据分析方法,支持急切 API 的一个子集。此功能类似于 Apache Spark 的查询语言,其中特定领域的查询转换为逻辑计划,概述了执行过程中每一步的操作。在运行这些计划之前,它们会被优化以实现最大性能,通过必要时重新排列操作并添加隐式类型转换,以防止在运行时发生潜在错误。

  • [**Polars/ops**](https://github.com/pola-rs/polars/tree/main/polars/polars-ops):此 Polars 模块提供了强大的工具来处理 DataFrames 和 Series 上的复杂操作。这些高级功能使我们能够轻松提取唯一值、执行整除、计算对数、透视表和连接 DataFrames。此外,这些操作经过优化,性能极快,非常适合处理大型数据集!Polars/ops组件内的功能使数据处理变得轻而易举,让分析师能够无缝分析数据,挖掘有价值的见解。

  • [**Polars/row**](https://github.com/pola-rs/polars/tree/main/polars/polars-row):该模块具有一个显著的功能,包括提供额外实用工具的算法,这些功能包括多种编码技术,如整数编码、浮点编码和字典编码,以减少大型数据集所需的存储空间。此外,排序机制如 null 排序算法或反向列排序有助于高效组织和分类数据,特别是在处理大量信息时。利用此组件可以显著提高处理和分析数据集的效率。

  • [**Polars/SQL**](https://github.com/pola-rs/polars/tree/main/polars/polars-sql):该模块包含许多算法,简化了 SQL 数据库与 Polars 函数之间的交互。这些方法包括选择查询、连接操作和其他数据检索技术,简化了与 SQL 数据库的工作。这个功能对于需要访问存储在这些系统中的信息的数据科学家尤其有用,因为他们可以轻松地将其集成到他们的分析中,使用 Polars/sql 功能。

  • [**Polars/time**](https://github.com/pola-rs/polars/tree/main/polars/polars-time):这个 Polars 组件包含处理时间记录和时间序列数据的功能。时间序列数据是基于时间索引的,例如股票价格或天气数据。Polars/time 提供了处理时间数据的功能,如在不同时间格式之间转换、执行时间序列算术运算以及重新采样时间序列数据。这个 Polars 组件对于任何处理时间序列数据的数据科学家来说都是必不可少的。

  • **p**[**olars/util**](https://github.com/pola-rs/polars/tree/main/polars/polars-utils):该模块包含有助于调试的工具。这个模块提供了一系列功能和技术,用于跟踪错误、显示调试数据以及评估代码性能。该模块中的实用工具对于解决在使用 Polars 时遇到的任何问题并提高代码效率,进行更有效的数据操作和分析都非常有帮助。

了解这些组件将使你能够高效地操作和分析数据。

使用案例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Polars 使用案例(图片由作者提供)

Polars 具有在各种领域应用的潜力。它帮助专家从数据分析中获得有价值的见解,并做出明智的结论。以下是 Polars 应用的一些领域:

  • 推荐系统:推荐系统是像 Netflix 和 Amazon 这样的公司强大的工具,因为它们利用数据为用户提供个性化推荐。通过分析用户行为和偏好,Polars 可以帮助做出量身定制的推荐,增加用户参与度,同时推动收入增长。这使得推荐系统在任何公司通过定制内容建议提供卓越客户体验的战略中变得至关重要。

  • 市场营销:在市场营销中实施 Polars 可以帮助分析消费者行为、偏好和购买模式。这使得公司可以更好地理解目标受众,从而对广告活动做出明智的决策。企业可以通过利用数据驱动的洞察来优化广告效果,从而提高(ROI)。

  • 股票预测:金融行业可以利用 Polars 来分析过去和现在的财务数据,从而预测股票价格并监控市场趋势。通过检测这些信息中的模式,投资者获得了做出明智投资决策所需的知识——这种决策可能带来显著的经济回报。

  • 神经科学:Polars 在神经科学领域具有潜力,用于检查和呈现复杂的神经信息。研究人员可以利用 Polars 的特殊数据分析功能,提取有关脑功能和行为的重要知识。

  • 自然语言处理:NLP 是一个令人印象深刻的工具,可以有效地分析和预处理文本数据。Polars 为企业提供了处理大量客户情感信息的能力,从而在产品开发和服务交付中做出明智决策。NLP 完成的任务包括情感分析、主题建模和文本分类,这些任务提供了有关客户需求的宝贵洞察,同时指导公司朝着数据驱动的改进方向前进,以提供更高质量的产品。

鉴于以上使用案例,你可以看到 Polars 具有广泛的应用,显著影响我们的日常生活。通过掌握这个库,你可以投资于你的分析职业,并在各个行业解锁众多机会。这将为你在当今数据驱动的世界中提供竞争优势。

架构

Polars 是一个非常强大的数据处理工具,利用 Apache Arrow 作为其核心,提供闪电般的计算性能。专为处理大型数据集和执行复杂操作而设计,具有毫不费力的高效性。与其他仅利用 Arrow 读取 Parquet 文件 的库不同,Polars 具有强大的集成。它采用基于 Rust 的 Arrow 内存格式实现,按列存储数据,使 Polars 能够利用高度优化和高效的 Arrow 数据结构,同时专注于处理存储的信息。

展望未来的发展,Polars 正在积极扩展其分发系统能力,通过 DataFusion —— 一个旨在成为当今最先进系统之一的新平台!虽然这项创新技术与 Apache Spark 或其他现有竞争产品之间可能存在一些重叠,但请放心,Polars 团队致力于利用通过与 Arrows 后端架构集成提供的每一个可能的优势——确保在每个计算过程中最大限度地提升性能!

核心对象

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Polars 数据帧和系列表示(作者提供的图片)

在本节中,我们将探索 Polars 的基本概念。为了提升学习体验,Jupyter Notebook 被用来执行本文中的代码片段。众所周知,Jupyter 是一个通过任何网页浏览器访问的交互式计算平台,使我们能够创建和分享包含实时代码、可视化和解释性文本的文档,使学习变得比以往更具吸引力!

Series 对象

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列对象表示(作者提供的图片)

要全面理解如何使用Polars进行数据清洗,首先需要从基础开始。这包括处理一维数据,通常用Series 对象来表示。

Series 对象 是 Polars 中一个重要的数据结构,表示一维数据,简称 1-D 信息。它结合了 vectorHashMap 的特性,具有有序序列标签,便于检索。一个简单的类比是想象一列存储实际数据值并附有标签。这使得在代码中管理大量结构化数据变得更加方便和高效。

要在 Polars 中创建一个 Series 对象,请使用 [**Series::new**](https://docs.rs/polars/latest/polars/prelude/struct.Series.html#method.new-37) 方法进行初始化。这个强大的函数允许创建具有指定值和推断数据类型的自定义 Series 对象,以满足您的需求。这个过程可以通过以下代码进行演示。通过使用 [**Series::new**](https://docs.rs/polars/latest/polars/prelude/struct.Series.html#method.new-37) 方法,我们可以创建一个标记为 **series** 的 Series 对象,并为其分配值 **[1, 2, 3]**

use polars::prelude::*;

let series: Series = [1, 2, 3].iter().collect();

// or

let series: Series = Series::new("", &[1, 2, 3]);

println!("{:?}", series);

在 Jupyter notebook 单元格中运行上述代码将产生以下输出:

shape: (3,)
Series: ‘’ [i32]
[
 1
 2
 3
]

使用 **Series::new** 方法创建的 Series 对象的输出显示了 Polars 中一维数据的表示。Series 对象包含一个带标签的有序值序列,以便于轻松检索。索引默认为整数,从 0 开始,每个值的索引递增 1。

值得注意的是,Series 对象在定制化方面相对于其他数据结构具有显著优势。列名用于更好地理解数据。可以将它们视为标签,以更好地理解每一列/特征。

Polars 的系列对象非常灵活,支持多种数据类型,如整数、字符串、布尔值或日期时间值。要创建一个只包含字符串的新系列对象,命名为**series**,请使用**Series::new**方法,同时传入一个包含**string**对象的向量,以将其存储为字符串。

let seasons_ser: Series = Series::new("seasons", &["Winter", "Spring", "Summer", "Fall"]);
println!("{:?}", seasons_ser);

运行这段代码将产生以下输出:

shape: (4,)
Series: 'seasons' [str]
[
    "Winter"
    "Spring"
    "Summer"
    "Fall"
]

结果是一个在终端上很好地呈现的系列对象。我们可以看到,Polars 已自动识别此系列中的数据类型为**str**,并将**dtype**属性设置为适当的值。

Python中,处理数据时,常会遇到由 None 类型表示的缺失或空值。然而,当处理像 Python pandas 系列对象那样的有类型列表时,我们必须以不同的方式处理这些缺失值。在这种情况下,Pandas会自动将列表转换为对象类型数组,并插入一个**None**的占位符值。

为了更好地理解这个概念,我们考虑一个场景,其中有一个季节列表,但一个季节没有名称;我们可以使用**None**来表示缺失的信息。

>>> import pandas as pd
>>> seasons = ["Winter", "Spring", "Summer", None]
>>> pd.Series(seasons)

0    Winter
1    Spring
2    Summer
3      None
dtype: object

当在Pandas中创建包含至少一个**None**实例的字符串时,结果系列将被转换为对象类型数组,同时插入**None**作为其指定的替代值,从而在数据集中保持其他元素的数据类型的一致性。

以下示例展示了Pandas如何处理整数列表中的null 值。在这种情况下,Pandas会将数据类型转换为浮点数,并生成一个**NaN**值。这一功能非常有用,因为它确保了在所有数据类型中表示缺失信息时的一致性。

>>> numbers = [1, 2, None]
>>> pd.Series(numbers)
0    1.0
1    2.0
2    NaN
dtype: float64

重要的是要认识到,**NaN** 是合法的浮点数,并符合IEEE-754 标准。因此,它可以在数学计算和比较中使用,而不会触发错误。

然而,在Rust中,处理整数时,**None**值会转换为**Null**。虽然乍看之下这似乎是一个微不足道的差异,但在处理大型数据集或进行复杂分析时,可能会对数据类型的保持产生重要影响。

let s: Series = Series::new("seasons", &[None, Some(1), Some(2)]);

// Output:

// shape: (3,)
// Series: 'seasons' [i32]
// [
//  null
//  1
//  2
// ] 

如前所述,经过对 Rust Polars 中创建 Series 对象过程的深入观察,与 Python Pandas 相比,存在一些明显的差异。首先,Rust Polars 使用 null 值表示缺失数据,而不是 Python Pandas 中的 NaN 值。其次,Rust Polars 将 Series 的数据类型设置为 32 位整数,而不是像 Python Pandas 中那样自动转换为 浮点数。这种行为差异可以归因于 Rust 的显式类型系统,它会隐式地分配数据类型。因此,指定 dtype 为 **int** 是合适的,因为 1 和 2 是整数。另一方面,在 Python pandas 中,缺失数据通过将 None 值转换为 NaN(浮点数)来表示,而 整数 可以 castfloat

在使用 Rust 进行科学计算时,强调 NoneNaN 的表示差异至关重要。虽然数据科学家可能会交替使用它们来表示缺失数据,但在底层它们并不以相同的方式表示。一个关键点是 NaN 不等于 None,并且它们之间的等式测试总是会返回 False

在 Rust 中,NaN 不能与自身比较。因此,尝试这样做将产生 False 结果。这突显了 NaN 与任何值,包括自身,都不等同。

Some(f64::NAN)==None
// false
f64::NAN==f64::NAN
// false

因此,在处理包含 **NaN** 值的数据时,必须妥善处理这些值。

必须注意的是,Rust Polars 将 null 值计为零,删除它们不会消除这些值。这是因为 Rust Polars 中的 null 值与 **NaN** 不同,后者用一个独特的值表示缺失数据。因此,理解数据集中缺失信息的出现方式对数据的准确分析和处理至关重要。

let s: Series = Series::new("numbers", &[Some(f64::NAN), Some(1.), Some(2.)]);

println!("{:?}", s.null_count());

// Output:

// 0

s.drop_nulls()

// Output:

// shape: (3,)
// Series: 'numbers' [f64]
// [
//     NaN
//     1.0
//     2.0
// ]

无疑可以将序列的元素从一种数据类型转换为另一种数据类型。例如,考虑我们之前的示例及其转换为整数值。下面的代码片段有效地演示了这种转换:

let s: Series = Series::new("numbers", &[Some(f64::NAN), Some(1.), Some(2.)]);
println!("{:?}", s.cast(&DataType::Int64).unwrap());

// Output:

// shape: (3,)
// Series: 'numbers' [i64]
// [
//     null
//     1
//     2
// ]

[**cast**](https://docs.rs/polars/latest/polars/prelude/struct.Series.html#method.cast) 函数用于将初始的 **s** 序列转换为新的 64 位整数 类型序列。返回值可以使用 **println!** 宏进行显示,但值得注意的是,转换后 **NaN** 值将变为 null。

重要的是要记住,将一个系列从一种数据类型转换为另一种数据类型可能会导致某些值的丢失或修改。例如,如果你将一个 浮点系列 转换成 整数 系列,所有的小数点将会被 截断。此外,尝试将系列中的 非数值 数据转换为 数值 类型会导致错误。因此,执行任何潜在转换之前,务必要全面谨慎地权衡其后果。

系列创建

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列对象(图片由作者提供)

本节集中于使用 Polars 创建数据系列。据我了解,Polars 提供了 四种不同的方法 来创建系列,每种方法都针对特定的需求。创建系列的主要原因是为了处理 带标签的 数据

初始方法涉及通过利用 [**new_empty**](https://docs.rs/polars/latest/polars/prelude/struct.Series.html#method.new_empty) 方法从头创建一个数组:

let s = Series::new_empty("Height", &DataType::Float32);
println!("{:?}", s);

// Output:

// shape: (0,)
// Series: 'Height' [f32]
// [
// ]

生成系列的第二种技术是 利用数据数组。这一过程涉及直接从给定的集合构建系列,并根据其中包含的值确定其结果数据类型。如果其值是字符串,则 **str** 将被指定为该系列实例的结果数据类型。

let s: Series = Series::new("employees", &["Mahmoud", "Ferris"]);
println!("{:?}", s);

// Output:

// shape: (2,)
// Series: 'employees' [str]
// [
//     "Mahmoud"
//     "Ferris"
// ]

输出显示由于字符串数据类型,Polars 将 **str** 指定为系列的数据类型。在创建系列之后,我们可以使用名称函数检索其名称对象。

s.name()

// Output

// "employees"

你也可以使用向量方法来创建系列。通过利用 [**vec!**](https://doc.rust-lang.org/std/macro.vec.html) 宏,我们可以创建一个向量,并获得一个与原始向量数据类型相同的系列。当你已经拥有以向量形式存在的数据时,这种方法特别有用,可以方便地转换成系列格式。

let s: Series = Series::new("employees", &vec!["Mahmoud", "Ferris"]);
println!("{:?}", s);

// Output:

// shape: (2,)
// Series: 'employees' [str]
// [
//     "Mahmoud"
//     "Ferris"
// ]

此操作的输出与之前的方法相同。

通过利用像 [**UInt32Chunked**](https://docs.rs/polars/latest/polars/prelude/type.UInt32Chunked.html) 这样的分块类型,可以实现第四种创建系列的方法。这种技术在处理大规模数据集时特别有用,使你能够从这些数据类型中创建一个系列。将分块类型转换为系列后,其结果数据格式将与原始格式保持一致。

let s = Float64Chunked::new("b", &[1., 2., 3.]).into_series();
println!("{:?}", s);

// Output:

// shape: (3,)
// Series: 'b' [f64]
// [
//     1.0
//     2.0
//     3.0
// ]

此操作的输出按预期显示系列的数据元素。

总结来说,本节重点介绍了使用 Polars 创建数据系列的四种技术。这些技术包括直接从数据数组创建系列,使用 **vec!** 宏创建向量,以及使用像 **UInt32Chunked** 这样的分块类型生成序列。每种方法的实用性取决于数据集的性质和结构。因此,在处理 Polars 中的 Series 时,我们可以利用这些方法。

Datetime 类型

Rust 中的 [**chrono**](https://docs.rs/chrono/latest/chrono/) crate 是管理日期和时间的强大工具。借助其提供的功能,我们可以轻松地创建适用于各种用例的 DateTime 数据字段。给定的代码片段演示了如何将创建的 DateTime 数据字段集成到一个 Series 中,以便进一步操作。

let date: DateTime<Utc> = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap();
let s = Series::new("b", &[date.date_naive()]);
println!("{:?}", s);

// shape: (1,)
// Series: 'b' [date]
// [
//     2020-01-01
// ]

在这个代码片段中使用 [**with_ymd_and_hms**](https://docs.rs/chrono/latest/chrono/offset/trait.TimeZone.html#method.with_ymd_and_hms) 函数使我们能够创建符合我们年、月、日、时、分和秒值需求的 DateTime 对象。此函数返回 Result 类型,可以轻松地解包以获取 DateTime 对象。这个结果的 DateTime 对象作为一个输入参数,在使用 Series::new 方法创建另一个 Series 时使用,该方法只包含一个数据点。

通过调用 [**date_naive**](https://docs.rs/chrono/latest/chrono/struct.DateTime.html#method.date_naive) 函数,我们可以在 DateTime 对象上创建一个数据类型为日期的 Series。这个巧妙的函数从给定对象中提取相关信息,并将其转换为 NaiveDate 格式。要全面观察得到的 Series — 包括形状、名称、数据类型和包含的点 — 只需简单地使用 **println!** 宏。

本质上,Chrono 包是一个有价值的工具,用于创建可在任何给定系列中轻松集成的适应性 DateTime 数据,以促进进一步的分析或操作。

索引与切片

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

series 的获取和切片方法(作者提供的图片)

到目前为止,您已经看到了让您从 预定义数据 创建 Series 等对象的操作。一旦初始化了诸如系列之类的数据对象,就可以利用 Polars 提供的一些直观功能从该对象中提取相关数据。其中一个功能是 索引

索引子集 的过程是数据分析中广泛采用的技术之一,用于从系列中提取特定段落。这种方法能够精确地操作和检查数据,对理解复杂数据集至关重要。Rust 的 [**Series**](https://docs.rs/polars/latest/polars/prelude/struct.Series.html#) 结构提供了多种索引方法,可用于从系列中检索特定元素。

let s: Series = Series::new("employees", &vec!["Ferris", "Mahmoud"]);
println!("{:?}", s.get(0).unwrap());

// Output:

// Utf8("Ferris")

展示的代码片段说明了如何有效地使用 get 方法从 series 中索引特定值。此函数通过提供一个索引值作为输入,检索并返回 series 中的相应数据点。在我们的示例中,我们通过 **println!** 宏提取并显示第一个元素(索引 0)。

除了索引单个值外,通过切片,我们可以通过指定偏移量和长度从 series 中提取一个子集。此方法允许高效提取大数据集,因为它返回零拷贝视图。偏移量参数确定起始索引,而要提取的值的数量通过长度参数指定。在我们的代码片段示例中,我们使用切片仅检索一个值——具体来说,就是 series 中的第一个元素,偏移量设置为 0,并且提取一个值(长度=1)。

let s: Series = Series::new("employees", &vec!["Mahmoud", "Ferris"]);
println!("{:?}", s.slice(0, 1));

// Output:

// shape: (1,)
// Series: 'employees' [str]
// [
//     "Mahmoud"
// ]

长度

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

series len 方法(图像由作者提供)

在数据分析中,确定序列中的元素数量通常是非常重要的。幸运的是,有一个简单而有效的方法——[**len**](https://docs.rs/polars/latest/polars/prelude/trait.SeriesTrait.html#tymethod.len) 方法。

使用此方法的语法通常很简单,通常表现为 **fn len(&self) -> usize**。一旦在所需的 series 上调用,此函数将返回一个整数值,表示其总元素数量。

let s: Series = Series::new("employees", &vec!["Mahmoud", "Ferris"]);
println!("{:?}", s.len());

// Output:

// 2

反转

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

series reverse 方法(图像由作者提供)

[**reverse**](https://docs.rs/polars/latest/polars/prelude/trait.SeriesTrait.html#tymethod.reverse) 函数是一个强大的方法,可以帮助你以特定的方式操作数据。如果你曾经需要更改 series 中元素的顺序,可以利用这个方法。

该方法的语法通常很简单——在一个 series 对象上调用函数。调用后,它将创建一个新的、与原始 series 完全相同但顺序相反的 series。

let s: Series = Series::new("employees", &vec!["Mahmoud", "Ferris"]);
println!("{:?}", s.reverse());

// Output:

// shape: (2,)
// Series: 'employees' [str]
// [
//     "Ferris"
//     "Mahmoud"
// ]

空检查

[**is_empty**](https://docs.rs/polars/latest/polars/prelude/trait.SeriesTrait.html#method.is_empty) 方法是一个可以在 Polars Series 对象上调用的方法。它返回一个布尔值,指示 Series 是否为空。如果 Series 为空,则返回 True;否则,返回 False。

let s: Series = Series::new("employees", &vec!["Mahmoud", "Ferris"]);
println!("{:?}", s.is_empty());

// Output:

// false

丢弃空值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Series drop_nulls 函数可视化(图像由作者提供)

数据分析涉及一个关键方面,即处理缺失信息。Polars 的 [**drop_nulls**](https://docs.rs/polars/latest/polars/prelude/trait.SeriesTrait.html#method.drop_nulls) 方法提供了一个有效的解决方案。此函数帮助从 Series 中移除包含空值的行,并有效消除由不完整数据集造成的差异。

let s: Series = Series::new("employees", &vec![Some("Ferris"), None]);
println!("{:?}", s.drop_nulls());

// Output:

// shape: (1,)
// Series: 'employees' [str]
// [
//     "Ferris"
// ]

总结

作为数据分析师或科学家,你可能经常处理大量数据集。Polars 中用于查询数据的两个强大方法是 Head 和 Tail。[**head**](https://docs.rs/polars/latest/polars/series/struct.Series.html#method.head) 方法是一个有用的工具,可以快速预览数据集的初始行。默认情况下,它显示前十行,但可以通过传递参数进行自定义,以显示更多或更少的行。

let s = Series::new("Measurements", &[-1.01,  0.86, -4.60, 3.98,  0.53, -7.04, 3.98,  0.53, -7.04, 0.86, 0.16, 0.26, 0.81]);
println!("{:?}", s.head(None));

// Output:

// shape: (10,)
// Series: 'Measurements' [f64]
// [
//     -1.01
//     0.86
//     -4.6
//     3.98
//     0.53
//     -7.04
//     3.98
//     0.53
//     -7.04
//     0.86
// ]

另一方面,[**tail**](https://docs.rs/polars/latest/polars/series/struct.Series.html#method.tail) 方法允许你检查数据末尾的趋势和模式。默认情况下,它显示最后十行,但也可以通过参数进行调整。

let s = Series::new("Measurements", &[-1.01,  0.86, -4.60, 3.98,  0.53, -7.04, 3.98,  0.53, -7.04, 0.86, 0.16, 0.26, 0.81]);
println!("{:?}", s.tail(None));

// Output:

// shape: (10,)
// Series: 'Measurements' [f64]
// [
//     3.98
//     0.53
//     -7.04
//     3.98
//     0.53
//     -7.04
//     0.86
//     0.16
//     0.26
//     0.81
// ]

追加

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列追加功能的可视化(作者提供的图片)

通过使用 [**append**](https://docs.rs/polars/latest/polars/series/struct.Series.html#method.append) 方法,你可以合并两个不同的系列。此函数将一个系列的块与另一个系列结合起来,需要注意的是,两个系列必须具有相同的数据类型。需要牢记的是,为了使这个过程成功进行,需要一个可以修改给定系列的可变调用者。

let mut s1 = Series::new("Age", &[23., 27.]);
let s2 = Series::new("Height", &[1.84, 1.78]);
println!("{:?}", s1.append(&s2));

// Output;

// shape: (4,)
// Series: 'Age' [f64]
// [
//     23.0
//     27.0
//     1.84
//     1.78
// ]

转换

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列转换功能的可视化(作者提供的图片)

转换 是将一种数据类型转变为另一种数据类型的重要技术。在 Polars 中,这个方法在更改 Series 中的列数据类型时尤其有用。例如,你可能需要将列中的字符串值转换为整数或浮点数。幸运的是,通过 [**cast**](https://docs.rs/polars/latest/polars/series/struct.Series.html#method.cast) 方法,这种转换变得轻而易举。

有效使用转换功能需要首先选择需要转换的列,然后调用方法时传递所需的目标数据类型作为参数——Float32 只是众多可用选项中的一个例子,用于基于浮点数的转换。

let mut s = Series::new("Measurements", &[-1.01,  0.86, -4.60, 3.98,  0.53, -7.04, 3.98,  0.53, -7.04]);
println!("{:?}", s.cast(&DataType::Int32).unwrap());

// Output:

// shape: (9,)
// Series: 'Measurements' [f32]
// [
//     -1
//     0
//     -4
//     3
//     0
//     -7
//     3
//     0
//     -7
// ]

需要注意的是,由于存在与浮点型和整数型格式不兼容的非数字字符,某些格式在转换过程中可能会遇到错误。在这种情况下,确保相关数据集在转换之前经过彻底清理是非常重要的,以便它们在未来的使用中能够更有效地转换而不会出现问题。

填充空值

作为数据分析师或科学家,你对处理空值的挑战并不陌生。幸运的是,Polars为处理数据集中的缺失信息提供了出色的解决方案。利用其[**fill_null**](https://docs.rs/polars/latest/polars/series/struct.Series.html#method.fill_null)方法,你可以轻松地用对分析有意义的特定值替换这些空值。此函数为你提供了选择替换空值时的九种不同策略

  • 前向填充(用前一个值替换None):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列前向填充策略(作者提供的图像)

let s = Series::new("some_missing", &[Some(1), None, Some(3), Some(4), None, Some(6)]);
let filled = s.fill_null(FillNullStrategy::Forward(None))?;
println!("{:?}", filled);

// Output

// Ok(shape: (6,)
// Series: 'some_missing' [i32]
// [
//     1
//     1
//     3
//     4
//     4
//     6
// ])
  • 后向填充(用后一个值替换None):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列后向填充策略(作者提供的图像)

let s = Series::new("some_missing", &[Some(1), None, Some(3), Some(4), None, Some(6)]);
let filled = s.fill_null(FillNullStrategy::Backward(None))?;
println!("{:?}", filled);

// Output

// Ok(shape: (6,)
// Series: 'some_missing' [i32]
// [
//     1
//     3
//     3
//     4
//     6
//     6
// ])
  • 均值填充(用整个数组的均值替换None):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列均值填充策略(作者提供的图像)

let s = Series::new("some_missing", &[Some(1), None, Some(3), Some(4), None, Some(6)]);
let filled = s.fill_null(FillNullStrategy::Mean)?;
println!("{:?}", filled);

// Output

// Ok(shape: (6,)
// Series: 'some_missing' [i32]
// [
//     1
//     3
//     3
//     4
//     3
//     6
// ])
  • 最小值填充(用整个数组的最小值替换None):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列最小值填充策略(作者提供的图像)

let s = Series::new("some_missing", &[Some(1), None, Some(3), Some(4), None, Some(6)]);
let filled = s.fill_null(FillNullStrategy::Min)?;
println!("{:?}", filled);

// Output

// Ok(shape: (6,)
// Series: 'some_missing' [i32]
// [
//     1
//     1
//     3
//     4
//     1
//     6
// ])
  • 最大值填充(用整个数组的最大值替换None):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系列最大值填充策略(作者提供的图像)

let s = Series::new("some_missing", &[Some(1), None, Some(3), Some(4), None, Some(6)]);
let filled = s.fill_null(FillNullStrategy::Max)?;
println!("{:?}", filled);

// Output

// Ok(shape: (6,)
// Series: 'some_missing' [i32]
// [
//     1
//     6
//     3
//     4
//     6
//     6
// ])

抽样

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由Testalize.me提供,来自Unsplash

在进行任何分析之前,重要的是要记住我们的样本必须是随机具有代表性的。这意味着我们需要无偏的数据收集方法(例如,不能仅仅问某个体育队的球迷是否喜欢这支队伍)。理想情况下,应该包括来自人口中所有不同群体的成员在我们的样本中。

根据手头的数据类型,可能需要采用不同的抽样方法来实现这一目标。对于一般情况下没有明显分组的情况,简单的随机抽样通常是最佳选择;在这里,我们使用随机数生成器来随机选择行。然而,对于具有明确子组的数据集,应采用随机抽样,以保持整体数据集中每个子组的比例。

在通过这些传统技术无法获得足够相关数据的情况下,例如处理小样本或不平衡样本时,自助法可以通过使用替换选择策略生成额外的随机子集来提供替代解决方案,直到达到所需水平,而不会因为初始回合中的便利选择引入偏差,这些选择可能会使结果倾向于更早发现的更常见值,而不是准确地代表实际的人群!

要对 Polars 系列对象进行部分取样,我们可以使用 [**sample_n**](https://docs.rs/polars/latest/polars/series/struct.Series.html#method.sample_n) 方法。

let s = Series::new("Measurements", &[-1.01,  0.86, -4.60, 3.98,  0.53, -7.04, 3.98,  0.53, -7.04]);
// Take 4 samples with replacement and shuffle (Terms used in probability).
println!("{:?}", s.sample_n(4, true, true, Some(9999)));

// Output:

// shape: (4,)
// Series: 'Numbers' [f64]
// [
//     0.53
//     -1.01
//     3.98
//     0.53
// ])

描述性统计

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Stephen DawsonUnsplash 的照片

描述性统计在数据分析中的重要性不容小觑,因为它使研究人员能够理解他们收集的数据。单变量统计是该领域的一个重要组成部分,允许对单独的 变量进行独立分析和评估。通过对每个变量单独使用统计测量,如均值众数中位数标准差,我们可以揭示数据集中分布模式的有价值见解

例如,如果提供了100 个观察值,详细记录了速度和距离对,单变量统计将使我们能够单独计算平均速度和距离,从而使它们之间的任何相关性独立性变得明显。此外,通过这些计算识别异常值,我们可以在后续的进一步分析中最小化它们对整体结果的影响!

数据分析的初步步骤是确定其集中趋势,可以通过均值、中位数或众数来评估。这一测量有助于理解大多数数据点的位置,并作为比较的参考点。此外,通过确定值之间的差距离散程度,使用范围方差标准差等测量指标来识别值的离散程度也至关重要。这种分析提供了对数据集中变异性的有价值见解。

作为研究人员/分析师,描述性统计提供了强大的工具,使全面数据概览成为可能,从而基于统计分析中的发现做出明智的决策。

集中趋势的测量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Stephen Phillips - Hostreviews.co.uk 提供,发布于 Unsplash

在任何统计分析中,关注数据的中心至关重要。描述性统计提供了帮助我们理解分布和集中趋势度量的工具。最常见的有均值中位数众数,每种都有其优点,取决于数据集的特性,如正态性偏斜性

需要注意的是,这些度量仅提供概览,而不是对变异性或分布的完整洞察。因此,我们还必须包括其他描述性统计,如离散度****测量,以获得对数据集的更全面的视角。

均值

均值是一个常用的统计函数,用于以易于解释的方式总结数据。它让我们洞察数据集的集中趋势,无论是对于整个总体还是一个观察样本。符号μ(发音为“mu”)代表总体均值,而𝑥𝑥̅则表示样本平均值。在数学术语中,均值的公式是Σxi/n。这里,Σ(希腊字母大写 sigma)表示从1 到 n的求和,其中n是观察数。整体变量用大写字母表示,而具体的观察值用小写字母表示。计算这个值涉及到将所有值相加并除以它们的总数,就是这么简单!例如,如果我们有数字0, 4, 3, 2, 和 5,它们的总和14)除以大约等于二点八(2.8)。

然而,必须认识到异常值可能会显著影响这个度量的准确性。正如你所知,异常值是由完全不同的过程生成的,与分布中的其他值相比,可能会偏斜结果;例如,一个值远大于或远小于其他值,可能会分别拉高或拉低平均值,从而使其成为不准确的代表数据集的值。

因此,在分析数据时,意识到异常值的存在至关重要。正如在之前的一篇文章中讨论的,一种考虑异常值的方法是使用中位数代替均值来测量集中趋势,因为中位数对这些异常值的敏感度较低,同时仍能准确表示数据集中的中间值,即使在这些异常值对整体结果的影响下也是如此。

在使用 Polars 处理数据集时,遇到缺失或空值并不少见。在这种情况下,[**均值**](https://docs.rs/polars/latest/polars/prelude/trait.SeriesTrait.html#method.mean)函数可能会非常有用。然而,由于在被分析的数字集合中可能存在缺失条目,因此该函数返回一个其类型标记为**Option<f64>**的可选值。本质上,这意味着返回值可能是空值,因此需要用户谨慎处理。

let s = Series::new("Measurements", &[-1.01,  0.86, -4.60, 3.98,  0.53, -7.04, 3.98,  0.53, -7.04]);

println!("{:?}", s.mean().unwrap());

// Output:

// -1.09

还应注意,使用此方法计算平均值时,只考虑非空值;如果数据集中有个数字,但由于某种原因遗漏了两个,则只有八个数字会参与通过**均值**函数进行的任何计算。

中位数

在分析数据时,选择适当的集中趋势度量是至关重要的。均值通常被青睐,因为它考虑了所有数据点,并提供了对平均值的合理近似。然而,异常值可能会轻易影响均值的准确性,并扭曲整体结果。这时,使用中位数变得尤为重要。

中位数是一个在统计学中强大的函数,它准确地代表了数据的集中趋势,即使在存在异常值的情况下也是如此。从有序列表中选择中间值提供了一个精确且可靠的分析测量。在值数量为偶数的情况下,取两个中间值的平均值可以确保准确性。这使得它在处理具有极端或偏斜值的数据集时特别有用,例如美国的收入分布。**前 11.6%**的高收入者收入显著高于大多数美国人,这可能会扭曲均值。

然而,通过使用中位数计算,我们可以获得更准确的结果,因为它代表了将一半50%)高收入者与低收入者分开的数据点,为我们提供了对现实经济趋势的宝贵见解,而不受极端富裕人士收入所带来的统计异常的影响!

Polars提供了这个函数来轻松处理DataFrameSeries对象。让我们考虑以下示例:

let s = Series::new("Measurements", &[-1.01,  0.86, -4.60, 3.98,  0.53, -7.04, 3.98,  0.53, -7.04]);

println!("{:?}", s.median().unwrap());

// Output:

// 0.53

众数

众数是一个基本的统计概念,在数据分析中扮演着重要角色。它表示数据集中最频繁出现的值,并且可以揭示模式和趋势,尤其是在处理庞大或复杂的数据集时。如果存在两个众数,例如双峰****分布,这可能表明数据集中存在不同的子群体。

尽管乍看之下似乎简单易懂,但了解模式如何融入更广泛的分析框架中,以及如何与均值和中位数等度量方法结合,对从结果中得出准确结论至关重要。

关于分类数据模式被认为是衡量集中趋势的首选工具。这个强大的工具表示在数据集中出现频率最高的值,非常适合确定数据集中的常见类别或响应。例如,在分析调查结果时,这种方法可以帮助确定参与者选择的频率更高的响应。

另一方面,数值数据集需要不同的度量,如均值中位数值,以准确确定其集中趋势。给定数据集的平均值由均值表示。同时,它的中点对应于中位数,这两种方法都是识别这些类型数据集中的趋势的常用方法。

尽管由于连续集与分类集之间的差异,它通常不会在分析中使用,但它仍然具有相关性,因为它有助于描述在任何特定研究结果中发现的重复模式,这些模式存在于相似变量之间!

Polars 提供了一种强大的方法,称为 [**mode**](https://docs.rs/polars/latest/polars/prelude/trait.SeriesTrait.html#method.mode) 函数,可以帮助实现这一目标。尽管仅在 **mode** crate 功能中可用,但在访问时,它可以为数据集提供有价值的见解。**mode** 函数返回的结果值通常以一个 Series 的形式呈现,其中包含数据集中最常见的元素。

let s = Series::new("Measurements", &[-1.01,  0.86, -4.60, 3.98,  0.53, -7.04, 3.98,  0.53, -7.04]);

println!("{:?}", s.mode().unwrap());

// Output:

// shape: (3,)
// Series: 'Measurements' [f64]
// [
//     -7.04
//     0.53
//     3.98
// ]

为了有效分析数据,了解每种度量的优缺点是至关重要的。这一知识将使我们能够根据特定情况的具体要求做出明智的决策。准确而高效的分析依赖于这种理解。

离散程度度量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Martin Sanchez 提供的照片,来源于 Unsplash

在分析数据时,单纯知道分布的中心点是不够的。理解数据的广泛****分散程度同样重要。这就是离散程度度量发挥作用的地方;它们帮助我们评估值之间的距离,并更有效地总结我们的发现。分散越广,范围越大;相反,分布越窄,结果越紧密

我们可以用多种统计术语来描述这种扩展:例如,通过四分位数范围标准差(各有利弊)。了解哪种度量最适合每种情况取决于具体背景和可用信息;如果处理的是极端异常值,范围可能最有帮助,而通常分布的数据集则可能更适合使用标准差作为指标。

四分位数范围

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

箱形图(图片来源:作者)

[**quantile_as_series**](https://docs.rs/polars/latest/polars/prelude/trait.SeriesTrait.html#method.quantile_as_series) 函数是计算统计度量的强大工具,其中最重要之一是 四分位数范围 (IQR)。该度量表示数据变异性的中间 50%,通过Q1Q3 相减来确定。IQR 实质上展示了数据点围绕其 中位数分布宽度IQR 用于识别潜在的异常值,因为任何落在 Q1–1.5 × IQR 和 Q3 + 1.5 × IQR 之外的数据点都被认为是 异常值

在 Polars 中,[**quantile_as_series**](https://docs.rs/polars/latest/polars/prelude/trait.SeriesTrait.html#method.quantile_as_series) 方法可以用于获取 **ChunkedArray** 的四分位数,结果是一个长度为 1 的新系列。通过指定分位数参数,如 0.250.75,我们可以分别得到 Q1Q3。插值参数可以设置为 Nearest,例如,用于指定在值不完全时如何计算分位数。

use polars::chunked_array::object::QuantileInterpolOptions;

let s = Series::new("Measurements", &[-1.01,  0.86, -4.60, 3.98,  0.53, -7.04, 3.98,  0.53, -7.04]);
println!("{:?}", s.quantile_as_series(0.75, QuantileInterpolOptions::Nearest).unwrap());

// Output:

// shape: (1,)
// Series: 'Measurements' [f64]
// [
//     0.86
// ]

箱形图通常使用该指标来可视化数据集中潜在的异常值,同时准确地表示它们的分布。对于那些希望根据可靠的统计数据而不是单凭猜测做出明智决策的研究人员和科学家来说,使用分位数作为序列计算将提供他们更多对研究结果基础趋势的全面洞察,而不仅仅是标准差范围或简单的最小值-最大值比较。

在本节中,我们关注了 Polars 库提供的最基础数据类型之一——Series。你已经了解了 Series 的创建、索引等技巧,其中强大的 getslice 操作已经对你开放,同时也掌握了 Series 创建的相关知识。

诚然,Series 提供了一个优秀的一维数据管理解决方案,但真正的挑战在于多维数据。这就是DataFrames 发挥作用的地方。在第二部分,我们将简要介绍 DataFrames——这个在众多领域中被广泛有效使用的多维数据结构。

结论

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由Saurav Mahto提供,发布在Unsplash

本文使你熟悉了 Polars 中的基本数据结构——Series。此外,我们还探讨了查询和修改 Series 的基本概念。因此,这应该能让你在今后处理 Series 时充满信心。这将是接下来系列文章中的一个重要组成部分。特别是,我们接下来的重点是学习 Polars 的数据框架,以及 Polars 如何促进高效的数据输入/输出操作,这对任何成功的分析项目至关重要!

在这篇文章中,我们涵盖了以下主题:

  • Polars 世界的介绍。

  • 探索 Polars 的历史和演变。

  • Polars 的组成部分和应用。

  • 理解 Polars 的基本概念,例如 Series。

还有更多内容。随着我们逐步深入接下来的文章,你对 Polars 的知识将扩展到更多高级特性和技术。在第二部分中,我们将深入探索 DataFrames 的强大世界!这些强大的结构在广泛领域中用于轻松管理和分析多维数据。我们将提供对 DataFrames 的清晰而简明的解释,包括聚合函数等。通过掌握这个库,你将获得坚实而宝贵的技能,使你能够轻松应对复杂的数据分析任务,同时轻松处理庞大的数据集。

结束语

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由Aaron Burden提供,发布在Unsplash

在我们结束本教程时,我要向所有为完成这项工作付出时间和精力的人员表示诚挚的感谢。能够与你们一起展示 Rust 编程语言的非凡能力,我感到非常荣幸。

一如既往,热爱数据科学的我承诺,从现在开始,我每周至少会撰写一篇关于相关主题的综合文章。如果你对我的工作感兴趣,请考虑通过各种社交媒体平台与我联系,或者直接联系我以获取其他帮助。

感谢!

资源

GitHub - wiseaidev/rust-data-analysis: 使用 Rust 进行终极数据分析课程

这个库是 Jupyter notebooks 的集合,所有笔记本都由 Rust 内核支持。使用这些笔记本,你将能够……

github.com [## SeriesTrait 在 polars::prelude - Rust

Rust SeriesTrait 特征在 crate polars 中的 API 文档。

docs.rs [## polars::series 中的 Series - Rust

Series

在 polars::series - Rust Series docs.rs [## chrono 中的 DateTime - Rust

pub struct DateTime { /* 私有字段 */ } 扩展描述 ISO 8601 组合日期和时间及时区。还有……

docs.rs [## std - Rust

Rust 标准库是可移植 Rust 软件的基础,提供了一组经过最小化和实战检验的共享……

doc.rust-lang.org [## Rust By Example

Rust 是一种现代系统编程语言,专注于安全性、速度和并发。它通过这些目标……

doc.rust-lang.org [## Rust 编程语言

由 Steve Klabnik 和 Carol Nichols 编写,Rust 社区贡献 这个版本的文本假设你是……

doc.rust-lang.org

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值