Python 大数据分析(一)

原文:annas-archive.org/md5/5058e6970bd2a8d818ecc1f7f8fef74a

译者:飞龙

协议:CC BY-NC-SA 4.0

第一章:前言

关于

本节简要介绍了作者、这本书的内容、你入门所需的技术技能,以及完成活动和练习所需的硬件和软件要求。

关于本书

实时处理大数据具有挑战性,因为它涉及到可扩展性、信息不一致性和容错性等问题。《Python 大数据分析》教你如何使用工具来应对数据的爆炸性增长。在本书中,你将学习如何将数据聚合成有用的维度以供后续分析,提取统计量,并将数据集转换成特征以供其他系统使用。

本书以介绍如何使用 pandas 在 Python 中进行数据处理开始。然后,你将熟悉统计分析和绘图技术。通过多种动手实践,你将学会如何使用 Dask 分析分布在多台计算机上的数据。随着进度的推进,你将学习如何在数据无法完全放入内存时,如何聚合数据以用于绘图。你还将探索 Hadoop(HDFS 和 YARN),这将帮助你处理更大的数据集。本书还涵盖了 Spark,并解释了它如何与其他工具互动。

本书结束时,你将能够启动自己的 Python 环境,处理大文件,并操控数据生成统计信息、指标和图表。

关于作者

伊凡·马林是 Daitan Group(总部位于坎皮纳斯的软件公司)的一名系统架构师和数据科学家。他为大规模数据设计大数据系统,并使用 Python 和 Spark 实现端到端的机器学习管道。他还是圣保罗数据科学、机器学习和 Python 的积极组织者,并曾在大学层级开设过 Python 数据科学课程。

安基特·舒克拉是世界技术公司(World Wide Technology,一家领先的美国技术解决方案提供商)的一名数据科学家,他负责开发和部署机器学习与人工智能解决方案,以解决商业问题并为客户创造实际的经济价值。他还参与公司的研发计划,负责生产知识产权,建立新领域的能力,并在企业白皮书中发布前沿研究。除了调试 AI/ML 模型外,他还喜欢阅读,并且是个大食客。

萨朗·VK是 StraitsBridge Advisors 的首席数据科学家,他的职责包括需求收集、解决方案设计、开发以及使用开源技术开发和产品化可扩展的机器学习、人工智能和分析解决方案。与此同时,他还支持售前和能力建设。

学习目标

  • 使用 Python 读取并将数据转换成不同格式

  • 使用磁盘上的数据生成基本统计信息和指标

  • 使用分布在集群上的计算任务

  • 将各种来源的数据转换为存储或查询格式

  • 为统计分析、可视化和机器学习准备数据

  • 以有效的视觉形式呈现数据

方法

《Python 大数据分析》采取实践方法,帮助理解如何使用 Python 和 Spark 处理数据并将其转化为有用的内容。它包含多个活动,使用实际商业场景,让你在高度相关的背景下实践并应用你的新技能。

受众

《Python 大数据分析》是为希望掌握数据控制与转化成有价值洞察方法的 Python 开发者、数据分析师和数据科学家设计的。对统计测量和关系型数据库的基本知识将帮助你理解本书中解释的各种概念。

最低硬件要求

为了获得最佳学生体验,我们建议以下硬件配置:

处理器:Intel 或 AMD 4 核或更高

内存:8 GB RAM

存储:20 GB 可用空间

软件要求

你需要提前安装以下软件。

以下任一操作系统:

  • Windows 7 SP1 32/64 位

  • Windows 8.1 32/64 位 或 Windows 10 32/64 位

  • Ubuntu 14.04 或更高版本

  • macOS Sierra 或更高版本

  • 浏览器:Google Chrome 或 Mozilla Firefox

  • Jupyter lab

你还需要提前安装以下软件:

  • Python 3.5+

  • Anaconda 4.3+

以下 Python 库已包含在 Anaconda 安装中:

  • matplotlib 2.1.0+

  • iPython 6.1.0+

  • requests 2.18.4+

  • NumPy 1.13.1+

  • pandas 0.20.3+

  • scikit-learn 0.19.0+

  • seaborn 0.8.0+

  • bokeh 0.12.10+

这些 Python 库需要手动安装:

  • mlxtend

  • version_information

  • ipython-sql

  • pdir2

  • graphviz

约定

文本中的代码字、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 账户名如下所示:“要将数据转换为正确的数据类型,我们可以使用转换函数,例如 to_datetimeto_numericastype。”

一段代码如下所示:

before the sort function:[23, 66, 12, 54, 98, 3]
after the sort function: [3, 12, 23, 54, 66, 98]

新术语和重要词汇用粗体显示。屏幕上看到的词汇,例如在菜单或对话框中,按如下方式出现在文本中:“Pandas (pandas.pydata.org) 是一个广泛应用于数据科学社区的数据处理和分析库。”

安装与设置

安装 Anaconda:

  1. 访问 www.anaconda.com/download/ 在浏览器中。

  2. 根据你使用的操作系统点击 Windows、Mac 或 Linux。

  3. 接下来,点击下载选项,确保下载最新版本。

  4. 下载后打开安装程序。

  5. 按照安装程序中的步骤操作,就完成了!你的 Anaconda 分发版已经准备好。

PySpark 可以在 PyPi 上获得。要安装 PySpark,请运行以下命令:

pip install pyspark --upgrade

更新 Jupyter 并安装依赖项:

  1. 搜索 Anaconda Prompt 并打开它。

  2. 输入以下命令来更新 Conda 和 Jupyter:

    #Update conda
    conda update conda
    #Update Jupyter
    conda update Jupyter
    #install packages
    conda install numpy
    conda install pandas
    conda install statsmodels
    conda install matplotlib
    conda install seaborn
    
  3. 从 Anaconda Prompt 打开 Jupyter Notebook,请使用以下命令:

    jupyter notebook
    pip install -U scikit-learn
    

安装代码包

将课堂代码包复制到 C:/Code 文件夹中。

附加资源

本书的代码包也托管在 GitHub 上,网址为:github.com/TrainingByPackt/Big-Data-Analysis-with-Python

我们还提供了其他代码包,来自我们丰富的图书和视频目录,网址为:github.com/PacktPublishing/。赶快去看看吧!

第二章:第一章

Python 数据科学栈

学习目标

我们将通过理解 Python 在数据操作和可视化中的强大能力,开启我们的学习之旅,进行有用的分析。

到本章结束时,你将能够:

  • 使用 Python 数据科学栈的所有组件

  • 使用 pandas DataFrame 操作数据

  • 使用 pandas 和 Matplotlib 创建简单的图表

在本章中,我们将学习如何使用 NumPy、Pandas、Matplotlib、IPython、Jupyter notebook。稍后我们将探讨virtualenvpyenv的部署方式,紧接着我们将使用 Matplotlib 和 Seaborn 库绘制基本的可视化图表。

引言

Python 数据科学栈是一个非正式的名称,用来描述一组协同工作的库,用于解决数据科学问题。关于哪些库应当列入此名单,并没有统一的标准;通常这取决于数据科学家和待解决的问题。我们将介绍最常用的库,并解释如何使用它们。

在本章中,我们将学习如何使用 Python 数据科学栈来操作表格数据。Python 数据科学栈是处理大规模数据集的第一步,尽管这些库本身通常不用于大数据处理。这里使用的思想和方法将对我们后续处理大数据集时非常有帮助。

Python 库和包

Python 之所以是一门强大的编程语言,其中一个重要原因就是它所附带的库和包。Python 包索引PyPI)中有超过 130,000 个包,且这个数字还在增长!让我们一起来探索一些数据科学栈中的库和包。

数据科学栈的组件如下:

  • NumPy:一个数值计算库

  • pandas:一个数据操作和分析库

  • SciPy 库:建立在 NumPy 基础上的一组数学算法

  • Matplotlib:一个绘图和图表库

  • IPython:一个交互式 Python shell

  • Jupyter notebook:用于交互式计算的 Web 文档应用

这些库的组合构成了一个强大的工具集,用于处理数据操作和分析。我们将逐一介绍每个库,探索它们的功能,并展示它们是如何协同工作的。让我们从解释器开始。

IPython:一个强大的交互式 shell

IPython shell(ipython.org/)是一个交互式 Python 命令解释器,支持多种语言。它允许我们快速测试想法,而不需要创建文件并运行它们。大多数 Python 安装包中都会包含一个命令解释器,通常被称为shell,你可以在其中逐行执行命令。尽管很方便,但标准的 Python shell 使用起来有些繁琐。IPython 提供了更多的功能:

  • 会话之间的输入历史记录功能,确保你在重启 shell 后,之前输入的命令可以被重新使用。

  • 使用Tab自动补全命令和变量,你可以输入 Python 命令、函数或变量的首字母,IPython 会自动补全它。

  • 魔法命令扩展了 shell 的功能。魔法函数可以增强 IPython 的功能,例如添加一个模块,可以在模块在磁盘上被修改后重新加载,而不需要重启 IPython。

  • 语法高亮。

练习 1:使用 IPython 命令与 Python Shell 交互

启动 Python shell 很简单。让我们按照以下步骤与 IPython shell 互动:

  1. 要启动 Python shell,请在控制台中输入ipython命令:

    > ipython
    In [1]:
    

    IPython shell 现在已准备好,等待进一步的命令。首先,让我们做一个简单的练习,解决一个排序问题,使用一种基本的排序方法,称为直接插入法

  2. 在 IPython shell 中,复制并粘贴以下代码:

    import numpy as np
    vec = np.random.randint(0, 100, size=5)
    print(vec)
    

    现在,随机生成的数字的输出将类似于以下内容:

    [23, 66, 12, 54, 98, 3]
    
  3. 使用以下逻辑按升序打印vec数组的元素:

    for j in np.arange(1, vec.size):
        v = vec[j]
        i = j
        while i > 0 and vec[i-1] > v:
            vec[i] = vec[i-1]
            i = i - 1
        vec[i] = v
    

    使用print(vec)命令在控制台打印输出:

    [3, 12, 23, 54, 66, 98]
    
  4. 现在修改代码。将参数从创建一个包含 5 个元素的数组更改为创建一个包含 20 个元素的数组,使用箭头编辑粘贴的代码。修改相关部分后,使用箭头移动到代码的末尾并按Enter键执行。

注意左侧的数字,表示指令编号。这个数字会一直增加。我们将值赋给一个变量并对该变量执行操作,得到交互式结果。我们将在接下来的章节中使用 IPython。

Jupyter Notebook

Jupyter notebook (jupyter.org/) 最初作为 IPython 的一部分,但在版本 4 中被分离并扩展,现在作为一个独立的项目存在。Notebook 概念基于扩展交互式 shell 模型,创建可以运行代码、显示文档并展示结果(如图表和图像)的文档。

Jupyter 是一个 Web 应用程序,因此它直接在 Web 浏览器中运行,无需安装单独的软件,且可以在互联网上使用。Jupyter 可以使用 IPython 作为运行 Python 的内核,但它支持超过 40 个由开发者社区贡献的内核。

注意

在 Jupyter 的术语中,内核是执行代码单元中的代码的计算引擎。例如,IPython 内核执行 notebook 中的 Python 代码。也有其他语言的内核,如 R 和 Julia。

它已经成为一个事实上的平台,从初学者到高级用户,从小型到大型企业,甚至是学术界,都可以用它来执行与数据科学相关的操作。在过去的几年里,它的受欢迎程度大幅度提高。一个 Jupyter notebook 包含你在其上运行的代码的输入和输出。它支持文本、图像、数学公式等,是一个开发代码和交流结果的绝佳平台。由于它的 Web 格式,notebook 可以通过互联网共享。它还支持 Markdown 标记语言,并将 Markdown 文本渲染为富文本,支持格式化及其他特性。

正如我们之前所见,每个 notebook 都有一个内核。这个内核是执行单元格中代码的解释器。一个 notebook 的基本单位叫做单元格。单元格是一个容器,可以容纳代码或文本。我们有两种主要类型的单元格:

  • 代码单元格

  • Markdown 单元格

代码单元格接受要在内核中执行的代码,并在其下方显示输出。Markdown 单元格接受 Markdown,当单元格执行时,会将文本解析为格式化文本。

让我们运行以下练习,获得在 Jupyter notebook 中的实际操作经验。

一个 notebook 的基本组件是单元格,单元格根据选择的模式可以接受代码或文本。

让我们启动一个 notebook,演示如何使用单元格,单元格有两种状态:

  • 编辑模式

  • 运行模式

在编辑模式下,单元格的内容可以编辑,而在运行模式下,单元格准备好执行,可以由内核执行或被解析为格式化文本。

你可以通过使用插入菜单选项或使用键盘快捷键Ctrl + B来添加新单元格。单元格可以通过菜单或快捷键Y(代码单元格)和M(Markdown 单元格)在 Markdown 模式和代码模式之间转换。

要执行一个单元格,点击运行选项或使用Ctrl + Enter快捷键。

练习 2:开始使用 Jupyter Notebook

让我们执行以下步骤,演示如何开始在 Jupyter notebook 中执行简单的程序。

第一次使用 Jupyter notebook 可能会有些困惑,但让我们尝试探索它的界面和功能。这个练习的参考 notebook 可以在 GitHub 上找到。

现在,启动一个 Jupyter notebook 服务器并按照以下步骤进行操作:

  1. 要启动 Jupyter notebook 服务器,在控制台运行以下命令:

    > jupyter notebook
    
  2. 成功运行或安装 Jupyter 后,打开浏览器窗口并访问localhost:8888来访问 notebook。

  3. 你应该能看到一个类似于以下截图的 notebook:图 1.1:Jupyter notebook

    图 1.1:Jupyter notebook
  4. 接下来,在右上角点击新建,从列表中选择Python 3

  5. 一个新的 notebook 应该会出现。首先出现的输入单元格是 代码 单元格。默认单元格类型是 代码。你可以通过 单元格 菜单下的 单元格类型 选项来更改它:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_02.jpg

    图 1.2:Jupyter 单元格菜单中的选项
  6. 现在,在新生成的 代码 单元格中,在第一个单元格中添加以下算术函数:

    In []: x = 2
           print(x*2)
    Out []: 4
    
  7. 现在,添加一个返回两个数字算术平均值的函数,然后执行该单元格:

    In []: def mean(a,b):
           return (a+b)/2
    
  8. 现在,使用 mean 函数,并用两个值 10 和 20 调用该函数。执行此单元格。会发生什么?函数被调用,答案会被打印出来:

    In []: mean(10,20)
    Out[]: 15.0
    
  9. 我们需要记录这个函数。现在,创建一个新的 Markdown 单元格,并编辑单元格中的文本,记录该函数的功能:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_03.jpg

    图 1.3:Jupyter 中的 Markdown
  10. 然后,插入一个来自网络的图片。其目的是 notebook 作为一个文档,应该记录分析的所有部分,因此有时我们需要从其他来源插入一个图表或图形来解释某个观点。

  11. 现在,最后在同一个 Markdown 单元格中插入 LaTex 数学表达式:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_04.jpg

图 1.4:Jupyter Markdown 中的 LaTex 表达式

正如我们将在本书其余部分看到的,notebook 是我们分析过程的基石。我们刚才遵循的步骤展示了不同类型单元格的使用及我们记录分析过程的不同方式。

IPython 或 Jupyter?

IPython 和 Jupyter 在分析工作流中都有其作用。通常,IPython shell 用于快速交互和更为数据密集的工作,如调试脚本或运行异步任务。而 Jupyter notebook 则非常适合用来展示结果,并通过代码、文本和图形生成可视化叙事。我们将展示的大多数示例都可以在这两者中执行,除了图形部分。

