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

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

停止在数据科学项目中硬编码——改用配置文件

原文:towardsdatascience.com/stop-hard-coding-in-a-data-science-project-use-config-files-instead-479ac8ffc76f

如何在 Python 中高效地与配置文件交互

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

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

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

作者提供的图片

最初发布于 https://mathdatasimplified.com 2023 年 5 月 26 日。

问题

在你的数据科学项目中,某些值经常会变化,例如文件名、选定的特征、训练-测试分割比例和模型的超参数。

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

作者提供的图片

在编写临时代码用于假设测试或演示目的时,硬编码这些值是可以接受的。然而,随着代码库和团队的扩展,避免硬编码变得至关重要,因为它可能导致各种问题:

  • 可维护性:如果值在代码库中分散,保持一致地更新它们会变得更加困难。这可能导致在值需要更新时出现错误或不一致。

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

作者提供的图片

  • 可重用性:硬编码值限制了代码在不同场景下的重用性。

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

作者提供的图片

  • 安全问题:将敏感信息如密码或 API 密钥直接硬编码到代码中可能会带来安全风险。如果代码被共享或暴露,可能会导致未经授权的访问或数据泄露。
def connect_to_database():
    # Hardcoded sensitive information
    host = 'localhost'
    username = 'admin'
    password = 'secretpassword'
    database = 'mydatabase'

    # Code to connect to the database
  • 测试和调试:硬编码的值可能使测试和调试变得更加困难。如果值被硬编码到代码中,就很难有效地模拟不同的场景或测试边界情况。
def divide(num1: int):
    num2 = 3
    return num1 / num2

def test_divide():
    assert divide(3) == 1.0 # passed
    # We can't test num2 = 0 because it is hard-coded

解决方案——配置文件

配置文件通过提供以下好处来解决这些问题:

  • 配置与代码分离:配置文件允许你将参数与代码分开存储,这提高了代码的可维护性和可读性。

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

作者提供的图片

  • 灵活性和可修改性:通过配置文件,你可以轻松修改项目配置,而不必修改代码本身。这种灵活性允许快速实验、参数调整,并将项目适应不同的场景或环境。

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

作者提供的图片

  • 版本控制:将配置文件存储在版本控制中可以跟踪配置的更改。这有助于维护项目配置的历史记录,并促进团队成员之间的协作。

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

作者提供的图片

  • 部署:当将数据科学项目部署到生产环境时,配置文件可以轻松自定义生产环境的特定设置,而无需修改代码。这种配置与代码的分离简化了部署过程。

Hydra 简介

有许多 Python 库可以用来创建配置文件,如 pyyaml、configparser、ConfigObj。然而,Hydra这款开源 Python 库脱颖而出,成为我首选的配置管理工具,因为它拥有一套令人印象深刻的功能,包括:

  • 便捷的参数访问

  • 命令行配置覆盖

  • 从多个来源组合配置

  • 执行具有不同配置的多个作业

让我们深入探讨这些功能。

随意玩耍并在这里分叉本文的源代码:

[## GitHub - khuyentran1401/hydra-demo

你现在不能执行该操作。你在另一个标签页或窗口中登录了。你在另一个标签页或…

github.com](https://github.com/khuyentran1401/hydra-demo?source=post_page-----479ac8ffc76f--------------------------------)

便捷的参数访问

假设所有的配置文件都存储在conf文件夹下,所有的 Python 脚本都存储在src文件夹下。

.
├── conf/
│   └── main.yaml
└── src/
    ├── __init__.py
    ├── process.py
    └── train_model.py

main.yaml文件如下所示:

# main.yaml
data:
  raw: data/raw/winequality-red.csv
  intermediate: data/intermediate

model: models

processs:
  cols_to_drop:
  - free sulfur dioxide
  feature: quality
  test_size: 0.2

在 Python 脚本中访问配置文件就像在你的 Python 函数上应用一个装饰器一样简单。

import hydra
from omegaconf import DictConfig

@hydra.main(config_path="../conf", config_name="main", version_base=None)
def process_data(config: DictConfig):
    ...

要从配置文件中访问特定参数,我们可以使用点符号(例如,config.process.cols_to_drop),这比使用括号(例如,config['process']['cols_to_drop'])更简洁直观。

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

作者提供的图片

这种直接的方法使你可以轻松地检索所需的参数。

命令行配置覆盖

假设你正在尝试不同的test_size。一遍遍打开配置文件并修改test_size值是很耗时的。

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

作者图片

幸运的是,Hydra 使得直接从命令行覆盖配置变得简单。这种灵活性允许快速调整和微调,而无需修改底层配置文件。

python src/process_data.py processs.test_size=0.3

从多个来源组合配置

想象一下你希望尝试各种数据处理方法和模型超参数的组合。虽然你可以每次运行新实验时手动编辑配置文件,但这种方法可能非常耗时。

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

作者图片

Hydra 通过配置组支持从多个来源组合配置。要创建一个数据处理的配置组,创建一个名为process的目录来保存每种处理方法的文件:

.
└── conf/
    ├── process/
    │   ├── process1.yaml
    │   └── process2.yaml
    └── main.yaml

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

作者图片

如果你想默认使用process1.yaml文件,请将其添加到 Hydra 的默认列表中。

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

作者图片

按照相同的程序创建训练超参数的配置组:

.
└── conf/
    ├── process/
    │   ├── process1.yaml
    │   └── process2.yaml
    ├── train/
    │   ├── train1.yaml
    │   └── train2.yaml
    └── main.yaml

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

作者图片

train1设置为默认配置文件:

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

作者图片

现在运行应用程序将默认使用process1.yaml文件和model1.yaml文件中的参数:

$ python src/process.py --help

process:
  cols_to_drop:
   - free sulfur dioxide  
  feature: quality
  test_size: 0.2
train:
  hyperparameters:
    svm__kernel:
      - rbf
    svm__C:
      - 0.1
      - 1

这种功能特别有用,当需要无缝组合不同的配置文件时。

多次运行

假设你想对多种处理方法进行实验,一个一个地应用每个配置可能是一个耗时的任务。

$ python src/process.py process=process1 # wait for this to finish

$ python src/process.py process=process2 # then run the application with another config

幸运的是,Hydra 允许你同时使用不同的配置运行相同的应用程序。

$ python src/process.py --multirun process=process1,process2

这种方法简化了使用各种参数运行应用程序的过程,最终节省了宝贵的时间和精力

结论

恭喜!你刚刚了解了使用配置文件的重要性以及如何使用 Hydra 创建配置文件。我希望这篇文章能为你提供创建自己配置文件所需的知识。

我喜欢撰写关于数据科学概念的文章,并玩弄各种数据科学工具。你可以通过以下方式获取我最新的帖子:

停止使用 PowerPoint 来做你的机器学习演示,试试这个替代工具

原文:towardsdatascience.com/stop-using-powerpoint-for-your-ml-presentations-and-try-this-instead-f943c2e9e284

Gradio 是打动技术和非技术利益相关者的可靠方式——为什么更多的数据科学家和机器学习工程师不使用它呢?

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

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

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

图片来自 Will PoradaUnsplash

PowerPoint 演示文稿很糟糕。

至少,差的确实如此。

不好的 PowerPoint 会让观众分心(他们会关闭摄像头并进行多任务处理),而且容易让演讲者养成使用过多技术术语和长时间冗长讲解等不良习惯。

那么为什么数据科学家会如此频繁地使用 PowerPoint 呢?

在最近一个关于这个话题的 Reddit 讨论串 中,从事数据科学的受访者报告称,他们花费了 10% 到 60% 的时间制作幻灯片或进行演讲。我意识到这不是一个非常可靠的统计数据,但不论真实分布如何,这种情绪对于许多从事数据科学工作的人来说是准确的:我们使用 PowerPoint——非常多——来展示从模型卡片到 ROC 曲线和 Shapley 值的截图。

无论你喜欢与否,PowerPoint 是现代机器学习技术栈的重要组成部分,它不会消失。

或者它并非如此?

在这篇文章中,我将向你介绍 Gradio,一个免费的工具,允许你:

  1. 通过你的浏览器或 Jupyter Notebook 可视化机器学习模型

  2. 通过互动且易于理解的可视化给你的非技术利益相关者留下深刻印象

  3. 测试你的模型并识别弱点和特征重要性

我与 Gradio 没有任何关联,也不试图向你推销任何东西——我只是想展示一个在我作为数据科学家的工作中效果良好的工具,特别是对于使用表格数据的模型,如 XGBoost

介绍 Gradio:一个免费的、互动的方式来展示和测试您的机器学习模型

在开发者自己的话语中,

Gradio 是演示机器学习模型的最快方法,它提供了一个友好的网络界面,使任何人都可以在任何地方使用它!

这怎么运作的?其实非常简单。

你好,世界!

首先,通过 pip 安装 Gradio

pip install gradio

接下来,导入 Gradio 并定义一个可以接受输入的函数。然后,将您的模型包装在‘gradio.Interface()’类中,——瞧——您的模型就拥有了一个友好的互动界面,可以嵌入到笔记本或网页中。这里是一个使用非常简单的“Hello {user}!”函数的示例:

import gradio as gr

def greet(name):
    return "Hello " + name + "!"

demo = gr.Interface(fn=greet, inputs="text", outputs="text")

demo.launch() 

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

图片由作者提供

如果您在 Jupyter Notebook 中运行它,上面的演示将自动出现在新单元格中。如果您在脚本中运行,它将出现在您的浏览器中,网址为 http://localhost:7860。如果您愿意,您还可以“自动生成一个公共链接,您可以与同事分享,让他们通过自己的设备远程与您计算机上的模型进行互动”(docs)。

如何将 Gradio 与您的机器学习模型一起使用

举一个稍微复杂一点的例子,假设我们有一个可以识别手绘图像的机器学习模型。使用 Gradio,我们可以创建一个接受用户输入的草图板……

import gradio as gr
def sketch_recognition(img):
    pass# Implement your sketch recognition model here...

gr.Interface(fn=sketch_recognition, inputs="sketchpad", outputs="label").launch()

…给我们提供了一种巧妙的方法来绘制草图,将其传递给模型,并实时演示模型:

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

图片由作者提供

注意:为了保持本文的长度在可管理范围内,我没有包括有关模型本身的详细信息;如果您正在寻找模型,您可能想查看 HuggingFace,这是一个很棒的预训练模型库,可以很容易地加载到 Jupyter 笔记本或 Python 脚本中,并与 Gradio 一起使用。

只需几行代码,Gradio 就能轻松地以任何人都能理解的互动方式展示您的模型。想想您团队中的可能性——这将使您向团队或利益相关者展示模型变得多么容易?

如果您喜欢这个故事,点击我的‘关注’按钮对我意义重大——只有 1%的读者这样做!感谢阅读。

那 XGBoost 呢?

是的,我知道——用一些炫酷的图像识别模型做演示很好,但实际上,XGBoost 通常在实际的数据科学团队中占据主导地位。

好吧,如果这是您在想的,我有一些好消息要告诉您:Gradio 可以与各种模型一起使用,包括像 XGBoost 这样的表格数据模型。

这是一个示例,展示了一个用 Gradio 展示的 XGBoost 模型,该模型用于预测人们的收入:

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

图片由作者提供。代码可在 此处 获取。

如你所见,Gradio 使得通过调整开关和滑块与模型互动成为可能,并观察这些调整如何影响预测。你甚至可以使用 Shapley 值来查看不同特征在确定模型预测中的重要性!

我如何使用 Gradio

Gradio 永远不会取代传统的模型测试和评估方法(例如分类报告和使用适当的样本外测试数据集),但我发现它在我日常工作中有两个方面很有用:

  1. 向非技术利益相关者解释模型——Gradio 帮助我回答利益相关者的临时问题,比如“如果我们改变这个变量,模型的预测会发生什么?”而且,你不需要拥有 AI 博士学位来调整开关,因此每个人都可以尝试操作模型,建立自己对模型工作的理解,无论他们是否有技术背景或懂得编码。

  2. 测试我的模型——Gradio 让你实时查询模型,测试你的假设,并迅速识别模型中的隐藏弱点(例如发现意外行为)。这是比批量测试更快速的替代方案,而且——至关重要的是——它使测试过程民主化。使用 Gradio,你不需要依赖一个孤立的数据科学家来运行笔记本中的批量测试——你可以公开托管模型(例如在 HuggingFace Spaces 上),分享模型的链接,每个团队成员都可以参与并尝试探测模型。

Gradio 是预言中的 PowerPoint 杀手吗?

不——但这并不是重点。

PowerPoint 的多功能性意味着像 Gradio 这样的狭窄工具不能完全替代它。此外,如果 PowerPoint 使用得,它实际上可以是展示 ML 生命周期部分(如模型卡和业务案例)的极其有效的方法。

但是对于 ML 生命周期的其他部分(如展示特征重要性和模型结果),PowerPoint 并不总是效果很好,你可能会发现 Gradio 有助于解决它的一些弱点,防止观众在演示过程中打瞌睡。

所以,如果你是一个数据科学家或机器学习工程师,觉得 PowerPoint 无聊或效果不好,为什么不试试 Gradio 呢?对我来说,它确实帮助很大。

哦,还有一件事——

我开设了一个名为 AI in Five 的免费通讯,每周分享 5 个要点,涵盖最新的 AI 新闻、编码技巧和数据科学家/分析师的职业故事。没有炒作,没有“数据是新石油”的废话,也没有来自埃隆的推文——只有实用的技巧和见解,帮助你在职业发展中进步。如果这对你有吸引力,点击这里订阅

[## AI in Five | Matt Chapman | Substack

最新的数据科学和人工智能领域的新闻、职业故事和编码技巧,总结为 5 个要点…

aiinfive.substack.com](https://aiinfive.substack.com/?source=post_page-----f943c2e9e284--------------------------------)

在 TensorFlow 记录文件中存储图像

原文:towardsdatascience.com/storing-images-in-tensorflow-record-files-166d030269fb

如何使用 TFRecord 文件,这是一种针对 TensorFlow 的高效数据存储和读取的数据格式,在处理图像时

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

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

你知道 TensorFlow 有一种自定义格式来存储数据吗?它叫做 TensorFlowRecords——简称 TFRecords——并建立在一个简单的原则上:

将数据按顺序存储(在一个文件中),以便快速访问连续的数据块。

这种方法基于协议缓冲区,这是一种跨平台的结构化数据存储方法。我们不需要深入探讨背景;我们需要知道的是数据以类似字典的映射形式存储:

{"string": value}

一个文件可以包含许多这样的“字典”,在 TensorFlow 中称为Examples,如下图所示:

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

TensorFlow 记录文件背后的概念概述。图片由作者提供。

在每个Example——或字典——内,单独的数据条目被存储。这种格式非常灵活:你可以存储图像、文本、音频以及任何可以转换为字节表示的数据。此外,数据类型可以混合,这使我们可以保留,例如,图像和边界框以及文本描述。然而,在过早深入之前,我们将专注于一种模态:图像。其余的模态,音频和文本数据,将在未来的帖子中涵盖。

根据我的经验,最好用简单的示例来讲解这种高级主题,以最佳展示底层的工作流程。在这种情况下,我们使用随机的(图像形状)矩阵。

存储图片

创建随机数据

考虑一个包含 1000 张图片的数据集,每张图片的尺寸为 224 x 224,包含三个颜色通道。这个虚拟数据集的每个样本都标记为 0 到 9 中的一个类别。仅使用 numpy 库,我们可以轻松创建这样的数据集:

这段代码的结果是一个充满图像数据的数据集(这里是 numpy 数组)。

辅助函数

在我们拥有一个可用的数据集后,我们必须将其转换为字节数据。

为此,我们创建了四个辅助函数(也见这里)。前三个辅助函数将某些数据类型(如浮点数)转换为 TFRecord 兼容的表示。最后一个辅助函数将数组转换为二进制数据字符串:

创建 TFRecord 数据集

这些函数在我们开始创建 TFRecords 文件时发挥作用。在这里,我们需要一个函数来创建单个Example的布局,即我们要存储的图像的内部表示布局。使用之前的简化视觉表示,这样的Example具有多个包含数据的槽,称为Features

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

关于数据如何在Example中存储的概念性概述。图片由作者提供。

对于第一次使用者来说,创建这样的浓缩表示可能会感到不知所措,所以让我们逐一介绍。首先,我们需要存储信息以恢复输入的数据维度。对于我们的图像用例,这些是高度、宽度(224)和通道数(3)。每个数字都是整数,这意味着我们可以将它们存储为整数数据。

其次,我们需要存储图像的字节表示。

第三,我们需要存储标签,标签像数据维度一样以整数数据形式存储。在代码中,这三个要求建模如下:

接下来,我们需要一个函数,它处理包含随机图像及其同样随机标签的数据集,并为存储做好准备。首先,我们打开一个处理将数据写入磁盘的 writer 对象。之后,我们使用一个遍历 numpy 数组的 for 循环,创建图像-标签对,并使用前面描述的方法将它们存储在 TFRecord 文件中。最后,在我们完成遍历数据集后,我们关闭 writer:

就这样!调用这个函数后,我们将拥有一个存储整个数据集的文件!

检索图像

提取字节数据

当我们在之后的时间点想要处理 TFRecords 时,我们需要检索存储的数据。从概念上讲,我们现在是反向存储过程。在这里,我们准备结构,但尚未填充数据。要小心:占位符必须具有相同的名称和适当的数据类型,否则提取将失败。然后,对于 TFRecord 文件中的每个Example,我们提取内容并重新塑造图像:

创建数据集

在编写提取数据的例程后,我们需要一种方法来将其应用于 TFRecord 文件中的每个样本。这一过程,即将数据解析为正确格式,是通过将提取函数映射到每个Example来完成的。在这里,我们依赖于 TensorFlow 的tf.data API,它具有这样的功能:

之后,我们将此函数指向之前创建的 TFRecord 文件(这里是“random_images.tfrecords”),并检索数据。然后,作为一个 sanity check,我们可以比较图像的形状,看看它是否被正确恢复:

注意事项

我们在这篇文章中涵盖的是如何将图像数据放入 TFRecord 文件。这里有两个注意事项,分别是前提假设:

首先,我们从已经加载到内存中的图像(我们的 numpy 数组)开始。第二,在我们的设置中,所有示例的形状都是相同的——这在实际应用中不太可能。

第一点很容易解决:使用许多优秀的库之一来完成。这里的例子包括imageio库或Pillow。对于这些库,存在大量教程,展示了加载数据所需的步骤。

第二点稍微复杂一些。挑战在于 TFRecord 文件的创建,而不是数据加载与批处理的结合。记得我们通过之前的函数存储了原始图像数据及其形状吗?在解析 TFRecord 文件时,这些信息使我们能够恢复图像的适当形状。然而,现在,当将多个示例组合成一个批次时,我们面临着数据维度各异的可能性:图像 1 可能是 224x224 像素,但下一个可能是 124x356 像素。

对于这种情况,我们有一个解决方案:TensorFlow 的padded_batch()方法。为了帮助你入门,这里是之前的数据集创建代码(最初没有使用任何批处理;样本是一个一个返回的),但这次使用了填充批处理:

有趣的部分从第 10 行开始,这一行将数据集中的每个批次填充到由padded_shapes参数指定的固定形状。元组的第一个元素填充为[256, None, 3],这意味着张量的第一个维度固定为 256,第二个维度填充为适合该批次所有示例的最小支持长度,第三个维度固定为 3。批次元组的第二个元素,即标签,不需要填充,这就是我们写[]的原因,表示不应应用任何填充。

总结

在这篇文章中,我们介绍了将一种数据模态——图像——存储到 TFRecord 文件中,这是一个用于高效数据存储和读取的 TensorFlow 专用数据格式。在介绍相关工作流程时,我们生成了一组随机的“图像”和同样随机的标签。然后,我们使用这些数据集展示了如何使用三个辅助函数准备数据以供存储。最后,在使用 TensorFlow 原生方法将数据写入磁盘后,我们还编写了相反的过程:从文件中提取数据。从概念上讲,这涉及通过填充占位符字典来逆转存储过程。最后,我们还简要讨论了两个注意事项及其解决方法。

可视化故事讲述——哪个区域的社会经济评分最高,为什么

原文:towardsdatascience.com/story-telling-with-visualization-which-area-has-the-highest-socio-economic-score-and-why-c1205b2450c7

使用实际地理数据演示

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

·发表于Towards Data Science ·阅读时间 8 分钟·2023 年 12 月 26 日

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

图片由Joash Viriah拍摄,来源于Unsplash

背景

有时,为了更有效地分配资源,政府可能会收集个人或家庭的关于人口统计特征的数据,例如年龄、性别和出生国,以及他们的社会经济特征,例如收入、职业和支出。这些数据的一部分会被按地理区域汇总,并向公众提供。

在我居住的澳大利亚,政府通过澳大利亚统计局(ABS)校准一个称为经济资源指数(IER)的指标,该指标利用来自五年一次的人口普查数据的多种变量评分地理区域的相对社会经济状态。

IER 可以通过将澳大利亚划分为不同大小的地理区域的各种数字边界进行汇总。例如,州界(图 1 中的虚线)将澳大利亚划分为 8 个州和领地,而统计区域 1(SA1)边界(图 2 中)则将澳大利亚划分为更细的区域,有时是几条街道的集群。

在检查 ABS 提供的互动地图中的 IER 时,如下图所示,我发现 IER 在不同地区甚至街道级别上差异很大,我思考这可能是由什么因素驱动的。

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

图像 1 — 按 SA1 划分的 IER,全国视图。澳大利亚统计局,人口与住房普查:地区社会经济指数(SEIFA),澳大利亚,2021 年,ABS 网站,访问日期 2023 年 12 月 24 日。

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

图像 2 — 按 SA1 划分的 IER,街道视图。澳大利亚统计局,人口与住房普查:地区社会经济指数(SEIFA),澳大利亚,2021 年,ABS 网站,访问日期 2023 年 12 月 24 日

我们能否通过可视化揭示 ABS 如何根据地理区域区分 IER 得分?继续阅读!

数据

感谢 ABS,我们可以很方便地在一个地方获取 SA1 的 IER 得分及其支持变量,这在此网页的数据下载部分,“标准化变量比例数据立方体”1

为了本文的目的,我将提供创建一系列可视化图表的 Python 代码,这可能有助于读者理解每个支持变量对 SA1 的 IER 得分的贡献。

我将从加载所需的包、读取和检查数据开始。

## Load the required packages

import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns

import numpy as np
import pandas as pd

import polars as pl
import hiplot as hip
## Read and inspect data

data_proportion = pd.read_excel('/content/gdrive/MyDrive/SEIFA/Statistical Area Level 1 (SA1), Standardised Variable Proportions, SEIFA 2021.xlsx', 
sheet_name = 'Table 4', header = 5, usecols = 'A:Q')

data_proportion.head()

初看下面的图像,支持变量的列命名规则似乎不够直观。

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

图像 3 — 示例数据。作者提供的图像。

为了解决这个问题,ABS 提供了用于校准 IER 得分的 14 个变量的数据字典,点击这里查看。总结如下:

  • INC_LOW: 年收入在$1 和$25,999 AUD 之间的家庭中居住的人的比例

  • INC_HIGH: 年收入大于$91,000 AUD 的人的比例

  • UNEMPLOYED_IER: 15 岁及以上失业者的比例

  • HIGHBED: 占用私人物业中有四间或更多卧室的比例

  • HIGHMORTGAGE: 占用私人物业中每月按揭金额大于$2,800 AUD 的比例

  • LOWRENT: 占用私人物业中每周租金低于$250 AUD 的比例

  • OWNING: 没有抵押贷款的私有住房的百分比

  • MORTGAGE: 有抵押贷款的私有住房的百分比

  • GROUP: 被群体占用的私有住房的百分比(例如公寓或单元房)

  • LONE: 孤身占用的私有住房的百分比

  • OVERCROWD: 根据加拿大国家居住标准,要求额外卧室的私有住房占比

  • NOCAR: 没有汽车的私有住房占比

  • ONEPARENT: 单亲家庭的百分比

  • UNINCORP: 至少有一个是企业主的物业的百分比

另外,ABS 出版物中‘Score’列的 IER 评分经过标准化,均值为 1,000,标准差为 100,以显示特定区域的相对社会经济状况。

现在让我们来看看一些视觉效果!

可视化

下面的 Python 代码对数据进行了一些小的转换,以便将数据转换为适合可视化的格式。

## Rename the first column (optional)
data_proportion.rename(columns = {'2021 Statistical Area Level 1  (SA1) 11-Digit Code': 'SA1'}, inplace = True)

## Create a SCORE_HIGH class column with 1 indicating a high score based on standardised mean
data_proportion['SCORE_HIGH'] = np.where(data_proportion['Score'] > 1000, 1, 0)

## Select only the columns needed for visualization
column_select = [
                 'SCORE_HIGH', 'INC_LOW', 'INC_HIGH',
                 'UNEMPLOYED_IER', 'HIGHBED', 'HIGHMORTGAGE', 'LOWRENT', 'OWNING',
                 'MORTGAGE', 'GROUP', 'LONE', 'OVERCROWD', 'NOCAR', 'ONEPARENT',
                 'UNINCORP'
                ]

data = data_proportion[column_select]

## Remove rows with missing value (113 out of 60k rows)
data_dropna = data.dropna().reset_index(drop = True)

## Create a Polars dataframe 
df = pl.from_pandas(data_dropna) 

现在我们有效地拥有一个包含 14 个特征和一个类别变量的数据框,我们正在尝试对其进行可视化。与其在数据科学工作流程中通常对单一特征和目标变量进行低维度单向分析,不如使用下面的 Python 代码,可以交互式地可视化所有 14 个特征与目标变量之间的关系。

df_ml = pl.concat([
    df.filter(pl.col("SCORE_HIGH") == 0).sample(5_000),
    df.filter(pl.col("SCORE_HIGH") == 1).sample(5_000),
]
) # Note that I'm selecting a sample of 5,000 data points for each class
  # as I don't want the visual to look too busy for the purpose of this demonstration

hip.Experiment.from_iterable(df_ml.to_dicts()).display()

如下图所示,通过选择‘SCORE_HIGH’列进行着色:

  • 可视化中的每一条线代表一行数据。特别是,橙色线代表 IER 评分相对较高的 SA1 区域,蓝色线代表 IER 评分相对较低的 SA1 区域。

  • 通过检查每个单独特征的颜色分布(回顾一下,这些特征是根据数据字典的某些特征的百分比),我们可以轻松判断特定特征与 IER 评分之间的关系。

  • 例如,有证据表明,IER 评分与 SA1 区域内高收入人群的百分比(由 INC_HIGH 变量表示)呈正相关,而 IER 评分与 SA1 区域内单亲家庭的百分比(由 ONEPARENT 变量表示)呈负相关。这些观察结果都是直观上合理的。

  • 从高层次来看,IER 评分可以通过与收入、物业价值和家庭构成相关的变量进行校准。

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

图像 4 — 可视化输出。图片由作者提供。

令人惊讶的是,除了少数几个 SA1 区域,大多数变量与 IER 评分的关系相对明确。

增强

由于上图所示每个变量的数值(即百分比)被标准化为均值 0 和标准差 1,如 ABS 方法论所述,因此可能很难确切解释某一特定变量的百分比对高或低 IER 分数的贡献。例如,高收入人群或具有 4 间或更多卧室的房产,在特定地区需要多少百分比才能获得高于平均水平的 IER 分数?

幸运的是,应要求,ABS 能够提供这些变量的非标准化(即原始)值,这提供了更全面的视角,如下所示。

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

图 5 — 可视化输出,原始百分比。图像由作者提供。

有用的可视化示例

你知道仅仅通过可视化也可以进行预测建模,这可能会超越某些更先进的模型(如神经网络)下的预测吗?

使用与上述类似的高维度可视化技术,目的是“记录”可能对特定类别有预测性的值范围。

例如,在上面的可视化中,为了预测高 IER 分数,我们可能需要的值范围是 UNINCORP >15%,HIGHBED > 50%,INC_LOW < 10% 等,这些可以用来制定一个简单的嵌套 IF ELSE 语句(在任何编程语言中)。

以下视频演示了如何使用上图中的可视化进行交互式操作。

视频 6 — 如何识别高 IER 分数的预测变量

为了进一步实验,我将其应用于 Google 开发的欺诈检测分类问题,该问题用于推广Keras 包(用于构建神经网络模型),并产生了以下比较模型评估结果。

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

表 7 — 评估指标比较。表格由作者提供。

不仅可视化超越了神经网络,而且通过记录影响“IF-ELSE”规则的特征值,我们能够比一些其他不易解释的模型(如神经网络)更清晰地向预测者提供建议。

结论思考

在本演示中,除了创建有助于揭示各种特征与目标变量之间关系的可视化外,我还希望强调能够解释模型和解释输出的重要性,这可能与仅基于评估指标拟合模型一样重要。

我是可视化的忠实粉丝,我坚信每一个统计数据都有一个故事。如在 这篇文章 中提到的另一个使用案例,我的经验是良好的可视化大大有助于讲故事,从而最终提升了我个人品牌的可信度并与利益相关者建立了信任。因此,不论单独还是作为数据科学管道中的重要环节,可视化都是至关重要的。

参考资料

1 澳大利亚统计局 (2021),地区社会经济指数 (SEIFA),ABS 网站,访问日期 2023 年 12 月 24 日(创意共享许可

我之前在以下文章中博文过其他可视化技术。如果你喜欢这些,确保关注 Medium 上的作者 Medium

在 PowerBI 中使用形状图可视化进行互动地理空间可视化

用 Python 创建互动地理空间可视化

当我顺应 AI/ML 浪潮时,我喜欢用全面的语言编写和分享逐步指南和操作教程,并附有现成的代码。如果你想访问我所有的文章(以及其他 Medium 上的从业者/作者的文章),可以使用 这个链接 注册!

使用图表讲故事

原文:towardsdatascience.com/storytelling-with-charts-23dd41096721

第一部分:显示单一定量变量

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

· 发表在数据科学前沿 ·8 分钟阅读·2023 年 2 月 10 日

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

图片由Derek Story拍摄,Unsplash

每个数据集都包含大量细节。此外,许多数据集只是充满了没有任何分类的数字列表。

例如:

· 阿根廷中部地区过去 20 年 1 月份的平均降雨量。

· 信息系统工程学生的智商测试结果。

· 根据 2022 年普查数据,阿根廷 24 个省份的人口。

· 根据一天中的星期几和小时计算的阿根廷车祸致死人数。

以上是典型的包含相对较少单一定量变量的数据集示例。请记住,定量数据表示数量。同时,记住根据值是测量还是计数,定量变量可以是连续的或离散的。

为什么数据分析师应该对这样一组数字感兴趣?首先,尽管许多科学、商业或管理问题涉及数字变量之间的比较、关系、组成或趋势,但对数据集中每个变量的可视化作为基本的探索性数据分析,对于理解这些变量的变化模式非常重要。其次,如上例所示,探索性图表可以帮助理解生成数据集中存储数字的过程。

我之前所述:“在探索性图表中可以发现数据集的三个重要特征:1)异常值,数据集中与其他数据非常不同且不符合相同模式的数据。这些异常值可能代表了有价值的信息用于分析。首先,必须验证这些异常值的存在是否由于数据测量错误;2)间隙,一个不包含数据的区间。数据间隙的可视化合理化了对其存在原因的深入分析;3),孤立的数据点组,也可能需要对其在图表中存在的原因进行特定分析。当然,间隙和簇可能代表数据收集方法中的错误。”

本文将展示三种简单的(基于 Python)探索性图表,以便可视化单一定量变量的分布。

点图

点图,也称为点 chart,是最简单的可视化图之一,由在二维 x 轴和 y 轴方案中绘制的数据值点(小圆圈)组成。一个轴显示数据值分组的类别或范围,而另一个轴显示每个不同组的数据点数量。每个小圆圈代表一个值。根据分析师的喜好,点可以垂直或水平堆叠。

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

图 1:作者使用 Matplotlib 制作的点图。

它们适用于小到中等规模的数据集(10–45 个值),并且非常有助于突出异常值、间隙、簇和偏斜。

点图有两种类型:1)威尔金森点图;2)克利夫兰点图。前者表示连续数据值的分布,而后者是条形图的替代方案。本文专门讨论威尔金森点图。

Matplotlib 没有专门绘制点图的方法。Plotly 使用px.scatter来绘制克利夫兰点图。我发现了一段非常有趣的 Python 代码,来源于帕特里克·菲茨杰拉德(1)在stackoverflow(2)上:

# Create random data
rng = np.random.default_rng(1) # random number generator
data = rng.integers(10, 30, size=45)
values, counts = np.unique(data, return_counts=True)