IPython 能显示图形,但通常图形的加入在 notebook 中更为自然。在本书中,我们通常使用 Jupyter notebook,但这些指令同样适用于 IPython notebook。

活动 1:IPython 和 Jupyter

让我们展示在 IPython 和 Jupyter 中常见的 Python 开发。我们将导入 NumPy,定义一个函数,并迭代结果:

  1. 打开 python_script_student.py 文件,在文本编辑器中复制其内容,粘贴到 IPython 的 notebook 中,并执行操作。

  2. 将 Python 脚本中的代码复制并粘贴到 Jupyter notebook 中。

  3. 现在,更新 xc 常量的值。然后,修改函数的定义。

    注意

    该活动的解决方案可以在第 200 页找到。

现在我们已经知道如何在 notebook 中处理函数并动态更改函数定义。当我们在探索和发现某段代码或分析的正确方法时,这非常有帮助。Notebook 支持的迭代方法在原型设计中非常高效,并且比写代码到脚本中再执行、检查结果并重新修改脚本要更快。

NumPy

NumPy (www.numpy.org) 是一个来自 Python 科学计算社区的包。NumPy 非常适合操作多维数组,并对这些数组应用线性代数函数。它还具备与 C、C++ 和 Fortran 代码集成的工具,进一步提高了性能。许多使用 NumPy 作为数值引擎的 Python 包,包括 pandas 和 scikit-learn,都是 SciPy 生态系统的一部分,专门用于数学、科学和工程领域。

要导入这个包,请打开之前活动中使用的 Jupyter notebook,并输入以下命令:

import numpy as np

基本的 NumPy 对象是 ndarray,它是一个同质的多维数组,通常由数字组成,但也可以存储通用数据。NumPy 还包含多个用于数组操作、线性代数、矩阵运算、统计学和其他领域的函数。NumPy 的一个亮点是在科学计算中,矩阵和线性代数操作非常常见。NumPy 的另一个优势是它可以与 C++ 和 FORTRAN 代码集成。NumPy 也被其他 Python 库广泛使用,如 pandas。

SciPy

SciPy (www.scipy.org) 是一个数学、科学和工程学的库生态系统。NumPy、SciPy、scikit-learn 等都是这个生态系统的一部分。它也是一个库的名称,包含了许多科学领域的核心功能。

Matplotlib

Matplotlib (matplotlib.org) 是一个用于 Python 的二维图形绘制库。它能够生成多种硬拷贝格式的图形,供互动使用。它可以使用原生 Python 数据类型、NumPy 数组和 pandas DataFrame 作为数据源。Matplotlib 支持多个后端——支持互动或文件格式输出的部分。这使得 Matplotlib 可以跨平台运行。这种灵活性还允许 Matplotlib 扩展工具包,用于生成其他类型的图形,例如地理图和 3D 图形。

Matplotlib 的交互式界面灵感来自 MATLAB 的绘图界面。可以通过 matplotlib.pyplot 模块访问。文件输出可以直接写入磁盘。Matplotlib 可以在脚本、IPython 或 Jupyter 环境中使用,也可以在 Web 服务器和其他平台中使用。Matplotlib 有时被认为是低级的,因为生成包含更多细节的图表需要多行代码。在本书中,我们将介绍一个常用于分析中的绘图工具——Seaborn 库,它是我们之前提到的扩展之一。

要导入交互式界面,请在 Jupyter notebook 中使用以下命令:

import matplotlib.pyplot as plt

要访问绘图功能,我们将在下一章中更详细地展示如何使用 Matplotlib。

Pandas

Pandas (pandas.pydata.org) 是一个广泛用于数据科学社区的数据操作和分析库。Pandas 旨在处理类似 SQL 表格和 Excel 文件的表格型或标签型数据。

我们将更详细地探讨 pandas 提供的各种操作。现在,了解两个基本的 pandas 数据结构非常重要:series,一种一维数据结构;以及数据科学的工作马——二维数据结构 DataFrame,它支持索引。

DataFrame 和 series 中的数据可以是有序的或无序的,既可以是同质的,也可以是异质的。pandas 的其他优秀功能包括轻松添加或删除行和列,以及 SQL 用户更熟悉的操作,如 GroupBy、连接、子集提取和索引列。Pandas 在处理时间序列数据方面也非常强大,具有易用且灵活的日期时间索引和选择功能。

让我们使用以下命令在之前的 Jupyter notebook 中导入 pandas:

import pandas as pd

使用 Pandas

我们将演示如何使用 pandas 进行数据操作。这个方法被作为其他数据操作工具(如 Spark)的标准,因此学习如何使用 pandas 操作数据是很有帮助的。在大数据管道中,常常将部分数据或数据样本转换为 pandas DataFrame,以应用更复杂的转换、可视化数据,或使用更精细的机器学习模型(例如 scikit-learn 库)。Pandas 在内存中进行单机操作时也非常快速。尽管数据大小与 pandas DataFrame 之间存在内存开销,但它仍然可以快速操作大量数据。

我们将学习如何应用基本操作:

  • 将数据读取到 DataFrame 中

  • 选择和过滤

  • 将函数应用于数据

  • GroupBy 和聚合

  • 可视化来自 DataFrame 的数据

让我们从将数据读取到 pandas DataFrame 开始。

读取数据

Pandas 支持多种数据格式和数据导入方式。我们从更常见的方式开始,读取一个 CSV 文件。Pandas 有一个名为 read_csv 的函数,可以用来读取 CSV 文件,无论是本地文件还是来自 URL 的文件。我们将从美国环保局(EPA)的 Socrata 开放数据计划中读取一些数据,这些数据列出了 EPA 收集的放射性物质含量。

练习 3:使用 Pandas 读取数据

一个分析师如何在没有数据的情况下开始数据分析?我们需要学习如何将数据从互联网源导入到我们的笔记本中,才能开始分析。让我们展示如何使用 pandas 从互联网源读取 CSV 数据,以便我们进行分析:

  1. 导入 pandas 库。

    import pandas as pd
    
  2. 读取汽车里程数据集,可通过以下 URL 获取:github.com/TrainingByPackt/Big-Data-Analysis-with-Python/blob/master/Lesson01/imports-85.data。将其转换为 CSV 格式。

  3. 使用列名为数据命名,通过 read_csv 函数中的 names 参数。

    Sample code : df = pd.read_csv("/path/to/imports-85.csv", names = columns)
    
  4. 使用 pandas 的 read_csv 函数并通过调用 DataFrame 的 head 方法显示前几行:

    import pandas as pd
    df = pd.read_csv("imports-85.csv")
    df.head()
    

    输出如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_05.jpg

图 1.5:汽车里程数据集的条目

Pandas 可以读取更多格式:

  • JSON

  • Excel

  • HTML

  • HDF5

  • Parquet(使用 PyArrow)

  • SQL 数据库

  • Google Big Query

尝试从 pandas 中读取其他格式,如 Excel 表格。

数据处理

数据处理是指对数据进行任何选择、转换或聚合操作。数据处理可以出于多种原因进行:

  • 选择一个数据子集进行分析

  • 清洗数据集,移除无效、错误或缺失的值

  • 将数据分组为有意义的集合并应用聚合函数

Pandas 的设计旨在让分析师以高效的方式进行这些转换。

选择与过滤

Pandas DataFrame 可以像 Python 列表一样进行切片。例如,要选择 DataFrame 的前 10 行子集,可以使用 [0:10] 语法。在以下截图中,我们看到选择 [1:3] 区间,NumPy 表示法选择了行 12

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_06.jpg

图 1.6:Pandas DataFrame 中的选择

在接下来的章节中,我们将深入探讨选择与过滤操作。

使用切片选择行

在进行数据分析时,我们通常希望查看数据在特定条件下的不同表现,例如比较几列数据、选择仅有几列帮助阅读数据,或进行绘图。我们可能想查看特定值,例如当一列具有特定值时,其他数据的表现如何。

在使用切片选择之后,我们可以使用其他方法,例如 head 方法,从数据框的开头只选择几行。但如何选择数据框中的某些列呢?

要选择一列,只需使用列名。我们将使用 notebook。使用以下命令选择数据框中的 cylinders 列:

df['State']

输出如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_07.jpg

图 1.7:显示状态的数据显示框

另一种选择方式是通过列中的特定值进行筛选。例如,假设我们想选择所有State列中值为MN的行。我们该如何做呢?尝试使用 Python 的相等运算符和数据框选择操作:

df[df.State == "MN"]

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_08.jpg

图 1.8:显示 MN 状态的数据显示框

可以同时应用多个筛选器。当组合多个筛选器时,可以使用 ORNOTAND 逻辑运算符。例如,要选择所有 State 等于 AKLocationNome 的行,请使用 & 运算符:

df[(df.State == "AK") & (df.Location == "Nome")]

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_09.jpg

图 1.9:显示状态为 AK 和位置为 Nome 的数据框

另一个强大的方法是 .loc。该方法有两个参数,一个是行选择,另一个是列选择,能够实现精细的选择。此时一个重要的注意事项是,根据所应用的操作,返回的类型可以是数据框或系列。当只选择一列时,.loc 方法返回一个系列。这是预期的,因为每个数据框列本身就是一个系列。当需要选择多个列时,也需要特别注意。为此,可以使用两个括号而不是一个,选择你想要的多个列。

练习 4:数据选择和 .loc 方法

正如我们之前看到的,选择数据、分离变量并查看感兴趣的列和行是分析过程的基础。假设我们想分析I-131明尼苏达州的辐射:

  1. 在 Jupyter notebook 中使用以下命令导入 NumPy 和 pandas 库:

    import numpy as np
    import pandas as pd
    
  2. 从 EPA 获取 RadNet 数据集,该数据集可以从 Socrata 项目的github.com/TrainingByPackt/Big-Data-Analysis-with-Python/blob/master/Lesson01/RadNet_Laboratory_Analysis.csv下载:

    url = "https://opendata.socrata.com/api/views/cf4r-dfwe/rows.csv?accessType=DOWNLOAD"
    df = pd.read_csv(url)
    
  3. 首先,使用 ['<列名>'] 符号选择一列。选择 State 列:

    df['State'].head()
    

    输出如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_10.jpg

    图 1.10:状态列中的数据
  4. 现在,使用 MN 列名筛选所选列中的值:

    df[df.State == "MN"]
    

    输出如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_11.jpg

    图 1.11:显示包含 MN 状态的数据显示框
  5. 根据条件选择多个列。为过滤添加Sample Type列:

    df[(df.State == 'CA') & (df['Sample Type'] == 'Drinking Water')]
    

    输出如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_12.jpg

    图 1.12:具有加利福尼亚州和样本类型为饮用水的 DataFrame
  6. 接下来,选择MN州和同位素I-131

    df[(df.State == "MN") ]["I-131"]
    

    输出如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_13.jpg

    图 1.13:显示具有明尼苏达州和同位素 I-131 的 DataFrame 数据

    明尼苏达州(ID 为555)的辐射值最高。

  7. 我们可以更轻松地使用.loc方法,通过州进行过滤,并在同一.loc调用中选择一列:

    df_rad.loc[df_rad.State == "MN", "I-131"]
    df[['I-132']].head()
    

    输出如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_14.jpg

图 1.14:包含 I-132 的 DataFrame

在这个练习中,我们学习了如何使用 NumPy 切片表示法或.loc方法来过滤和选择值,无论是在列还是行上。这可以帮助我们在分析数据时,仅检查和操作数据的一个子集,而不必一次性处理整个数据集。

注意

.loc筛选的结果是一个.loc对象。因为 DataFrame 可以被理解为一个二维的系列组合,选择一列将返回一个系列。为了使选择仍然返回一个 DataFrame,请使用双括号:

df[['I-132']].head()

对列应用函数

数据永远不会是干净的。在数据集可以被分析之前,总是需要做一些清理工作。数据清理中最常见的任务之一是对列应用一个函数,将值更改为更合适的值。在我们的示例数据集中,当没有测量浓度时,会插入non-detect值。由于这一列是数字型的,分析它可能会变得复杂。我们可以对该列应用转换,将non-detect更改为numpy.NaN,这使得操作数值更为简单,之后可以填充其他值,比如均值等。

要对多个列应用函数,请使用applymap方法,其逻辑与apply方法相同。例如,另一个常见的操作是去除字符串中的空格。我们可以使用applyapplymap函数来修复数据。我们还可以将函数应用于行而不是列,使用 axis 参数(0表示行,1表示列)。

活动 2:处理数据问题

在开始分析之前,我们需要检查数据问题,当我们发现问题时(这非常常见!),我们必须通过转换 DataFrame 来纠正问题。例如,可以通过对某一列或整个 DataFrame 应用函数来解决问题。通常,DataFrame 中的一些数字在读取时未能正确转换为浮点数。我们可以通过应用函数来修复这个问题:

  1. 导入pandasnumpy库。

  2. 从美国环保局读取 RadNet 数据集。

  3. 创建一个包含 RadNet 数据集中放射性核素的数字列的列表。

  4. 对单列使用apply方法,配合 lambda 函数比较Non-detect字符串。

  5. 将某一列中的文本值替换为NaN,使用np.nan

  6. 使用相同的 lambda 比较方法,并在多个列上同时使用applymap方法,使用第一步中创建的列表。

  7. 创建一个包含所有非数字列的列表。

  8. 删除这些列中的空格。

  9. 使用选择和过滤方法,验证字符串列的列名中是否还存在空格。

    注意

    该活动的解决方案可以在第 200 页找到。

列名中的空格可能是多余的,且会使选择和过滤变得更加复杂。修正数字类型对于需要使用数据计算统计值时非常有帮助。如果某个数字列中的值不合法,例如一个数字列中有字符串,统计操作将无法执行。例如,在数据输入过程中,操作员手动输入信息时可能会出错,或者存储文件从一种格式转换为另一种格式时,导致列中留下了不正确的值。

数据类型转换

数据清理中的另一个常见操作是确保数据类型正确。这有助于检测无效值并应用正确的操作。pandas 中主要存储的类型如下:

  • floatfloat64float32

  • integerint64int32

  • datetimedatetime64[ns, tz]

  • timedeltatimedelta[ns]

  • bool

  • object

  • category

数据类型可以通过 pandas 进行设置、读取或推断。通常,如果 pandas 无法检测到列的数据类型,它会假设该列是存储为字符串的对象类型。

为了将数据转换为正确的数据类型,我们可以使用转换函数,如to_datetimeto_numericastype。类别类型,即只能选择有限选项的列,会被编码为category类型。

练习 5:探索数据类型

使用 pandas 的astype函数将我们示例数据框中的数据类型转换为正确的类型。我们将使用来自opendata.socrata.com/的示例数据集:

  1. 按照此处所示导入所需的库:

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    
  2. 按如下方式读取数据集中的数据:

    url = "https://opendata.socrata.com/api/views/cf4r-dfwe/rows.csv?accessType=DOWNLOAD"
    df = pd.read_csv(url)
    
  3. 使用dtypes函数检查 DataFrame 的当前数据类型:

    df.dtypes
    
  4. 使用to_datetime方法将日期从字符串格式转换为datetime格式:

    df['Date Posted'] = pd.to_datetime(df['Date Posted'])
    df['Date Collected'] = pd.to_datetime(df['Date Collected'])
    columns = df.columns
    id_cols = ['State', 'Location', "Date Posted", 'Date Collected', 'Sample Type', 'Unit']
    columns = list(set(columns) - set(id_cols))
    columns
    

    输出结果如下:

    ['Co-60',
     'Cs-136',
     'I-131',
     'Te-129',
     'Ba-140',
     'Cs-137',
     'Cs-134',
     'I-133',
     'I-132',
     'Te-132',
     'Te-129m']
    
  5. 使用 Lambda 函数:

    df['Cs-134'] = df['Cs-134'].apply(lambda x: np.nan if x == "Non-detect" else x)
    df.loc[:, columns] = df.loc[:, columns].applymap(lambda x: np.nan if x == 'Non-detect' else x)
    df.loc[:, columns] = df.loc[:, columns].applymap(lambda x: np.nan if x == 'ND' else x)
    
  6. 使用to_numeric方法对前一个活动中创建的数字列列表应用,转换列为正确的数字类型:

    for col in columns:
        df[col] = pd.to_numeric(df[col])
    
  7. 再次检查列的数据类型。数字列应该是float64类型,日期列应该是datetime64[ns]类型:

    df.dypes
    
  8. 使用astype方法将非数字列转换为category类型:

    df['State'] = df['State'].astype('category')
    df['Location'] = df['Location'].astype('category')
    df['Unit'] = df['Unit'].astype('category')
    df['Sample Type'] = df['Sample Type'].astype('category')
    
  9. 最后一次使用dtype函数检查数据类型:

    df.dtypes
    

    输出结果如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_15.jpg

图 1.15:DataFrame 及其类型

现在我们的数据集看起来不错,所有值都已正确转换为正确的数据类型。但数据修正只是其中的一部分。作为分析师,我们希望从不同的角度理解数据。例如,我们可能想知道哪个州的污染最严重,或者哪种放射性核素在各城市中最不常见。我们可能会问数据集中有效测量的数量。所有这些问题都有一个共同点,那就是涉及对数据进行分组并聚合多个值的转换。使用 pandas,我们可以通过 GroupBy 来实现这一点。让我们看看如何按键进行分组并聚合数据。

聚合与分组

在获得数据集后,分析师可能需要回答一些问题。例如,我们知道每个城市的放射性核素浓度,但分析师可能会被要求回答:平均而言,哪个州的放射性核素浓度最高?

为了回答提出的问题,我们需要以某种方式对数据进行分组,并对其进行聚合计算。但在进入数据分组之前,我们需要准备数据集,以便能够以高效的方式进行操作。在 pandas 的 DataFrame 中获得正确的数据类型可以极大地提高性能,并有助于执行数据一致性检查—它确保数值数据确实是数值数据,并允许我们执行所需的操作来获得答案。

GroupBy 允许我们从更一般的角度查看特征,通过给定 GroupBy 键和聚合操作来排列数据。在 pandas 中,这个操作是通过 GroupBy 方法完成的,操作的列可以是例如 State。请注意 GroupBy 方法后的聚合操作。以下是一些可以应用的操作示例:

  • mean

  • median

  • std (标准差)

  • mad (均值绝对偏差)

  • sum

  • count

  • abs

    一些统计量,例如 均值标准差,只有在数值数据的情况下才有意义。

应用 GroupBy 后,可以选择特定的列并对其应用聚合操作,或者可以对所有剩余的列使用相同的聚合函数进行聚合。像 SQL 一样,GroupBy 可以同时应用于多列,并且可以对选定的列应用多个聚合操作,每列一个操作。

Pandas 中的 GroupBy 命令有一些选项,例如 as_index,可以覆盖将分组键的列转换为索引并将其保留为普通列的标准。当在 GroupBy 操作后将创建新索引时,这很有帮助,例如。

聚合操作可以在多个列上同时进行,并且使用 agg 方法可以同时应用不同的统计方法,通过传递一个字典,字典的键是列名,值是统计操作的列表。

练习 6:聚合和分组数据

记住,我们需要回答哪个州的放射性核素浓度平均值最高。由于每个州有多个城市,我们需要将一个州内所有城市的值合并并计算平均值。这是 GroupBy 的一个应用:按分组计算一个变量的平均值。我们可以使用 GroupBy 来回答这个问题:

  1. 导入所需的库:

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    
  2. opendata.socrata.com/ 加载数据集:

    df = pd.read_csv('RadNet_Laboratory_Analysis.csv')
    
  3. 使用 State 列对 DataFrame 进行分组。

    df.groupby('State')
    
  4. 选择放射性核素 Cs-134 并计算每组的平均值:

    df.groupby('State')['Cs-134'].head()
    
  5. 对所有列执行相同的操作,按州分组并直接应用 mean 函数:

    df.groupby('State').mean().head()
    
  6. 现在,根据多个列进行分组,使用一个包含多个分组列的列表。

  7. 使用 agg 方法对每一列执行多个聚合操作。使用 StateLocation 列:

    df.groupby(['State', 'Location']).agg({'Cs-134':['mean', 'std'], 'Te-129':['min', 'max']})
    

NumPy 与 Pandas

NumPy 函数可以直接应用于 DataFrame,也可以通过 applyapplymap 方法应用。其他 NumPy 函数,如 np.where,也可以与 DataFrame 一起使用。

从 Pandas 导出数据

在 pandas 中创建了中间数据集或最终数据集之后,我们可以将 DataFrame 中的值导出到其他格式。最常用的格式是 CSV,执行此操作的命令是df.to_csv('filename.csv')。其他格式,如 Parquet 和 JSON,也受到支持。

注意

Parquet 格式特别有趣,它是我们将在本书后面讨论的一个大数据格式之一。

练习 7:导出不同格式的数据

完成分析后,我们可能想保存我们的转换数据集并包含所有的修正,这样如果我们想分享这个数据集或重新进行分析,就不必再次转换数据集。我们还可以将分析作为更大数据管道的一部分,甚至将准备好的数据用作机器学习算法的输入。我们可以通过将 DataFrame 导出到正确格式的文件来实现数据导出:

  1. 导入所有必要的库并使用以下命令从数据集中读取数据:

    import numpy as np
    import pandas as pd
    url = "https://opendata.socrata.com/api/views/cf4r-dfwe/rows.csv?accessType=DOWNLOAD"
    df = pd.read_csv(url)
    

    对 RadNet 数据中的数据类型(日期、数值、类别)重新进行所有调整。类型应与 练习 6:聚合和分组数据 中的相同。

  2. 选择数值型列和类别型列,为每个列创建一个列表:

    columns = df.columns
    id_cols = ['State', 'Location', "Date Posted", 'Date Collected', 'Sample Type', 'Unit']
    columns = list(set(columns) - set(id_cols))
    columns
    

    输出如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_16.jpg

    图 1.16:列列表
  3. 应用替换 Non-detectnp.nan 的 lambda 函数:

    df['Cs-134'] = df['Cs-134'].apply(lambda x: np.nan if x == "Non-detect" else x)
    df.loc[:, columns] = df.loc[:, columns].applymap(lambda x: np.nan if x == 'Non-detect' else x)
    df.loc[:, columns] = df.loc[:, columns].applymap(lambda x: np.nan if x == 'ND' else x)
    
  4. 删除类别列中的空格:

    df.loc[:, ['State', 'Location', 'Sample Type', 'Unit']] = df.loc[:, ['State', 'Location', 'Sample Type', 'Unit']].applymap(lambda x: x.strip())
    
  5. 将日期列转换为 datetime 格式:

    df['Date Posted'] = pd.to_datetime(df['Date Posted'])
    df['Date Collected'] = pd.to_datetime(df['Date Collected'])
    
  6. 使用 to_numeric 方法将所有数值型列转换为正确的数值格式:

    for col in columns:
        df[col] = pd.to_numeric(df[col])
    
  7. 将所有类别变量转换为category类型:

    df['State'] = df['State'].astype('category')
    df['Location'] = df['Location'].astype('category')
    df['Unit'] = df['Unit'].astype('category')
    df['Sample Type'] = df['Sample Type'].astype('category')
    
  8. 使用to_csv函数将我们转换后的 DataFrame 导出为 CSV 格式,确保包含正确的值和列。通过index=False排除索引,使用分号作为分隔符sep=";",并将数据编码为 UTF-8 格式encoding="utf-8"

    df.to_csv('radiation_clean.csv', index=False, sep=';', encoding='utf-8')
    
  9. 使用to_parquet方法将相同的 DataFrame 导出为 Parquet 列式和二进制格式:

    df.to_parquet('radiation_clean.prq', index=False)
    
    注意

    转换 datetime 为字符串时要小心!

使用 Pandas 进行可视化

Pandas 可以被看作是一个数据的瑞士军刀,而数据科学家在分析数据时总是需要的一项技能就是可视化数据。我们将在后续详细介绍可应用于分析的各种图表类型。现在的目标是展示如何直接从 pandas 创建快速且简单的图表。

plot函数可以直接从 DataFrame 选择调用,实现快速可视化。通过使用 Matplotlib 并将数据从 DataFrame 传递到绘图函数,可以创建散点图。现在我们了解了工具,接下来让我们专注于 pandas 的数据处理接口。这个接口非常强大,其他一些项目(如 Spark)也复制了它。我们将在下一章更详细地解释图表的组成部分和方法。

你将在下一章看到如何创建对统计分析有用的图表。在这里,重点是了解如何从 pandas 创建图表以进行快速可视化。

活动 3:使用 Pandas 绘制数据

为了完成我们的活动,让我们重新做一遍之前的所有步骤,并用结果绘制图表,就像在初步分析中所做的那样:

  1. 使用我们之前处理过的 RadNet DataFrame。

  2. 修复所有的数据类型问题,正如我们之前所看到的。

  3. 创建一个按Location过滤的图表,选择San Bernardino城市,并选择一个放射性核素,x-轴为日期,y-轴为放射性核素I-131https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_17.jpg

    图 1.17:带 I-131 的地点图表
  4. 创建一个散点图,显示两个相关放射性核素I-131I-132的浓度:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_18.jpg

图 1.18:I-131 和 I-132 的图表
注意

本活动的解决方案可以在 203 页找到。

我们在这里稍微有些超前了,因此不必担心图表的细节,或者如何设置标题、标签等。这里的关键是理解我们可以直接从 DataFrame 绘制图表进行快速分析和可视化。

摘要

我们已经了解了数据分析和数据科学中最常用的 Python 库,它们组成了 Python 数据科学栈。我们学习了如何获取数据、选择数据、过滤数据并进行聚合。我们还学习了如何导出分析结果并生成一些快速图表。

这些步骤几乎适用于任何数据分析。这里展示的思路和操作可以应用于大数据的数据处理。Spark DataFrame 的创建考虑了 pandas 接口,许多操作在 pandas 和 Spark 中以非常相似的方式执行,这大大简化了分析过程。掌握 pandas 的另一个巨大优势是,Spark 可以将其 DataFrame 转换为 pandas DataFrame,然后再转换回来,使分析师能够使用最适合任务的工具。

在进入大数据之前,我们需要了解如何更好地可视化分析结果。如果我们使用正确的图表来可视化数据,我们对数据及其行为的理解可以大大增强。通过绘制数据,我们能够做出推断,并观察到异常和模式。

在下一章,我们将学习如何为每种数据和分析选择合适的图表,并如何使用 Matplotlib 和 Seaborn 绘制它。

第三章:第二章

统计可视化

学习目标

我们将从理解 Python 在数据处理和可视化方面的强大能力开始,创造出有用的分析结果。

本章结束时,你将能够:

  • 使用图表进行数据分析

  • 创建各种类型的图表

  • 更改图表参数,如颜色、标题和轴

  • 导出图表以用于展示、打印及其他用途

在本章中,我们将演示学生如何使用 Matplotlib 和 Seaborn 生成可视化图表。

引言

在上一章中,我们学习了最常用于数据科学的 Python 库。虽然这些库本身并不是大数据库,但 Python 数据科学堆栈的库(NumPyJupyterIPythonPandasMatplotlib)在大数据分析中非常重要。

正如本章所演示的,任何分析都离不开可视化,哪怕是大数据集也不例外,因此,掌握如何从数据中生成图像和图表,对于我们大数据分析的目标非常重要。在接下来的章节中,我们将演示如何处理大量数据并使用 Python 工具进行聚合和可视化。

Python 有多个可视化库,如 Plotly、Bokeh 等。但其中最古老、最灵活、使用最广泛的就是 Matplotlib。在详细讲解如何使用 Matplotlib 创建图表之前,我们先来了解哪些类型的图表对于分析是相关的。

图表的类型及其使用时机

每一次分析,无论是针对小型数据集还是大型数据集,都涉及到一个描述性统计步骤,在这个步骤中,数据通过均值、中位数、百分比和相关性等统计量进行汇总和描述。这个步骤通常是分析工作流程中的第一步,它有助于初步理解数据及其一般模式和行为,为分析师提出假设提供基础,并指导分析的下一步。图表是帮助此步骤的有力工具,它使分析师能够可视化数据,创建新的视图和概念,并将其传达给更广泛的受众。

关于可视化信息的统计学文献浩如烟海。Edward Tufte 的经典书籍《Envisioning Information》展示了如何以图形形式呈现信息的美丽而有用的例子。在另一本书《The Visual Display of Quantitative Information》中,Tufte 列举了一个用于分析和传递信息(包括统计数据)的图表应该具备的几个特征:

  • 显示数据

  • 避免扭曲数据所传达的信息

  • 使大型数据集具有可理解性

  • 具有合理清晰的目的——描述、探索、表格化或装饰

图表必须揭示信息。在创建分析时,我们应牢记这些原则来创建图表。

一个图表还应该能够独立于分析而突出表现。假设你正在撰写一份分析报告,这份报告变得非常详细。现在,我们需要对这份详细的分析做一个总结。为了使分析的要点清晰,图表可以用来表示数据。这个图表应该能够在没有整个详细分析的情况下支持这个总结。为了让图表能提供更多信息,并且能够在总结中独立存在,我们需要为它添加更多信息,例如标题和标签。

练习 8:绘制分析函数

在这个练习中,我们将使用 Matplotlib 库创建一个基本的图表,我们将可视化一个二元函数,例如,y = f(x),其中 f(x)

  1. 首先,创建一个新的 Jupyter Notebook 并导入所有必需的库:

    %matplotlib inline
    import pandas as pd
    import numpy as np
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    
  2. 现在,让我们生成一个数据集并使用以下代码绘制它:

    x = np.linspace(-50, 50, 100)
    y = np.power(x, 2)
    
  3. 使用以下命令创建一个基本的 Matplotlib 图表:

    plt.plot(x, y)
    

    输出结果如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_01.jpg

    图 2.1:X 轴和 Y 轴的基本图表
  4. 现在,将数据生成函数从 修改为 ,保持相同的区间 [-50,50],并重新绘制线图:

    y_hat = np.power(x, 3)
    plt.plot(x, y_hat)
    

    输出结果如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_02.jpg

图 2.2:X 轴和 Y 轴的基本图表

如你所见,函数的形状发生了变化,正如预期的那样。我们使用的基本图表类型足以看到 yy_hat 值之间的变化。但仍然有一些问题:我们只绘制了一个数学函数,但通常我们收集的数据是有维度的,比如长度、时间和质量。我们如何将这些信息添加到图表中?我们如何添加标题?让我们在下一节中探讨这个问题。

图表的组成部分

每个图表都有一组可以调整的公共组成部分。Matplotlib 使用的这些组件名称将在下图中展示:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_03.jpg

图 2.3:图表的组成部分