# Set formatting parameters based on data
data_range = max(values)-min(values)
width = data_range/2 if data_range<30 else 15
height = max(counts)/3 if data_range<50 else max(counts)/4
marker_size = 10 if data_range<50 else np.ceil(30/(data_range//10))

# Create dot plot with appropriate format
fig, ax = plt.subplots(figsize=(width, height))
for value, count in zip(values, counts):
    ax.plot([value]*count, list(range(count)), marker='o', color='green',
            ms=marker_size, linestyle='')
for spine in ['top', 'right', 'left']:
    ax.spines[spine].set_visible(False)
ax.yaxis.set_visible(False)
ax.set_ylim(-1, max(counts))
ax.set_xticks(range(min(values), max(values)+1))
ax.tick_params(axis='x', length=0, pad=10)
ax.set_title('Dot Plot of Random Integer Values')
plt.show()

图 2 是通过代码创建的。很容易注意到在 21 和 24 之间存在一个间隙,该间隙被样本中最高浓度的值所包围。聪明的数据分析师应该探究这种特殊值分布的原因。

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

图 2:作者使用 Matplotlib 制作的点图。

茎叶图

这种表格图在 1900 年左右非常流行,并在约翰·W·图基在普林斯顿大学的讲座中被重新发现(3)。这种图表之所以得名,是因为每个值被分为叶子和茎。

我们如何手动绘制茎叶图?我们首先确定数据的范围。然后,根据数据集的大小,我们将范围划分为固定长度的区间。接下来,我们绘制一条垂直线,将数字的前几位(千位、百位或十位)除了最后一位 按升序排列在垂直线的左侧。这就是茎。我们再次遍历数据集,将下一个显著的(最后)数字写在垂直线的右侧。这就是叶子。在最左侧,从底部到顶部,我们累计我们要绘制的值的数量。

例如,给定以下列表:[16, 25, 47, 56, 23, 45, 19, 55, 44, 27],对应的茎叶图是(区间长度等于 10):

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

图 3:作者用 Stemgraphic 制作的茎叶图。

在图的底部,我们标出了值 16 和 19,然后是 23,再上一行放置 25 和 27。接着是 44,再上一行是 45 和 47,最后在两个单独的叶子中是 55 和 56。

Matplotlib 和 Plotly 都没有茎叶图。专门为此目的设计了一个名为Stemgraphic的 Python 模块。它支持任何大小的数据,并可以生成准备打印的图表(4)。

首先,你必须安装它:

pip3 install -U stemgraphic

Stemgraphic 需要 docopt、Matplotlib 和 pandas。可选地,安装 Scipy 会提供次级图表(5)。

假设我们有来自两个信息科学工程课程的 IQ 数据。让我们使用stemgraphic创建一个茎叶图来分析这些数据的分布。

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import Stemgraphic

data_IQ1 = [111, 85, 83, 98, 107, 101, 100, 94, 101, 86,105, 122, 104, 106, 90, 123, 102, 107, 93, 109]
data_IQ2 = [146, 81, 91, 88, 98, 121, 83, 105, 97, 116, 93, 105, 108, 87, 104, 120, 109, 108, 107, 75, 93, 108, 104]

data_IQ = data_IQ1 + data_IQ2
df_IQ = pd.DataFrame(data_IQ, columns = ["IQ"])

stemgraphic.stem_graphic(df_IQ['IQ'])

print('')
print(df_IQ.describe())
print("")

print(df_IQ.agg({

        "IQ": ["count", "min", "max", "mean", "median", "skew"],

                })
)

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

图 4:作者用 Stemgraphic 制作的茎叶图。

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

图 5:作者制作。

我们可以清晰地观察到两个离群值,一个在下端(75),另一个在排名顶部(146)。图形还表明中位数在 104 和 105 之间。数据呈适度偏斜。最左边的列包含累计计数。茎叶图的主要优点是我们可以通过观察图表轻松重建源数据。

直方图

也叫柱状图,它们是数据集分布的图形表示。它们是二维图,具有两个坐标轴:水平轴被划分为箱子(数值范围的区间);垂直轴是频率轴,其值来源于每个箱子的计数。每个箱子的频率通过垂直矩形条的面积显示。

直方图提供了连续定量变量分布的视觉总结。你可以推断数据集的位置、分布、对称性和偏斜程度。你还可以注意到是否存在集群、间隙和离群值。

你可以在我之前的 Medium 文章中找到更多理论概念和几个示例,关于直方图的内容:1) 直方图,为什么以及如何。讲故事、技巧和扩展;2) 使用 Plotly Express 的直方图,主题和模板。

我在与蒙特卡洛模拟相关的几篇文章中使用了 Matplotlib 来绘制直方图。

例如,在 蒙特卡洛模拟。第二部分 中,我使用了以下代码来显示图 6:

fig, ax = plt.subplots(figsize=(8, 6))
ax.hist(list_of_costs,  histtype ='bar', bins=20, color = 'c',
        edgecolor='k', alpha=0.65, density = True)  #density=True show probability

ax.axvline(media, color='g', linestyle='dashed', linewidth=3)
ax.axvline(Sum_most_like, color='r', linestyle='dashed', linewidth=3)
ax.text(48,0.185, 'Mean - - -', color = 'green'  )
ax.text(48,0.155, 'Most Like - - -', color = 'red'  )
ax.set_title('Frequency Chart')
ax.set_ylabel('Probability')
ax.set_xlabel('Total Cost (MM*1000 U$S)')
ax.grid(axis = 'y')

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

图 6:作者用 Matplotlib 制作的直方图。

在 蒙特卡洛模拟。第三部分 中,我使用了以下代码来显示图 7 中所示的直方图:

fig, ax = plt.subplots(figsize=(8, 6))
ax.hist(list_cost_TTR,  histtype ='bar', bins=20, color = 'c',
         edgecolor='k', alpha=0.65, density = False)  # density=False show counts

ax.axvline(media, color='g', linestyle='dashed', linewidth=3)
ax.axvline(median, color='r', linestyle='dashed', linewidth=3)

ax.set_title('Frequency Chart')
ax.set_ylabel('Counts')
ax.set_xlabel('U$S')
ax.grid(axis = 'y')

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

图 7:作者用 Matplotlib 制作的直方图。

结论

约翰·图基曾表示,探索性数据分析就像是数字侦探工作。他试图解释说,数据中的统计结构是建立模型或理论之前的基础,这些模型或理论可以解释生成存储在数据集中的数字的过程。

每个数据分析师都必须具备某些可视化工具,用于分析和展示数据。特别是,对于单一的定量变量。

在本文中,我描述了三种简单的基于 Python 的探索性图表,用于可视化单一定量变量的分布:点图;茎叶图;直方图。

在三个图形之间选择的主要标准是数据集的大小。建议点图不应显示超过 50 个值。还建议茎叶图不应显示超过 300 个数值。相反,直方图在所表示的数值增加时会得到改善。在前面提到的文章中(与蒙特卡洛模拟相关),这些直方图总结了来自 5000 次复制(5000 个数值)的模拟运行获得的信息。

参考文献

(1) stackoverflow.com/users/14148248/patrick-fitzgerald

(2) stackoverflow.com/questions/49703938/how-to-create-a-dot-plot-in-matplotlib-not-a-scatter-plot

(3) Tukey, John. “Exploratory Data Analysis”, Addison-Wesley, 1977

(4) stemgraphic.org/doc/modules.html

(5) pypi.org/project/stemgraphic/

用图表讲故事

原文:towardsdatascience.com/storytelling-with-charts-29e233182be6

第四部分(II):你想展示组成吗?

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

·发布于Towards Data Science ·阅读时间 7 分钟·2023 年 7 月 1 日

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

照片由Jonatan PieUnsplash上提供

这是第二部分(第四篇文章中的一部分),其目的是指明当传达给受众的消息目的是展示数据的组成时,最佳的数据可视化技术。

为了更好地理解本文内容,强烈建议阅读(或重温)上一篇文章,其中描述了组成的概念及其一些分析元素。

在上一篇文章中,我们指出,以下是最常用于展示组成的六种图表:饼图;堆叠条形图;树图;堆叠面积图;瀑布图;Marimekko 图表。

列表中的前三种已在那篇文章中详细描述。现在,我们将集中讨论列表中的后三种(堆叠面积图;Marimekko 图表;瀑布图)。

堆叠面积图

首先,让我们定义一下什么是面积图:它是一种线图,其中连接数据点的线与水平轴之间的区域填充了特定颜色

种不同类型的面积图:1) 标准面积图;2) 堆叠面积图;3) 百分比堆叠面积图;4) 重叠面积图。只有堆叠面积图(StACs)和百分比堆叠面积图(%StACs)用于显示组成。

在两个堆叠区域图中,多个区域堆叠在一起。 它们展示了随时间变化的数值变量(动态组成),并使用通常是分类的第三个变量来显示组成。

与 StAC 相关,这是一种部分与整体图表,其中每个区域表示相对于类别总量的每个部分的绝对值。与 %StAC 相关,这也是一种部分与整体图表,其中每个区域表示相对于类别总量的每个部分的百分比不同区域之间没有重叠。 在 StAC 中,纵轴的最终高度与所表示的所有数值之和相关。在 %StAC 中,纵轴的最终高度始终为 100%。

图 1 显示了一个 StAC,表示 2013 年至 2018 年间四个不同地区的 PS4 销售情况。图表右上角显示的图例指示不同颜色区域属于哪个地区。可以看到每个地区(每个区域,每个部分)如何对总销售(整体,总销售额)做出贡献。每个区域的高度代表了每个特定地区的销售绝对值,而最终高度是这些值的总和,表示每年的总销售额。可以看出,StAC 应主要用于传达整体趋势和每个部分对整体的相对贡献,而不必关注显示每个部分的精确数值。

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

图 1:堆叠区域图。图表由作者使用 Plotly Express 制作。

图 2 是一个 %StAC,表示相同的 PS4 销售数据。每个区域表示每个地区相对于全球 PS4 销售总量的百分比。如上所述,最终高度为 100%。毫无疑问,这种图表比图 1 中的图表更好地分析了全球销售的组成。

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

图 2:百分比堆叠区域图。图表由作者使用 Plotly Express 制作。

最终警告:StAC 和 %StAC 相对难以读取和理解,因为它们依赖于观众通过比较堆叠区域解码数据信息的能力。我们建议仅使用它们来传达整体趋势和每个部分对整体的相对贡献。

Marimekko 图表

它们是一种特殊的可变宽度条形图。 Marimekko 图表(MCs)类似于 100% 堆叠条形图,但不同之处在于它们的矩形条可以有不同的宽度。

MC 用于显示数据集中每个类别的两个数值变量。它们有两个轴:垂直轴有 100% 数值刻度,而水平轴可以是分类的或数值的。矩形条形图以垂直方向排列,中间没有空隙。水平轴的整个宽度都被占据。

图 3 显示了一个 Marimekko 图表。该图表显示了每个品牌和地区的年度收入。百分比垂直轴表示每个地区的百分比,而水平轴表示每个品牌的年度收入。我们在一个图表中指示了每个类别和子类别的两个数值。

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