图表的组成部分如下:

  • 图形:图表的基础,所有其他组件都绘制在这里。

  • 坐标轴:包含图形元素并设置坐标系统。

  • 标题:标题给图表命名。

  • X 轴标签x 轴的名称,通常带有单位。

  • Y 轴标签y 轴的名称,通常带有单位。

  • 图例:图例是对图表中数据的描述,帮助你识别图中的曲线和点。

  • 刻度和刻度标签:它们表示图表中刻度上的参考点,数据值所在的位置。标签则表示具体的数值。

  • 线图:这些是与数据一起绘制的线条。

  • 标记:标记是用来标示数据点的图形符号。

  • 坐标轴:限定图表区域的线条,数据将在该区域中绘制。

这些组件中的每一个都可以根据当前可视化任务的需求进行配置。我们将逐一介绍每种图形类型以及如何调整之前描述的组件。

练习 9:创建一个图形

使用 Matplotlib 创建图形有多种方式。第一种方式与 MATLAB 的方法非常相似,称为 Pyplot。Pyplot 是一个 API,是 Matplotlib 的基于状态的接口,这意味着它会将配置和其他参数保存在对象中。Pyplot 被设计为一种简单的用法。

执行以下步骤,使用 Matplotlib 库绘制正弦函数图形:

  1. 导入所有需要的库,就像我们在前面的练习中做的那样:

    %matplotlib inline
    import pandas as pd
    import numpy as np
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    

    第二个 API,称为 plt.subplots 模块。

  2. 现在,要获取图形和坐标轴,请使用以下命令:

    fig, ax = plt.subplots()
    
    注意

    图形是所有其他图形组件的顶层容器。坐标轴设置了诸如轴、坐标系统等内容,并包含了图形元素,如线条、文本等。

  3. 要将一个图形添加到使用面向对象 API 创建的图表中,请使用以下命令:

    x = np.linspace(0,100,500)
    y = np.sin(2*np.pi*x/100)
    ax.plot(x, y)
    

    输出结果如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_04.jpg

图 2.4:使用面向对象 API 绘制的图形输出

我们正在将一条线性图添加到属于 fig 图形的 ax 坐标轴中。图形的修改,如标签名称、标题等,将在本章稍后演示。现在,让我们看看如何创建我们可能在分析中使用的每种类型的图形。

练习 10:为数学函数创建图形

练习 1:绘制一个解析函数中,我们使用类似 MATLAB 的接口 Pyplot 为一个数学函数创建了图表。现在我们已经知道如何使用 Matplotlib 的面向对象 API,让我们使用它创建一个新图表。当使用面向对象 API 时,分析人员可以根据数据来源灵活地创建图表。

让我们使用面向对象 API 和 NumPy 的 sine 函数,在区间 [0,100] 上创建一个图形:

  1. 创建 x 轴的数据点:

    import numpy as np
    x = np.linspace(0,100,200)
    y = np.sin(x)
    
  2. 创建 Matplotlib 的 API 接口:

    %matplotlib inline
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    
  3. 使用坐标轴对象 ax 添加图表:

    ax.plot(x, y)
    

    输出结果如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_05.jpg

图 2.5:数学函数的图形

请注意,我们再次使用 linspace 函数在线性区间 [0, 100] 上创建了 200 个值,然后对这些值应用了 sine 函数,生成了 y 轴。这是在创建数据区间时常用的一种方法。

Seaborn

Seaborn (seaborn.pydata.org/) 是 PyData 工具家族的一部分,是一个基于 Matplotlib 的可视化库,旨在更轻松地创建统计图形。它可以直接操作 DataFrame 和 Series,进行内部的聚合和映射。Seaborn 使用颜色调色板和样式,使可视化更加一致且信息更丰富。它还具有一些可以计算统计数据的函数,例如回归、估计和误差。Seaborn 也能轻松创建一些特殊的图形,如小提琴图和多面图。

应该使用哪个工具?

Seaborn 尝试使一些常见分析图表的创建比直接使用 Matplotlib 更加简单。Matplotlib 可以被认为比 Seaborn 更低级,虽然这使得它有些繁琐和冗长,但它为分析师提供了更大的灵活性。有些图表,在 Seaborn 中只需调用一个函数即可创建,而在 Matplotlib 中可能需要几行代码才能实现。

没有明确的规则来确定分析师应该只使用 pandas 的绘图接口、直接使用 Matplotlib 还是使用 Seaborn。分析师应当牢记可视化需求以及创建所需图形所需的配置级别。

Pandas 的绘图接口更易于使用,但功能较为受限和局限。Seaborn 提供了多种现成的图表模式,包括常见的统计图表,如配对图和箱型图,但要求数据必须以整洁的格式进行整理,并且对图表的外观有较为明确的意见。Matplotlib 是两者的基础,比这两者更灵活,但要创建与其他两者相同的可视化效果,需要写更多的代码。

本书中的经验法则是:如何用最少的代码、且不改变数据的情况下,创建我需要的图形?有了这个原则,我们将使用三种选择,有时同时使用它们,以达到我们的可视化目标。分析师不应该局限于使用其中一种选项。我们鼓励使用任何能够创建有意义可视化的工具。

让我们来了解统计分析中最常见的几种图表类型。

图形类型

我们将展示的第一种图形是折线图线性图。折线图通过在两个坐标轴(xy)上连接的点来显示数据,通常是笛卡尔坐标系,并通常按 x 轴顺序排列。折线图用于展示数据的趋势,比如时间序列数据。

与折线图相关的图形是散点图。散点图通过笛卡尔坐标系将数据表示为点。通常,这个图表展示两个变量,尽管如果通过类别对数据进行颜色编码或大小编码,可能会传达更多的信息。散点图对于展示变量之间的关系和可能的相关性非常有用。

直方图用于表示数据的分布。与前两个示例不同,直方图通常只显示一个变量,通常在 x-轴上,而 y-轴显示数据的发生频率。创建直方图的过程比折线图和散点图要复杂一些,因此我们将更详细地解释它们。

箱线图也可用于表示频率分布,但它有助于通过一些统计量,如均值、中位数和标准差,比较数据组。箱线图用于可视化数据分布和异常值。

每种图表类型都有其应用,选择正确的图表类型对于分析的成功至关重要。例如,折线图可以用于展示过去一个世纪的经济增长趋势,而箱线图则很难用于此类分析。另一个常见的数据分析任务是识别变量之间的相关性:理解两个变量是否表现出相关的行为。散点图是常用的可视化工具。直方图对于可视化某个范围或区间内的数据点数量很有用,例如显示每加仑油耗在 10 到 20 英里之间的汽车数量。

折线图

如前一节所述,折线图通过连接数据点来展示数据。折线图非常适合展示趋势和倾向。在同一图表上可以绘制多条折线,用于比较各条线的行为,尽管必须确保图表上的单位一致。折线图还可以展示自变量和因变量之间的关系。一个常见的例子就是时间序列。

时间序列图

时间序列图顾名思义,用于展示数据相对于时间的变化。时间序列图在金融领域和环境科学中使用频繁。例如,以下图表展示了历史温度异常的时间序列:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_06.jpg

图 2.6: 时间序列图
来源

upload.wikimedia.org/wikipedia/commons/c/c1/2000_Year_Temperature_Comparison.png

通常,时间序列图的 time 变量位于 x-轴。

练习 11: 使用不同库创建折线图

让我们比较 Matplotlib、Pandas 和 Seaborn 之间的创建过程。我们将创建一个包含随机值的 Pandas DataFrame,并使用不同的方法进行绘制:

  1. 创建一个包含随机值的数据集:

    import numpy as np
    X = np.arange(0,100)
    Y = np.random.randint(0,200, size=X.shape[0])
    
  2. 使用 Matplotlib Pyplot 接口绘制数据:

    %matplotlib inline
    import matplotlib.pyplot as plt
    plt.plot(X, Y)
    
  3. 现在,让我们使用创建的值创建一个 Pandas DataFrame:

    import pandas as pd
    df = pd.DataFrame({'x':X, 'y_col':Y})
    
  4. 使用 Pyplot 接口绘制图表,但需要使用 data 参数:

    plt.plot('x', 'y_col', data=df)
    

    输出结果如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_07.jpg

    图 2.7: 使用不同库的折线图
  5. 使用相同的 DataFrame,我们还可以直接从 Pandas DataFrame 绘制:

    df.plot('x', 'y_col')
    

    输出结果如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_08.jpg

    图 2.8:来自 pandas DataFrame 的折线图
  6. 那么 Seaborn 怎么样?我们来使用 Seaborn 绘制相同的折线图:

    import seaborn as sns
    sns.lineplot(X, Y)
    sns.lineplot('x', 'y_col', data=df)
    

    输出结果如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_09.jpg

图 2.9:来自 Seaborn DataFrame 的折线图

我们可以看到,在这种情况下,Matplotlib 和 Seaborn 使用的接口非常相似。

Pandas DataFrames 和分组数据

正如我们在上一章中学习的,当分析数据并使用 Pandas 时,我们可以使用 Pandas 的 plot 函数或直接使用 Matplotlib。Pandas 在后台使用 Matplotlib,因此集成得非常好。根据具体情况,我们可以直接从 pandas 绘制,或者使用 Matplotlib 创建 figureaxes,并将其传递给 pandas 来绘制。例如,当进行 GroupBy 时,我们可以根据 GroupBy 键将数据分开。那么如何绘制 GroupBy 的结果呢?我们有几种方法可以选择。例如,如果 DataFrame 已经是正确格式,我们可以直接使用 pandas:

注意

以下代码是示例,无法执行。

fig, ax = plt.subplots()
df = pd.read_csv('data/dow_jones_index.data')
df[df.stock.isin(['MSFT', 'GE', 'PG'])].groupby('stock')['volume'].plot(ax=ax)

或者,我们可以将每个 GroupBy 键绘制在同一个图上:

fig, ax = plt.subplots()
df.groupby('stock').volume.plot(ax=ax)

在以下活动中,我们将使用上一章中学到的内容,从 URL 读取 CSV 文件并解析它。数据集是 Auto-MPG 数据集(raw.githubusercontent.com/TrainingByPackt/Big-Data-Analysis-with-Python/master/Lesson02/Dataset/auto-mpg.data)。

注意

该数据集是从 StatLib 库提供的数据集修改而来的。原始数据集可以在 auto-mpg.data-original 文件中找到。

数据涉及城市循环的每加仑燃油消耗,包含三种多值离散属性和五种连续属性。

活动 4:使用面向对象 API 和 Pandas DataFrame 绘制折线图

在本活动中,我们将从 Auto-MPG 数据集中创建一个时间序列折线图,作为使用 pandas 和面向对象 API 绘图的第一个示例。这种类型的图在分析中非常常见,帮助回答诸如“平均马力是否随着时间的推移增加或减少?”的问题。

现在,按照这些步骤使用 pandas 和面向对象 API 绘制每年平均马力的图:

  1. 将所需的库和包导入 Jupyter 笔记本。

  2. 将 Auto-MPG 数据集读入 Spark 对象。

  3. 提供列名以简化数据集,如下所示:

    column_names = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'year', 'origin', 'name']
    
  4. 现在读取带列名的新数据集并显示它。

  5. horsepoweryear 数据类型转换为浮动和整数。

  6. 现在使用 pandas 绘制每年平均马力的图:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image39479.jpg

图 2.10:使用面向对象 API 和 Pandas DataFrame 绘制的折线图
注意

此活动的解答可以在第 205 页找到。

请注意,我们使用的是来自 pandas 的绘图函数,但将我们通过 Matplotlib 创建的轴直接作为参数传递给这些函数。如前一章所述,虽然这不是必需的,但它可以让你在 pandas 之外配置图形,并在之后更改其配置。这种行为也适用于其他类型的图形。现在让我们来处理散点图。

散点图

为了理解两个变量之间的相关性,通常使用散点图,因为它们可以让我们看到点的分布。使用 Matplotlib 创建散点图与创建线图类似,但我们使用的是 scatter 方法,而不是 plot 方法。

让我们来看一个使用 Auto-MPG 数据集的例子(archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/):

fig, ax = plt.subplots()
ax.scatter(x = df['horsepower'], y=df['weight'])

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_11.jpg

图 2.11:使用 Matplotlib 库绘制的散点图

请注意,我们是直接从轴上调用散点图方法的。在 Matplotlib 的术语中,我们向属于 fig 图形的轴 ax 添加了一个散点图。我们还可以通过 Seaborn 很容易地向图中添加更多维度,比如 color 和点的 size

import seaborn as sns
sns.scatterplot(data=df, x='horsepower', y='weight', hue='cylinders', size='mpg')

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_12.jpg

图 2.12:使用 Seaborn 库绘制的散点图

如我们所见,散点图对于理解两个变量,甚至更多变量之间的关系非常有帮助。我们可以推断出,例如,horsepower(马力)和 weight(重量)之间存在正相关。通过散点图,我们还可以轻松地看到一个异常值,而使用其他类型的图形时,可能会更复杂。我们在折线图中看到的关于分组数据和 pandas DataFrame 的相同原则也适用于散点图。

我们可以通过 kind 参数直接从 pandas 生成散点图:

df.plot(kind='scatter', x='horsepower', y='weight')

创建一个图形并将其传递给 Pandas:

fig, ax = plt.subplots()
df.plot(kind='scatter', x='horsepower', y='weight', ax =ax)

活动 5:使用散点图理解变量之间的关系

为了继续我们的数据分析并学习如何绘制数据,让我们来看一个散点图可以帮助解答的问题。例如,使用散点图回答以下问题:

马力与重量之间是否存在关系?

为了回答这个问题,我们需要使用 Auto-MPG 数据创建一个散点图:

  1. 使用已经导入的 Auto-MPG 数据集。

    注意

    请参考前面的练习,了解如何导入数据集。

  2. 使用 Matplotlib 的面向对象 API:

    %matplotlib inline
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    
  3. 使用 scatter 方法创建一个散点图:

    ax.scatter(x = df['horsepower'], y=df['weight'])
    
    注意

    此活动的解答可以在第 208 页找到。

我们可以识别出马力与重量之间的大致线性关系,其中有一些异常值,它们的马力较高但重量较低。这种图形有助于分析师解读数据的行为。

直方图

直方图与我们迄今所见的图形稍有不同,因为它们只尝试可视化一个变量的分布,而不是两个或多个。直方图的目标是可视化一个变量的概率分布,换句话说,就是计算某些值在固定区间(或箱子)内出现的次数。

区间是连续且相邻的,但不需要具有相同的大小,尽管这种安排最为常见。

区间数和区间大小的选择更多依赖于数据和分析目标,而非任何固定的通用规则。区间数越大,每个区间的大小就越小(更窄),反之亦然。例如,当数据有很多噪声或变化时,少量的区间(较大的区间)可以显示数据的总体轮廓,从而减少噪声在初步分析中的影响。当数据的密度较高时,更多的区间会更有用。

练习 12:创建马力分布的直方图

在我们努力理解数据时,现在我们想看看所有汽车的马力分布。具有适当直方图的分析问题包括:一个变量的最频繁值是多少?分布是集中在中间还是有尾巴?让我们绘制一个马力分布的直方图:

  1. 在 Jupyter 笔记本中导入所需的库,并从 Auto-MPG 数据集仓库读取数据集:

    import pandas as pd
    import numpy as np
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    import seaborn as sns
    url = "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data"
    df = pd.read_csv(url)
    
  2. 提供列名称以简化数据集,如下所示:

    column_names = ['mpg', 'Cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'year', 'origin', 'name']
    
  3. 现在读取带有列名的新数据集并显示它:

    df = pd.read_csv(url, names= column_names, delim_whitespace=True)
    df.head()
    

    图形如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_13.jpg

    图 2.13:自动 mpg 数据集
  4. 使用以下命令将horsepower(马力)和year(年份)数据类型转换为浮动和整数:

    df.loc[df.horsepower == '?', 'horsepower'] = np.nan
    df['horsepower'] = pd.to_numeric(df['horsepower'])
    df['full_date'] = pd.to_datetime(df.year, format='%y')
    df['year'] = df['full_date'].dt.year
    
  5. 使用plot函数和kind='hist'从 Pandas DataFrame 直接创建图形:

    df.horsepower.plot(kind='hist')
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_14.jpg

    图 2.14:直方图图
  6. 识别horsepower(马力)浓度:

    sns.distplot(df['weight'])
    

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image39528.jpg

图 2.15:直方图浓度图

我们可以从这张图中看到,值的分布偏向左侧,比如在50100马力之间的汽车更多,而大于200马力的汽车较少。这对于理解一些数据在分析中的变化可能非常有用。

箱型图

箱型图也用于查看值的变化,但现在是每列内的变化。我们希望看到当按另一个变量分组时,值如何比较。例如,由于它们的格式,箱型图有时被称为胡须图箱型胡须图,因为从主框中垂直延伸出的线条:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_16.jpg

图 2.16:箱型图
来源

en.wikipedia.org/wiki/File:Michelsonmorley-boxplot.svg

箱型图使用四分位数(第一和第三四分位数)来创建箱子和须。箱子中的线是第二四分位数——即中位数。须的定义可以有所不同,例如使用数据的平均值上下一个标准差,但通常使用 1.5 倍的四分位差(Q3 - Q1)从箱子的边缘开始。如果某个值超出这些范围,不论是上方还是下方,都将绘制为一个,通常被认为是异常值。

练习 13:使用箱型图分析缸数和马力的关系

有时我们不仅希望看到每个变量的分布,还希望看到所关注的变量相对于另一个属性的变化。例如,我们想知道给定缸数时,马力如何变化。让我们使用 Seaborn 创建一个箱型图,将马力分布与缸数进行比较:

  1. 在 Jupyter Notebook 中导入所需的库并从 Auto-MPG 数据集仓库读取数据集:

    %matplotlib inline
    import pandas as pd
    import numpy as np
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    import seaborn as sns
    url = "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data"
    df = pd.read_csv(url)
    
  2. 提供列名以简化数据集,如下所示:

    column_names = ['mpg', 'Cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'year', 'origin', 'name']
    
  3. 现在读取带有列名的新数据集并显示它:

    df = pd.read_csv(url, names= column_names, delim_whitespace=True)
    df.head()
    

    图形如下所示:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_17.jpg

    图 2.17:auto-mpg 数据集
  4. 使用以下命令将马力和年份的数据类型转换为浮动型和整数型:

    df.loc[df.horsepower == '?', 'horsepower'] = np.nan
    df['horsepower'] = pd.to_numeric(df['horsepower'])
    df['full_date'] = pd.to_datetime(df.year, format='%y')
    df['year'] = df['full_date'].dt.year
    
  5. 使用 Seaborn 的 boxplot 函数创建一个箱型图:

    sns.boxplot(data=df, x="cylinders", y="horsepower")
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_18.jpg

    图 2.18:使用 Seaborn 箱型图函数
  6. 现在,为了对比目的,直接使用 pandas 创建相同的boxplot

    df.boxplot(column='horsepower', by='cylinders')
    

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_19.jpg

图 2.19:使用 pandas 的箱型图

在分析方面,我们可以看到,3 缸的马力变动范围小于 8 缸。我们还可以看到,6 缸和 8 缸的数据显示了异常值。至于绘图,Seaborn 函数更为完整,自动为不同的缸数显示不同的颜色,并将 DataFrame 列的名称作为标签显示在图表中。

更改图形设计:修改图形组件

到目前为止,我们已经查看了用于分析数据的主要图表,无论是直接展示还是按组展示,用于比较和趋势可视化。但我们可以看到的一点是,每个图形的设计都与其他图形不同,我们没有基本的元素,比如标题和图例。

我们已经了解到,图形由多个组件组成,例如图形标题xy 标签等。在使用 Seaborn 时,图形已经有了xy 标签,并且列的名称被作为标签显示。而使用 Matplotlib 时,我们没有这些。这样的变化不仅仅是外观上的。

除了标签和标题外,通过调整线宽、颜色和点大小等因素,我们可以大大改善图形的理解性。一个图形必须能够独立存在,因此标题、图例和单位至关重要。我们如何应用前面描述的概念,在 Matplotlib 和 Seaborn 中制作出好的、富有信息的图形呢?

绘图的配置方式多种多样,选择极为丰富。Matplotlib 在配置方面功能强大,但也牺牲了简便性。在使用 Matplotlib 更改图形的某些基本参数时可能会显得笨重,而这时 Seaborn 和其他库可以提供帮助。但在某些情况下,这种方式是值得的,例如在自定义图形时,因此在技术栈中具备此类能力是必要的。我们将在本节中重点讨论如何更改一些基本的绘图参数。

坐标轴对象的标题和标签配置

如前所述,Matplotlib 的面向对象 API 提供了更大的灵活性。让我们在接下来的练习中探讨如何配置坐标轴对象的标题和标签。

练习 14:配置坐标轴对象的标题和标签

执行以下步骤以配置标题和坐标轴对象的标签。我们将从上一个练习继续,并按照这些步骤进行操作:

  1. 通过调用 set 方法设置 title、x 轴标签和 y 轴标签:

    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    ax.set(title="Graph title", xlabel="Label of x axis (units)", ylabel="Label of y axis (units)")
    ax.plot()
    

    绘图如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_20.jpg

    图 2.20:配置标题和标签
  2. 图例可以通过外部传递(仅使用 Matplotlib 时),也可以在 Pandas 绘图时设置,并与坐标轴一起绘制。使用以下命令绘制图例:

    fig, ax = plt.subplots()
    df.groupby('year')['horsepower'].mean().plot(ax=ax, label='horsepower')
    ax.legend()
    

    绘图如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_21.jpg

    图 2.21:带有图例的折线图
  3. 绘制图例的替代方法如下:

    fig, ax = plt.subplots()
    df.groupby('year')['horsepower'].mean().plot(ax=ax)
    ax.legend(['horsepower'])
    

    绘图如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_22.jpg

图 2.22:带有图例的折线图(替代方法)

线条样式和颜色

对于折线图,可以通过 lslwmarkercolor 参数来配置线条的颜色、粗细、标记和样式:

df.groupby('year')['horsepower'].mean().plot(ls='-.', color='r', lw=3)

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_23.jpg

图 2.23:带有颜色和样式的折线图

图形大小

我们还可以配置图形的大小。figsize 参数可以作为一个元组(x 轴,y 轴)传递给所有绘图函数,单位为英寸:

df.plot(kind='scatter', x='weight', y='horsepower', figsize=(20,10))

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_24.jpg

图 2.24:更大图形大小的绘图

练习 15:使用 Matplotlib 样式表

Matplotlib 有一些样式表,定义了图形的一般规则,如背景 颜色刻度线图形颜色调色板。假设我们想更改样式,以便我们的图形在打印时具有更好的颜色。为此,按照以下步骤操作:

  1. 首先使用以下命令打印可用样式列表:

    import matplotlib.pyplot as plt
    print(plt.style.available)
    

    输出如下:

    ['bmh', 'classic', 'dark_background', 'fast', 'fivethirtyeight', 'ggplot', 'grayscale', 'seaborn-bright', 'seaborn-colorblind', 'seaborn-dark-palette', 'seaborn-dark', 'seaborn-darkgrid', 'seaborn-deep', 'seaborn-muted', 'seaborn-notebook', 'seaborn-paper', 'seaborn-pastel', 'seaborn-poster', 'seaborn-talk', 'seaborn-ticks', 'seaborn-white', 'seaborn-whitegrid', 'seaborn', 'Solarize_Light2', 'tableau-colorblind10', '_classic_test']
    
  2. 现在,让我们使用 classic 样式创建一个散点图。在继续之前,确保先导入 Matplotlib 库:

    %matplotlib inline
    import numpy as np
    import matplotlib.pyplot as plt
    url = ('https://raw.githubusercontent.com/TrainingByPackt/Big-Data-Analysis-with-Python/master/Lesson02/Dataset/auto-mpg.data')
    df = pd.read_csv(url)
    column_names = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'year', 'origin', 'name']
    df = pd.read_csv(url, names= column_names, delim_whitespace=True)
    df.loc[df.horsepower == '?', 'horsepower'] = np.nan
    df['horsepower'] = pd.to_numeric(df['horsepower'])
    plt.style.use(['classic'])
    df.plot(kind='scatter', x='weight', y='horsepower')
    

    输出如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image39625.jpg

图 2.25:使用 classic 样式的散点图
注意

要使用样式表,请使用以下命令:

plt.style.use('presentation')

Seaborn 在导入时所做的其中一项更改是将一些样式添加到可用样式列表中。样式在为不同受众创建图像时也非常有用,例如为笔记本中的可视化创建一个样式,为打印或演示创建另一个样式。

导出图表

在生成可视化并配置好细节后,我们可以将图表导出为硬拷贝格式,如 PNG、JPEG 或 SVG。如果我们在笔记本中使用交互式 API,我们只需在 pyplot 接口上调用 savefig 函数,最后生成的图表将被导出到文件:

df.plot(kind='scatter', x='weight', y='horsepower', figsize=(20,10))
plt.savefig('horsepower_weight_scatter.png')

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_26.jpg

图 2.26:导出图表

所有的图表配置将传递到 plot。要在使用面向对象的 API 时导出图表,我们可以从图形中调用 savefig

fig, ax = plt.subplots()
df.plot(kind='scatter', x='weight', y='horsepower', figsize=(20,10), ax=ax)
fig.savefig('horsepower_weight_scatter.jpg')

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_27.jpg

图 2.27:保存图表

我们可以更改一些保存图像的参数:

  • dpi:调整保存的图像分辨率。

  • facecolor:图形的面颜色。

  • edgecolor:图形周围的边框颜色。

  • format:通常为 PNG、PDF、PS、EPS、JPG 或 SVG。根据文件名扩展名推断。

Seaborn 也使用相同的底层 Matplotlib 机制来保存图形。直接从 Seaborn 图表中调用 savefig 方法:

sns_scatter = sns.scatterplot(data=df, x='horsepower', y='weight', hue='cylinders', size='mpg')
plt.savefig('scatter_fig.png', dpi=300)

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_28.jpg

图 2.28:使用 savefig 方法绘制图表

通过这些补充选项,分析人员可以为不同的受众生成可视化内容,无论是在笔记本中、网站上,还是打印出来。

活动 6:将图表导出到磁盘文件

将我们的工作保存到文件中是一个很好的方法,可以使结果在不同媒介中共享。如果我们想要将其留作未来参考,这也是一个好方法。让我们创建一个图表并保存到磁盘:

  1. 导入 Auto-MPG 数据集。

  2. 使用 Matplotlib 面向对象的 API 创建任何类型的图表。例如,这里是一个体重的直方图:

    %matplotlib inline
    import matplotlib.pyplot
    fig, ax = plt.subplots()
    df.weight.plot(kind='hist', ax=ax)
    
  3. 使用 savefig 函数将其导出为 PNG 文件:

    fig.savefig('weight_hist.png')
    
    注意

    本活动的解决方案可以在第 209 页找到。

活动 7:完整的图表设计

为了让我们的图表独立存在,与分析分离,我们需要在图表中添加更多信息,以便其他分析人员或用户能够理解图表的内容,并明白所表示的内容。我们现在将结合本章所学,创建一个完整的图表,包括标题、标签和图例,并调整图表大小。

作为分析师,我们希望了解每年平均每加仑英里数是否增加,并希望按气缸数进行分组。例如,三缸车的燃油消耗随时间变化如何?它的表现是高于还是低于四缸车?

按照以下步骤创建我们的最终图表:

  1. 导入 Auto-MPG 数据集。

  2. yearcylinders进行groupby操作,并取消将它们作为索引的选项。

  3. 计算分组后的平均每加仑英里数,并将年份设置为索引。

  4. 将年份设置为数据框的索引。

  5. 使用面向对象的 API 创建图形和坐标轴。

  6. df_g数据集上按气缸数进行groupby操作,并使用大小为(10,8)的坐标轴绘制每加仑英里数变量。

  7. 在坐标轴上设置标题x轴标签和y轴标签。

  8. 在图表中包含图例。

  9. 将图形保存为 PNG 文件到磁盘。

    注意

    这个活动的解决方案可以在第 211 页找到。

我们可以从这张图表中推断出,四缸车比八缸车更为经济。我们还可以推断出,在研究期间,所有汽车的燃油效率都有所提高,1980 到 1982 年间,四缸车的燃油效率有所下降。

注意

注意,借助标签轴和图例,使用 Pandas 所做的复杂转换(按组汇总和平均,之后设置索引)在最终结果中变得容易解释。

总结

在本章中,我们已经看到,在分析数据时,创建有意义且有趣的可视化图形的重要性。一张好的数据可视化图能够极大地帮助分析师的工作,以一种能够触及更广泛受众的方式呈现数据,并解释那些可能难以用文字表达或用表格展示的概念。

一张有效的图表,作为数据可视化工具,必须展示数据,避免失真,使理解大型数据集变得容易,并有明确的目的,比如描述或探索。图表的主要目标是传达数据,因此分析师在创建图表时必须牢记这一点。一张有用的图表比一张美丽的图表更为重要。

我们展示了一些在分析中常用的图表类型:折线图、散点图、直方图和箱形图。每种图表都有其目的和应用,取决于数据和目标。我们还展示了如何直接从 Matplotlib、从 pandas,或是两者结合使用 Matplotlib 的 API(Pyplot、交互式 API 和面向对象 API)来创建图表。我们以解释如何更改图表外观的选项(从线条样式到标记和颜色),以及如何将图表保存为打印或共享的硬拷贝格式,结束了本章。

这里我们没有覆盖的还有许多配置图形的方式。可视化是一个庞大的领域,工具也提供了大量的选项。

在接下来的章节中,我们将重点讨论数据处理,包括使用 Hadoop 和 Spark 操作大规模数据。在学习这两种工具的基础知识后,我们将回到分析过程,其中将包含各种形式的图表。

第四章:第三章

使用大数据框架

学习目标

到本章结束时,你将能够:

  • 解释 HDFS 和 YARN Hadoop 组件

  • 执行 HDFS 文件操作

  • 比较 pandas DataFrame 和 Spark DataFrame

  • 使用 Spark 从本地文件系统和 HDFS 中读取文件

  • 使用 Spark 以 Parquet 格式写入文件

  • 编写分区文件以便快速分析

  • 使用 Spark 操作非结构化数据

在本章中,我们将探讨大数据工具,如 Hadoop 和 Spark。

介绍

在前几章中,我们看到如何使用 pandas 和 Matplotlib 进行数据操作与可视化,以及 Python 数据科学栈中的其他工具。到目前为止,我们使用的数据集相对较小,且结构相对简单。现实中的数据集可能比单台机器的内存容量要大几个数量级,处理这些数据集所需的时间也可能很长,常用的软件工具可能无法胜任。这就是通常定义的大数据:一种无法装入内存,或者无法在合理的时间内通过常规软件方法进行处理或分析的数据量。对某些人来说是大数据的东西,对于其他人来说可能不是大数据,这一定义可能因提问对象的不同而有所变化。

大数据还与 3V(后扩展为 4V)相关联:

  • 容量:顾名思义,大数据通常与非常大的数据量相关联。什么是“大”取决于上下文:对于一个系统,千兆字节就可以算作大数据,而对于另一个系统,则可能需要达到 PB 级数据。

  • 多样性:通常,大数据与不同的数据格式和类型相关联,如文本、视频和音频。数据可以是结构化的,如关系表,或是非结构化的,如文本和视频。

  • 速度:数据生成和存储的速度比其他系统更快,并且生产速度更加连续。流数据可以通过像电信运营商、在线商店甚至 Twitter 这样的平台生成。

  • 真实性:这一点是后来加入的,旨在说明在任何分析工作中,了解正在使用的数据及其含义非常重要。我们需要检查数据是否与我们预期的相符,转换过程是否改变了数据,数据是否反映了所收集的内容。

但让大数据具有吸引力的一个方面是分析组件:大数据平台旨在支持对这些庞大数据集进行分析和信息提取。这一章从这里开始:我们将学习如何使用两个最常见、最灵活的大数据框架——Hadoop 和 Spark——来操作、存储和分析大数据集。

Hadoop

Apache Hadoop 是一个为大规模数据的并行存储和计算而创建的软件组件集合。它在最初的设计理念是使用普通计算机进行分布式处理,具备高容错性和分布式计算能力。随着 Hadoop 的成功,越来越多的高端计算机开始被用于 Hadoop 集群,尽管普通硬件仍然是常见的使用案例。

并行存储是指使用多个通过网络互联的节点,以并行方式存储和检索数据的任何系统。

Hadoop 由以下组件组成:

  • Hadoop Common:基础的 Hadoop 通用项目

  • Hadoop YARN:资源和作业管理器

  • Hadoop MapReduce:一个大规模并行处理引擎

  • Hadoop 分布式文件系统HDFS):顾名思义,HDFS 是一个可以分布在多台计算机上的文件系统,通过使用本地磁盘,创建一个大规模的存储池:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_03_01.jpg