图 3:一个 Marimekko 图表。由 Vizzlo 制作并获得许可 (#1)。

如我之前所述的我之前提到的: “Marimekko 图表的特点包括:一个矩形区域被划分为不同宽度的小矩形;垂直堆叠的矩形;占据整个图表宽度的水平轴;带有百分比刻度的垂直轴;顶部基线上的每个品牌的总收入;不同的条形宽度可以计算每个品牌对总收入的相对贡献”。

Marimekko 图表可以用作 100% 堆叠条形图的替代品,但仅用于静态分析(展示某一时刻的组成)。它们绝不应用于展示随时间变化的组成。

与堆叠面积图相同的警告:MCs 难以解释,因为人类不擅长计算面积,特别是当矩形数量增加时。

瀑布图

瀑布图(WCs)是一种特殊类型的条形图,表示数据在添加和减去之间的累积效应。其信息是讲述两个数据点之间的组成变化

WC 由一个初始垂直条形图、一组中间垂直条形图和一个最终垂直条形图组成。通常(且建议的)布局是初始和最终的垂直条形图(列)具有相同的颜色,而中间条形图(浮动条形图)显示添加的绿色值和减少的红色值。第一列和最后一列通常从零基线开始。

图 4 显示了一个基于类别的瀑布图,具有上述特征。这种类型的 WC 通常用于人力资源(显示某个部门的招聘和离职情况)、特定业务(显示收入和支出)、仓库(库存增加、库存减少)以及许多其他数据在正负值之间波动的情况。基于时间的 WC 用于金融行业(显示在单个时间段内的收益和损失)。

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

图 4:作者使用 Plotly 制作的基于类别的瀑布图。

瀑布图提供比标准条形图更多的上下文信息。后者仅显示初始值和最终值,而前者则指示加法和减法元素对总量的贡献,以及这些初始值和最终值之间变化的组成。

讲述初始值与最终值之间变化的这一显著能力,其复杂性在于正确解读这些变化的幅度。这是因为浮动列中缺乏共同的基准,使得比较连续的加法和减法的具体大小变得困难。最佳做法是在列中添加数字注释,并通过连接的水平线将它们连接起来(图 4 和图 5)。

图 5 显示了一个基于时间的瀑布图,展示了一个虚拟网页每月访客数量变化的故事。任何其他视觉表现形式对普通观众理解这种特定情况会更复杂。

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

图 5:作者使用 Plotly 制作的基于时间的瀑布图。

结论

在任何数据可视化项目中,一个关键问题是:“我选择了正确的图表来讲述我的故事吗?”

选择最合适的图表取决于要传达给观众的消息的性质。

当传达的消息是组成时,使用六种不同类型的图表:饼图;堆积条形图;树图;堆积面积图;瀑布图;马里梅科图。

我们的建议是,对于静态组成使用饼图,对于动态组成使用堆积条形图。当整体由十个或数千个部分组成时,树图是一个有效的替代方案。马里梅科图适用于表示两个数值变量,包括一个主要类别及其子类别。最后,瀑布图仅显示初始值和最终值之间变化的组成。

如果您觉得这篇文章有趣,请阅读我之前的 56 篇文章中的任何一篇:medium.com/@dar.wtz关于数据可视化、模拟、蒙特卡罗技术、仪表盘等的浏览量超过 30 万次。

#1: vizzlo.com/

用图表讲故事

原文:towardsdatascience.com/storytelling-with-charts-c59c52c49871

第三部分:你想要比较项目吗?

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

·发布于 Towards Data Science ·阅读时间 7 分钟·2023 年 5 月 8 日

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

图片由 charlesdeluvio 提供,来源于 Unsplash

这是系列中的第三篇文章,旨在帮助从事数据可视化活动的人选择最合适的图表,以便向他们的观众展示他们试图传达的信息类型。

在系列的第一篇文章中,指出了三种基于 Python 的图表,这些图表允许展示单个定量变量的分布

在信息包括显示一组特定数字的大小时,最合适的图表在系列的第二篇文章中已有指示。

现在信息是比较项目。以下是常用的图表,用于呈现这种图形表示:

  • · 标准条形图

  • · 聚类条形图

  • · 重叠条形图

  • · 棒棒糖图

  • · 哑铃图

  • · 分歧条形图

标准条形图

标准条形图(SBCs)每个项目或类别只比较一个数字变量。它们试图回答这样一个问题:“每个类别有多少?” 请记住,类别或项目指的是诸如国家、城市、姓氏、公司、品牌、年份、月份、日期、数据科学方法等定性元素。

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

图 1:垂直和水平 SBC 的示意图。由作者使用 Matplotlib 制作。

有垂直条形图(柱状图)和水平条形图。柱状图通过垂直矩形条形的高度比较项目。水平条形图通过水平矩形条形的长度比较数量。矩形的最终值(每个条形的长度或高度)与打算比较的数值成正比。每个条形代表一个单一项目,条形之间通常留有一定的空间。

观众对这些类型的图表很熟悉,因此可以专注于信息而无需浪费时间理解图示。更多细节可以在我的上一篇文章中找到。

聚类条形图

聚类条形图(CBCs)展示了主要项目或类别与其子组之间存在的相对比例的数据信息,这些子组属于第二个分类变量。一个变量被称为分类变量,如果其观察值可以分配到不重叠的类别中。通常,它们只能取有限(通常是固定)的值。

与 SBCs 类似,它们可以水平或垂直排列。每个主要项目被分为一个条形图簇,表示第二个分类变量的子组。每个子类别的数量由一些相邻的矩形条形的高度或长度表示,这些条形排列在一起形成簇,簇之间的间隙略宽于单个标准条形。

CBCs 用于讲述比较和比例,但重点在于组成(整体分析的一部分)。因此,当整体被划分为多个部分时,CBCs 特别有效。它们可以进行跨子组的比较(堆积条形图进行子组内的比较)。

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

图 2:垂直和水平 CBCs 的示意布局。由作者使用 Matplotlib 制作。

下图显示了一个虚拟公司在 2016–2019 年期间的销售、支出和利润信息。这是一个垂直排列的 CBC,以年份作为主要类别。销售、支出和利润每年以条形簇的形式表示。图表显示,尽管 2018 年支出显著增加,但利润仅减少了少量。

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

图 3:虚拟公司在 2016–2019 年期间的经济表现。该图由作者使用 Matplotlib 开发。

观众也对这些类型的图表很熟悉,因此可以专注于信息而无需浪费时间理解图示。更多细节可以在我的上一篇文章中找到。

重叠条形图

当我们希望在单一图表中比较每个项目或类别的两个数值变量时,可以使用重叠条形图(OVCs)。当然,这两个数值变量必须具有足够的相关性以证明比较的合理性。

概念上,通过重叠的方式对比两个变量的数值。这种重叠允许我们以更强的表达力讲述故事。

与 SBCs 和 CBCs 类似,经典布局包括两个轴和可以水平或垂直定向的矩形条。一个轴显示类别,另一个轴显示与待比较变量相关的数值。当然,这两个数值变量必须共享相同的数值尺度。每个数值变量的条宽不同,较小的条在前以便于阅读,尽管这种情况并不总是适用于所有图表。

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

图 4:作者使用 Matplotlib 开发的重叠条形图。

可以用部分重叠的条形图来比较两个以上的数值变量。在这种类型的图表中,表示不同数值变量的条形图会被前面位置的其他矩形部分遮挡。从概念上讲,部分重叠的条形图类似于 CBC,只不过表示不同子组的矩形开始重叠而不是并排放置。在这方面,必须非常小心,以免混淆观众。因此,建议仅用于比较最多三个不同的数值变量。

更多细节可以在我的上一篇文章中找到。

棒棒糖图

从概念上讲,与 SBCs 类似,棒棒糖图(LCs)用于对不同项目或类别之间进行比较。它们只比较每个项目的一个数值变量。不同之处在于 LCs 用一条末端带点的线代替了矩形条。该点的位置表示相应的数值。因此,这些线末端的点的位置等同于垂直或水平标准条的高度或长度。

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

图 5:标准条形图与棒棒糖图的比较。由作者使用 Matplotlib 制作。

经典布局包括两个轴和可以垂直或水平定向的非常细的线条。一个轴表示类别,另一个轴具有与待比较项目相关的数值尺度(最好带单位)。

当需要显示大量相似值时,建议使用 LCs 作为 SBCs 的替代方案。这样,我们可以避免显示杂乱的图表,也可以防止观众经历一种令人烦恼的光学效应,称为摩尔纹。

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

图 6:作者使用 Plotly 制作的棒棒糖图。

哑铃图

虽然哑铃图与棒棒糖图相似,但它们的主要目标是表示两个数据点之间的变化。在这方面,它们通常用于对比两个类别。

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

图 7:作者使用 Plotly 制作的哑铃图。

我们建议将它们用于比较范围、分布、变化和两个数值变量之间的差异或点之间的距离。

发散条形图

发散条形图(DBC)基本上由两个水平矩形(条形)组成,它们对齐,使得一个矩形从右到左延伸,另一个从左到右延伸,并且两个矩形都从一个共同的垂直基线开始,通常位于图表的中心。如前所述,每个矩形(条形)的长度与其显示的数值成比例。每个条形表示一个项目或分类变量,并且它们之间必须留有一些空间。对于 DBC 的最佳编码是当需要比较两个数值选项时。

蝴蝶图是一种特殊的 DBC。通常,它们在条形之间留有一些空间,以放置被比较的变量名称。

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

图 8:蝴蝶图。来源:#1。

另一组图表也可以用来比较项目:雷达图、Mekko 图和 Marimekko 图。问题是它们很难解释,因为它们依赖于受众通过比较角度或面积解码数字信息的能力。因此,总是更倾向于选择之前提到的图表。更多细节请参见我的上一篇文章。

数据可视化是讲述数据背后故事的最强有力工具。但如果我们没有正确选择最适合我们想要展示的信息的可视化技术,我们的受众可能会感到困惑。

这系列文章的目的就是指出哪些图表和图示最适合特定类型的信息。

在本文中,我们指出了六种不同的图表[标准条形图、簇状条形图、重叠条形图、棒棒糖图、哑铃图、发散条形图]**,旨在对比项目。**我们指出了它们之间的相似性和差异。

数据科学家和数据分析师接受与受众所需信息类型相关的培训是至关重要的。与上述内容相关的是选择最适合讲述我们数字背后故事的图表。

敬请关注即将发布的文章。

#1:https://www.slideteam.net/butterfly-chart-tornado-chart-for-price-comparison-powerpoint-slide.html

图表讲故事

原文:towardsdatascience.com/storytelling-with-charts-dae59034f60

第二部分:你想展示数量吗?

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

·发表于 Towards Data Science ·8 分钟阅读·2023 年 4 月 3 日

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

图片由 Claudio Schwarz 提供,来源于 Unsplash

数据可视化的第一个规则是:“数据可视化是一种沟通工具”。它是讲述商业、商业、科学、学术和创业环境中的故事的最佳工具。

但请记住,你总是有一个观众。也许这个观众包括你的老板、同事、客户、员工、公务员,或者你自己。

在任何情况下,关键问题是:我是否清晰地传达了信息?

下一个问题是:我是否选择了正确的图表来讲述我的故事?

本系列的第一篇文章 中,我们介绍了三种基于 Python 的探索性图表,允许可视化单一定量变量的分布。在本文(以及接下来的几篇文章中),我们将建议根据要传达给观众的信息的性质选择最合适的图表。

信息 1:展示数量

这个信息包括展示某一组数字的大小:销售额、销售的项目数量、缴纳的税款、制造的物品、生产的项目、完成的程度、人口数据、性能数据、商业智能仪表板中的关键绩效指标(KPIs)等。

当要展示的数据只有一项(一个数值度量)时,通常会附带其他指标来添加额外的信息并增强信息的传达效果。

常用的数字图形表示图表如下:

  • 数值指标

  • 角度指标

  • 子弹图

  • 图示符号

  • 柱状图

  • 横向条形图

数值指标

它们是定制的小部件,用于显示一个或两个数值,通常伴有标题和副标题、一些注释以及趋势指示器。它们经常用于在商业智能仪表盘中显示关键绩效指标(KPI)。

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

图 1:作者使用 Plotly 制作。

Plotly包括一个名为Indicator的图形,具有几种模式。对于上述图中的数字指示器,Plotly tracego.Indicator(),对应的参数有:mode = “number+delta” 用于可视化数值量以及该数值与参考值之间的差异;value 用于指示要显示的数值;domain 用于指示图形的位置;title 用于数值上方的文本。delta 有几个属性:reference 是要比较的值;relative = False 表示数量与参考值之间的差异必须以绝对方式计算;position 确定相对于数值的位置;valueformat 编码差异的数值格式。mode = “delta” 仅显示数量与参考值之间的差异。

import plotly.graph_objects as go

# defining the path where the charts will be stored.  
path  =  your_path

fig = go.Figure()

fig.add_trace(go.Indicator(
    mode = "number+delta",
    value = 300,
    delta = {'reference': 380, 'relative': False,
             "valueformat": "0.0f"},
    title = {'text': "Total Revenue (MU$S)", 'font': {'size': 32}},
    domain = {'x': [0.25, 0.75], 'y': [0.5, 1.0]}
    ))

fig.add_trace(go.Indicator(
    mode = "delta",
    value = 450,
    delta = {'reference': 350, 'relative': False,
             'position' : "bottom", "valueformat": "0.0f"},
    title = {'text': "Total Quantities (units)", 'font': {'size': 32}},
    domain = {'x': [0.25, 0.75], 'y': [0.0, 0.4]}
    ))

fig.update_layout(paper_bgcolor = "lightblue")

fig.write_image(path + 'figIndic1.png')
fig.show() 

角度指示器

最著名的角度指示器是标准仪表图。这是一种非常简单的图表,它提供了有关单一数值测量的准确信息,允许将显示的值与目标值进行比较,并与由不同颜色带表示的数值范围进行对比。目标值可以是基准、需要超越的前一个值,或是需要达到的目标。

标准仪表图类似于汽车的速度计。它有一个径向数值刻度,分为几个部分,每个部分由特定的颜色标识。它们还显示了下限和上限值,特别是目标值。当前值也用指针或指示针标示,或者用弯曲条在径向数值刻度上移动。最常见的配置有三个部分及其对应的颜色 [红色、黄色、绿色]。通常红色表示差、警告或低性能;黄色表示一般、警示或正常性能;绿色表示好、满意或高性能。红色还意味着数值测量低于下限,黄色表示数值测量在阈值之间,绿色则表示高于上限。

以下图示表明当前销售值(45)处于平均范围内,但比预定目标低 25%。

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

图 2:作者使用 Plotly 制作的标准仪表图。目标为 60。阈值为 25 和 50。

图 2 是用以下 Python 代码制作的。更多细节可以在我的上一篇文章中找到。

import plotly.graph_objects as go

path  =  your_path

val   = 45
title = 'Sales (MU$S)'

fig1  = go.Figure(go.Indicator(
    domain = {'x': [0, 1], 'y': [0, 1]},

    title = {'text': title, 'font_size' : 40},
    value = val,  
    number= {'font_size' : 50},
    mode  = "gauge + number",

    gauge = { 'shape' : 'angular',
              'steps' : [
                        {'range': [0, 25],  'color': "red"},
                        {'range': [25, 50], 'color': "yellow"},
                        {'range': [50, 100],'color': "lightgreen"}
                        ],
             'bar' : {'color' : "black", 'thickness': 0.5},
             'threshold' : {'line': {'color': "orange", 'width':8},
                            'thickness': 0.8, 'value': 60},
             'axis': {'range': [None, 100], 'tickformat' : '$'},
             }
                 ))

fig1.add_annotation(x=0.5, y=0.4, text="-25%", 
                    font=dict(size = 30, color="darkred"),
                    showarrow=False)
# Add a circle
fig1.add_shape(type="circle", x0=0.4,  y0=0.3, x1=0.6, y1=0.5,
               line_color="purple")

fig1.update_layout(paper_bgcolor = "lightblue")

fig1.write_image(path + 'figIndic1.png')
fig1.show()

子弹图

从概念上讲,它们类似于标准仪表图。它们显示关于单一定量测量的信息,将其当前值与目标值进行比较,并与由不同颜色带表示的一组数值范围进行对比。在视觉上,它们类似于标准条形图(水平或垂直),因为它们也通过矩形条的长度或高度来编码信息。主要的区别在于,子弹图包括一个中央狭窄的条,表示报告的数值测量的当前值。

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

图 3:作者使用 Plotly 制作的子弹图。目标是 450。阈值为 250 和 350。

目标值由 450 处的红色垂直线表示。类似于仪表图,有多个扇区(从三个到五个),用颜色或单一色调的不同强度来表示。扇区指示定性值,如[差,满意,良好],[警报,警告,满意],[差绩效,平均绩效,高绩效]。扇区也可以表示与显示的数值相关的不同数值范围。

子弹图相较于仪表图的主要优点是它们占用的屏幕空间较少,没有分散注意力的装饰物,并且最重要的是,其较小的尺寸允许你在单一图表中比较多个不同的类别。

更多细节和 Python 代码可以在我的上一篇文章中找到。

象形图

又称:象形图,图示单位图,图片图表。

象形图使用图标显示离散的数据集。通常,图标代表与所示数据相关的主题类别;例如,人口数据使用人物图标(见下图)。每个图标可以代表一个单位或其他数量(如 10,100,1000)。数据集通过图标的列或行的接近程度进行比较。

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

Edward Howell拍摄,来源于Unsplash

使用图标有时有助于克服语言、文化和教育水平的差异。观众通常很容易理解它们**。** 图标还可以提供数据的更具代表性和全面的视图。

在象形图中,始终避免显示大量图标,这可能会使显示的值难以计数。也要避免部分图标,这可能会使观众感到困惑并影响信息的传达。

柱状图

又称:柱形图,垂直条形图

这是特别类型的垂直标准条形图。它们有两个坐标轴:水平轴显示类别,垂直轴显示数量。每个类别的数量通过垂直矩形条的高度显示。每条的高度与要显示的数值成比例。每条代表一个类别,并且它们之间留有一些空隙。

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

图 5:由作者使用 Matplotlib 制作的柱形图。

虽然柱形图的经典用途通常是进行比较、显示排名或指示时间趋势,但它们**对于简单展示数值也是非常有用的。**这些数值通过每个矩形的最终高度进行编码。柱形图也适用于显示负数值。

通过在条形图内或条形图上方使用注释,可以大大提高信息的清晰度。显示网格线也很方便,但仅限于水平网格线。

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

图 6:由作者使用 Plotly 制作的柱形图。

好的实践建议将垂直轴从 0 开始,因为如果基线被修改,我们不可避免地会扭曲视觉效果。还必须避免所有 3D 效果,因为这些效果违反了适当讲述的所有规则。最后,必须避免使用圆角而不是锐角矩形,因为这会使最终数值的读取变得困难。

始终记住,最多 10% 的男性观众可能有色觉障碍。在这方面,尽量不要在同一图表中使用绿色和红色条形。 色盲症是一种缺乏绿色感知锥体的情况,而红盲症则是缺乏红色感知锥体的情况。

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

图 7:由作者使用 Plotly 制作的柱形图。

水平条形图

这是特别类型的水平标准条形图。它们有两个坐标轴:水平轴显示数量,垂直轴显示类别。每个类别的数量通过垂直矩形条的长度显示。每条的长度与要显示的数值成比例。每条代表一个类别,并且它们之间留有一些空隙。

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

图 8:由作者使用 Matplotlib 制作的水平条形图。

通过在条形图内或条形图右侧使用注释,可以大大提高信息的清晰度。显示网格线也很方便,但仅限于垂直网格线。

在绘制多个类别时,尤其是带有非常长标签的类别时,水平条形图更为合适。

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

图 9:由作者使用 Plotly 制作的水平条形图。

正如之前在柱状图描述中提到的,你必须将数值轴从 0 开始,避免所有的 3D 效果,并且避免使用圆角边缘。同时,还要考虑红绿色盲的问题。

数据可视化是将某种信息转化为视觉上下文的过程。它是向多样化的受众讲述故事的强大工具。为此,提供了大量的图表、图形和示意图。完成任务的关键步骤是选择合适的图表来讲述你的故事,这取决于你想传达的信息。

在这篇文章中,我们介绍了六种不同的图表 [数值指示器、角度指示器、子弹图、图标图、柱状图和水平条形图],这些图表旨在展示一个或几个数值,通常还附带注释和趋势指示器。我们介绍了优点,一些良好的实践,警告以及几个 Python 代码。

明智地选择,但始终问自己:我是否在清晰地沟通?

图表讲故事

原文:towardsdatascience.com/storytelling-with-charts-fbd23ebb70ee

第四部分(I):你想展示组成部分吗?

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

· 发表在 Towards Data Science · 阅读时间 7 分钟 · 2023 年 6 月 1 日

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

图片由 Hiral Parikh 提供,来源于 Unsplash

这是系列中的第四篇文章,旨在帮助人们根据他们试图向特定观众展示的信息来决定使用哪种类型的图表。

前三篇文章聚焦于以下内容:文章 1,展示了单一数值变量的分布;文章 2,显示了一系列数字的大小;文章 3,比较了各项。

本文的目的是指示在展示组成部分时最常用的图表。请记住,组成部分涉及一个可以分为各个部分的整体,以及每个部分与该整体的关系(绝对或相对)。分析可以是静态(显示某一时刻的组成部分)或动态(显示组成部分随时间的变化)。

常用于展示组成部分的图表如下:

· 饼图

· 堆叠条形图

· 堆叠面积图

· 瀑布图

· Mekko 图

· 树状图

本文将集中描述以下图表类型:饼图;堆叠条形图;以及树状图。在接下来的文章中,我们将描述剩余的三种图表。

饼图

饼图(PCs)(图 1)是圆形图表,分成楔形扇区,用于显示整体的部分,这些部分是互斥的且不重叠的类别。完整的圆表示整体,而楔形(切片、扇区、段)表示部分。因此,完整的圆必须表示所有数据的总和,并且必须始终加总到 100%。包含在一个切片中的数值不能包含在另一个切片中,因为如前所述,扇区必须是互斥的,重叠是禁止的。从概念上讲,它们表示整体的简单份额。

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

图 1:作者使用 Plotly Express 制作的饼图。

饼图通过两个视觉标记来编码数值:1) 每个扇区的面积;2) 每个扇区沿圆周的长度。与大多数其他图表不同,饼图的轴和刻度不是线性的。

人类在视觉上计算曲线周围的面积或距离并不容易。这是对这类图表的主要反对意见,也是无休止争议的源头:它们非常简单易制,观众也习惯了它们的使用,但如果没有注释和百分比来澄清上下文,它们的解释则非常困难。

有时,可以通过以下替代方案增强饼图传达的信息:A1)甜甜圈图;A2)扇区分离。

A1: 甜甜圈图(图 2),概念上等同于饼图,但与饼图不同的是,它们在图表的中心有一个空白区域(类似于一个洞),其中显示某种附加信息,以增强叙事效果。

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

图 2:作者使用 plotly.graph_objects 制作的带注释的甜甜圈图。

中心的空白区域不允许进行面积比较,因此甜甜圈图只有一个视觉标记:每个扇区的数值仅通过圆周上的弧长进行编码。

A2: 扇区分离,通过从标准饼图或甜甜圈图中拉出或分离一个(或几个)扇区,可以增强信息传达效果。

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

图 3:作者使用 plotly.graph_objects 制作的带有拉出扇区的甜甜圈图。