图 3.1:HDFS 架构

另一个重要组件是YARNYet Another Resource Negotiator),是 Hadoop 的资源管理器和作业调度器。它负责管理提交到 Hadoop 集群的作业,根据所需和可用资源分配内存和 CPU。

Hadoop 普及了一种并行计算模型,叫做 MapReduce,这是 Google 最初开发的一种分布式计算范式。在 Hadoop 中直接运行使用 MapReduce 的程序是可能的。但自从 Hadoop 诞生以来,已经开发出了其他并行计算范式和框架(如 Spark),因此 MapReduce 在数据分析中并不常用。在深入了解 Spark 之前,让我们先看看如何在 HDFS 上操作文件。

使用 HDFS 操作数据

HDFS 是一个分布式文件系统,其一个重要特点是:它被设计用来运行在成千上万台并非专门为此设计的计算机上——也就是所谓的商品硬件。它不需要特殊的网络设备或特殊硬盘,可以运行在普通硬件上。HDFS 的另一个理念是容错性:硬件总会发生故障,因此 HDFS 通过高度容错的方式绕过故障,而不是试图避免它。它假定在大规模环境下会发生故障,因此 HDFS 实现了故障检测机制,以实现快速和自动恢复。它也具有可移植性,能在不同平台上运行,并且可以容纳单个文件,数据容量达到 TB 级。

从用户的角度来看,HDFS 的一个大优点是它支持传统的层次化文件结构组织(文件夹和文件的树形结构),因此用户可以在每一层创建文件夹和文件,简化了使用和操作。文件和文件夹可以移动、删除和重命名,因此用户无需了解数据复制或NameNode/DataNode架构即可使用 HDFS;它看起来与 Linux 文件系统类似。在演示如何访问文件之前,我们需要先解释一下访问 Hadoop 数据所使用的地址。例如,访问 HDFS 中文件的 URI 格式如下:

hdfs://hadoopnamenode.domainname/path/to/file

其中,namenode.domainname是 Hadoop 中配置的地址。Hadoop 用户指南(exitcondition.com/install-hadoop-windows/)详细介绍了如何访问 Hadoop 系统的不同部分。让我们通过几个例子更好地理解这一切是如何工作的。

练习 16:在 HDFS 中操作文件

假设一个分析师刚刚收到了一个要分析的大型数据集,并且它存储在 HDFS 系统中。这个分析师如何列出、复制、重命名和移动这些文件?假设分析师收到了一个名为new_data.csv的原始数据文件:

  1. 如果您使用的是 Linux 系统,请在终端中使用以下命令,或者如果使用 Windows 系统,请在命令提示符下执行此命令,以开始检查当前的目录和文件:

    hdfs dfs -ls /
    
  2. 我们在本地磁盘上有一个名为new_data.csv的文件,我们希望将其复制到 HDFS 数据文件夹中:

    hdfs dfs -put C:/Users/admin/Desktop/Lesson03/new_data.csv /
    
  3. 注意,命令的最后部分是 HDFS 中的路径。现在,使用mkdir命令在 HDFS 中创建一个文件夹:

    hdfs dfs -mkdir /data
    
  4. 然后将文件移动到 HDFS 中的数据文件夹:

    hdfs dfs -mv /data_file.csv /data
    
  5. 更改 CSV 文件的名称:

    hdfs dfs -mv /data/new_data.csv /data/other_data.csv
    
  6. 使用以下命令检查文件是否存在于当前路径:

    hadoop fs -ls /data
    

    输出结果如下:

    other_data.csv
    
    注意

    HDFS 部分之后的命令与 Linux Shell 中的命令相同。

了解如何在 HDFS 中操作文件和目录是大数据分析的重要部分,但通常,直接操作仅限于数据摄取时进行。要分析数据,HDFS 通常不会直接使用,像 Spark 这样的工具更为强大。让我们按顺序看看如何使用 Spark。

Spark

Sparkspark.apache.org)是一个用于大规模数据处理的统一分析引擎。Spark 最初是由加利福尼亚大学伯克利分校于 2009 年发起的项目,并于 2013 年移交给 Apache 软件基金会。

Spark 的设计旨在解决使用 Hadoop 架构进行分析时的一些问题,如数据流、SQL 操作存储在 HDFS 上的文件和机器学习。它可以将数据分布到集群中的所有计算节点,以减少每个计算步骤的延迟。Spark 的另一个特点是它的灵活性:它有适用于 Java、Scala、SQL、R 和 Python 的接口,以及适用于不同问题的库,例如用于机器学习的 MLlib、用于图计算的 GraphX 和用于流式工作负载的 Spark Streaming。

Spark 使用工作节点抽象,具有一个接收用户输入以启动并行执行的驱动进程,以及在集群节点上执行任务的工作进程。它具有内置的集群管理工具,并支持其他工具,如 Hadoop YARN、Apache Mesos(甚至 Kubernetes),可以集成到不同的环境和资源分配场景中。

Spark 也可以非常快速,因为它首先尝试将数据分布到所有节点,并将其保留在内存中,而不是仅仅依赖磁盘上的数据。它可以处理比所有可用内存总和还大的数据集,通过在内存和磁盘之间切换数据,但这个过程会比将整个数据集完全适配到所有节点的内存中时慢。

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_03_02.jpg

图 3.2:Spark 工作机制

其他显著优势是,Spark 具有适用于多种本地和分布式存储系统的接口,如 HDFS、Amazon S3、Cassandra 等;可以通过 JDBC 或 ODBC 连接器连接到 RDBMS,如 PostgreSQL 和 MySQL;还可以使用Hive Metastore直接对 HDFS 文件运行 SQL。CSV、Parquet 和 ORC 等文件格式也可以被 Spark 直接读取。

这种灵活性在处理大数据源时非常有帮助,因为大数据源可能具有不同的格式。

Spark 可以作为交互式 Shell 使用,支持 Scala、Python 和 R,也可以作为作业提交平台使用,使用 spark-submit命令将作业分发到 Spark 集群。submit 方法用于将作业调度到 Spark 集群,该作业是通过脚本编码的。Spark 的 Python 接口被称为 PySpark,可以直接从终端访问,使用默认的 Python 版本;也可以通过 IPython shell 或在 Jupyter 笔记本内访问。

Spark SQL 和 Pandas DataFrames

RDD(弹性分布式数据集)是 Spark 用来处理数据的基础抽象。从 Spark 2.0 版本开始,推荐使用的 API 是 DataFrame API。DataFrame API 是在 RDD API 之上构建的,尽管仍然可以访问 RDD API。

使用 RDD 被认为是低级的,所有操作都可以在 DataFrame API 中实现,但学习一些关于 RDD API 的内容也无妨。

SQL 模块使用户能够使用 SQL 查询在 Spark 中查询数据,类似于常见的关系型数据库。DataFrame API 是 SQL 模块的一部分,处理结构化数据。这个数据接口有助于创建额外的优化,使用相同的执行引擎,无论使用什么 API 或语言来表达这些计算。

DataFrame API 类似于 Pandas DataFrame。在 Spark 中,DataFrame 是一个分布式的数据集合,按列组织,每一列都有一个名称。随着 Spark 2.0 的发布,DataFrame 成为更通用的 Dataset API 的一部分,但由于该 API 仅适用于 Java 和 Scala 语言,我们将只讨论 DataFrame API(在文档中称为 Untyped Dataset Operations)。

Spark DataFrame 的接口类似于 pandas 接口,但也有一些重要的区别:

  • 第一个区别是 Spark DataFrame 是 不可变的:创建后无法修改。

  • 第二个区别是 Spark 有两种不同的操作:转换行动

    转换 是应用于 DataFrame 元素的操作,并且是排队待执行的,数据尚未被提取。

    只有在调用 行动 时,数据才会被提取,并且所有排队的转换操作都会执行。这叫做延迟计算。

练习 17:在 Spark 中执行 DataFrame 操作

让我们开始使用 Spark 执行输入/输出和简单的聚合操作。正如我们之前所说,Spark 的接口灵感来自 pandas 接口。在 第二章使用 Matplotlib 和 Seaborn 的统计可视化 中学到的内容可以应用到这里,从而加速执行更复杂的分析,包括聚合、统计、计算和对聚合数据的可视化。我们希望读取一个 CSV 文件,就像我们之前做的那样,对其进行一些分析:

  1. 首先,在 Jupyter notebook 中使用以下命令创建一个 Spark 会话:

    from pyspark.sql import SparkSession
    >>> spark = SparkSession \
        .builder \
        .appName(“Python Spark Session”) \
        .getOrCreate()
    
  2. 现在,让我们使用以下命令从 mydata.csv 文件中读取数据:

    df = spark.read.csv(/data/mydata.csv’, header=True)
    
  3. 如前所述,Spark 的计算是延迟的,因此如果我们想显示 DataFrame 中的值,我们需要调用行动,如这里所示:

    df.show()
    +------+----+-------+
    |  name| age| height|
    +------+----+-------+
    |  Jonh|  22|   1.80|
    |Hughes|  34|   1.96|
    |  Mary|  27|   1.56|
    +------+----+-------+
    
    注意

    这在 pandas 中不是必需的:直接打印 DataFrame 就能显示内容。

练习 18:使用 Spark 访问数据

在读取 DataFrame 并显示其内容后,我们希望开始操作数据,以便进行分析。我们可以使用相同的 NumPy 选择语法来访问数据,提供列名作为 Column

  1. 让我们选择上一练习中导入的 DataFrame 中的某一列:

    df[‘age’].Column[‘age’]
    

    这与我们在 pandas 中看到的有所不同。选择 Spark DataFrame 中列的值的方法是 select。那么,让我们看看当我们使用这个方法时会发生什么。

  2. 再次使用相同的 DataFrame,使用 select 方法选择名称列:

    df.select(df[‘name’])DataFrame[age: string]
    
  3. 现在,它从 Column 变成了 DataFrame。因此,我们可以使用 DataFrame 的方法。使用 show 方法显示 select 方法对 age 列的结果:

    df.select(df[‘age’]).show()
    +---+
    |age|
    +---+
    | 22|
    | 34|
    | 27|
    +---+
    
  4. 让我们选择多个列。我们可以使用列的名称来做到这一点:

    df.select(df[‘age’], df[‘height’]).show()
    +---+------+
    |age|height|
    +---+------+
    | 22|  1.80|
    | 34|  1.96|
    | 27|  1.56|
    +---+------+
    

这对于其他列是可扩展的,通过名称选择,语法相同。我们将在下一章中讨论更复杂的操作,例如 带有 GroupBy 的聚合

练习 19:从本地文件系统和 HDFS 读取数据

如前所述,要从本地磁盘读取文件,只需将路径提供给 Spark。我们还可以读取位于不同存储系统中的其他文件格式。Spark 可以读取以下格式的文件:

  • CSV

  • JSON

  • ORC

  • Parquet

  • 文本

并且可以从以下存储系统中读取:

  • JDBC

  • ODBC

  • Hive

  • S3

  • HDFS

基于 URL 方案,作为练习,我们来读取来自不同位置和格式的数据:

  1. 在 Jupyter Notebook 中导入必要的库:

    from pyspark.sql import SparkSession
    spark = SparkSession \
        .builder \
        .appName(“Python Spark Session”) \
        .getOrCreate()
    
  2. 假设我们需要从一个 JSON 文件中获取一些数据,这对于从网络 API 收集的数据很常见。要直接从 HDFS 读取文件,请使用以下 URL:

    df = spark.read.json(‘hdfs://hadoopnamenode/data/myjsonfile.json’)
    

    请注意,使用这种 URL 时,我们必须提供 HDFS 端点的完整地址。我们也可以只使用简化路径,前提是 Spark 已经配置了正确的选项。

  3. 现在,使用以下命令将数据读取到 Spark 对象中:

    df = spark.read.json(‘hdfs://data/myjsonfile.json’)
    
  4. 因此,我们在 read 方法中选择格式,并在访问 URL 中选择存储系统。对于 JDBC 连接也使用相同的方法,但通常我们需要提供用户名和密码来连接。让我们看看如何连接到 PostgreSQL 数据库:

    url = “jdbc:postgresql://posgreserver:5432/mydatabase”
    properties = {“user”: “my_postgre_user”,  password: “mypassword”, “driver”: “org.postgresql.Driver”}
    df = spark.read.jdbc(url, table = “mytable”, properties = properties)
    

练习 20:将数据写回 HDFS 和 PostgreSQL

正如我们在使用 pandas 时看到的那样,执行一些操作和转换后,假设我们想将结果写回本地文件系统。当我们完成分析并希望与其他团队共享结果时,或者我们希望使用其他工具展示数据和结果时,这非常有用:

  1. 我们可以直接在 HDFS 上使用 write 方法从 DataFrame 写入:

    df.write.csv(‘results.csv’, header=True)
    
  2. 对于关系型数据库,使用与这里演示的相同 URL 和属性字典:

    df = spark.write.jdbc(url, table = “mytable”, properties = properties)
    

    这使得 Spark 在处理大型数据集并将其组合进行分析时具有极大的灵活性。

    注意

    Spark 可以作为一个中间工具来转换数据,包括聚合或修复数据问题,并以不同的格式保存供其他应用使用。

写入 Parquet 文件

Parquet 数据格式(parquet.apache.org/)是一种二进制的列式存储格式,可以被不同的工具使用,包括 Hadoop 和 Spark。它被构建以支持压缩,从而实现更高的性能和存储利用率。它的列式设计有助于在性能上进行数据选择,因为只会检索所需列的数据,而不是在不需要的行中查找并丢弃值,从而减少了大数据场景下的检索时间,在这些场景中数据是分布式并存储在磁盘上。Parquet 文件也可以通过外部应用程序读取和写入,使用 C++ 库,甚至可以直接从 pandas 中操作。

Parquet 库目前正在与 Arrow 项目arrow.apache.org/)一起开发。

在 Spark 中考虑更复杂的查询时,将数据存储为 Parquet 格式可以提高性能,特别是当查询需要搜索大规模数据集时。压缩有助于减少在 Spark 执行操作时需要传输的数据量,从而降低网络 I/O。它还支持模式和嵌套模式,类似于 JSON,Spark 可以直接从文件中读取模式。

Spark 中的 Parquet 写入器有几个选项,例如模式(追加、覆盖、忽略或错误,默认为错误)和压缩,选择压缩算法的参数。可用的算法如下:

  • gzip

  • lzo

  • brottli

  • lz4

  • 快速压缩

  • 未压缩

默认算法是 snappy

练习 21:写入 Parquet 文件

假设我们收到大量的 CSV 文件,需要对其进行一些分析,并且还需要减小数据体积。我们可以使用 Spark 和 Parquet 来实现。在开始分析之前,让我们将 CSV 文件转换为 Parquet 格式:

  1. 首先,从 HDFS 读取 CSV 文件:

    df = spark.read.csv(‘hdfs:/data/very_large_file.csv’, header=True)
    
  2. 将 DataFrame 中的 CSV 文件以 Parquet 格式写回到 HDFS:

    df.write.parquet(‘hdfs:/data/data_file’, compression=”snappy”)
    
  3. 现在将 Parquet 文件读取到一个新的 DataFrame 中:

    df_pq = spark.read.parquet(“hdfs:/data/data_file”)
    
    注意

    write.parquet 方法会创建一个名为 data_file 的文件夹,并生成一个长文件名的文件,例如 part-00000-1932c1b2-e776-48c8-9c96-2875bf76769b-c000.snappy.parquet

使用 Parquet 和分区提高分析性能

Parquet 支持并且也能提高查询性能的一个重要概念是分区。分区的想法是将数据拆分成可以更快速访问的部分。分区键是一个列,其值用于拆分数据集。分区在数据中存在有意义的划分时非常有用,这些划分可以单独处理。例如,如果你的数据是基于时间间隔的,则分区列可以是年份值。这样,当查询使用基于年份的筛选值时,只会读取与请求年份匹配的分区中的数据,而不是整个数据集。

分区也可以是嵌套的,并通过 Parquet 中的目录结构表示。所以,假设我们还想按月份列进行分区,那么 Parquet 数据集的文件夹结构将类似于以下形式:

hdfs -fs ls /data/data_file
year=2015
year=2016
year=2017
hdfs -fs ls /data/data_file/year=2017
month=01
month=02
month=03
month=04
month=05

当对分区进行过滤时,分区可以提高性能,因为只会读取所选分区中的数据,从而提高性能。要保存分区文件,应该使用 partitionBy 选项,可以在 parquet 命令中使用,或者将前一个命令与写入操作链式调用:

df.write.parquet(“hdfs:/data/data_file_partitioned”, partitionBy=[“year”, “month”])

另一种方法是:

df.write.partittionBy([“year”, “month”]).format(“parquet”).save(“hdfs:/data/data_file_partitioned”)

后者格式可以与前面的操作一起使用。读取分区数据时,Spark 可以根据目录结构推断分区结构。

如果正确使用分区,分析人员可以显著提高查询性能。但如果没有正确选择分区列,分区反而可能会影响性能。例如,如果数据集中只有一年的数据,按年份分区就没有任何好处。如果某列有太多不同的值,按该列进行分区也可能会产生问题,导致创建过多的分区,无法提升速度,甚至可能降低性能。

练习 22:创建分区数据集

在我们的初步分析中,我们发现数据中包含日期列,其中一个表示年份,一个表示月份,另一个表示日期。我们将对这些数据进行汇总,以获取每年、每月和每天的最小值、平均值和最大值。让我们从数据库中创建一个保存为 Parquet 格式的分区数据集:

  1. 定义一个 PostgreSQL 连接:

    url = “jdbc:postgresql://posgreserver:5432/timestamped_db”
    properties = {“user”: “my_postgre_user”,  password: “mypassword”, “driver”: “org.postgresql.Driver”}
    
  2. 使用 JDBC 连接器从 PostgreSQL 读取数据到 DataFrame:

    df = spark.read.jdbc(url, table = “sales”, properties = properties)
    
  3. 接下来,我们将把这些数据转换为分区 Parquet 格式:

    df.write.parquet(“hdfs:/data/data_file_partitioned”, partitionBy=[“year”, “month”, “day”], compression=”snappy”)
    

使用 Spark 作为不同数据源的中介,并考虑其数据处理和转换能力,使其成为结合和分析数据的优秀工具。

处理非结构化数据

非结构化数据通常指没有固定格式的数据。例如,CSV 文件是结构化的,而 JSON 文件也可以被认为是结构化的,尽管它不是表格形式。另一方面,计算机日志没有固定结构,不同的程序和守护进程会输出没有共同模式的消息。图像也是另一种非结构化数据的例子,类似于自由文本。

我们可以利用 Spark 在读取数据时的灵活性,解析非结构化格式并将所需信息提取到更结构化的格式中,便于分析。这一步通常称为 预处理数据清洗

练习 23:解析文本并清洗数据

在本练习中,我们将读取一个文本文件,将其拆分成行,并从给定的字符串中移除 thea 这两个词:

  1. 使用text方法将文本文件shake.txtraw.githubusercontent.com/TrainingByPackt/Big-Data-Analysis-with-Python/master/Lesson03/data/shake.txt)读入 Spark 对象:

    from operator import add
    rdd_df = spark.read.text(/shake.txt”).rdd
    
  2. 使用以下命令从文本中提取行:

    lines = rdd_df.map(lambda line: line[0])
    
  3. 这将把文件中的每一行拆分为列表中的一个条目。要检查结果,可以使用collect方法,它会将所有数据收集到驱动程序进程中:

    lines.collect()
    
  4. 现在,让我们使用count方法计算行数:

    lines.count()
    
    注意

    使用collect方法时要小心!如果收集的 DataFrame 或 RDD 的大小超过了本地驱动程序的内存,Spark 将抛出错误。

  5. 现在,让我们首先将每一行拆分为单词,通过周围的空格进行分割,并合并所有元素,移除大写字母的单词:

    splits = lines.flatMap(lambda x: x.split(‘ ‘))
    lower_splits = splits.map(lambda x: x.lower().strip())
    
  6. 让我们还移除字符串中的thea,以及像‘.’,‘,’这样的标点符号:

    prep = [‘the’, ‘a’,,,.]
    
  7. 使用以下命令从我们的标记列表中移除停用词:

    tokens = lower_splits.filter(lambda x: x and x not in prep)
    

    我们现在可以处理我们的标记列表并统计唯一的单词。其思想是生成一个元组列表,其中第一个元素是标记,第二个元素是该标记的计数。

  8. 让我们将我们的标记映射到列表中:

    token_list = tokens.map(lambda x: [x, 1])
    
  9. 使用reduceByKey操作,它会对每个列表应用此操作:

    count = token_list.reduceByKey(add).sortBy(lambda x: x[1], ascending=False)
    count.collect()
    

    输出如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_03_03.jpg

图 3.3:解析文本并清理
注意

记住,collect()会将所有数据收集到驱动节点!使用tophtop等工具检查是否有足够的内存。

活动 8:从文本中移除停用词

在本活动中,我们将读取一个文本文件,将其拆分为行,并从文本中移除停用词

  1. 读取在练习 8 中使用的文本文件shake.txt

  2. 从文本中提取行并创建一个包含每行的列表。

  3. 将每一行拆分为单词,按空格进行分割,并移除大写字母的单词。

  4. 从我们的标记列表中移除停用词:‘of’,‘a’,‘and’,‘to’。

  5. 处理标记列表并统计唯一单词,生成由标记及其计数组成的元组列表。

  6. 使用reduceByKey操作将我们的标记映射到列表中。

    输出如下:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_03_04.jpg

图 3.4:从文本中移除停用词
注意

本活动的解决方案可以在第 213 页找到。

我们得到一个元组列表,每个元组包含一个标记和该单词在文本中出现的次数。请注意,在最终对计数进行collect操作(一个动作)之前,作为转换的操作并没有立即执行:我们需要通过count这个动作操作来启动 Spark 执行所有步骤。

其他类型的非结构化数据可以使用前面的示例进行解析,并可以直接操作,如前面的活动所示,或者稍后转换为 DataFrame。

总结

在回顾了大数据的定义之后,我们学习了一些专为存储和处理大数据量而设计的工具。Hadoop 是一个完整的生态系统,包括 HDFS 等工具和框架,旨在在大量廉价计算节点上以分布式方式存储数据,以及资源和作业管理器 YARN。我们看到了如何使用 HDFS fs 命令直接操作 HDFS 上的数据。

我们还学习了关于 Spark 的知识,这是一个非常强大和灵活的并行处理框架,与 Hadoop 集成良好。Spark 拥有不同的 API,如 SQL、GraphX 和 Streaming。我们学习了 Spark 如何使用 DataFrame API 表示数据,以及其计算方式类似于 pandas 的方法。我们还看到了如何使用 Parquet 文件格式高效存储数据,并在分析数据时通过分区来提高性能。最后,我们学习了如何处理诸如文本之类的非结构化数据文件。

在下一章中,我们将深入探讨如何使用更高级的技术和 Spark 进行有意义的统计分析,并学习如何在 Spark 中使用 Jupyter 笔记本。

第五章:第四章

深入了解 Spark

学习目标

到本章结束时,你将能够:

  • 实现基本的 Spark DataFrame API。

  • 从不同数据源读取数据并创建 Spark DataFrame。

  • 使用不同的 Spark DataFrame 选项来操作和处理数据。

  • 使用不同的图表可视化 Spark DataFrame 中的数据。

在本章中,我们将使用 Spark 作为大数据集的分析工具。

介绍

上一章介绍了 Spark,这是一个最受欢迎的分布式数据处理平台,用于处理大数据。

在本章中,我们将学习如何使用 Python API——PySpark来操作 Spark 和 Spark DataFrame。它使我们能够处理 PB 级数据,同时也在实时环境中实现机器学习ML)算法。本章将重点介绍使用 Spark DataFrame 在 PySpark 中的数据处理部分。

注意

在本章中,我们会频繁使用“DataFrame”这个术语。这里指的明确是 Spark 的 DataFrame,除非特别说明。请不要将其与 pandas 的 DataFrame 混淆。

Spark DataFrame 是分布式的数据集合,以命名列的形式组织。它们的灵感来自 R 和 Python 的 DataFrame,并在后台有复杂的优化,使得它们快速、优化且可扩展。

DataFrame API 作为Project Tungsten的一部分开发,旨在提高 Spark 的性能和可扩展性。它首次在 Spark 1.3 中引入。

Spark DataFrame 比其前身 RDD 更容易使用和操作。它们像 RDD 一样是不可变的,并且支持延迟加载,这意味着除非调用动作,否则对 DataFrame 不会执行任何变换。DataFrame 的执行计划由 Spark 本身准备,因此更加优化,使得在 DataFrame 上的操作比在 RDD 上的操作更快。

入门 Spark DataFrame

要开始使用 Spark DataFrame,我们首先需要创建一个名为 SparkContext 的对象。SparkContext 配置了内部服务并促进了来自 Spark 执行环境的命令执行。

注意

我们将使用 Spark 版本 2.1.1,运行在 Python 3.7.1 环境中。Spark 和 Python 安装在一台 MacBook Pro 上,操作系统是 macOS Mojave 10.14.3,配备 2.7 GHz Intel Core i5 处理器和 8 GB 1867 MHz DDR3 RAM。

以下代码片段用于创建SparkContext

from pyspark import SparkContext
sc = SparkContext()
注意

如果你在 PySpark shell 中工作,应该跳过这一步,因为该 shell 在启动时会自动创建sc(SparkContext)变量。不过,在创建 PySpark 脚本或使用 Jupyter Notebook 时,务必创建sc变量,否则代码会抛出错误。

在开始使用 DataFrame 之前,我们还需要创建一个SQLContext。Spark 中的SQLContext是一个类,提供了类似 SQL 的功能。我们可以使用SparkContext来创建SQLContext

from pyspark.sql import SQLContext
sqlc = SQLContext(sc)

在 Spark 中创建 DataFrame 有三种不同的方法:

  • 我们可以以编程方式指定 DataFrame 的模式并手动输入数据。然而,由于 Spark 通常用于处理大数据,除了为小型测试/示例案例创建数据外,这种方法几乎没有其他用途。

  • 创建 DataFrame 的另一种方法是从现有的 Spark RDD 对象中创建。这是有用的,因为在 DataFrame 上工作比直接在 RDD 上工作要容易得多。

  • 我们还可以直接从数据源读取数据以创建 Spark DataFrame。Spark 支持多种外部数据源,包括 CSV、JSON、Parquet、关系型数据库表和 Hive 表。

练习 24:指定 DataFrame 的模式

在本练习中,我们将通过手动指定模式并输入数据来创建一个小的示例 DataFrame。尽管这种方法在实际场景中的应用较少,但它是开始学习 Spark DataFrames 的一个不错的起点:

  1. 导入必要的文件:

    from pyspark import SparkContext
    sc = SparkContext()
    from pyspark.sql import SQLContext
    sqlc = SQLContext(sc)
    
  2. 从 PySpark 模块导入 SQL 工具并指定示例 DataFrame 的模式:

    from pyspark.sql import *
    na_schema = Row("Name","Age")
    
  3. 根据指定模式创建 DataFrame 的行:

    row1 = na_schema("Ankit", 23)
    row2 = na_schema("Tyler", 26)
    row3 = na_schema("Preity", 36)
    
  4. 将行合并在一起创建 DataFrame:

    na_list = [row1, row2, row3]
    df_na = sqlc.createDataFrame(na_list)
    type(df_na)
    
  5. 现在,使用以下命令显示 DataFrame:

    df_na.show()
    

    输出结果如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_01.jpg

图 4.1:示例 PySpark DataFrame

练习 25:从现有 RDD 创建 DataFrame

在本练习中,我们将从现有的 Spark RDD 对象创建一个小的示例 DataFrame:

  1. 创建一个 RDD 对象,我们将把它转换成 DataFrame:

    data = [("Ankit",23),("Tyler",26),("Preity",36)]
    data_rdd = sc.parallelize(data)
    type(data_rdd)
    
  2. 将 RDD 对象转换为 DataFrame:

    data_sd = sqlc.createDataFrame(data_rdd)
    
  3. 现在,使用以下命令显示 DataFrame:

    data_sd.show()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_02.jpg

图 4.2:从 RDD 对象转换的 DataFrame

练习 25:使用 CSV 文件创建 DataFrame

可以使用多种不同的数据源来创建 DataFrame。在本练习中,我们将使用开源的 Iris 数据集,该数据集可以在 scikit-learn 库的 datasets 中找到。Iris 数据集是一个多变量数据集,包含 150 条记录,每个品种的 Iris 花(Iris Setosa、Iris Virginica 和 Iris Versicolor)有 50 条记录。

该数据集包含每个 Iris 品种的五个属性,即 花瓣长度花瓣宽度萼片长度萼片宽度品种。我们已将此数据集存储在一个外部 CSV 文件中,我们将其读入 Spark:

  1. 从 Databricks 网站下载并安装 PySpark CSV 阅读器包:

    pyspark –packages com.databricks:spark-csv_2.10:1.4.0
    
  2. 将数据从 CSV 文件读取到 Spark DataFrame 中:

    df = sqlc.read.format('com.databricks.spark.csv').options(header='true', inferschema='true').load('iris.csv')
    type(df)
    
  3. 现在,使用以下命令显示 DataFrame:

    df.show(4)
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_03.jpg