当然,必须有充分的理由来证明这种分离的合理性,因为观众的注意力不可避免地会集中在该扇区。此外,还有一种视觉失真,使得与其他扇区进行直接比较变得困难。

最后,饼图只显示某一时刻的组成(静态组成)。有关饼图的更多细节可以在我的上一篇文章中找到。

堆叠条形图

堆叠条形图(SBCs)(图 4)是可以垂直(水平)排列的矩形条形图。它们有两个轴:一个轴显示类别,另一个轴显示数值及其对应的刻度。每个条形代表一个主要类别,并被分割成代表第二个分类变量子类别的矩形扇区。这些矩形段的高度(长度)显示了每个子类别的数值,这些矩形段垂直(水平)堆叠在一起。每个主要条形的最终高度(长度)表示每个类别的总量(百分比堆叠条形图除外)。

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

图 4:作者使用 Matplotlib 制作的简单堆叠条形图。

有两种特定类型的 SBCs:1)简单堆叠条形图(图 4);2)百分比堆叠条形图(图 5)。

简单 SBs 将每个子类别的绝对值堆叠在前一个子类别上,而百分比 SBs 将每个子类别的百分比堆叠在前一个子类别上。简单 SBs 中的主要条形通常具有不同的高度(长度),而在百分比 SBs 中,所有主要条形具有相同的高度。当仅相对差异重要时,必须使用百分比 SBs;当相对和绝对差异都重要时,使用简单 SBs。

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

图 5:作者使用 Matplotlib 制作的百分比堆叠条形图。

SBCs 在 展示随时间变化的组成(动态组成)方面表现出色。** 对于这种类型的动态分析,必须使用垂直方向堆叠条形图,并且与时间(天、月、年、时间范围)相关的变量始终 放在横轴上(图 6)。

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

图 6:作者使用 Matplotlib 制作的堆叠条形图。

在堆叠扇区数量或长时间绘制图表时应当谨慎。建议每个主要条形上堆叠的扇区不超过四个或五个。当主要条形过多或很长时间内扇区超过三个时,观众可能会感到困惑。在这种情况下,我们建议使用堆叠面积图,当你需要展示大量的时间数据和/或每个主要条形上有四个或更多的扇区时。

更多细节可以在我的上一篇文章中找到。

树形图

这种特定类型的图表由马里兰大学计算机科学教授 Ben Shneiderman 发明,他在寻找“目录树结构的紧凑可视化”时发明了它(#2)。

用我自己的话说:“树形图是一种基于矩形的可视化工具,允许你表示一个层次结构(树状结构)数据集。概念是比较数量并在物理限制的空间中展示某些层次结构的模式。为此,使用不同大小和颜色的矩形从不同角度展示数据集。目标不是指示确切的数值,而是将数据集‘拆解’成其组成部分,并快速识别出其较大和较小的组件” (#3)。

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

图 7:作者使用 Plotly Express 制作的树形图。

后来发现它们可以作为饼图的替代方案,显示部分与整体的关系。由于每个矩形的面积与其所代表的数值成正比,它们开始用于指示部分之间的相对比例和差异。整个矩形的面积必须表示所有数据的总和。树形图仅显示某一时刻的组成情况(静态组成)。

相比饼图,树形图有两个主要优点:1)它们可以在相对较小的空间内包含十个或上千个部分的嵌套矩形;2)它们用面积编码数值,这是比圆周上的弧长更好的视觉属性。

必须始终用适当的注释标明数值,因为缺乏共同的基线严重影响了部分矩形之间的比较。

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

图 8:作者使用 Plotly Express 制作的带注释的树形图。

更多详细信息请参见我的上一篇文章

待续

我们很多时候需要向观众展示组成情况。这种部分与整体的分析对我们的特定观众并不总是容易解读的。因此,事先我们必须分析我们有哪些方法及其优缺点,如何与我们的数据和信息相关联。

如前所述,展示组成情况可以使用六种不同类型的图表:饼图;堆叠条形图;树形图;堆叠面积图;Mekko 图;瀑布图。这里,我们描述了其中的三种,特别是它们的特性、优点以及需要注意的一些事项。

请关注接下来的文章,描述其余图表。

参考文献

#1: https://serialmentor.com/dataviz/visualizing-proportions.html

#2 Ben Shneiderman (1992). “使用树形图的树形可视化:2D 空间填充方法”。ACM Transactions on Graphics. 11: 92–99. doi:10.1145/102377.115768

#3 medium.com/towards-data-science/treemaps-why-and-how-cfb1e1c863e8

如果你觉得这篇文章有趣,请阅读我之前的 55 篇文章中的任何一篇:medium.com/@dar.wtz

用表格讲故事

原文:towardsdatascience.com/storytelling-with-tables-514412adc4b7

第二部分:良好表格的建议指南

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

·发表于Towards Data Science ·8 分钟阅读·2023 年 1 月 10 日

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

图片由Joao Viegas提供,来源于Unsplash

我们已经努力工作了几个月,我们相信有一些非常重要的报告。

我们如何让观众理解我们贡献的深度?

简单:通过恰当地讲述我们的故事。

为了更好的讲述故事,我们通常使用视觉元素,如图表、图解、表格、插图和图片。

你可以查看我在 Medium 上的为什么与如何列表,了解使用经典图表(条形图、散点图、饼图、直方图)以及不常见图表(平行坐标图、Mekko 图、面积图、仪表盘图表)时的基本概念、技巧和需要避免的陷阱。

你还可以查看我在 Medium 上使用 Plotly 进行数据可视化的列表,其中展示了如何使用Plotly ExpressPlotly.graph_objects来实现类似的目的。

但如果你需要使用表格,强烈建议你首先阅读以下指南

表格

记住,表格是一种由行和列组成的结构,其主要目的是在标记的列中显示数字和/或文本的列表。

在这一系列的第一篇文章中,我指出了一个设计良好的表格应该具备的内容,并建议了使用表格的合适时机。

现在,我会提供一些建议和需要避免的陷阱。

表格的 10 个技巧

每个表格必须能够让观众理解,而无需参考周围的文本;

所有表格必须按照它们在文本中引用的准确顺序使用阿拉伯数字进行顺序编号;

将数值右对齐,以便比较它们的大小;

将包含文本的列的标题和内容左对齐;

使用适当且有意义的小数位数。统一小数位数并右对齐;

根据列内容对齐列名称。每列标题的首字母应大写;

设计表格时,确保要比较的数据是连续的或接近彼此的;

每个表格必须有一个标题或自解释的标题;

始终包括脚注以提供额外的说明、解释缩写或不常见的定义;

始终包括一个来源行,以指示表格中数据的来源;

避免的 10 个陷阱

首先问自己 你的观众是否需要表格来帮助他们理解你的故事

如果你的表格只有一到两列且行数很少,也许可以在文本中展示这些数据;

如果数据已经在文本中,不要在表格中重复它;

表格不应占据整个屏幕(整页)。另一方面,表格也不应小到难以阅读;

“Table” 这个词不应该像“Fig.”用于图表那样被缩写;

始终在每一列中使用相同的小数位数。同时,不要在每列中更改计量单位;

使用不同背景颜色交替的行时要谨慎(斑马条纹)。它们通常在大型数据集中的使用是合适的,以帮助数据的可读性。尽量使用柔和的色彩调色板;

尽量避免使用垂直线来分隔列;

列标题不应明显宽于列中最宽的数据;

始终记住,表格 比图表需要更多的处理时间。给观众足够的时间来理解。

使用 plotly.figure.factory 的表格

如果你确实急需用最少的代码绘制表格,我建议你使用 plotly.figure_factory 模块。该模块包含一些包装函数,用于扩展 Plotly 的绘图功能。让我们来看看它是如何工作的:

# Tables with Plotly Figure Factory

import plotly.figure_factory as ff

data_to_table1 = [['Year', 'Battery Electric', 'Plug-in Hybrid', 'Full Hybrid', 'Petrol', 'Diesel'],
                  [2013, 5.79, 0.00, 6.68, 34.99, 52.54],
                  [2014, 12.56, 1.15, 6.95, 30.67,48.67],
                  [2015, 17.11, 5.30, 7.13, 29.64, 40.82],
                  [2016, 15.23, 13.82,11.31,28.84, 30.80],
                  [2017, 18.93, 19.70, 12.69, 25.54, 23.14],
                  [2018, 31.19, 17.81, 10.66, 22.64, 17.69],
                  [2019, 42.38, 13.55, 12.35, 15.68, 16.03]]

fig_ff1 = ff.create_table(data_to_table1)

fig_ff1.write_image(your_path + 'FF_Table1.png', scale = 2)
fig_ff1.show()

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

表格 1:由作者使用 plotly.figure.factory 制作

表格中显示的数据对应于 2013 年至 2019 年挪威按类型分类的新乘用车注册 1

这是一个漂亮的表格,具有清晰的标题、易读的字体类型和大小、充足的空白空间,以及适当的斑马条纹颜色调色板。此外,你可以改变字体颜色和大小、背景颜色以及行高。

但该模块有一些限制,如果你需要更详细的表格,你必须求助于 Plotly Express。

使用 Plotly 图形对象的表格

参考 文章 1: “你可以使用 Table 方法在 Plotly 中创建表格。在其最基本的变体中,程序是使用 fig.add_trace()go.Table 和两个参数:headercells。前者如其名所示,代表表格的标题(第一行),而 cells 代表我们想展示给观众的数值或非数值” 2

首先,我们需要转置在 data_to_table1 列表中的数字数据。

# Plotly Table 1

import numpy as np
import plotly.graph_objects as go

headers_tb1 = ['Year', 'Battery Electric', 'Plug-in Hybrid', 'Full Hybrid', 'Petrol', 'Diesel']

values_tb1 = [[2013, 5.79,  0.00,  6.68,  34.99, 52.54],
              [2014, 12.56, 1.15,  6.95,  30.67, 48.67],
              [2015, 17.11, 5.30,  7.13,  29.64, 40.82],
              [2016, 15.23, 13.82, 11.31, 28.84, 30.80],
              [2017, 18.93, 19.77, 12.69, 25.40, 23.14],
              [2018, 31.19, 17.81, 10.66, 22.64, 17.69],
              [2019, 42.38, 13.55, 12.35, 15.68, 16.03]]

transposed_tb1 = np.array(values_tb1).T.tolist()

fig_tb1 = go.Figure()
fig_tb1.add_trace( 
                go.Table(
                         header = dict(values = headers_tb1 ),
                         cells  = dict(values = transposed_tb1) 
                         ))

fig_tb1.write_image(your_path + 'Table_tb1.png', scale = 2)
fig_tb1.show()

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

表 2:由作者使用 plotly.graph.objects 制作

我们还有很多工作要做,直到我们的表格准备好展示给观众。

然后,我们必须解决对齐问题:年份列将居中对齐,因为它是有序的,而其余的数字值将右对齐。

# Plotly Table 2                    

aligns = ['center','right', 'right','right','right', 'right']

fig_tb2 = go.Figure()
fig_tb2.add_trace( 
                  go.Table(
                     header = dict(values = headers_tb1,    align = aligns ),
                     cells  = dict(values = transposed_tb1, align = aligns )
                           ))

fig_tb2.write_image(your_path + 'Table_tb2.png', scale = 2)
fig_tb2.show()

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

表 3:由作者使用 plotly.graph.objects 制作

虽然有所改善,但我们仍然面临某些单元格小数位数不同的问题。

# Plotly Table 3

fig_tb3 = go.Figure()
fig_tb3.add_trace( 
                go.Table(
                   header = dict(values = headers_tb1,    align = aligns ),
                   cells  = dict(values = transposed_tb1, align = aligns,
                                 format = [None, ",.2f"])
                         ))

fig_tb3.write_image(your_path + 'Table_tb3.png', scale = 2)
fig_tb3.show()

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

表 4:由作者使用 plotly.graph.objects 制作

现在是时候进行装饰和最终细节的调整了。

首先,我们使用了 Plotly Express 模块 px.colors.qualitative 更改了颜色调色板,该模块包含内置的颜色序列,如 Pastel2\。我们使用了‘darkslategray’作为边框颜色,背景颜色则使用了三种不同的柔和色调:

# Plotly Table 4

fill_color_h = px.colors.qualitative.Pastel2[1]
line_color_h = 'darkslategray'

fill_color_c = [px.colors.qualitative.Pastel2[0], px.colors.qualitative.Pastel2[2],
                px.colors.qualitative.Pastel2[2], px.colors.qualitative.Pastel2[2],
                px.colors.qualitative.Pastel2[2], px.colors.qualitative.Pastel2[2]]
line_color_c = 'darkslategray'

fig_tb4 = go.Figure()
fig_tb4.add_trace( 
                go.Table(
                   header = dict(values = headers_tb1,    align = aligns,
                                 fill_color = fill_color_h,
                                 line_color = line_color_h),
                   cells  = dict(values = transposed_tb1, align = aligns,
                                 fill_color = fill_color_c,
                                 line_color = line_color_c,
                                 format = [None, ",.2f"])
                         ))

fig_tb4.write_image(your_path + 'Table_tb4.png', scale = 2)
fig_tb4.show()

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

表 5:由作者使用 plotly.graph.objects 制作

接下来,我们按照上面的说明(技巧 8 和 9)添加了标题和来源行。我们还通过增加单元格高度来增加了空白空间。

# Plotly Table 5

fig_tb5 = go.Figure()
fig_tb5.add_trace( 
                go.Table(
                   header = dict(values = headers_tb1,    align = aligns,
                                 fill_color = fill_color_h,
                                 line_color = line_color_h),

                   cells  = dict(values = transposed_tb1, align = aligns,
                                 fill_color = fill_color_c,
                                 line_color = line_color_c,
                                 height = 35, format = [None, ",.2f"])
                         ))

fig_tb5.update_layout(title =  "New Passenger Vehicle Registrations in Norway (%)", 
                      title_font_size = 20, title_x = 0.5)
fig_tb5.add_annotation(x=1, yref= 'paper', y = 0.01,
                       text="Source: Our World in Data (2021)",
                       showarrow=False, 
                       font_size = 15, font_color = 'blue')  
fig_tb5.update_layout(autosize = False, 
                      margin=dict(l=20, r=20, t=70, b=100))

fig_tb5.write_image(your_path + 'Table_tb5.png', scale = 2)
fig_tb5.show()

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

表 6:由作者使用 plotly.graph.objects 制作

第 2 和第 3 列(电池电动和插电式混合动力)的标题字符数多于其他列。增加这两列的宽度(columnwidth=)会使视觉效果更加美观。

# Plotly Table 6

fig_tb6 = go.Figure()
fig_tb6.add_trace( 
                go.Table(
                   columnwidth = [80,90,90,80, 80,80], 
                   header = dict(values = headers_tb1,    align = aligns,
                                 fill_color = fill_color_h,
                                 line_color = line_color_h),

                   cells  = dict(values = transposed_tb1, align = aligns,
                                 fill_color = fill_color_c,
                                 line_color = line_color_c,
                                 height = 35, format = [None, ",.2f"])
                         ))

fig_tb6.update_layout(title =  "New Passenger Vehicle Registrations in Norway (%)", 
                      title_font_size = 20, title_x = 0.5)
fig_tb6.add_annotation(x=1, yref= 'paper', y = 0.01,
                       text="Source: Our World in Data (2021)",
                       showarrow=False, 
                       font_size = 15, font_color = 'blue')  
fig_tb6.update_layout(autosize = False, 
                      margin=dict(l=20, r=20, t=70, b=100))

fig_tb6.write_image(your_path + 'Table_tb6.png', scale = 2)
fig_tb6.show()

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

表 7:由作者使用 plotly.graph.objects 制作

结论

表格是数据分析师向技术、商业或管理观众展示其发现时的基本工具。因此,其准备工作必须非常细致。

在这篇文章中,我们提供了 10 个改进表格设计的提示和 10 个准备过程中应避免的陷阱。我们还介绍了如何使用 plotly.figure_factory 准备一个基本表格。最后,我们描述了一系列在设计表格时应遵循的步骤,使用 plotly.graph_objects

不要让设计糟糕的表格毁掉你的叙述。

参考文献

战略数据分析(第二部分):描述性问题

原文:towardsdatascience.com/strategic-data-analysis-for-descriptive-questions-b6c9e469b32f?source=collection_archive---------3-----------------------#2023-10-14

深入探讨回答描述性问题的方法

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

·

关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 10 月 14 日

这是关于战略数据分析的系列文章的一部分。

战略数据分析(第一部分) 战略数据分析(第二部分):描述性问题战略数据分析(第三部分):诊断性问题(第三部分) 战略数据分析(第四部分):预测性问题 ← 即将推出!

战略数据分析(第五部分):处方性问题 ← 即将推出!*

第一部分中,我讨论了数据分析师尝试回答的四种问题类型以及识别每种问题类型的方法。如果你记得,当我们问描述性问题时,我们试图获取对某事的理解。这些问题通常以“what/is/does”开头,并涉及现在时或过去时。现在,让我们深入探讨如何回答这些问题的策略。

描述性问题的回答策略

描述性问题通常是数据分析师遇到的最多的问题,而这些问题的答案往往为后续问题提供基础。通常,经验丰富的分析师已经有一个策略(或至少一些指南)来回答描述性问题。更具体的策略根据问题、行业、个人偏好和知识等不同而有所不同。然而,任何策略的骨架应包括以下内容:

  1. 评估问题的意图

  2. 确定问题中的变量

  3. 定义问题的分析目标

这些步骤应指导你选择最佳方法论并提供最合适的答案。让我们深入了解一下。

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

作者制作的图表

步骤 1:评估问题的意图

在应用任何技术来回答决策者提出的问题之前,我们必须首先理解为什么会提出这个问题。这可以显著影响我们的策略和最终选择的方法。意图中的一些考虑因素包括:

  • 答案将如何被解释,

  • 我们的答案将通知哪些决策,以及

  • 我们受众的技术或统计素养

我最喜欢的意图意识的例子(也是我最常与同行分享的)是Tyler Buffington, PhD写的文章:均值还是中位数?根据决策选择,而不是分布。在这篇出色的关于选择正确方法论的评审中,Tyler 认为分布的偏斜度不应构成均值或中位数作为“平均值”指标的选择。相反,分析师应关注这个指标将如何被决策者用于推断。

问题的意图也可以帮助我们选择正确的数据点。让我们看一个例子:“我们今年第二季度的销售额是多少?”我们的回答可以是总销售额(销售单位数量乘以每单位价格)或净销售额(总销售额减去折扣和促销)。在某些情况下,我们的决策者可能不知道这个区别,因此教育他们或澄清这个值将如何被使用应该能告诉我们使用哪个值。

另一个考虑因素是受众,这是意图的一部分。如果我们试图回答一个要求我们比较不同组之间分布的问题,向不懂如何阅读箱型图的决策者展示复杂的可视化图表可能并不明智。简单的统计数据可能是更好的选择,特别是对那些每天做出数百个决策且没有时间查看复杂图表的商业伙伴(例如,C 级高管)。另一方面,如果我们想向具有统计知识的数据科学家展示信息,那么箱型图可能是更好的选择。

第二步:识别问题中的变量

下一步是识别和澄清问题中的变量,这些变量我们希望以某种方式进行描述,并确保这些变量具有代表性数据

例如,在“今年第二季度我们的销售额是多少?”这个问题中,单一变量显而易见——即今年第二季度的销售额,我们可以从销售账本中轻松获取数据。

然而,如果问题缺乏明显的变量,那么问题应该被重新表述,以便涉及明确且可以用数据表示的变量。

例如,在“我们的临床患者护理中是否存在性别偏见?”这个问题中,变量是“性别偏见”,但“性别偏见”不一定是数据点本身。然而,“性别之间的结果差异”或“性别之间的患者满意度”是“性别偏见”的潜在衡量标准。因此,我们可以将问题重新表述为“在我们的临床患者护理中,不同性别之间的患者结果是否存在差异?”

透彻审视问题的复杂性也是很重要的。有些问题可能涉及多个名词,但要求我们找出特定的变量,我们应该从问题中孤立出这个变量

例如,“来自哪个城市的游客倾向于在我们酒店逗留更长时间?”这个问题涉及游客、城市和酒店,但我们寻找的变量是游客的来源城市。对于问题:“在我们增加了更多的呼叫中心代表后,等待时间是否有变化?”两个变量是:1. 时间序列(帮助我们推断变化前后的信息)和 2. 客户等待的时间。

第三步:定义问题的分析目标

在识别出问题中的变量后,我们现在可以对问题的目标进行分类。这可以通过将其重新表述为一个指令,并对该指令进行分类来实现。识别目标可以帮助我们缩小合适的定量技术范围,从而回答原始问题。

记住:分析目标和问题的意图是不同的。问题的意图确定了决策者计划如何利用答案或他们计划如何解读分析结果。问题的分析目标决定了我们在识别变量后希望如何使用这些变量

描述性问题可能有三种类型的目标,这些目标取决于我们之前识别的变量:

  1. 描述变量 如果问题的目标是描述单个变量,那么答案将要求我们找到描述主题的一些参数或一组参数。如果我们可以使用“找到”这个关键词,并跟上问题的主题来重新表述问题,那么问题的目标就是描述变量。

    例如:“今年第二季度我们的销售额是多少?”其目标是得到一个代表所有销售额的值;因此,它要求我们找到销售总额。作为指令,我们可以将问题重述为“找到今年第二季度的销售总额”。

    大多数可以用来回答这些问题的技术包括计算描述性统计(如总和、均值、众数、范围等)或可视化工具(如直方图或核密度估计图)。不过,根据问题的性质,也存在更高级的技术。

  2. 比较组或变量 如果问题的目标是比较变量中的组或比较不同的变量,那么我们可以使用“比较”关键词重新表述问题。这些问题也可以包括时间上的比较,这可能要求我们从时间序列中创建一个变量来作为时间类别(如“之前/之后”、小时、月份等)。

    例如,“我们的临床患者护理是否存在性别偏见?”这个问题旨在比较性别组之间的患者护理,也可以重新表述为指令:“比较所有性别的临床患者护理。”

    有许多技术可以帮助比较组或变量。可视化工具如条形图或饼图可以帮助比较组,直方图和密度图可以帮助比较两个变量之间的值分布,折线图可以帮助比较随时间变化的值,而散点图可以帮助比较个体点。描述性统计和统计比较测试(如 t 检验或 ANOVA)可以用来比较两个或多个分布[1]。

  3. 识别趋势或关系 如果问题的目标是识别一个系列中的模式(如时间)或两个或多个变量之间的模式,那么我们可以使用关键词“识别连接/相关性”将描述性问题重新表述为指令。需要注意的是,关系不意味着因果关系,而只是试图建立变量之间的联系;因果关系在诊断性问题中解决。

    例如:“我们今年的收入变化如何?”旨在识别收入随时间的变化趋势。我们可以将其重新表述为一个指令:“识别收入与时间之间的关系。”

    问题“空气温度和海水温度是否相关”旨在找出两种温度之间的关系。我们可以将其重新表述为“识别空气温度和海水温度之间的相关性”。

    为了识别变量之间的关系,散点图、气泡图和热力图可以在视觉上提供帮助,而像 Pearson 或 Spearman 相关性这样的统计方法可以帮助识别变量是否存在联系。识别时间/序列中的趋势最好通过使用折线图和像 ARIMA 这样的统计方法来实现。

一个案例研究

让我们来看一个来自第一部分的问题:“火车是否晚点?”为了找到正确有效的技术来回答这个问题,让我们遵循上述策略步骤。

评估意图: 假设这个问题来自于火车运营公司的副总裁。通过与她的对话,我们了解到副总裁想知道如果火车确实晚点,是否需要采取措施来调整当前的火车时间表。如果火车实际上并没有晚点,她还希望将晚点情况建立为一个 KPI 指标并继续监测。此外,副总裁告诉我们,她认为“火车晚点”是指大多数火车晚点超过一分钟。

识别变量: 在问题“火车是否晚点”中,感兴趣的身份是火车的晚点情况,但哪个变量或变量组合可以正确地代表这一身份?通过对问题及其意图的分析,我们可以确定几个变量选择的选项:

  • 两个变量:火车预期到达时间和火车实际到达时间

  • 一个变量:火车实际到达时间与预期到达时间的差异

  • 一个变量:如果火车实际到达时间与预期到达时间的差异大于 1 分钟,则设置为 1 的二进制标志

我们的变量选择应根据问题的意图,并且一定会影响我们如何确定问题的目标。从意图中,我们知道副总裁认为如果大多数列车迟到,那么列车就算是迟到。因此,我们实际上只需要一个二元标志来识别每列车是否真的迟到。这是我们可以提供的最简单的信息,帮助我们理解整体列车迟到情况,并帮助决策者确定她的下一步。

定义分析目标: 既然我们已经确定了意图和相关变量,我们现在可以定义分析目标并选择技术。由于我们正在处理一个单一变量,即二元“迟到列车”标志,我们知道问题的目标是描述该变量。问题的意图是确定大多数列车是否迟到。因此,我们可以选择的技术之一是计算所有迟到列车的百分比,以确定是否有超过 50%的列车迟到。我们可以将最终信息传达给我们的副总裁,以便她决定下一步做什么。

如果问题的意图或受众不同,这一策略将有显著差异。如果我们的决策者想要了解列车迟到的分布,我们应该选择实际到达时间与预期到达时间的差异,并选择如直方图这样的可视化技术来传达列车迟到的分布。

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

图表由作者制作

一些最终说明

你可以根据自己的需要使用上述策略,但这里有一些建议来帮助

  • 保持简单,根据需要逐步增加复杂性。

  • 战略过程应该是直观的,但写下意图、变量和目标总是个好主意,这样你对任务有清晰的认识,或者在你的方法中培养纪律性。

  • 保持灵活——你的策略可能会随着时间的推移而改变或演变。这份文件是一个良好的开端,但不要让它限制你的创造力和思考。

  • 不要忘记进行分析!有些问题不像其他问题那样直观,需要我们思考和分析以理解并找到最佳答案。

感谢阅读!在我的下一篇文章中,我将深入探讨诊断问题,请继续关注,并在评论中告诉我你的想法!

来源:

1 www.scribbr.com/statistics/statistical-tests/

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

照片由 Scott GrahamUnsplash 上拍摄

战略数据分析(第一部分)

原文:towardsdatascience.com/strategic-data-analysis-part-1-fb2df3a43831?source=collection_archive---------3-----------------------#2023-10-07

数据分析及其试图回答的四种问题

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

·

关注 发表在 Towards Data Science ·8 分钟阅读·2023 年 10 月 7 日

这是《战略数据分析》系列的一部分。

***→ 战略数据分析(第一部分)***战略数据分析(第二部分):描述性问题战略数据分析(第三部分):诊断性问题(第三部分)

战略数据分析(第四部分):预测性问题 ← 敬请期待!

战略数据分析(第五部分):预测性问题 ← 敬请期待!

在我十年的数据工作经验中,我注意到学习定量技术以进行数据分析的重点非常突出。我花费了数千小时来完善我对从统计学到机器学习再到经济学等各方面的知识。然而,我发现关于通过数据分析回答商业问题的战略方法指导非常有限。我还遇到过许多初级分析师,他们常常将数据分析误认为是定量技术,而忽视了分析是一种强大的思维方式和极好的问题解决工具——即数据分析不仅仅是其方法的产物。

在这个多部分系列中,我希望提供一个数据分析入门指南,这将为使用分析回答商业问题提供结构化的方法。在第一部分中,我将介绍数据分析及其能够帮助回答的四种问题类型。这可以用作正确识别分析问题的指导。在随后的帖子中,我将提出回答每种问题类型的策略和选择正确技术的方法。我希望你觉得这个指南有用——在评论中告诉我吧!

什么是数据分析?

那么数据分析是什么,它试图实现什么?一般来说,分析是通过将复杂的信息拆解成更小、更简单的部分并首先理解这些部分的过程。这个过程用于帮助解决问题或回答问题。与一般情况一样,数据分析是通过尝试理解关于复杂数据的更可管理的信息来理解一些东西。

分析师可以使用各种技术来进行数据分析。例如,如果我们与医疗机构的管理者合作,他们要求我们描述其典型患者,我们可以使用统计方法如取均值或计算范围来描述患者群体。因此,我们可以通过一些描述患者总体的简单统计数据来了解诊所的所有患者。这个问题要求我们理解复杂规模的数据,而我们可以通过理解其更简单的部分来实现。

数据分析是“分析数据以回答问题、提取见解和识别趋势的过程和实践”1。然而,尽管数据分析需要借鉴统计学、机器学习、数学和其他学科的技术,数据分析师并不是统计学家、数据科学家或数学家。虽然数据科学家应该对他们所研究的主题有深入的了解,但他们不一定是该主题的专业专家。数据分析师的目标是对各种技术足够熟悉,并在应用这些技术时成为专家,以生成见解和建议,并帮助业务伙伴做出更好的数据驱动决策。但你不必成为数据分析师才能进行数据分析,任何熟悉定量技术和数据分析策略的人都可以利用这些技术帮助做出数据驱动的决策。

几乎所有需要数据分析的问题都属于四大类:描述性、诊断性、预测性和规范性。有些问题涉及已知的值和变量(如描述性和诊断性问题);有些问题则更具假设性而非具体性(如诊断性和规范性问题)。回答这些问题需要批判性思维、创造性解决问题和逻辑推理。然而,如果我们能够将需要数据分析的问题进行分类,我们可以根据其类别制定回答策略。因此,有必要熟悉问题类型及其应对策略。

本文的其余部分介绍了四种问题类型,描述了它们,并提供了示例,以帮助我们识别每种类型。

描述性问题

描述性问题旨在获得对某些具体事物的理解。这可以包括对一个群体的描述、不同变量之间的关系或各种趋势。这些问题通常最容易识别——它们通常指的是当前状态或过去的情况,并且通常以“what”或“is/does/did”关键词开头。由于并非所有描述性问题都以这些关键词开头,另一种识别描述性问题的方法是检查问题的关键词是否可以重述为以“what”开头。这些问题的一些示例如下:

  • 我们在今年第二季度的销售额是多少?2

  • 我们的收入自上季度以来是否有所增加?

  • 我们今年的收入变化如何?

  • 客户取消订阅的频率有多高?

  • 火车是否经常晚点?

  • 我们的临床患者护理中是否存在性别偏见?

  • 来自哪个城市的游客倾向于在我们酒店逗留更长时间?

  • 上个月的温度变化如何?

  • 空气温度海水温度之间是否有关联?

  • 我们在雇佣更多呼叫中心代表后,等待时间有变化吗?

上述问题都涉及一些已知的变量,这些变量可以用于分析——诊所中的性别记录、温度记录或年度收入。如前所述,所有这些问题都可以重新表述为以“什么”或“是否”开头:“空气温度和海水温度有关吗?”与“空气和海水温度之间是否存在关系?”是相同的问题,“客户取消订阅的频率是多少?”与“客户订阅取消的频率是多少?”也是相同的问题。

诊断性问题

诊断性问题旨在理解某件事情发生的原因或如何发生,并试图评估变量之间的依赖关系。这些问题以“为什么”及其同义词(如“怎么会”、“是什么导致的”等)开头,涉及已经发生或正在发生的事件。

诊断性问题的关键在于要求分析师提出潜在原因并验证这些原因是否正确。这是非常直观的,也是大多数人尝试诊断根本原因的方式。通常,相关的因变量发生了变化,我们想知道原因。我们还可以将诊断性问题视为“因果关系”问题,其中“因”是未知的。一些诊断性问题的例子包括:

  • 为什么某一客户群体比其他客户群体更愿意与我们互动?

  • 为什么我们的销售额在这个季度下降了?

  • 是什么导致了热浪?

  • 为什么我们的客户取消了他们的订阅?

  • 为什么火车晚点了?

  • 怎么会有些病人最终进入重症监护室?

在诊断性问题中,未知的是效果的原因。如果我们能够识别已知效果和未知原因,我们可能正在处理一个诊断性问题。

预测性问题

预测性问题旨在识别已知或未知变量中的未知值。我们想预测的值可能涉及部分已知和完全未知的变量。例如,在预测未来销售时,“销售”变量是部分已知的(我们有当前或过去销售的数据);在客户细分中,“客户群体”是完全未知的变量,我们必须依赖其他特征或信息来推测新变量的值。

决策者通常会提出预测性问题,以便进行战略性决策或评估他们对未来状态的准备。预测性问题通常用于寻找未知信息,但与描述性问题不同,答案总是不确定的。一些预测性问题的例子包括:

  • 下个季度我们的销售额会是多少

  • 我们的酒店在接下来的 90 天里预计会有多少名客人

  • 我们的 Instagram 帖子会得到多少个赞

  • 我们的客户给我们在 Yelp 上打五星的可能性有多大

  • 今年冬天会有很多雪吗

  • 我们如何根据植物的物理特征分组家居植物?

  • 驼背鲸的人口将来会如何变化

  • 火车会继续晚点吗?

如前所述,预测性问题不仅仅是尝试预测未来。它们处理的是部分或完全未知的事物。问题“我们如何根据植物的物理特征分组家居植物?”与未来时态无关,而是希望解决家居植物的未知参数。问题“我们的 Instagram 帖子会获得多少个赞?”很可能涉及一个部分未知的变量:我们可能知道其他 Instagram 帖子获得的赞的数量,但这个特定帖子会收到多少赞是未知的。

规定性问题

规定性问题旨在预测在特定决策下会发生什么 [3]。从这个意义上说,提问的决策者希望根据一组预测结果获得推荐意见。通常,这些问题以两种方式中的一种来表达:“如果…会发生什么?”或“应该怎么做,以便…。”