图 4.3:Iris DataFrame,前四行
给讲师的备注

激励学生探索其他数据源,如制表符分隔文件、Parquet 文件和关系型数据库等。

从 Spark DataFrame 写入输出

Spark 使我们能够将存储在 Spark DataFrame 中的数据写入本地 pandas DataFrame,或写入 CSV 等外部结构化文件格式。然而,在将 Spark DataFrame 转换为本地 pandas DataFrame 之前,请确保数据能够适应本地驱动程序内存。

在接下来的练习中,我们将探索如何将 Spark DataFrame 转换为 pandas DataFrame。

练习 27: 将 Spark DataFrame 转换为 Pandas DataFrame

在本练习中,我们将使用前一个练习中预创建的 Iris 数据集的 Spark DataFrame,并将其转换为本地 pandas DataFrame。然后我们将把这个 DataFrame 存储到 CSV 文件中。执行以下步骤:

  1. 使用以下命令将 Spark DataFrame 转换为 pandas DataFrame:

    import pandas as pd
    df.toPandas()
    
  2. 现在使用以下命令将 pandas DataFrame 写入 CSV 文件:

    df.toPandas().to_csv('iris.csv')
    
    注意

    将 Spark DataFrame 的内容写入 CSV 文件需要使用 spark-csv 包的一行代码:

    df.write.csv('iris.csv')

探索 Spark DataFrame

Spark DataFrame 相比传统的 RDDs 的一个主要优势是数据的易用性和探索性。数据以更结构化的表格格式存储在 DataFrame 中,因此更容易理解。我们可以计算基本统计信息,如行数和列数,查看模式,并计算摘要统计信息,如均值和标准差。

练习 28: 显示基本的 DataFrame 统计信息

在本练习中,我们将展示数据前几行的基本 DataFrame 统计信息,以及所有数值 DataFrame 列和单个 DataFrame 列的摘要统计信息:

  1. 查看 DataFrame 的模式。模式会以树形结构显示在控制台上:

    df.printSchema()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image39860.jpg

    图 4.4: Iris DataFrame 模式
  2. 现在,使用以下命令打印 Spark DataFrame 的列名:

    df.schema.names
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_05.jpg

    图 4.5: Iris 列名
  3. 要获取 Spark DataFrame 中行数和列数,请使用以下命令:

    ## Counting the number of rows in DataFrame
    df.count()#134
    ## Counting the number of columns in DataFrame
    len(df.columns)#5
    
  4. 让我们获取数据的前 n 行。我们可以使用 head() 方法来实现。然而,我们使用 show() 方法,因为它可以以更好的格式显示数据:

    df.show(4)
    

    输出如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_06.jpg

    图 4.6: Iris DataFrame,前四行
  5. 现在,计算所有数值列的摘要统计信息,如均值和标准差:

    df.describe().show()
    

    输出如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_07.jpg

    图 4.7: Iris DataFrame,摘要统计
  6. 要计算 Spark DataFrame 中某一数值列的摘要统计信息,请使用以下命令:

    df.describe('Sepalwidth').show()
    

    输出如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_08.jpg

图 4.8: Iris DataFrame,Sepalwidth 列的摘要统计

活动 9:Spark DataFrames 入门

在本活动中,我们将利用前几部分学习的概念,使用三种方法创建一个 Spark DataFrame。我们还将计算 DataFrame 的统计信息,最后将相同的数据写入 CSV 文件。你可以随意使用任何开源数据集来完成这个活动:

  1. 通过手动指定模式创建一个示例 DataFrame。

  2. 从现有的 RDD 创建一个示例 DataFrame。

  3. 通过从 CSV 文件中读取数据创建一个示例 DataFrame。

  4. 打印第 3 步中读取的示例 DataFrame 的前七行。

  5. 打印第 3 步中读取的示例 DataFrame 的模式。

  6. 打印示例 DataFrame 中的行数和列数。

  7. 打印 DataFrame 的摘要统计信息以及任何两个单独的数值列。

  8. 使用练习中提到的两种方法将示例 DataFrame 的前 7 行写入 CSV 文件。

    注意

    本活动的解决方案可以在第 215 页找到。

使用 Spark DataFrame 进行数据操作

数据操作是任何数据分析的前提。为了从数据中提取有意义的洞察,我们首先需要理解、处理和调整数据。但随着数据量的增加,这一步变得尤为困难。由于数据的规模,即使是简单的操作,如过滤和排序,也会变成复杂的编码问题。Spark DataFrame 使得在大数据上进行数据操作变得轻而易举。

在 Spark DataFrame 中进行数据操作与在常规 pandas DataFrame 中的操作非常相似。大多数 Spark DataFrame 的数据操作都可以通过简单直观的单行代码完成。我们将使用在之前练习中创建的包含鸢尾花数据集的 Spark DataFrame 来进行这些数据操作练习。

练习 29:选择并重命名 DataFrame 中的列

在本练习中,我们将首先使用 withColumnRenamed 方法重命名列,然后使用 select 方法选择并打印模式。

执行以下步骤:

  1. 使用 withColumnRenamed() 方法重命名 Spark DataFrame 的列:

    df = df.withColumnRenamed('Sepal.Width','Sepalwidth')
    
    注意

    Spark 无法识别包含点号(.)的列名。确保使用此方法重命名它们。

  2. 使用 select 方法从 Spark DataFrame 中选择单个列或多个列:

    df.select('Sepalwidth','Sepallength').show(4)
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_09.jpg

图 4.9:鸢尾花 DataFrame,Sepalwidth 和 Sepallength 列

练习 30:向 DataFrame 中添加和移除列

在本练习中,我们将使用 withColumn 方法在数据集中添加新列,之后使用 drop 函数将其移除。现在,让我们执行以下步骤:

  1. 使用 withColumn 方法在 Spark DataFrame 中添加一个新列:

    df = df.withColumn('Half_sepal_width', df['Sepalwidth']/2.0)
    
  2. 使用以下命令显示包含新添加列的数据集:

    df.show(4)
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_10.jpg

    图 4.10:引入新列 Half_sepal_width
  3. 现在,要在 Spark 数据框中移除一列,请使用这里说明的 drop 方法:

    df = df.drop('Half_sepal_width')
    
  4. 让我们展示数据集以验证列是否已被移除:

    df.show(4)
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_11.jpg

图 4.11:移除 Half_sepal_width 列后的鸢尾花数据框

练习 31:在数据框中显示和计数不同的值

要显示数据框中的不同值,我们使用 distinct().show() 方法。同样,要计数不同的值,我们将使用 distinct().count() 方法。执行以下步骤以打印不同的值及其总数:

  1. 使用 distinct 方法,结合 select 方法,从 Spark 数据框中选择任何列的不同值:

    df.select('Species').distinct().show()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_12.jpg

    图 4.12:鸢尾花数据框,物种列
  2. 要计算 Spark 数据框中任何列的不同值,请使用 count 方法,并结合 distinct 方法:

    df.select('Species').distinct().count()
    

练习 32:移除重复行和过滤数据框中的行

在这个练习中,我们将学习如何从数据集中移除重复的行,并随后在同一列上执行过滤操作。

执行这些步骤:

  1. 使用 dropDuplicates() 方法从数据框中移除重复值:

    df.select('Species').dropDuplicates().show()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_13.jpg

    图 4.13:移除重复列后的鸢尾花数据框,物种列
  2. 使用一个或多个条件从数据框中过滤行。这些多个条件可以通过布尔运算符如 &(与)或 |(或)传递给数据框,类似于我们对 pandas 数据框的操作:

    # Filtering using a single condition
    df.filter(df.Species == 'setosa').show(4)
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image39953.jpg

    图 4.14:使用单一条件过滤后的鸢尾花数据框
  3. 现在,要使用多个条件过滤列,请使用以下命令:

    df.filter((df.Sepallength > 5) & (df.Species == 'setosa')).show(4)
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_16.jpg

图 4.15:使用多个条件过滤后的鸢尾花数据框

练习 33:对数据框中的行进行排序

在这个练习中,我们将探索如何按升序和降序对数据框中的行进行排序。让我们执行以下步骤:

  1. 使用一个或多个条件按升序或降序排序数据框中的行:

    df.orderBy(df.Sepallength).show(5)
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_161.jpg

    图 4.16:过滤后的鸢尾花数据框
  2. 要按降序排序行,请使用以下命令:

    df.orderBy(df.Sepallength.desc()).show(5)
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_17.jpg

图 4.17:按降序排序后的鸢尾花数据框

练习 34:在数据框中聚合值

我们可以通过一个或多个变量对 DataFrame 中的值进行分组,并计算汇总指标,如 meansumcount 等等。在这个练习中,我们将计算鸢尾花数据集中每个花卉物种的平均花萼宽度。我们还将计算每个物种的行数:

  1. 使用以下命令计算每个物种的平均花萼宽度:

    df.groupby('Species').agg({'Sepalwidth' : 'mean'}).show()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image39999.jpg

    图 4.18:鸢尾花 DataFrame,计算平均花萼宽度
  2. 现在,让我们使用以下命令计算每个物种的行数:

    df.groupby('Species').count().show()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_19.jpg

图 4.19:鸢尾花 DataFrame,计算每个物种的行数
注意
.agg function; however, the method we used is more popular.

活动 10:使用 Spark DataFrame 进行数据操作

在本活动中,我们将使用之前部分中学到的概念,操作使用鸢尾花数据集创建的 Spark DataFrame。我们将执行基本的数据操作步骤,测试我们在 Spark DataFrame 中处理数据的能力。你可以自由使用任何开源数据集进行此活动。确保所用数据集包含数值变量和类别变量:

  1. 重命名 DataFrame 中的五列。如果 DataFrame 中有更多列,则重命名所有列。

  2. 从 DataFrame 中选择两列数值型列和一列类别型列。

  3. 计算类别变量中不同类别的数量。

  4. 在 DataFrame 中创建两个新列,分别通过将两列数值型列相加和相乘得到。

  5. 删除两个原始数值列。

  6. 按照类别列对数据进行排序。

  7. 计算每个类别变量中每个不同类别的求和列的平均值。

  8. 过滤出花萼宽度大于步骤 7 中计算出的所有平均值的行。

  9. 对结果 DataFrame 进行去重,确保它只包含唯一记录。

    注意

    本活动的解决方案可以在第 219 页找到。

Spark 中的图表

有效可视化数据的能力至关重要。数据的可视化帮助用户更好地理解数据,并发现文本形式中可能忽略的趋势。在 Python 中,有许多类型的图表,每种图表都有其特定的使用场景。

我们将探索一些图表,包括条形图、密度图、箱线图和线性图,用于 Spark DataFrame,使用流行的 Python 绘图包 Matplotlib 和 Seaborn。需要注意的是,Spark 处理的是大数据。因此,在绘制图表之前,请确保数据大小足够合理(即能容纳在计算机的内存中)。这可以通过过滤、汇总或抽样数据来实现。

我们使用的是鸢尾花数据集,它较小,因此我们不需要进行任何预处理步骤来减少数据大小。

教师说明

用户应在开发环境中预先安装并加载 Matplotlib 和 Seaborn 包,然后再开始本节的练习。如果您不熟悉如何安装和加载这些包,请访问 Matplotlib 和 Seaborn 的官方网站。

练习 35:创建条形图

在本练习中,我们将尝试通过条形图绘制每种物种的记录数量。我们需要首先聚合数据并计算每种物种的记录数。然后,我们可以将聚合后的数据转换为常规的 pandas DataFrame,并使用 Matplotlib 和 Seaborn 包创建我们需要的任何类型的图表:

  1. 首先,计算每种花卉物种的行数,并将结果转换为 pandas DataFrame:

    data = df.groupby('Species').count().toPandas()
    
  2. 现在,从结果的 pandas DataFrame 创建一个条形图:

    import seaborn as sns
    import matplotlib.pyplot as plt
    sns.barplot( x = data['Species'], y = data['count'])
    plt.xlabel('Species')
    plt.ylabel('count')
    plt.title('Number of rows per species')
    

    绘制的图形如下:

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_20.jpg

图 4.20:计算每个花卉物种的行数后,Iris DataFrame 的条形图

练习 36:创建线性模型图

在本练习中,我们将绘制两个不同变量的数据点,并在其上拟合一条直线。这类似于在两个变量上拟合一个线性模型,并有助于识别这两个变量之间的相关性:

  1. 从 pandas DataFrame 创建一个data对象:

    data = df.toPandas()
    sns.lmplot(x = "Sepallength", y = "Sepalwidth", data = data)
    
  2. 使用以下命令绘制 DataFrame:

    plt.show()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_21.jpg

图 4.21:Iris DataFrame 的线性模型图

练习 37:创建 KDE 图和箱线图

在本练习中,我们将创建一个核密度估计KDE)图,并接着绘制一个箱线图。请按照以下步骤操作:

  1. 首先,绘制一个 KDE 图,展示变量的分布情况。确保它能帮助我们了解变量的偏斜度和峰度:

    import seaborn as sns
    data = df.toPandas()
    sns.kdeplot(data.Sepalwidth, shade = True)
    plt.show()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_22.jpg

    图 4.22:Iris DataFrame 的 KDE 图
  2. 现在,使用以下命令绘制 Iris 数据集的箱线图:

    sns.boxplot(x = "Sepallength", y = "Sepalwidth", data = data)
    plt.show()
    

    https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_23.jpg

图 4.23:Iris DataFrame 的箱线图

箱线图是查看数据分布并定位异常值的好方法。它们通过 1st 四分位数、中位数、3rd 四分位数和四分位间距(25%到 75%的百分位数)来表示数据分布。

活动 11:Spark 中的图表

在此活动中,我们将使用 Python 的绘图库,通过不同类型的图表来可视化探索数据。我们使用的是 Kaggle 上的mtcars数据集(www.kaggle.com/ruiromanini/mtcars):

  1. 在 Jupyter Notebook 中导入所有必需的包和库。

  2. mtcars数据集将数据读取到 Spark 对象中。

  3. 使用直方图可视化数据集中任意连续数值变量的离散频率分布:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_24.jpg

    图 4.24:Iris 数据框的直方图
  4. 使用饼图可视化数据集中各类别的百分比份额:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_25.jpg

    图 4.25:Iris 数据框的饼图
  5. 使用箱型图绘制连续变量在类别变量各类别下的分布:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image40067.jpg

    图 4.26:Iris 数据框的箱型图
  6. 使用线性图表可视化连续数值变量的值:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_27.jpg

    图 4.27:Iris 数据框的线性图表
  7. 在同一条线性图表中绘制多个连续数值变量的值:

https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_28.jpg

图 4.28:Iris 数据框中绘制多个连续数值变量的线性图表
注意

本活动的解决方案可以在第 224 页找到。

小结

在本章中,我们介绍了 Spark 数据框的基本概念,并探讨了它们为何优于 RDD。我们探索了创建 Spark 数据框的不同方法,并将 Spark 数据框的内容写入常规的 pandas 数据框和输出文件。

我们在 PySpark 中尝试了实际的数据探索,通过计算 Spark 数据框的基本统计和指标。我们在 Spark 数据框中操作数据,执行数据处理操作,如过滤、选择和聚合。我们还尝试通过绘制数据生成有意义的可视化图表。

此外,我们通过实践操作和活动巩固了对各种概念的理解。

在下一章中,我们将探讨如何处理缺失值以及如何计算 PySpark 中变量之间的相关性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值