规定性问题通过评估当前情况的变化如何导致特定结果或识别能带来最佳结果的最优变化,进一步推动了预测性问题的进展。像回答预测性问题一样,我们的结果永远不会是确定的,存在一定的不确定性。然而,答案可以帮助数据驱动的决策,或引导研究来验证预测结果。

一些规定性问题的例子包括:

  • 如果我们降低价格,销售会增加吗? 2

  • 我怎样才能最大化员工生产力? 2

  • 我们如何减少碳排放?

  • 我们的商店每天应该开多长时间?

  • 如果我们强制实施高等教育入学考试,毕业率会增加吗?

  • 我们如何减少急诊部门的患者等待时间?

  • 我们的产品价格应该是多少?

规定性问题可能会或可能不会建议决策者计划采取的潜在行动。例如,“如果我们降低价格,销售会增加吗?”包含了一个我们将分析的潜在行动:价格的降低。但不同的问题,如“我们如何减少碳排放?”不包含任何行动,而是要求列出可能减少碳排放的候选行动。这意味着我们需要在策略中采取额外的步骤,以制定候选行动列表。

希望你喜欢这篇数据分析入门文章。敬请期待下一部分,我将分享选择正确技术来回答描述性问题的策略。

来源:

1 online.hbs.edu/blog/post/diagnostic-analytics

2 www.pragmaticinstitute.com/resources/articles/data/32-business-questions-for-data-analysis/

[3] www.pragmaticinstitute.com/resources/articles/data/32-business-questions-for-data-analysis/

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

照片来自Gia OrisUnsplash

《战略数据分析(第三部分):诊断性问题》

原文:towardsdatascience.com/strategic-data-analysis-part-3-diagnostic-questions-c0fcb840294b?source=collection_archive---------6-----------------------#2023-10-26

深入探讨回答“为什么”问题的方法

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

·

关注 发表在 Towards Data Science ·14 分钟阅读·2023 年 10 月 26 日

这是《战略数据分析》系列的一部分。

战略数据分析(第一部分)战略数据分析(第二部分):描述性问题→ 《战略数据分析(第三部分):诊断性问题》 战略数据分析(第四部分):预测性问题 ← 敬请期待!

《战略数据分析(第五部分):处方性问题》← 敬请期待!*

回答“为什么”问题对于任何数据分析师来说都可能很困难。缺乏主题知识、技术储备不足和缺乏战略性方法都可能对帮助决策者找到正确答案产生不利影响。然而,凭借扎实的基础和方向,这些诊断问题可以被任何人轻松解决。

诊断问题通常紧随描述性问题的答案。在提出诊断问题时,决策者的目的是了解某些信息是如何产生的或是什么导致了某件事的发生。因此,当我们考虑诊断问题时,我们通常会想到因果推断。因此,熟悉因果推断的一般原则是很好的。

在本文中:

  1. 因果推断简介

  2. 诊断问题回答策略

  3. 案例研究

  4. 一些最后的备注

因果推断简介

因果推断旨在揭示干预(或对现状的变化)如何影响结果。在因果推断中,我们假设因果关系发生在某个干预,即“处理”,施加到某个单位上,并导致该单位结果的变化。如果我们比较有无处理的单位的结果,我们将能够观察到处理的效果(即因果关系)。

例如,如果我们想知道在房屋上市出售之前是否粉刷外墙会使其更快出售,最理想的情况是我们需要同时比较粉刷和未粉刷房屋的销售时间。在这里,房屋是我们的单位,粉刷外墙是我们的处理,销售时间是我们的结果。然而,不可能同时对同一房屋进行粉刷和不粉刷。因此,“我们无法同时观察到同一单位的处理与未处理”1

这就是因果推断发挥作用的地方。我们不是直接测量特定单位上的处理效果,而是测量关联性和偏差。关联性是接受处理的所有单位与未接受处理的所有单位之间结果的平均差异。偏差通过捕捉所有结果不同的因素,将关联性与因果关系区分开来。

在我们的房屋出售示例中,我们可以比较所有粉刷过的房屋和所有未粉刷的房屋,并记录它们的销售时间。两组房屋之间的销售时间差异称为“关联”。如果没有偏差,我们可以确定在出售前粉刷房屋会使其更快出售

然而,大多数决定在出售前粉刷房屋的原房主也有能力这么做,因为他们住在更好的社区;而更好的社区中的房屋通常销售得更快。因此,偏差可能是房屋销售更快的原因不仅仅是因为新刷的油漆,还因为它们位于更好的社区。如果我们能消除这种偏差(以及其他偏差),我们可以确定在出售前粉刷房屋是否会导致它销售更快。

这是因果推断的核心。要深入了解,我强烈推荐 Matheus Facure Alves 的书籍:Causal Inference for the Brave and True,该书详细讲解了这一主题。因果推断的基础构建了回答诊断问题的策略,因此让我们更详细地探讨一下。

解决诊断问题的策略

诊断问题难以回答的原因是,它们可能需要对主题有深刻的了解。揭示某事发生或正在发生原因的一般策略需要理解所有可能的原因和偏差,然后通过严格的技术方法评估它们的效果。理解所有可能的原因需要付出努力和时间。因此,回答诊断问题的大部分时间都花在研究上。不幸的是,研究有时会将分析师引向各种死胡同。采用战略方法和严谨性有助于使过程更顺利。

一般来说,回答诊断问题的方法包括:

  1. 识别结果

  2. 识别可能的原因和潜在的偏差

  3. 评估因果关系

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

图片由 Mediamodifier 提供,来源于 Unsplash

在开始之前,需要注意的是,在几乎所有情况下,我们可能无法确定某件事的确切根本原因。相反,我们可以识别出最可能的影响因素,并评估它们的影响概率。

不仅要理解这一点,还要制定沟通策略,以便决策者在我们甚至还未开始回答他们的诊断问题之前,就能了解这一警告。在寻找诊断问题的答案时,决策者承受着风险。答案越不确定——风险越大。因此,决策者必须知道在基于所提供的答案做出决策时,必须权衡这种风险。

说明完毕,让我们详细看看这个策略。

步骤 1:识别结果

问题中的结果是经历了某种潜在原因影响的因变量。通常,诊断问题应该只有一个因变量。识别结果非常重要,以便明确它并验证它是否可以被测量。如果问题有多个因变量,则应该将问题拆分成不同的问题。

例如,在第一部分的“是什么导致了热浪”这个问题中,结果是热浪,这可以定义为温度的突然和剧烈升高。在“为什么我们的客户取消了他们的订阅”这个问题中,我们想要调查的结果是订阅取消。如果我们遇到一个类似于“为什么房价在上涨而租金在下降”的问题,我们应该回答两个独立的诊断问题:“为什么房价在上涨”和“为什么租金在下降”。

步骤 2:识别可能的原因和潜在的偏见

一旦我们确定了问题中的结果,我们必须列出所有可能解释它的因素,并帮助我们回答“为什么”。通常,这个过程可以分解为识别三件事:原因、偏见和因果机制。应构建图形因果模型来辅助识别过程。

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

图片由Mark Rabe提供,来源于Unsplash

潜在原因可以通过研究、专业知识、访谈和关联来确定。如果没有适当的主题知识或专家的帮助,这很难实现。因此,有必要尽可能多地收集有关该主题的知识(有关为什么建立知识很重要的更多细节,请查看我的文章首先我们必须发现,然后我们可以探索)。

在列出潜在原因的清单时,一个很好的工具是头脑风暴。头脑风暴的一种新颖方法是一个重复的过程,首先:列出尽可能多的原因,而不对其有效性进行判断,其次:遍历清单,确保列出的原因是合理和逻辑的。

例如,为了回答第一部分中的一个问题:“为什么我们的客户取消了他们的订阅”,我们可以首先进行调研,了解我们的流失客户是否报告了他们取消订阅的原因。我们可以采访客户成功团队,以了解他们经常收到的客户投诉。然后,我们可以通过与决策者的头脑风暴会议,提出任何额外的原因。

潜在偏差可能比潜在原因更难发现,但对答案有重大影响。就像原因一样,偏差可以通过建立主题专长来确定。然而,与主要依赖知识的潜在原因不同,偏差识别通常需要创造性和建设性的思维。

一个好的起点是熟悉数据分析中常见的偏差类型,并推测这些偏差是否出现在你的使用案例中。一些常见的偏差类型包括确认偏差、选择偏差、历史偏差、生存者偏差、可得性偏差和异常值偏差2(更多信息请查看这篇文章)。

一个非常突出的生存者偏差例子涉及了亚伯拉罕·瓦尔德在二战期间的工作。作为哥伦比亚大学统计研究小组的一部分,瓦尔德及其团队被委托优化战机应携带的装甲量:如果战机装甲过多——由于重量过大,它们将无法飞行;如果装甲过少——它们将没有保护。在分析了安全返回但有弹孔的战机后,亚伯拉罕·瓦尔德建议将装甲添加到战机上没有弹孔的地方(而不是弹孔所在的位置)。为什么?因为分析只包括了幸存的战机,因此那些没有幸存的战机可能在一些关键区域有弹孔。如果这些关键区域被击中,它们未能返回,所以在关键区域增加装甲是有意义的[3]。了解整个故事请查看这篇文章作者亚历山德罗·巴齐

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

来源: 维基共享资源

因果机制构成了潜在原因如何影响结果。没有因果机制,很难区分因果关系与巧合。这在选择推断因果关系的模型时起着重要作用。

一个很好的巧合例子是缅因州离婚率与人造黄油消费之间的相关性(见原始文章)。这两个趋势可能是平行的,但没有合理的机制来解释为什么一个会导致另一个。因此,我们不能认为缅因州离婚率的增加会导致人造黄油消费的增加,反之亦然。

图形因果模型应该被开发出来,以帮助识别原因和偏差,以及构成因果关系的机制。实质上,这些模型是包括所有原因和结果的有向图。开发一个图形模型以理解因果关系也可以帮助我们加深对主题的理解,并可以用来帮助我们与决策者沟通。

例如,图形因果模型可以帮助我们揭示混杂偏差。我们来自原因和偏差的变量不一定只是影响结果——它们实际上可以互相影响。如果某些变量影响我们的潜在原因和结果,那么我们就涉及到混杂偏差。为了消除这种偏差,我们应该控制所有共同的潜在原因。

假设我们正在研究在房屋上市出售之前进行粉刷是否会影响销售时间。我们可以假设收入较高可能会影响房主是否决定在出售之前粉刷房屋。然而,我们也可以认识到更高的收入意味着房主还可以利用减少销售时间的资源。这是混杂偏差的一个例子,我们应该在最终模型中控制收入因素。

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

由我在 draw.io 制作的图示

第 3 步:评估因果关系

现在我们有了一个结果、原因和偏差,以及构成我们依赖关系的机制,我们可以评估因果关系。这一步要求我们验证我们假设的观点是否合理。根据情况和可用资源,我们可以通过两种方式实现这一目标:1. 进行随机实验并比较结果,或 2. 利用历史数据开发统计模型来测量因果关系。

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

照片来自 Bradyn TrollipUnsplash

进行随机实验,包括处理组和对照组,可以帮助我们通过确保实验中的两个(或更多)组具有相似的人群代表性来减少偏差。如果这些组在组成上相似且我们的样本量足够,我们应该能够比较组间的结果,并识别出结果差异是否显著。

在我们的房屋销售示例中,我们可以抽取两组房屋卖家(确保两组在房主人群中具有相同的代表性)。我们可以要求其中一组在上市前粉刷他们的房子,而要求另一组保持外墙涂料不变。然后,我们将比较两组之间的销售时间分布。通过统计测试,我们可以查看销售时间指标是否存在显著差异。

在实践中,由于许多原因,这可能很难实现,其中一些原因包括让志愿的房主参与我们的实验、确保实验有足够的资金以及确保我们的样本是随机的并且代表了房屋销售人群。然而,即使我们无法进行这样的实验,我们仍然有选择。

建立统计模型使用历史数据可以帮助我们控制混杂原因和偏差,并估计直接原因对结果的影响。使用回归等技术,我们可以为每个原因和通用偏差度量分配权重。我们可以通过使用历史数据训练模型来估计模型的参数(模型中的权重)。最终结果应该帮助我们理解变量对最终结果的因果效应。“即使我们不能使用随机对照试验来保持处理组和对照组之间的其他因素相等,回归也可以通过将这些相同的因素包括在模型中来做到这一点,即使数据不是随机的!” 1

然而,无论我们选择什么技术来测量因果关系,都需要注意我们的模型不能确定因果关系。我们可以在回归模型中包括数百个特征,但仅仅因为它们被包括在内以及因为它们有一定的权重并不能保证它们是结果的原因。因此,捕捉图形因果模型中的可能因果机制是很重要的,这样我们可以避免包括不相关的特征,并确保我们获得充分的结果。

案例研究

让我们继续讨论第二部分的案例研究,在那里我提出了一个关于火车迟到的描述性问题的回答策略。假设我们的决策者现在想知道“为什么火车会迟到?”按照本文中概述的步骤,我们可以制定以下策略来回答这个问题:

识别结果。 问题“火车为什么迟到”的结果是火车迟到(我们定义为“如果火车实际到达时间与预期到达时间之间的差异大于 1 分钟,则设置为 1 的二进制标志”)

识别潜在的原因和偏差。

  1. 为了识别潜在的原因,我们可以与决策者进行一些访谈和头脑风暴会议,我们可以在平台上观察火车并进行火车旅行,还可以与列车员和乘客交谈。潜在原因的示例包括平台装卸时间延迟、轨道施工、缺乏专用轨道导致的火车交会和超车延迟、危险(如树叶、冰雪)、火车年龄和技术问题。对于每个原因,我们还应该识别一个机制,通过该机制原因对结果产生影响。

  2. 为了识别潜在的偏差,我们可以熟悉各种偏差类型,并评估这些偏差是否适用于我们的用例。例如,选择偏差可能不会对我们造成问题,因为我们可以将所有火车纳入研究,而不是仅选择一部分火车。另一方面,我们可能存在幸存者偏差的情况,因为某些火车的机械问题可能导致火车从未到达,因此被排除在晚点火车数据集之外。

  3. 为了识别潜在的因果机制,我们应该识别每个潜在原因如何影响或冲击结果。例如,某种危险(如落叶或雪)可能导致火车晚点,因为它会让火车因危险而减速。我们可以假设火车的年龄影响火车的晚点情况,因为老旧的火车较慢。但这是真的吗?收集相关数据并进行探索性数据分析可以帮助我们验证这一因果机制是否合理。

我们可以整理一个图形因果模型,以评估我们提出的原因和偏差与结果的关系,并为每个原因概述一个潜在机制。此时,我们还可以进行更多的探索性数据分析,以发现我们原因之间的隐性关联,并选择最终的潜在原因纳入模型。例如,如果我们发现有技术问题的火车大多数是老旧火车,我们就不需要将火车年龄作为模型参数,因为它已经通过技术问题参数得到了暗示。

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

来源:由我在 draw.io 中制作

评估因果关系。 最后,我们准备评估因果关系。对我们而言,进行一系列实验以测试每个潜在原因将会是困难且成本高昂的。然而,由于我们有详细的火车时刻表、火车问题以及天气和轨道条件记录,我们应该努力构建一个回归模型,以验证可能的原因。在我们的案例中,我们可以使用可能的原因构建一个逻辑回归模型,以预测火车是否确实晚点。模型训练后,与模型参数相关的权重应指示每个原因对结果的影响。

在选择了具有非零权重的原因后,我们可以向决策者展示我们的发现,并回答他们最初的问题:“为什么火车会晚点?”

几个最终说明

这篇文章较长,但我希望它能够阐明一个复杂的主题,并使其更容易处理。几点说明:

  • 我们可能无法确定过去或当前事件的确切原因。在大多数情况下,我们可以识别出最可能或最有可能的原因。因此,决策者需要承担一定的风险,并应对此有所了解。

  • 图形因果模型可以成为与决策者沟通的一个极好工具,帮助揭示潜在情况之间的关联,并有助于识别偏差。

  • 如果没有因果机制,一个与结果有非零关联的潜在原因可能只是巧合。

  • 作为分析师,运用批判性思维技能非常重要,特别是在处理诊断问题时。这些问题可能有很多曲折,可能会将你引向错误的路径。

感谢阅读!在我的下一篇文章中,我将深入探讨预测问题,请保持关注,并在评论中告诉我你的想法!

来源

1 matheusfacure.github.io/python-causality-handbook/01-Introduction-To-Causality.html

2 www.metabase.com/blog/6-most-common-type-of-data-bias-in-data-analysis

[3] www.cantorsparadise.com/survivorship-bias-and-the-mathematician-who-helped-win-wwii-356b174defa6

从云存储中流式传输大数据文件

原文:towardsdatascience.com/streaming-big-data-files-from-cloud-storage-634e54818e75?source=collection_archive---------9-----------------------#2023-01-25

高效处理大型文件的方法

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

·

关注 发表于 Towards Data Science ·13 分钟阅读·2023 年 1 月 25 日

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

图片由 Aron Visuals 提供,来源于 Unsplash

处理非常大的文件可能会给应用程序开发人员带来与高效资源管理和运行时性能相关的挑战。例如,文本文件编辑器可以分为能够处理大文件的编辑器和那些让你的 CPU 卡顿、让你的 PC 冻结、让你想尖叫的编辑器。当大型文件存储在远程存储位置时,这些挑战会更加严重。在这种情况下,必须考虑文件如何被拉取到应用程序中,同时考虑:带宽容量、网络延迟和应用程序的文件访问模式。在这篇文章中,我们考虑了我们的数据应用程序需要访问存储在云对象存储中的一个或多个大文件的情况。这是关于高效从云中获取数据的一系列文章中的一篇(例如,这里,这里 和 这里)。

在开始之前,让我们明确一下……在使用云存储时,通常建议处理特别大的文件。如果你正在处理多个数据文件,也建议不要选择特别小的文件大小(因为对存储服务发出的多个请求会产生额外开销)。最佳文件大小因平台而异,但通常在几 MB 到几百 MB 之间。(如果你严重依赖云存储,可能需要设计一个简单的实验来测试这一点。)不幸的是,我们并不总是能控制数据设计过程,有时只能接受现有的条件。

另一个好的实践,特别是在处理大文件时,是选择支持部分文件读取的格式——也就是说,选择一种不需要加载整个文件即可处理其任何部分的格式。这类文件的几个例子包括:

  • 一个简单的文本文件,

  • 一个顺序数据集——一个包含按顺序分组到单个文件中的单独记录的数据集,

  • 以列式格式存储的数据集,例如 Apache Parquet,这种格式专门设计用于仅加载选定的列,

  • 允许从任意时间偏移播放的视频文件(大多数格式都支持这种功能)。

在这篇文章中,我们假设我们的大文件允许部分读取。我们将考虑几种在 Python 中读取文件内容的选项,并测量它们在不同应用场景下的表现。尽管我们的演示将基于 AWS 的对象存储服务 Amazon S3,但我们所写的内容同样适用于任何其他对象存储服务。请注意,我们选择的具体服务、API、库等,不应被视为对这些选项的偏好。云端数据流的最佳解决方案很大程度上依赖于项目和环境的细节,强烈建议在得出任何结论之前进行深入分析。

从 Amazon S3 直接下载

在这篇文章中,我们将假设我们直接从 Amazon S3 下载文件。然而,需要注意的是,有许多服务和/或解决方案在对象存储和应用程序之间包括一个中间步骤。例如,AWS 提供了 Amazon FSxAmazon EFS 等服务,将数据镜像到云中的高性能文件系统中。AI Store 提供了一种基于 kubernetes 的解决方案,用于与数据消费应用程序相邻的轻量级存储堆栈。这些解决方案可能会缓解使用大文件时的一些挑战,例如,它们可能会减少延迟并支持更高的带宽。另一方面,它们通常会引入一系列与部署、维护、扩展性等相关的新挑战。此外,它们还会增加额外的费用。

比较性能测量指标

在接下来的几节中,我们将描述在 Python 中从 Amazon S3 拉取大文件的不同方法。为了比较这些方法的行为,我们将使用以下指标:

  • 首次采样时间 — 读取文件中第一个样本需要多长时间。

  • 平均顺序读取时间 — 在顺序遍历所有样本时,每个样本的平均读取时间是多少。

  • 总处理时间 — 整个数据文件的总处理时间是多少。

  • 平均随机读取时间 — 在读取任意偏移量的样本时的平均读取时间是多少。

不同的应用程序将对这些指标的优先级有不同的偏好。例如,视频流应用可能会优先考虑较低的首次样本时间,以提高观众体验。它还需要在任意偏移量处进行高效读取,以支持快进等功能。另一方面,只要平均每样本时间超过某个阈值(例如,每秒 30 帧),优化此指标就不那么重要。相比之下,深度学习训练应用可能会优先考虑减少平均顺序读取时间和总处理时间,以最小化训练流程中的潜在性能瓶颈。

玩具示例

为了方便讨论,我们创建了一个 2 GB 的二进制文件,并假设该文件包含 2,048 个数据样本,每个样本大小为 1 MB。下面的代码块包括以下片段:创建一个包含随机数据的文件并将其上传到 Amazon S3(使用 boto3),按顺序遍历所有样本,以及在非顺序文件偏移量处采样数据。对于此及所有后续代码片段,我们假设您的 AWS 账户和本地环境已被适当地 配置 以访问 Amazon S3。

import os, boto3

KB = 1024
MB = KB * KB

def write_and_upload():
    # write 2 GB file
    with open('2GB.bin', 'wb') as f:
        for i in range(2*KB):
            f.write(os.urandom(MB))

    # upload to S3
    bucket = '<s3 bucket name>'
    key = '<s3 key>'
    s3 = boto3.client('s3')
    s3.upload_file('2GB.bin', bucket, key)

def read_sequential(f, t0):
    t1 = time.time()
    x = f.read(MB)
    print(f'time of first sample: {time.time() - t1}')
    print(f'total to first sample: {time.time() - t0}')
    t1 = time.time()
    count = 0
    while True:
        x = f.read(MB)
        if len(x) == 0:
            break
        count += 1
    print(f'time of avg read: {(time.time() - t1)/count}')

def fast_forward(f):
    t1 = time.time()
    total = 10
    for i in range(total):
        f.seek(i * 100 * MB)
        t1 = time.time()
        x = f.read(MB)
    print(f'time of avg random read: {(time.time() - t1)/total}')

从 S3 下载到本地磁盘

我们考虑的第一个选项是将大型文件下载到本地磁盘,然后以读取任何其他本地文件的方式从那里读取它。有多种方法可以将文件下载到本地磁盘。我们在这里评估的三种方法是:Python boto3 API、AWS CLI 和 S5cmd。

Boto3 文件下载

在 Python 中从 Amazon S3 拉取文件的最直接方法是使用专用的 Boto3 Python 库。在下面的代码块中,我们展示了如何定义一个 S3 客户端 并使用 下载文件 API 将文件从 S3 拉取到本地路径。该 API 接受一个 TransferConfig 对象,其中包含调节下载行为的控制项。在我们的示例中,我们将设置保留为默认值。

import boto3, time

bucket = '<s3 bucket name>'
key = '<s3 key>'
local_path = '<local path>'

s3 = boto3.client('s3')

config = boto3.s3.transfer.TransferConfig(
    multipart_threshold=8 * MB,
    max_concurrency=10,
    multipart_chunksize=8 * MB,
    num_download_attempts=5,
    max_io_queue=100,
    io_chunksize=256 * KB,
    use_threads=True,
    max_bandwidth=None)

t0 = time.time()
s3.download_file(bucket, key, local_path, Config=config)

with open(local_path, 'rb') as f:
    read_sequential(f,t0)

print(f'total time: {time.time()-t0}')

我们在本地环境中运行了这个脚本(以及所有后续脚本),并将结果在 10 次试验中取了平均值。不出所料,平均首次样本时间相对较高,约为 21.3 秒。这是因为我们需要等待整个文件下载完成后才能打开。一旦下载完成,顺序和任意样本的平均读取时间都非常微小,就像我们从其他本地文件中预期的一样。

Boto3 包含一个类似的 API,download_fileobj,用于将文件直接下载到内存中(例如,使用io.BytesIO对象)。然而,这在处理大文件时通常推荐。

AWS CLI

AWS CLI工具提供了类似的命令行功能。AWS CLI 是用 Python 编写的,使用与 Boto3 相同的底层 API。一些开发者对这种使用方式感到更加舒适。下载配置设置通过AWS 配置文件进行控制。

import shlex, time
from subprocess import Popen

bucket = '<s3 bucket name>'
key = '<s3 key>'
local_path = '<local path>'

cmd = f'aws s3 cp s3://{bucket}/{key} {local_path}'
p = Popen(shlex.split(cmd)).wait()

with open(local_path, 'rb') as f:
    read_sequential(f,t0)

print(f'total time: {time.time()-t0}')

不出所料,运行此脚本(使用默认配置设置)的结果与之前的 Boto3 结果几乎完全相同。

S5cmd

我们在之前的文章中详细介绍了S5cmd命令行工具,展示了它在并行从云存储中下载数百个小文件的价值。与之前的方法不同,S5cmd 是用Go 编程语言编写的,因此能够更好地利用底层资源(例如,CPU 核心和 TCP 连接)。有关 S5cmd 如何工作及其显著性能优势的更多细节,请查看这篇信息丰富的博客。S5cmd 的concurrency标志允许控制下载速度。下面的代码块演示了将 S5cmd 的concurrency设置为 10 的使用方法。

import shlex, time
from subprocess import Popen

bucket = '<s3 bucket name>'
key = '<s3 key>'
local_path = '<local path>'

s5cmd = f's5cmd cp --concurrency 10 s3://{bucket}/{key} {local_path}'
p = Popen(shlex.split(cmd)).wait()

with open(local_path, 'rb') as f:
    read_sequential(f,t0)

print(f'total time: {time.time()-t0}')

很遗憾,我们未能再现 S5cmd 在50 GB 文件上的卓越性能提升。平均首次样本时间约为 23.1 秒,稍高于我们之前的结果。

多线程与单线程下载

之前的每种方法都在后台使用了多线程下载。在多线程下载中,多个线程并行运行,每个线程负责下载文件的一个不重叠的块。多线程下载对于及时从云端拉取大文件至关重要。为了演示其重要性,我们重新进行了 Boto3 实验,将use_threads标志设置为False,实际上禁用了多线程下载。这导致结果的平均首次样本时间飙升至 156 秒。

数据文件预取的艺术

预取是一种常用技术,用于遍历多个大型文件时。当进行预取时,应用程序会开始并行下载一个或多个后续文件,同时处理当前文件。通过这种方式,应用程序可以避免除第一个文件之外的所有文件的下载延迟。有效的预取需要适当的调整以达到最优结果。许多框架通过从云中预取数据来加速数据摄取速度。例如,PyTorchTensorFlow都支持预取训练数据文件以优化深度学习训练。

从 S3 流式传输数据

一些应用程序可能愿意在平均每个样本的读取时间上做出妥协,以换取较低的首样本时间。在这一节中,我们演示了一种 Boto3 选项,用于从 S3 流式传输文件,以便在完成文件下载之前就开始处理它。我们描述的方法涉及创建一个Linux FIFO 管道,并将其传递给 Boto3 的download_fileobj API:

import os, boto3, time, multiprocessing as mp

bucket = '<s3 bucket name>'
key = '<s3 key>'

t0 = time.time()

os.mkfifo(local_path)

def stream_file():
    s3 = boto3.client('s3')
    with open(local_path, 'wb') as f:
        s3.download_fileobj(bucket, key, f)

proc = mp.Process(target=stream_file)
proc.start()

with open(local_path, 'rb') as f:
    read_sequential(f, t0)

print(f'total time: {time.time()-t0}')

确实,平均首样本时间降至约 2.31 秒(从超过 20 秒降下)。另一方面,平均每样本时间和总文件处理时间分别增加到约 0.01 秒和 24.7 秒。

从任意偏移量读取数据

一些应用程序要求能够在任意偏移量处仅读取文件的特定部分。对于这些用例,下载整个文件可能是极其浪费的。在这里,我们展示了如何使用 Boto3 的get_object数据流 API 下载文件的特定字节范围。下面的代码块演示了 API 在流式传输整个文件和读取任意数据块时的使用。

import boto3, time

bucket = '<s3 bucket name>'
key = '<s3 key>'

s3 = boto3.client('s3')

# stream entire file
t0 = time.time()
response = s3.get_object(
    Bucket=bucket,
    Key=key
)
f = response['Body']
read_sequential(f,t0)

print(f'total time: {time.time()-t0}')

# fast forward
total = 10
t0 = time.time()

for i in range(total):
    response = s3.get_object(
        Bucket=bucket,
        Key=key,
        Range=f'bytes={i*100*MB}-{i*100*MB+MB-1}'
    )
    f = response['Body']
    x = f.read()

print(f'time of avg random read: {(time.time() - t0)/total}')

尽管此解决方案的首样本时间结果约为 1.37 秒,但总文件处理时间(约 119 秒)使其不适合顺序读取。其价值体现在读取任意样本的平均时间上——约 0.191 秒。

请注意,我们的示例没有利用任意偏移量是预先确定的这一事实。现实世界中的应用程序将使用这些信息来预取文件段并提升性能。

如上所述,从云中高效拉取大型文件依赖于并行多部分下载。实现这一点的一种方法是使用 Boto3 的get_object API 以刚刚展示的方式读取不连续的文件块。

使用 Amazon S3 Select 过滤数据

有时,我们所寻求的部分数据是从存储在 CSV、JSON 或 Apache Parquet 文件格式的大文件中提取的少量行和/或列。在这种情况下,我们可以简单地使用专用服务,如Amazon S3 Select来应用 SQL 过滤器。要对多个文件运行 SQL 过滤器,你可以考虑使用Amazon Athena。这两种服务都允许你将数据检索限制为所需的特定信息,避免了拉取一个或多个大型文件的高开销。请务必查看文档以了解更多信息。

使用 Goofys 挂载 S3 数据

到目前为止,我们讨论的所有解决方案都涉及直接从云存储中提取数据。其他解决方案则将云存储访问暴露给应用程序,作为(类似 POSIX 的)文件系统。Goofys是一个流行的基于FUSE的库,用于从 Amazon S3 中读取数据。下面的命令演示了如何将 S3 桶挂载到本地文件路径。

goofys -o ro -f <s3 bucket name> <local path>

一旦配置了 goofys 挂载,应用程序可以通过指向本地路径的方式访问大型文件,如下面的代码块所示:

bucket = '<s3 bucket name>'
key = '<s3 key>'
mount_dir = '<local goofys mount>'
sequential = True # toggle flag to run fast_forward

t0 = time.time()
with open(f'{mount_dir}/{key}', 'rb') as f:
    if sequential:
        read_sequential(f, t0)
        print(f'total time: {time.time()-t0}')
    else:
        fast_forward(f)

基于 goofys 的解决方案导致了约 1.36 秒的首次样本时间、约 27.6 秒的总文件处理时间和约 0.149 秒的读取任意样本的平均时间。

应该注意的是,在底层,goofys 尝试优化响应时间,即使是以增加对 Amazon S3 的额外调用为代价(例如,预取数据块,即使在它们被请求之前)。根据你的设置和应用程序的数据访问模式的细节,这可能导致相对于其他解决方案,Amazon S3 成本略有增加。Goofys 包括多个设置来控制其行为,例如,“ — cheap”标志用于在性能和潜在较低成本之间进行权衡。请注意,这些控制是在定义挂载时应用的。与基于 Boto3 的解决方案相反,goofys 不允许你在运行时调整控制(例如,块大小、预取行为等),以支持不同的数据摄取模式。

另一个需要注意的点是,goofys 读取的数据部分会被缓存。因此,如果在缓存中仍然存在相同的数据部分时再次读取,该响应将是即时的。在运行测试时请记住这一点,并确保在每次实验之前重置 goofys 挂载。

确保查看文档以获取更多关于 goofys 如何工作、如何控制其行为、其限制等方面的详细信息。

比较结果

下表总结了我们进行的实验结果:

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

从 S3 拉取 2GB 文件的比较结果(作者提供)

结果表明,处理大数据文件的最佳方法可能取决于应用程序的具体需求。根据这些结果,旨在最小化首次样本时间的应用程序将从基于 goofys 的解决方案中获益最大,而旨在最小化总处理时间的应用程序则会选择使用 Boto3 将文件下载到本地磁盘。

我们强烈建议不要依赖此表格来为你的应用程序做出任何决策。测试是在一个非常特定的环境下进行的,使用的是本文撰写时可用的工具版本。比较结果可能会因以下因素而有很大差异:设置、网络负载、与云存储设施的距离、所用工具的版本、应用程序的资源需求等。

我们的结果没有考虑不同解决方案之间 CPU、内存和其他系统资源的使用差异。在实际应用中,系统资源的负载应予以考虑,因为它们可能会影响应用程序的行为。

在做出任何设计决策之前,请务必进行自己的深入、基于用例的实验。

总结

在这篇文章中,我们讨论了从云存储中提取大型数据文件的话题。我们看到,最佳的方法可能会根据数据应用的具体需求而有所不同。我们的讨论突显了一个在所有关于云服务的文章中都存在的主题——尽管为技术发展和进步开辟了广泛的新机会,但它也带来了许多独特而令人兴奋的挑战,并促使我们重新思考常见的应用设计原则。

一如既往,欢迎提出评论、问题和更正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值