Python-数据分析第三版-全-
Python 数据分析第三版(全)
原文:
annas-archive.org/md5/74a7b24994c40ad3a90c290c07b529df
译者:飞龙
序言
数据分析使你能够通过发现新的模式和趋势,从大数据和小数据中创造价值,而 Python 是分析各种数据的最受欢迎工具之一。本书将带你快速入门 Python 数据分析,探索数据分析中的不同阶段和方法论,并学习如何使用 Python 生态系统中的现代库来创建高效的数据管道。
本书从使用 Python 进行基本统计和数据分析入手,你将通过简单易懂的示例进行复杂的数据分析与建模、数据处理、数据清理和数据可视化。接着,你将学习如何使用 ARMA 模型进行时间序列分析和信号处理。随着深入学习,你还将掌握如何使用机器学习算法(如回归、分类、主成分分析(PCA)和聚类)进行智能处理和数据分析。在最后几章中,你将通过实际案例分析文本数据和图像数据,分别使用自然语言处理(NLP)和图像分析技术。最后,本书还将展示如何使用 Dask 进行并行计算。
在阅读完本书后,你将掌握准备数据以进行分析并创建有意义的数据可视化的技能,从而能够根据数据预测值。
第一章:本书适用对象
本书适合数据分析师、商业分析师、统计学家以及希望学习如何使用 Python 进行数据分析的数据科学家。学生和学术人员也可以通过本书的实践方法学习和教授 Python 数据分析。具有基础数学知识和一定的 Python 使用经验将帮助你更好地入门。
本书内容概述
第一章,开始使用 Python 库,解释了数据分析过程以及 Python 库和 Anaconda 的成功安装。此外,我们还将讨论 Jupyter Notebook 及其高级功能。
第二章,NumPy 与 Pandas,介绍了 NumPy 和 Pandas。本章提供了 NumPy 数组、Pandas DataFrame 及其相关函数的基本概述。
第三章,统计学,简要概述了描述性统计和推断性统计。
第四章,线性代数,简要概述了线性代数及其相关的 NumPy 和 SciPy 函数。
第五章,数据可视化,向我们介绍了 matplotlib、seaborn、Pandas 绘图和 bokeh 可视化库。
第六章,检索、处理和存储数据,解释了如何读取和写入各种数据格式,如 CSV、Excel、JSON、HTML 和 Parquet。我们还将讨论如何从关系型和 NoSQL 数据库中获取数据。
第七章,清理杂乱数据,讲解了如何预处理原始数据并进行特征工程。
第八章,信号处理与时间序列,包含使用销售、啤酒生产和太阳黑子周期数据集进行时间序列和信号处理的示例。在这一章中,我们将主要使用 NumPy、SciPy 和 statsmodels。
第九章,监督学习——回归分析,详细讲解了线性回归和逻辑回归,并使用 scikit-learn 库提供了适当的示例。
第十章,监督学习——分类技术,讲解了多种分类技术,如朴素贝叶斯、决策树、K 最近邻和支持向量机(SVM)。此外,我们还将讨论模型性能评估措施。
第十一章,无监督学习——PCA 和聚类,详细讨论了降维和聚类技术。此外,我们还将评估聚类的性能。
第十二章,分析文本数据,简要概述了文本预处理、特征工程、情感分析和文本相似性。此章节主要使用 NLTK、SpaCy 和 scikit-learn 库。
第十三章,分析图像数据,提供了使用 OpenCV 进行图像处理操作的简要概述。同时,我们还将讨论人脸检测。
第十四章,使用 Dask 进行并行计算,解释了如何使用 Dask 进行数据预处理和机器学习建模的并行处理。
为了最大限度地利用本书
本书中提供的代码示例的执行需要在 Mac OS X、Linux 或 Microsoft Windows 上安装 Python 3.5 或更高版本。在本书中,我们将频繁使用 SciPy、NumPy、Pandas、scikit-learn、statsmodels、matplotlib 和 seaborn。第一章《Python 库入门》提供了安装说明和高级技巧,帮助你顺利进行工作。此外,特定和附加库的安装过程在相关章节中有所说明。Bokeh 的安装过程在第五章《数据可视化》中进行了讲解。类似地,NLTK 和 SpaCy 的安装说明在第十二章《分析文本数据》中进行了介绍。
我们还可以使用 pip
命令安装任何你想要探索的库或包。我们需要以管理员权限运行以下命令:
$ pip install <library name>
我们也可以通过在 pip
命令前加上 !
(感叹号)在 Jupyter Notebook 中安装它:
!pip install <library name>
要卸载通过 pip
安装的 Python 库或包,请使用以下命令:
$ pip uninstall <library name>
如果您正在使用本书的数字版,我们建议您自己输入代码,或者通过 GitHub 仓库(链接将在下一部分提供)访问代码。这样可以帮助您避免因复制粘贴代码而产生的潜在错误。
下载示例代码文件
您可以从 GitHub 下载本书的示例代码文件,网址为 github.com/PacktPublishing/Python-Data-Analysis-Third-Edition
。如果代码有更新,它将在现有的 GitHub 仓库中进行更新。
我们还提供来自我们丰富书籍和视频目录的其他代码包,您可以在 github.com/PacktPublishing/
找到它们。快去看看吧!
下载彩色图像
我们还提供了一个包含本书中截图/图表彩色图像的 PDF 文件,您可以在这里下载:static.packt-cdn.com/downloads/9781789955248_ColorImages.pdf
。
使用的约定
本书中,您会看到多种文本样式和约定。在这里,我们展示了一些这些样式的示例。文中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 账号如以下所示:“pandas
项目坚持的另一个约定是 import pandas as pd
导入语句。”
一段代码如下所示:
# Creating an array
import numpy as np
a = np.array([2,4,6,8,10])
print(a)
任何命令行输入或输出如下所示:
$ mkdir
$ cd css
粗体:表示新术语、重要词汇或在屏幕上看到的词语。例如,菜单或对话框中的词语会以这样的形式出现在文本中。举个例子:“从管理面板中选择系统信息。”
警告或重要说明如下所示。
小贴士和技巧如下所示。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果您对本书的任何内容有疑问,请在邮件主题中提及书名,并通过电子邮件联系我们 customercare@packtpub.com
。
勘误表:尽管我们已尽一切努力确保内容的准确性,但难免会有错误。如果您在本书中发现错误,我们将不胜感激您能向我们报告。请访问 www.packtpub.com/support/errata,选择您的书籍,点击“勘误表提交表单”链接,并填写相关细节。
盗版:如果您在网上发现任何形式的非法复制作品,我们将不胜感激您能提供位置地址或网站名称。请通过 copyright@packt.com
与我们联系,并附上相关链接。
如果您有兴趣成为作者:如果您在某个领域拥有专业知识,并且有兴趣撰写或参与编写一本书,请访问authors.packtpub.com。
评论
请留下评论。阅读并使用本书后,为什么不在您购买该书的网站上留下评论呢?潜在的读者可以查看并参考您的公正意见来做出购买决策,我们在 Packt 也可以了解您对我们产品的看法,而我们的作者也能看到您对他们书籍的反馈。谢谢!
如需了解有关 Packt 的更多信息,请访问packt.com。
第一部分:数据分析基础
本节的主要目标是为学习者建立基础的数据分析技能。这些技能包括使用 Jupyter Notebook,以及基本的 Python 库,如 NumPy、Pandas、Scipy 和 statsmodels。同时,本节重点讲解统计学和线性代数的主观知识,以提升数学能力。
本节包括以下章节:
-
第一章,Python 库入门
-
第二章,NumPy 和 pandas
-
第三章,统计学
-
第四章,线性代数
第一章:开始使用 Python 库
如你所知,Python 已成为最受欢迎的标准编程语言之一,是数据科学相关操作的完整工具包。Python 提供了大量的库,如 NumPy、Pandas、SciPy、Scikit-Learn、Matplotlib、Seaborn 和 Plotly。这些库为数据分析提供了一个完整的生态系统,广泛应用于数据分析师、数据科学家和业务分析师的工作中。Python 还具有灵活性、易学性、快速开发、大型活跃社区的特点,并能处理复杂的数值、科学和研究应用。所有这些特点使其成为数据分析的首选语言。
本章将重点介绍各种数据分析过程,如 KDD、SEMMA 和 CRISP-DM。之后,我们将提供数据分析与数据科学的比较,并探讨数据分析师和数据科学家的角色及不同的技能要求。最后,我们将转向安装各种 Python 库、IPython、Jupyter Lab 和 Jupyter Notebook,并查看 Jupyter Notebooks 的一些高级功能。
在本章导言中,我们将涵盖以下内容:
-
理解数据分析
-
数据分析的标准过程
-
KDD 过程
-
SEMMA
-
CRISP-DM
-
比较数据分析与数据科学
-
数据分析师和数据科学家的技能要求
-
安装 Python 3
-
本书使用的软件
-
使用 IPython 作为 Shell
-
使用 Jupyter Lab
-
使用 Jupyter Notebooks
-
Jupyter Notebooks 的高级功能
开始吧!
理解数据分析
21 世纪是信息的时代。我们正处于信息时代,这意味着我们日常生活的几乎每个方面都在生成数据。不仅如此,商业运营、政府运营和社交媒体的发布也在生成大量数据。这些数据随着业务、政府、科学、工程、健康、社会、气候和环境活动的不断生成而日积月累。在这些决策领域中,我们需要一个系统化、通用、高效且灵活的分析和科学处理系统,以便能够从生成的数据中获得有价值的见解。
在今天的智能世界里,数据分析为商业和政府运营提供了有效的决策过程。数据分析是检查、预处理、探索、描述和可视化给定数据集的活动。数据分析过程的主要目标是发现决策所需的信息。数据分析提供了多种方法、工具和技术,所有这些都可以应用于商业、社会科学和基础科学等不同领域。
让我们来看看 Python 生态系统中的一些核心基础数据分析库:
-
NumPy:这是“数字化 Python”的缩写。它是 Python 中最强大的科学计算库,能够高效处理多维数组、矩阵,并进行数学计算。
-
SciPy:这是一个强大的科学计算库,用于执行科学、数学和工程操作。
-
Pandas:这是一个数据探索和处理库,提供如 DataFrame 等表格数据结构,并提供多种数据分析和处理方法。
-
Scikit-learn:代表“机器学习科学工具包”。这是一个机器学习库,提供多种监督和无监督算法,如回归、分类、降维、聚类分析和异常检测。
-
Matplotlib:这是核心的数据可视化库,是 Python 中所有其他可视化库的基础库。它提供 2D 和 3D 图表、图形、图示和数据探索图表,运行在 NumPy 和 SciPy 之上。
-
Seaborn:基于 Matplotlib,提供易于绘制的高级互动图表和更有组织的可视化。
-
Plotly:Plotly 是一个数据可视化库,提供高质量的互动图表,如散点图、折线图、条形图、直方图、箱线图、热力图和子图。
安装所需库和软件的说明将在本书中按需提供。同时,我们将讨论各种数据分析过程,如标准过程、KDD、SEMMA 和 CRISP-DM。
数据分析的标准过程
数据分析是指对数据进行调查,从中发现有意义的洞察并得出结论。该过程的主要目标是收集、过滤、清理、转换、探索、描述、可视化并传达从数据中获得的洞察,以发现决策所需的信息。一般来说,数据分析过程包括以下几个阶段:
-
数据收集:从多个来源收集并整理数据。
-
数据预处理:过滤、清理并将数据转换为所需的格式。
-
数据分析与发现洞察:探索、描述和可视化数据,发现洞察并得出结论。
-
洞察解读:理解洞察,并找出每个变量对系统的影响。
-
讲故事:以故事的形式传达你的结果,使普通人也能理解。
我们可以通过以下过程图来总结数据分析过程的这些步骤:
本节中,我们已经涵盖了标准的数据分析过程,重点是寻找可解释的洞察并将其转化为用户故事。在接下来的章节中,我们将讨论 KDD 过程。
KDD 过程
KDD的全称是数据中的知识发现或数据库中的知识发现。许多人将 KDD 视为数据挖掘的同义词。数据挖掘被称为发现有趣模式的知识发现过程。KDD 的主要目标是从大型数据库、数据仓库以及其他 Web 和信息存储库中提取或发现隐藏的有趣模式。KDD 过程包括七个主要阶段:
-
数据清洗:在此第一阶段,数据进行预处理。在这里,噪声被去除,缺失值得到处理,异常值被检测。
-
数据集成:在此阶段,来自不同来源的数据通过数据迁移和 ETL 工具进行整合。
-
数据选择:在此阶段,相关的分析任务数据被重新收集。
-
数据转换:在此阶段,数据被转换为适合分析的所需形式。
-
数据挖掘:在此阶段,使用数据挖掘技术发现有用的未知模式。
-
模式评估:在此阶段,提取的模式将被评估。
-
知识呈现:在模式评估之后,提取的知识需要进行可视化并呈现给业务人员,以便做出决策。
完整的 KDD 过程如下图所示:
KDD 是一个迭代过程,旨在提升数据质量、集成和转换,以获取更优的系统。现在,让我们讨论 SEMMA 过程。
SEMMA
SEMMA的全称是样本、探索、修改、建模和评估。这个顺序的数据挖掘过程是由 SAS 公司开发的。SEMMA 过程包括五个主要阶段:
-
样本:在此阶段,我们识别不同的数据库并将它们合并。之后,我们选择足够的用于建模过程的数据样本。
-
探索:在此阶段,我们理解数据,发现变量之间的关系,进行数据可视化,并获得初步的解释。
-
修改:在此阶段,数据为建模准备好。该阶段涉及处理缺失值、检测异常值、转换特征和创建新的附加特征。
-
建模:在此阶段,主要关注选择和应用不同的建模技术,例如线性回归、逻辑回归、反向传播网络、KNN、支持向量机、决策树和随机森林。
-
评估:在最后阶段,开发的预测模型通过性能评估指标进行评估。
下图展示了这一过程:
上图展示了 SEMMA 过程中的各个步骤。SEMMA 强调模型构建和评估。现在,让我们讨论 CRISP-DM 过程。
CRISP-DM
CRISP-DM的全称是跨行业数据挖掘过程。CRISP-DM 是一个经过明确定义、结构良好、被验证的机器学习、数据挖掘和商业智能项目过程。它是一种强大、灵活、循环、有用且实用的方法,用于解决商业问题。该过程从多个数据库中发现隐藏的有价值的信息或模式。CRISP-DM 过程包含六个主要阶段:
-
商业理解:在第一阶段,主要目标是理解商业场景和需求,为设计分析目标和初步行动计划做准备。
-
数据理解:在这个阶段,主要目标是理解数据及其收集过程,进行数据质量检查,并获得初步的见解。
-
数据准备:在这个阶段,主要目标是准备适合分析的数据。这包括处理缺失值、检测并处理异常值、数据归一化以及特征工程。对于数据科学家/分析师而言,这一阶段是最耗时的。
-
建模:这是整个过程中最激动人心的阶段,因为这是设计预测模型的环节。首先,分析师需要决定建模技术,并根据数据开发模型。
-
评估:一旦模型开发完成,就该评估和测试模型在验证数据和测试数据上的表现,使用诸如 MSE、RMSE、R-Square(回归)以及准确率、精确率、召回率和 F1 值等模型评估指标。
-
部署:在最后阶段,前一步选择的模型将被部署到生产环境中。这需要数据科学家、软件开发人员、DevOps 专家和商业专业人士的团队协作。
以下图表展示了 CRISP-DM 过程的完整周期:
标准过程侧重于通过故事的形式发现洞察并进行解释,而 KDD 则侧重于基于数据的模式发现并进行可视化。SEMMA 主要关注模型构建任务,而 CRISP-DM 则专注于商业理解和部署。现在我们了解了与数据分析相关的一些过程,让我们比较一下数据分析和数据科学,了解它们之间的关系以及彼此的区别。
比较数据分析与数据科学
数据分析是探索数据的过程,目的是发现帮助我们做出业务决策的模式。它是数据科学的一个子领域。数据分析方法和工具被业务分析师、数据科学家和研究人员广泛应用于多个业务领域。其主要目标是提高生产力和利润。数据分析从不同来源提取和查询数据,进行探索性数据分析,数据可视化,准备报告,并呈现给业务决策者。
另一方面,数据科学是一个跨学科领域,采用科学方法从结构化和非结构化数据中提取洞察力。数据科学是所有相关领域的集合,包括数据分析、数据挖掘、机器学习和其他相关领域。数据科学不仅限于探索性数据分析,还用于开发模型和预测算法,如股票价格、天气、疾病、欺诈预测,以及推荐系统,如电影、书籍和音乐推荐。
数据分析师和数据科学家的角色
数据分析师收集、过滤、处理数据,并应用所需的统计概念,从数据中捕捉模式、趋势和洞察力,并准备报告以支持决策。数据分析师的主要目标是帮助公司通过发现的模式和趋势解决业务问题。数据分析师还评估数据质量,并处理与数据采集相关的问题。数据分析师应精通编写 SQL 查询、发现模式、使用可视化工具,以及使用报告工具,如 Microsoft Power BI、IBM Cognos、Tableau、QlikView、Oracle BI 等。
数据科学家比数据分析师更具技术性和数学性。数据科学家偏向于研究和学术,而数据分析师则更加偏向于应用。数据科学家通常需要预测未来事件,而数据分析师则从数据中提取重要洞察。数据科学家提出自己的问题,而数据分析师回答给定的问题。最后,数据科学家关注将要发生什么,而数据分析师关注迄今为止已经发生了什么。我们可以通过以下表格总结这两个角色:
特点 | 数据科学家 | 数据分析师 |
---|---|---|
背景 | 基于数据预测未来事件和场景 | 从数据中发现有意义的洞察 |
角色 | 提出有利于业务的问题 | 解决业务问题以作出决策 |
数据类型 | 处理结构化和非结构化数据 | 只处理结构化数据 |
编程 | 高级编程 | 基础编程 |
技能集 | 统计学、机器学习算法、自然语言处理和深度学习知识 | 统计学、SQL 和数据可视化知识 |
工具 | R、Python、SAS、Hadoop、Spark、TensorFlow 和 Keras | Excel、SQL、R、Tableau 和 QlikView |
现在我们已经了解了数据分析师和数据科学家的定义,以及它们之间的区别,让我们来看看成为其中一员所需要的各种技能。
数据分析师与数据科学家的技能集
数据分析师是通过数据发现洞察并创造价值的人。这有助于决策者了解业务表现如何。数据分析师必须掌握以下技能:
-
探索性数据分析(EDA):EDA 是数据分析师的一个重要技能。它有助于检查数据以发现模式、检验假设并确保假设的正确性。
-
关系型数据库:至少了解一种关系型数据库工具,如 MySQL 或 Postgre,是必须的。SQL 是处理关系型数据库时必备的技能。
-
可视化和商业智能工具:一图胜千言。视觉效果对人类有更大的影响,且视觉是表达洞察的清晰简便的方式。像 Tableau、QlikView、MS Power BI 和 IBM Cognos 这样的可视化和商业智能工具可以帮助分析师进行可视化并准备报告。
-
电子表格:了解 MS Excel、WPS、Libra 或 Google Sheets 是必不可少的,它们用于以表格形式存储和管理数据。
-
讲故事与展示技巧:讲故事的艺术是另一个必备技能。数据分析师应该擅长将数据事实与一个想法或事件联系起来,并将其转化为一个故事。
另一方面,数据科学家的主要工作是通过数据解决问题。为了做到这一点,他们需要了解客户的需求、领域、问题空间,并确保获得他们真正需要的东西。数据科学家的任务因公司而异。有些公司使用数据分析师,并仅将“数据科学家”这一职称用来提升职位的光环。还有一些公司将数据分析师的任务与数据工程师的工作结合起来,并赋予数据科学家这一职称;还有一些则将他们分配到以机器学习为主的数据可视化任务中。
数据科学家的任务因公司而异。有些公司将数据科学家视为知名的数据分析师,并将其职责与数据工程师结合起来。其他公司则让他们执行机器上的密集型数据可视化任务。
数据科学家必须是多面手,能够担任多个角色,包括数据分析师、统计学家、数学家、程序员、机器学习或自然语言处理工程师。大多数人并不在所有这些领域都有足够的技能或是专家。而且,要具备足够的技能需要大量的努力和耐心。这就是为什么数据科学无法在 3 到 6 个月内学成的原因。学习数据科学是一个过程。数据科学家应该拥有多种技能,诸如以下这些:
-
数学与统计学:大多数机器学习算法都基于数学和统计学。数学知识帮助数据科学家开发定制的解决方案。
-
数据库:掌握 SQL 知识,能让数据科学家与数据库交互,收集数据以进行预测和推荐。
-
机器学习:了解监督学习技术,如回归分析、分类技术,以及无监督学习技术,如聚类分析、异常值检测和降维。
-
编程技能:编程知识帮助数据科学家自动化其推荐的解决方案。建议掌握 Python 和 R。
-
故事讲述与演讲技巧:通过 PowerPoint 演示文稿以故事讲述的形式传达结果。
-
大数据技术:了解 Hadoop 和 Spark 等大数据平台,有助于数据科学家为大型企业开发大数据解决方案。
-
深度学习工具:深度学习工具,如 Tensorflow 和 Keras,在自然语言处理(NLP)和图像分析中得到应用。
除了这些技能外,数据科学专业人员还需要掌握网络抓取工具/包,用于从不同来源提取数据,以及用于设计原型解决方案的 Web 应用框架,如 Flask 或 Django。这些都是数据科学专业人员所需的技能。
现在我们已经介绍了数据分析和数据科学的基础知识,接下来让我们深入了解数据分析所需的基本设置。在下一节中,我们将学习如何安装 Python。
安装 Python 3
安装 Python 3 的安装程序文件可以从官方网站(www.python.org/downloads/
)轻松下载,支持 Windows、Linux 和 Mac 32 位或 64 位系统。只需双击该安装程序即可进行安装。该安装程序还包含一个名为 "IDLE" 的 IDE,可用于开发。在接下来的几个部分中,我们将深入探讨每个操作系统的安装方法。
Windows 上的 Python 安装与设置
本书基于最新的 Python 3 版本。书中的所有代码都是用 Python 3 编写的,因此在开始编码之前,我们需要先安装 Python 3。Python 是一种开源、分发和免费使用的语言,并且具有商业使用许可。Python 有许多实现,包括商业实现和分发版本。在本书中,我们将专注于标准的 Python 实现,它保证与 NumPy 兼容。
您可以从 Python 官方网站下载 Python 3.9.x:www.python.org/downloads/
。在这里,您可以找到适用于 Windows、Linux、Mac OS X 和其他操作系统平台的安装文件。您还可以在 docs.python.org/3.7/using/index.html
找到有关如何安装和使用 Python 的说明。
您需要在系统上安装 Python 3.5.x 或更高版本。Python 2.7 的支持结束日期从 2015 年推迟到 2020 年,但在撰写本书时,Python 2.7 将不再由 Python 社区提供支持和维护。
在撰写本书时,我们在 Windows 10 虚拟机上安装了 Python 3.8.3 作为先决条件:www.python.org/ftp/python/3.8.3/python-3.8.3.exe
。
在 Linux 上安装和设置 Python
与其他操作系统相比,在 Linux 上安装 Python 要容易得多。要安装基础库,请运行以下命令行指令:
$ pip3 install numpy scipy pandas matplotlib jupyter notebook
如果您在使用的机器上没有足够的权限,在执行上述命令之前,可能需要运行sudo
命令。
使用 GUI 安装程序在 Mac OS X 上安装和设置 Python
Python 可以通过从 Python 官方网站下载的安装文件进行安装。安装文件可以从其官方网页(www.python.org/downloads/mac-osx/
)下载,适用于 macOS。此安装程序还包含一个名为 "IDLE" 的 IDE,可用于开发。
在 Mac OS X 上使用 brew 安装和设置 Python
对于 Mac 系统,您可以使用 Homebrew 包管理器来安装 Python。它将简化为开发人员、研究人员和科学家安装所需应用程序的过程。brew install
命令用于安装其他应用程序,例如安装 python3 或任何其他 Python 包,如 NLTK 或 SpaCy。
要安装最新版本的 Python,您需要在终端执行以下命令:
$ brew install python3
安装后,您可以通过运行以下命令来确认您已安装的 Python 版本:
$ python3 --version
Python 3.7.4
您还可以通过运行以下命令从命令行打开 Python Shell:
$ python3
现在我们知道如何在系统上安装 Python,让我们深入了解开始数据分析所需的实际工具。
本书中使用的软件
让我们讨论一下本书中将使用的软件。在本书中,我们将使用 Anaconda IDE 来进行数据分析。在安装之前,我们先了解一下什么是 Anaconda。
只要系统安装了 Python 程序,Python 程序就可以轻松运行。我们可以在记事本上编写程序并在命令提示符上运行它。我们还可以在不同的 IDE(如 Jupyter Notebook、Spyder 和 PyCharm)上编写和运行 Python 程序。Anaconda 是一个免费可用的开源软件包,包含各种数据处理 IDE 和一些用于数据分析的包,如 NumPy、SciPy、Pandas、Scikit-learn 等等。Anaconda 可以轻松下载和安装,具体如下:
-
从
www.anaconda.com/distribution/
下载安装程序。 -
选择您正在使用的操作系统。
-
从 Python 3.7 版本部分选择 32 位或 64 位安装程序选项并开始下载。
-
双击运行安装程序。
-
安装完成后,在“开始”菜单中检查您的程序或搜索 Anaconda。
Anaconda 还有一个 Anaconda Navigator,这是一个桌面 GUI 应用程序,用于启动应用程序,如 Jupyter Notebook、Spyder、Rstudio、Visual Studio Code 和 JupyterLab:
现在,让我们看一下 IPython,这是一个基于 shell 的计算环境,用于数据分析。
使用 IPython 作为 shell
IPython 是一个交互式 shell,相当于 Matlab 或 Mathematica 等交互式计算环境。这个交互式 shell 是为快速实验而创建的。对于进行小型实验的数据专业人员非常有用。
IPython shell 提供以下功能:
-
方便访问系统命令。
-
轻松编辑内联命令。
-
Tab 键自动完成,帮助您查找命令并加速任务。
-
命令历史记录,帮助您查看先前使用的命令列表。
-
轻松执行外部 Python 脚本。
-
使用 Python 调试器轻松调试。
现在,让我们在 IPython 上执行一些命令。要启动 IPython,请在命令行上使用以下命令:
$ ipython3
当您运行上述命令时,将出现以下窗口:
现在,让我们了解并执行 IPython shell 提供的一些命令:
- 历史命令: 使用
history
命令来查看以前使用过的命令列表。下面的截图显示了如何在 IPython 中使用history
命令:
- 系统命令: 我们还可以使用感叹号 (
!
) 从 IPython 中运行系统命令。在这里,感叹号后的输入命令被视为系统命令。例如,!date
将显示系统的当前日期,而!pwd
将显示当前工作目录:
- 编写函数: 我们可以像在任何 IDE 中(如 Jupyter Notebook、Python IDLE、PyCharm 或 Spyder)编写函数一样编写它们。让我们看一个函数的例子:
- 退出 Ipython Shell: 你可以使用
quit()
或exit()
,或者按CTRL + D来退出或退出 IPython shell:
你还可以使用quit()
命令退出 IPython shell:
在本小节中,我们已经了解了一些可以在 IPython shell 中使用的基本命令。现在,让我们讨论如何在 IPython shell 中使用help
命令。
阅读手册页
在 IPython shell 中,我们可以使用help
命令打开可用命令列表。并不需要写出函数的完整名称。你只需输入几个初始字符,然后按tab键,它就会找到你正在寻找的词。例如,让我们使用arrange()
函数。我们可以通过两种方式来获取关于函数的帮助:
- 使用帮助功能: 让我们输入
help
并写入函数的几个初始字符。然后,按tab键,使用箭头键选择一个函数,按Enter键:
- 使用问号: 我们还可以在函数名称后使用问号。以下截图展示了这个例子:
在本小节中,我们了解了为模块函数提供的帮助和问号支持。我们还可以通过库文档获得帮助。让我们讨论如何获取 Python 数据分析库的文档。
在哪里找到 Python 数据分析库的帮助和参考资料
以下表格列出了我们在本章中讨论的 Python 数据分析库的文档网站:
包/软件 | 描述 |
---|---|
NumPy | numpy.org/doc/ |
SciPy | docs.scipy.org/doc/ |
Pandas | pandas.pydata.org/docs/ |
Matplotlib | matplotlib.org/3.2.1/contents.html |
Seaborn | seaborn.pydata.org/ |
Scikit-learn | scikit-learn.org/stable/ |
Anaconda | www.anaconda.com/distribution/ |
你还可以在 StackOverflow 平台上找到与 NumPy、SciPy、Pandas、Matplotlib、Seaborn 和 Scikit-learn 相关的各种 Python 编程问题的答案。你还可以在 GitHub 上提出与上述库相关的问题。
使用 JupyterLab
JupyterLab 是一个下一代基于 Web 的用户界面。它提供了一组合适的数据分析和机器学习产品开发工具,如文本编辑器、笔记本、代码控制台和终端。它是一个灵活且强大的工具,应该成为任何数据分析师工具包的一部分:
你可以使用conda
、pip
或pipenv
来安装 JupyterLab。
要使用conda
安装,可以使用以下命令:
$ conda install -c conda-forge jupyterlab
要使用pip
安装,可以使用以下命令:
$ pip install jupyterlab
要使用pipenv
安装,可以使用以下命令:
$ pipenv install jupyterlab
在这一节中,我们已经学习了如何安装 Jupyter Lab。在下一节中,我们将重点介绍 Jupyter Notebooks。
使用 Jupyter Notebooks
Jupyter Notebook 是一个 Web 应用程序,用于创建包含代码、文本、图形、链接、数学公式和图表的数据分析笔记本。最近,社区推出了基于 Web 的下一代 Jupyter Notebooks,称为 JupyterLab。你可以通过以下链接查看这些笔记本集:
这些笔记本通常作为教育工具使用,或者用于展示 Python 软件。我们可以从纯 Python 代码或特殊的笔记本格式导入或导出笔记本。笔记本可以在本地运行,或者我们可以通过运行专用的笔记本服务器使它们在网上可用。一些云计算解决方案,如 Wakari、PiCloud 和 Google Colaboratory,允许你在云端运行笔记本。
"Jupyter" 是一个首字母缩略词,代表 Julia、Python 和 R。最初,开发者为这三种语言实现了它,但现在,它也可以用于其他多种语言,包括 C、C++、Scala、Perl、Go、PySpark 和 Haskell:
Jupyter Notebook 提供以下功能:
-
它具有在浏览器中编辑代码并保持正确缩进的能力。
-
它具有从浏览器执行代码的能力。
-
它具有在浏览器中显示输出的能力。
-
它可以在单元格输出中渲染图形、图像和视频。
-
它具有以 PDF、HTML、Python 文件和 LaTex 格式导出代码的能力。
我们还可以通过在 Anaconda 提示符中运行以下命令,在 Jupyter Notebooks 中同时使用 Python 2 和 3:
# For Python 2.7
conda create -n py27 python=2.7 ipykernel
# For Python 3.5
conda create -n py35 python=3.5 ipykernel
现在我们已经了解了各种工具和库,并且也安装了 Python,让我们进入最常用工具之一 Jupyter Notebooks 的一些高级功能。
Jupyter Notebooks 的高级功能
Jupyter Notebook 提供了各种高级功能,例如快捷键、安装其他内核、执行 shell 命令和使用各种扩展来加快数据分析操作。让我们开始逐一了解这些功能。
快捷键
用户可以通过选择帮助菜单中的“快捷键”选项,或者使用Cmd + Shift + P快捷键,在 Jupyter Notebook 内找到所有可用的快捷命令。这将显示快速选择栏,其中包含所有快捷命令以及每个命令的简要说明。使用这个栏非常简单,用户可以在忘记某些快捷键时使用它:
安装其他内核
Jupyter 可以运行多个内核支持不同的语言。使用 Anaconda 设置特定语言的环境非常容易。例如,可以通过在 Anaconda 中运行以下命令设置 R 内核:
$ conda install -c r r-essentials
然后应会出现 R 内核,如下图所示:
运行 shell 命令
在 Jupyter Notebook 中,用户可以运行 Unix 和 Windows 的 shell 命令。shell 提供了一个与计算机进行通信的接口。用户在运行任何命令前需要加上 !
(感叹号):
Notebook 扩展
Notebook 扩展(或 nbextensions)相比基础的 Jupyter Notebook 增加了更多功能。这些扩展提升了用户的体验和界面。用户可以通过选择 NBextensions 标签轻松选择任何扩展。
在 Jupyter Notebook 中使用 conda
安装 nbextension
,运行以下命令:
conda install -c conda-forge jupyter_nbextensions_configurator
在 Jupyter Notebook 中使用 pip
安装 nbextension
,运行以下命令:
pip install jupyter_contrib_nbextensions && jupyter contrib nbextension install
如果在 macOS 上遇到权限错误,只需运行以下命令:
pip install jupyter_contrib_nbextensions && jupyter contrib nbextension install --user
所有可配置的 nbextensions 将显示在一个不同的标签中,如下图所示:
现在,让我们探索 Notebook 扩展的一些有用功能:
- Hinterland:此扩展为每个按键提供自动完成菜单,像 PyCharm 一样在单元格中工作:
- 目录:此扩展会在侧边栏或导航菜单中显示所有标题。它可以调整大小、拖动、折叠,并且可以停靠:
- 执行时间:此扩展显示单元格执行的时间以及完成单元格代码所需的时间:
-
拼写检查器:拼写检查器会检查并验证每个单元格中书写的拼写,并高亮显示任何拼写错误的单词。
-
变量选择器:此扩展跟踪用户的工作空间。它显示用户创建的所有变量的名称,并显示它们的类型、大小、形状和数值。
- 幻灯片放映:Notebook 结果可以通过幻灯片放映进行展示。这是讲故事的绝佳工具。用户可以轻松将 Jupyter Notebooks 转换为幻灯片,而无需使用 PowerPoint。如以下截图所示,可以通过视图菜单中的单元格工具栏选项启动幻灯片放映:
Jupyter Notebook 还允许你在幻灯片中显示或隐藏任何单元格。将幻灯片选项添加到视图菜单的单元格工具栏后,你可以在每个单元格中使用幻灯片类型下拉列表,并选择不同的选项,如下图所示:
- 嵌入 PDF 文档:Jupyter Notebook 用户可以轻松添加 PDF 文档。以下语法需要运行以添加 PDF 文档:
from IPython.display import IFrame
IFrame('https://arxiv.org/pdf/1811.02141.pdf', width=700, height=400)
这将产生如下输出:
- 嵌入 YouTube 视频:Jupyter Notebook 用户可以轻松添加 YouTube 视频。以下语法需要运行以添加 YouTube 视频:
from IPython.display import YouTubeVideo
YouTubeVideo('ukzFI9rgwfU', width=700, height=400)
这将产生如下输出:
通过这些,你现在了解了数据分析及其所涉及的过程,以及它所包含的角色。你还学习了如何安装 Python 并使用 Jupyter Lab 和 Jupyter Notebook。在接下来的章节中,你将进一步学习各种 Python 库和数据分析技术。
总结
在本章中,我们讨论了各种数据分析过程,包括 KDD、SEMMA 和 CRISP-DM。接着我们讨论了数据分析师和数据科学家的角色与技能要求。随后,我们安装了 NumPy、SciPy、Pandas、Matplotlib、IPython、Jupyter Notebook、Anaconda 和 Jupyter Lab,这些都是我们在本书中将要使用的工具。你也可以选择安装 Anaconda 或 Jupyter Lab,它们自带了 NumPy、Pandas、SciPy 和 Scikit-learn。
接着,我们实现了一个向量加法程序,并了解到 NumPy 相比其他库提供了更优的性能。我们还探讨了可用的文档和在线资源。此外,我们讨论了 Jupyter Lab、Jupyter Notebook 及其功能。
在下一章,第二章,NumPy 和 Pandas 中,我们将深入了解 NumPy 和 Pandas 的内部实现,并探索一些与数组和数据框(DataFrames)相关的基本概念。
第二章:NumPy 和 pandas
现在我们已经理解了数据分析、数据分析过程以及在不同平台上的安装,是时候学习 NumPy 数组和 pandas
DataFrames 了。本章将带你了解 NumPy 数组和 pandas
DataFrames 的基础知识。在本章结束时,你将对 NumPy 数组、pandas
DataFrames 及其相关函数有基本的理解。
pandas
以面板数据(一个计量经济学术语)和 Python 数据分析命名,是一个流行的开源 Python 库。在本章中,我们将学习 pandas
的基本功能、数据结构和操作。官方的 pandas
文档坚持将该项目命名为全小写的 pandas
。pandas
项目坚持的另一个惯例是使用 import pandas as pd
的导入语句。
本章我们将重点讨论以下主题:
-
理解 NumPy 数组
-
NumPy 数组数值数据类型
-
操作数组形状
-
NumPy 数组的堆叠
-
划分 NumPy 数组
-
更改 NumPy 数组的数据类型
-
创建 NumPy 视图和副本
-
切片 NumPy 数组
-
布尔和花式索引
-
广播数组
-
创建
pandas
DataFrames -
理解
pandas
Series -
阅读和查询 Quandl 数据
-
描述
pandas
DataFrames -
分组和连接
pandas
DataFrames -
处理缺失值
-
创建数据透视表
-
处理日期
技术要求
本章具有以下技术要求:
-
你可以在以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter02
。 -
所有代码块都可以在
ch2.ipynb
中找到。 -
本章使用四个 CSV 文件(
WHO_first9cols.csv
、dest.csv
、purchase.csv
和tips.csv
)进行练习。 -
本章我们将使用 NumPy、
pandas
和 Quandl Python 库。
理解 NumPy 数组
可以使用 pip
或 brew
在 PC 上安装 NumPy,但如果用户使用 Jupyter Notebook,则无需安装。NumPy 已经预装在 Jupyter Notebook 中。我建议你使用 Jupyter Notebook 作为 IDE,因为我们将在 Jupyter Notebook 中执行所有代码。我们在第一章中,Python 库入门,已经展示了如何安装 Anaconda,这是一个完整的数据分析套件。NumPy 数组是一系列同质的项目。同质意味着数组中的所有元素都是相同的数据类型。让我们使用 NumPy 创建一个数组。你可以使用 array()
函数结合一个项目列表来创建数组。用户还可以固定数组的数据类型。可能的数据类型有 bool
、int
、float
、long
、double
和 long double
。
让我们来看一下如何创建一个空数组:
# Creating an array
import numpy as np
a = np.array([2,4,6,8,10])
print(a)
Output:
[ 2 4 6 8 10]
创建 NumPy 数组的另一种方法是使用 arange()
。它会创建一个均匀间隔的 NumPy 数组。可以传递三个值 —— start、stop 和 step —— 给 arange(start,[stop],step)
函数。start 是范围的起始值,stop 是范围的结束值,而 step 是该范围内的增量。stop 参数是必须的。在以下示例中,我们使用 1
作为起始值,11
作为结束值。arange(1,11)
函数将返回从 1 到 10 的值,步长默认为 1。arange()
函数会生成一个比 stop 参数值小 1 的值。让我们通过以下示例来理解这一点:
# Creating an array using arange()
import numpy as np
a = np.arange(1,11)
print(a)
Output:
[ 1 2 3 4 5 6 7 8 9 10]
除了 array()
和 arange()
函数外,还有其他选项,如 zeros()
、ones()
、full()
、eye()
和 random()
,这些也可以用来创建 NumPy 数组,因为这些函数是初始占位符。以下是每个函数的详细说明:
-
zeros()
:zeros()
函数为给定维度创建一个全为 0 的数组。 -
ones()
:ones()
函数为给定维度创建一个全为 1 的数组。 -
fulls()
:full()
函数生成一个具有常数值的数组。 -
eyes()
:eye()
函数创建一个单位矩阵。 -
random()
:random()
函数创建一个具有任意给定维度的数组。
让我们通过以下示例来理解这些函数:
import numpy as np
# Create an array of all zeros
p = np.zeros((3,3))
print(p)
# Create an array of all ones
q = np.ones((2,2))
print(q)
# Create a constant array
r = np.full((2,2), 4)
print(r)
# Create a 2x2 identity matrix
s = np.eye(4)
print(s)
# Create an array filled with random values
t = np.random.random((3,3))
print(t)
这将产生以下输出:
[[0\. 0\. 0.]
[0\. 0\. 0.]
[0\. 0\. 0.]]
[[1\. 1.]
[1\. 1.]]
[[4 4]
[4 4]]
[[1\. 0\. 0\. 0.]
[0\. 1\. 0\. 0.]
[0\. 0\. 1\. 0.]
[0\. 0\. 0\. 1.]]
[[0.16681892 0.00398631 0.61954178]
[0.52461924 0.30234715 0.58848138]
[0.75172385 0.17752708 0.12665832]]
在前面的代码中,我们已经看到了一些用于创建全零值、全一值和全常数值数组的内置函数。之后,我们使用 eye()
函数创建了一个单位矩阵,并使用 random.random()
函数创建了一个随机矩阵。接下来,我们将在下一节看到一些其他的数组特性。
数组特性
一般来说,NumPy 数组是一种同质的数据结构,所有项的类型相同。数组的主要优点是其存储大小的确定性,因为它们包含相同类型的项。Python 列表使用循环来迭代元素并对其执行操作。NumPy 数组的另一个优点是提供矢量化操作,而不是逐个迭代每个项并对其进行操作。NumPy 数组的索引方式与 Python 列表一样,从 0 开始。NumPy 使用优化的 C API 来加速数组操作的处理。
让我们像前面章节一样,使用 arange()
函数创建一个数组,并检查它的数据类型:
# Creating an array using arange()
import numpy as np
a = np.arange(1,11)
print(type(a))
print(a.dtype)
Output: <class 'numpy.ndarray'>
int64
当你使用 type()
时,它会返回 numpy.ndarray
。这意味着 type()
函数返回的是容器的类型。当你使用 dtype()
时,它将返回 int64
,因为它是元素的类型。如果你使用的是 32 位的 Python,你也可能得到 int32
作为输出。这两种情况都使用整数(32 位和 64 位)。一维的 NumPy 数组也被称为向量。
让我们找出几分钟前我们生成的向量的形状:
print(a.shape)
Output: (10,)
如你所见,该向量有 10 个元素,值范围从 1 到 10。数组的 shape
属性是一个元组;在这个例子中,它是一个包含一个元素的元组,表示每个维度的长度。
选择数组元素
在这一部分,我们将学习如何选择数组中的元素。让我们看一个 2*2 矩阵的例子:
a = np.array([[5,6],[7,8]])
print(a)
Output: [[5 6]
[7 8]]
在前面的例子中,矩阵是通过 array()
函数创建的,输入是一个包含列表的列表。
选择数组元素非常简单。我们只需要指定矩阵的索引 a[m,n]
。这里,m
是行索引,n
是列索引。我们现在将按顺序选择矩阵中的每个元素,如下代码所示:
print(a[0,0])
Output: 5
print(a[0,1])
Output: 6
printa([1,0])
Output: 7
printa([1,1])
Output: 8
在前面的代码示例中,我们尝试使用数组索引访问数组的每个元素。你也可以通过这里的图示理解这一点:
在前面的图示中,我们可以看到它有四个块,每个块代表一个数组元素。每个块中的数值显示其索引。
在这一部分,我们已经了解了数组的基本概念。现在,让我们继续学习数值数据类型的数组。
NumPy 数组数值数据类型
Python 提供了三种数值数据类型:整数类型、浮点类型和复数类型。在实际应用中,我们需要更多的数据类型来进行科学计算操作,以确保精度、范围和大小。NumPy 提供了大量的数学类型和数值类型。让我们看看下面的 NumPy 数值类型表格:
数据类型 | 详细信息 |
---|---|
bool |
这是一个布尔类型,存储一个比特,取值为 True 或 False 。 |
inti |
平台整数可以是 int32 或 int64 。 |
int8 |
字节存储的值范围从 -128 到 127 。 |
int16 |
该类型存储从 -32768 到 32767 的整数。 |
int32 |
该类型存储从 -2 ** 31 到 2 ** 31 -1 的整数。 |
int64 |
该类型存储从 -2 ** 63 到 2 ** 63 -1 的整数。 |
uint8 |
该类型存储无符号整数,范围从 0 到 255 。 |
uint16 |
该类型存储无符号整数,范围从 0 到 65535 。 |
uint32 |
该类型存储无符号整数,范围从 0 到 2 ** 32 – 1 。 |
uint64 |
该类型存储无符号整数,范围从 0 到 2 ** 64 – 1 。 |
float16 |
半精度浮点数;符号位,5 位指数和 10 位尾数。 |
float32 |
单精度浮点数;符号位,8 位指数和 23 位尾数。 |
float64 或 float |
双精度浮点数;符号位,11 位指数和 52 位尾数。 |
complex64 |
复数类型存储两个 32 位浮点数:实数和虚数。 |
complex128 或 complex |
复数类型存储两个 64 位浮点数:实数和虚数。 |
对于每种数据类型,都存在一个相应的转换函数:
print(np.float64(21))
Output: 21.0
print(np.int8(21.0))
Output: 42
print(np.bool(21))
Output: True
print(np.bool(0))
Output: False
print(np.bool(21.0))
Output: True
print(np.float(True))
Output: 1.0
print(np.float(False))
Output: 0.0
许多函数都有一个数据类型参数,通常是可选的:
arr=np.arange(1,11, dtype= np.float32)
print(arr)
Output:
[ 1\. 2\. 3\. 4\. 5\. 6\. 7\. 8\. 9\. 10.]
需要注意的是,你不能将复数转换为整数。如果尝试将复合数据类型转换为整数,将会遇到 TypeError
。我们来看一下以下示例:
np.int(42.0 + 1.j)
这将产生以下输出:
如果你尝试将复数转换为浮点数,也会遇到同样的错误。
但是,你可以通过设置各个部分,将浮动值转换为复数。你也可以通过 real
和 imag
属性提取这些部分。我们通过以下示例来看一下:
c= complex(42, 1)
print(c)
Output: (42+1j)
print(c.real,c.imag)
Output: 42.0 1.0
在前面的示例中,你已经使用 complex()
方法定义了一个复数。同时,你通过 real
和 imag
属性提取了实部和虚部。接下来,让我们来看看 dtype
对象。
dtype
对象
我们在本章的前面部分已经看到,dtype
告诉我们数组中单个元素的类型。NumPy 数组的元素具有相同的数据类型,这意味着所有元素都有相同的 dtype
。dtype
对象是 numpy.dtype
类的实例:
# Creating an array
import numpy as np
a = np.array([2,4,6,8,10])
print(a.dtype)
Output: 'int64'
dtype
对象还通过 itemsize
属性告诉我们数据类型的字节大小:
print(a.dtype.itemsize)
Output:8
数据类型字符代码
字符代码是为了与 Numeric 兼容。Numeric 是 NumPy 的前身。虽然不推荐使用它,但代码仍然被提供,因为它在不同的地方会出现。你应该使用 dtype
对象。以下表格列出了几种不同的数据类型及其相关字符代码:
类型 | 字符代码 |
---|---|
整数 | i |
无符号整数 | u |
单精度浮动 | f |
双精度浮动 | d |
布尔值 | b |
复数 | D |
字符串 | S |
Unicode | U |
空类型 | V |
我们来看以下代码,生成单精度浮动数组:
# Create numpy array using arange() function
var1=np.arange(1,11, dtype='f')
print(var1)
Output:
[ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]
同样,以下代码创建了一个复数数组:
print(np.arange(1,6, dtype='D'))
Output:
[1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 5.+0.j]
dtype
构造函数
有许多方法可以通过构造函数创建数据类型。构造函数用于实例化或赋值给对象。在本节中,我们将通过浮动数据类型的例子来理解数据类型的创建:
- 要尝试一个通用的 Python 浮动,请使用以下代码:
print(np.dtype(float))
Output: float64
- 要尝试带有字符代码的单精度浮动,请使用以下代码:
print(np.dtype('f'))
Output: float32
- 要尝试带有字符代码的双精度浮动,请使用以下代码:
print(np.dtype('d'))
Output: float64
- 要尝试带有双字符代码的
dtype
构造函数,请使用以下代码:
print(np.dtype('f8'))
Output: float64
这里,第一个字符代表类型,第二个字符是指定类型字节数的数字,例如 2、4 或 8。
dtype
属性
dtype
类提供了多个有用的属性。例如,我们可以通过 dtype
属性获取数据类型的字符代码信息:
# Create numpy array
var2=np.array([1,2,3],dtype='float64')
print(var2.dtype.char)
Output: 'd'
type
属性对应数组元素的对象类型:
print(var2.dtype.type)
Output: <class 'numpy.float64'>
现在我们已经了解了 NumPy 数组中使用的各种数据类型,让我们在下一节中开始操作它们。
操作数组形状
在本节中,我们的主要关注点是数组操作。让我们学习一些 NumPy 的新 Python 函数,如 reshape()
、flatten()
、ravel()
、transpose()
和 resize()
:
reshape()
会改变数组的形状:
# Create an array
arr = np.arange(12)
print(arr)
Output: [ 0 1 2 3 4 5 6 7 8 9 10 11]
# Reshape the array dimension
new_arr=arr.reshape(4,3)
print(new_arr)
Output: [[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]]
# Reshape the array dimension
new_arr2=arr.reshape(3,4)
print(new_arr2)
Output:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
- 另一种可以应用于数组的操作是
flatten()
。flatten()
将 n 维数组转换为一维数组:
# Create an array
arr=np.arange(1,10).reshape(3,3)
print(arr)
Output:
[[1 2 3]
[4 5 6]
[7 8 9]]
print(arr.flatten())
Output:
[1 2 3 4 5 6 7 8 9]
ravel()
函数类似于flatten()
函数。它也将 n 维数组转换为一维数组。主要区别在于,flatten()
返回的是实际的数组,而ravel()
返回的是原数组的引用。由于ravel()
不占用额外的内存,它比flatten()
更快:
print(arr.ravel())
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
transpose()
函数是一个线性代数函数,它将给定的二维矩阵进行转置。转置的意思是将行转化为列,将列转化为行:
# Transpose the matrix
print(arr.transpose())
Output:
[[1 4 7]
[2 5 8]
[3 6 9]]
resize()
函数改变 NumPy 数组的大小。它类似于reshape()
,但它会改变原数组的形状:
# resize the matrix
arr.resize(1,9)
print(arr)
Output:[[1 2 3 4 5 6 7 8 9]]
在本节的所有代码中,我们看到了用于操作大小的内置函数,如 reshape()
、flatten()
、ravel()
、transpose()
和 resize()
。现在,是时候学习 NumPy 数组的堆叠了。
NumPy 数组的堆叠
NumPy 提供了数组堆叠功能。堆叠是指沿着新的轴将具有相同维度的数组连接。堆叠可以在水平方向、垂直方向、列方向、行方向或深度方向进行:
- 水平堆叠:在水平堆叠中,具有相同维度的数组沿水平轴通过
hstack()
和concatenate()
函数连接。让我们来看下面的例子:
arr1 = np.arange(1,10).reshape(3,3)
print(arr1)
Output: [[1 2 3]
[4 5 6]
[7 8 9]]
我们已经创建了一个 33 的数组,现在是时候创建另一个 33 的数组了:
arr2 = 2*arr1
print(arr2)
Output: [[ 2 4 6]
[ 8 10 12]
[14 16 18]]
创建两个数组后,我们将进行水平堆叠:
# Horizontal Stacking
arr3=np.hstack((arr1, arr2))
print(arr3)
Output: [[ 1 2 3 2 4 6]
[ 4 5 6 8 10 12]
[ 7 8 9 14 16 18]]
在前面的代码中,两个数组沿 x 轴被水平堆叠。concatenate()
函数也可以用来生成水平堆叠,设置轴参数值为 1
:
# Horizontal stacking using concatenate() function
arr4=np.concatenate((arr1, arr2), axis=1)
print(arr4)
Output: [[ 1 2 3 2 4 6]
[ 4 5 6 8 10 12]
[ 7 8 9 14 16 18]]
在前面的代码中,两个数组通过 concatenate()
函数被水平堆叠。
- 垂直堆叠:在垂直堆叠中,具有相同维度的数组沿垂直轴通过
vstack()
和concatenate()
函数连接。让我们来看下面的例子:
# Vertical stacking
arr5=np.vstack((arr1, arr2))
print(arr5)
Output: [[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[ 2 4 6]
[ 8 10 12]
[14 16 18]]
在前面的代码中,两个数组沿 y 轴被垂直堆叠。concatenate()
函数也可以用来生成垂直堆叠,设置轴参数值为 0
:
arr6=np.concatenate((arr1, arr2), axis=0)
print(arr6)
Output: [[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[ 2 4 6]
[ 8 10 12]
[14 16 18]]
在前面的代码中,两个数组通过 concatenate()
函数被垂直堆叠。
- 深度堆叠:在深度堆叠中,具有相同维度的数组沿第三个轴(深度)通过
dstack()
函数连接。让我们来看下面的例子:
arr7=np.dstack((arr1, arr2))
print(arr7)
Output: [[[ 1 2]
[ 2 4]
[ 3 6]]
[[ 4 8]
[ 5 10]
[ 6 12]]
[[ 7 14]
[ 8 16]
[ 9 18]]]
在前面的代码中,两个数组沿着第三个轴(深度)进行堆叠。
- 按列堆叠:按列堆叠将多个一维数组按列堆叠成一个二维数组。让我们来看一个按列堆叠的例子:
# Create 1-D array
arr1 = np.arange(4,7)
print(arr1)
Output: [4, 5, 6]
在前面的代码块中,我们创建了一个一维的 NumPy 数组。
# Create 1-D array
arr2 = 2 * arr1
print(arr2)
Output: [ 8, 10, 12]
在前面的代码块中,我们创建了另一个一维的 NumPy 数组。
# Create column stack
arr_col_stack=np.column_stack((arr1,arr2))
print(arr_col_stack)
Output: [[ 4 8]
[ 5 10]
[ 6 12]]
在前面的代码中,我们创建了两个一维数组并按列堆叠它们。
- 按行堆叠:按行堆叠将多个一维数组按行堆叠成一个二维数组。让我们来看一个按行堆叠的例子:
# Create row stack
arr_row_stack = np.row_stack((arr1,arr2))
print(arr_row_stack)
Output: [[ 4 5 6]
[ 8 10 12]]
在前面的代码中,两个一维数组按行堆叠。
现在让我们来看一下如何将一个 NumPy 数组划分为多个子数组。
NumPy 数组的划分
NumPy 数组可以被划分为多个子数组。NumPy 提供了三种划分功能:垂直划分、水平划分和深度划分。所有的划分函数默认将数组划分为相同大小的子数组,但我们也可以指定划分位置。让我们详细了解每个函数:
- 水平划分:在水平划分中,给定数组沿水平方向被划分为 N 个相等的子数组,使用
hsplit()
函数。让我们看看如何划分一个数组:
# Create an array
arr=np.arange(1,10).reshape(3,3)
print(arr)
Output:
[[1 2 3]
[4 5 6]
[7 8 9]]
# Peroform horizontal splitting
arr_hor_split=np.hsplit(arr, 3)
print(arr_hor_split)
Output: [array([[1],
[4],
[7]]), array([[2],
[5],
[8]]), array([[3],
[6],
[9]])]
在前面的代码中,hsplit(arr, 3)
函数将数组划分为三个子数组。每个部分是原始数组的一列。
- 垂直划分:在垂直划分中,给定数组沿垂直轴被划分为 N 个相等的子数组,使用
vsplit()
和split()
函数。split
函数在axis=0
时执行的操作与vsplit()
函数相同:
# vertical split
arr_ver_split=np.vsplit(arr, 3)
print(arr_ver_split)
Output: [array([[1, 2, 3]]), array([[4, 5, 6]]), array([[7, 8, 9]])]
在前面的代码中,vsplit(arr, 3)
函数将数组划分为三个子数组。每个部分是原始数组的一行。让我们看一下另一个可以用于垂直和水平划分的函数 split()
,以下是一个例子:
# split with axis=0
arr_split=np.split(arr,3,axis=0)
print(arr_split)
Output: [array([[1, 2, 3]]), array([[4, 5, 6]]), array([[7, 8, 9]])]
# split with axis=1
arr_split = np.split(arr,3,axis=1)
Output:
[array([[1],
[4],
[7]]), array([[2],
[5],
[8]]), array([[3],
[6],
[9]])]
在前面的代码中,split(arr, 3)
函数将数组划分为三个子数组。每个部分是原始数组的一行。split
的输出在 axis=0
时类似于 vsplit()
函数,而在 axis=1
时类似于 hsplit()
函数。
更改 NumPy 数组的数据类型
如前所述,NumPy 支持多种数据类型,例如 int
、float
和复数。astype()
函数可以将数组的数据类型转换。让我们来看一个 astype()
函数的例子:
# Create an array
arr=np.arange(1,10).reshape(3,3)
print("Integer Array:",arr)
# Change datatype of array
arr=arr.astype(float)
# print array
print("Float Array:", arr)
# Check new data type of array
print("Changed Datatype:", arr.dtype)
在前面的代码中,我们创建了一个 NumPy 数组,并使用 dtype
属性检查它的数据类型。
让我们使用 astype()
函数更改数组的数据类型:
# Change datatype of array
arr=arr.astype(float)
# Check new data type of array
print(arr.dtype)
Output: float64
在前面的代码中,我们使用 astype()
将列的数据类型从整数更改为浮点数。
tolist()
函数将 NumPy 数组转换为 Python 列表。让我们来看一个 tolist()
函数的例子:
# Create an array
arr=np.arange(1,10)
# Convert NumPy array to Python List
list1=arr.tolist()
print(list1)
Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]
在前面的代码中,我们使用 tolist()
函数将数组转换为 Python 列表对象。
创建 NumPy 视图和副本
一些 Python 函数返回输入数组的副本或视图。Python 的副本会将数组存储在另一个位置,而视图则使用相同的内存内容。这意味着副本是独立的对象,并在 Python 中被视为深拷贝。视图是原始的基础数组,并被视为浅拷贝。以下是副本和视图的一些特点:
-
视图中的修改会影响原始数据,而副本中的修改则不会影响原始数组。
-
视图使用共享内存的概念。
-
与视图相比,副本需要额外的空间。
-
副本比视图慢。
让我们通过以下示例来理解副本和视图的概念:
# Create NumPy Array
arr = np.arange(1,5).reshape(2,2)
print(arr)
Output: [[1, 2],
[3, 4]]
创建 NumPy 数组后,我们来进行对象复制操作:
# Create no copy only assignment
arr_no_copy=arr
# Create Deep Copy
arr_copy=arr.copy()
# Create shallow copy using View
arr_view=arr.view()
print("Original Array: ",id(arr))
print("Assignment: ",id(arr_no_copy))
print("Deep Copy: ",id(arr_copy))
print("Shallow Copy(View): ",id(arr_view))
Output: Original Array: 140426327484256
Assignment: 140426327484256
Deep Copy: 140426327483856
Shallow Copy(View): 140426327484496
在前面的示例中,你可以看到原始数组和分配的数组具有相同的对象 ID,意味着它们指向相同的对象。副本和视图具有不同的对象 ID;两者会有不同的对象,但视图对象会引用相同的原始数组,而副本会有该对象的不同副本。
让我们继续这个示例,并更新原始数组的值,查看其对视图和副本的影响:
# Update the values of original array
arr[1]=[99,89]
# Check values of array view
print("View Array:\n", arr_view)
# Check values of array copy
print("Copied Array:\n", arr_copy)
Output:
View Array:
[[ 1 2]
[99 89]]
Copied Array:
[[1 2]
[3 4]]
在前面的示例中,我们可以从结果得出结论:视图是原始数组。当我们更新原始数组时,值发生了变化,而副本是一个独立的对象,因为它的值保持不变。
切片 NumPy 数组
NumPy 中的切片与 Python 列表类似。索引操作倾向于选择单一值,而切片用于从数组中选择多个值。
NumPy 数组也支持负索引和切片。这里,负号表示反向方向,索引从右侧开始,起始值为 -1
:
让我们通过以下代码来检查这个:
# Create NumPy Array
arr = np.arange(0,10)
print(arr)
Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
在切片操作中,我们使用冒号符号来选择一组值。切片有三个值:起始、停止和步长:
print(arr[3:6])
Output: [3, 4, 5]
这可以表示如下:
在前面的示例中,我们使用了 3
作为起始索引,6
作为停止索引:
print(arr[3:])
Output: array([3, 4, 5, 6, 7, 8, 9])
在前面的示例中,仅给出了起始索引。3
是起始索引。此切片操作将从起始索引开始,选择数组从该索引到数组末尾的值:
print(arr[-3:])
Output: array([7, 8, 9])
这可以表示如下:
在前面的示例中,切片操作将选择从数组右侧第三个值开始到数组末尾的值:
print(arr[2:7:2])
Output: array([2, 4,6])
这可以表示如下:
在前面的示例中,起始、停止和步长索引分别为 2、7 和 2。在这里,切片操作从第二个索引到第六个(停止值减一)索引选择值,索引值增加 2。因此,输出将是 2、4 和 6。
布尔索引和花式索引
索引技术帮助我们从 NumPy 数组中选择和过滤元素。在本节中,我们将重点介绍布尔索引和花式索引。布尔索引在索引的地方使用布尔表达式(在方括号内)来过滤 NumPy 数组。此索引返回对布尔表达式为真的元素:
# Create NumPy Array
arr = np.arange(21,41,2)
print("Orignial Array:\n",arr)
# Boolean Indexing
print("After Boolean Condition:",arr[arr>30])
Output: Orignial Array:
[21 23 25 27 29 31 33 35 37 39]
After Boolean Condition: [31 33 35 37 39]
花式索引是一种特殊的索引类型,其中数组的元素由索引数组选择。这意味着我们在方括号中传递索引数组。花式索引还支持多维数组。这将帮助我们轻松地选择和修改复杂的多维数组集合。让我们看一个以下示例来理解花式索引:
# Create NumPy Array
arr = np.arange(1,21).reshape(5,4)
print("Orignial Array:\n",arr)
# Selecting 2nd and 3rd row
indices = [1,2]
print("Selected 1st and 2nd Row:\n", arr[indices])
# Selecting 3nd and 4th row
indices = [2,3]
print("Selected 3rd and 4th Row:\n", arr[indices])
Output:
Orignial Array:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
[13 14 15 16]
[17 18 19 20]]
Selected 1st and 2nd Row:
[[ 5 6 7 8]
[ 9 10 11 12]]
Selected 3rd and 4th Row:
[[ 9 10 11 12]
[13 14 15 16]]
在上述代码中,我们创建了一个 5*4 的矩阵,并使用整数索引选择了行。您还可以从以下图表中可视化或内部化此输出:
我们可以看到其代码如下:
# Create row and column indices
row = np.array([1, 2])
col = np.array([2, 3])
print("Selected Sub-Array:", arr[row, col])
Output:
Selected Sub-Array: [ 7 12]
前面的示例将结果作为第一个值[1,2]
和第二个值[2,3]
,作为行和列索引。数组将选择第一个和第二个索引值,分别为 7 和 12。
广播数组
Python 列表不支持直接向量化的算术操作。NumPy 提供了比 Python 列表循环操作更快的向量化数组操作。在这里,所有的循环操作都是在 C 中执行而不是 Python,这使得它更快。广播功能检查应用于数组不同形状的二进制函数(如加法、减法和乘法)的一组规则。
让我们看一个广播的例子:
# Create NumPy Array
arr1 = np.arange(1,5).reshape(2,2)
print(arr1)
Output: [[1 2]
[3 4]]
# Create another NumPy Array
arr2 = np.arange(5,9).reshape(2,2)
print(arr2)Output: [[5 6]
[7 8]]
# Add two matrices
print(arr1+arr2)
Output: [[ 6 8]
[10 12]]
在前面的三个示例中,我们可以看到两个大小相同数组的加法。这个概念称为广播:
# Multiply two matrices
print(arr1*arr2)
Output: [[ 5 12]
[21 32]]
在前面的示例中,两个矩阵进行了乘法运算。让我们执行加法和乘法与标量值:
# Add a scaler value
print(arr1 + 3)
Output: [[4 5]
[6 7]]
# Multiply with a scalar value
print(arr1 * 3)
Output: [[ 3 6]
[ 9 12]]
在前面的两个示例中,矩阵被加和乘以一个标量值。
创建 pandas 数据框
pandas
库设计用于处理面板或表格数据。pandas
是一个快速、高效且富有生产力的工具,用于处理和分析字符串、数字、日期时间和时间序列数据。pandas
提供了如 DataFrame 和 Series 等数据结构。pandas
DataFrame 是一个二维的带标签和索引的表格数据结构,具有行和列的网格。它的列是异质类型。它能够处理不同类型的对象,进行分组和连接操作,处理缺失值,创建透视表,处理日期。可以通过多种方式创建pandas
DataFrame。让我们创建一个空的 DataFrame:
# Import pandas library
import pandas as pd
# Create empty DataFrame
df = pd.DataFrame()
# Header of dataframe.
df.head()
Output:
_
在前面的示例中,我们创建了一个空的 DataFrame。让我们使用字典列表来创建一个 DataFrame:
# Create dictionary of list
data = {'Name': ['Vijay', 'Sundar', 'Satyam', 'Indira'], 'Age': [23, 45, 46, 52 ]}
# Create the pandas DataFrame
df = pd.DataFrame(data)
# Header of dataframe.
df.head()
Output:
Name Age0 Vijay 231 Sundar 45
2 Satyam 46
3 Indira 52
在前面的代码中,我们使用了一个字典列表来创建一个 DataFrame。这里,字典的键相当于列,而值则表示为一个列表,相当于 DataFrame 的行。让我们使用字典列表创建一个 DataFrame:
# Pandas DataFrame by lists of dicts.
# Initialise data to lists.
data =[ {'Name': 'Vijay', 'Age': 23},{'Name': 'Sundar', 'Age': 25},{'Name': 'Shankar', 'Age': 26}]
# Creates DataFrame.
df = pd.DataFrame(data,columns=['Name','Age'])
# Print dataframe header
df.head()
在前面的代码中,DataFrame 是通过使用字典列表来创建的。在列表中,每个项是一个字典。每个键是列名,每个值是某一行的单元格值。让我们使用元组列表创建一个 DataFrame:
# Creating DataFrame using list of tuples.
data = [('Vijay', 23),( 'Sundar', 45), ('Satyam', 46), ('Indira',52)]
# Create dataframe
df = pd.DataFrame(data, columns=['Name','Age'])
# Print dataframe header
df.head()
Output:
Name Age
0 Vijay 23
1 Sundar 25
2 Shankar 26
在前面的代码中,DataFrame 是通过使用元组列表来创建的。在列表中,每个项是一个元组,每个元组相当于一行的列。
理解 pandas Series
pandas
Series 是一个一维的顺序数据结构,能够处理任何类型的数据,如字符串、数字、日期时间、Python 列表和字典,具有标签和索引。Series 是 DataFrame 的其中一列。我们可以使用 Python 字典、NumPy 数组和标量值来创建 Series。我们还将在本节后面看到pandas
Series 的特性和属性。让我们创建一些 Python Series:
- 使用 Python 字典:创建一个字典对象,并将其传递给 Series 对象。让我们看一下下面的示例:
# Creating Pandas Series using Dictionary
dict1 = {0 : 'Ajay', 1 : 'Jay', 2 : 'Vijay'}
# Create Pandas Series
series = pd.Series(dict1)
# Show series
series
Output: 0 Ajay
1 Jay
2 Vijay
dtype: object
- 使用 NumPy 数组:创建一个 NumPy 数组对象,并将其传递给 Series 对象。让我们看一下下面的示例:
#Load Pandas and NumPy libraries
import pandas as pd
import numpy as np
# Create NumPy array
arr = np.array([51,65,48,59, 68])
# Create Pandas Series
series = pd.Series(arr)
series
Output: 0 51
1 65
2 48
3 59
4 68
dtype: int64
- 使用单一标量值:要创建一个包含标量值的
pandas
Series,可以将标量值和索引列表传递给 Series 对象:
# load Pandas and NumPy
import pandas as pd
import numpy as np
# Create Pandas Series
series = pd.Series(10, index=[0, 1, 2, 3, 4, 5])
series
Output: 0 10
1 10
2 10
3 10
4 10
5 10
dtype: int64
让我们探索一下pandas
Series 的一些特性:
- 我们也可以通过选择一列来创建一个序列,例如
country
,它恰好是数据文件中的第一列。然后,显示当前本地作用域中的对象类型:
# Import pandas
import pandas as pd
# Load data using read_csv()
df = pd.read_csv("WHO_first9cols.csv")
# Show initial 5 records
df.head()
这将产生以下输出:
在上述代码中,我们使用read_csv()
函数读取了WHO_first9cols.csv
文件。您可以从以下 GitHub 位置下载此文件:github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter02
。在输出中,您可以看到使用head()
函数查看的WHO_first9cols
数据集中的前五条记录:
# Select a series
country_series=df['Country']
# check datatype of series
type(country_series)
Output:
pandas.core.series.Series
pandas
Series 数据结构具有与 DataFrame 共享的一些共同属性,并且还具有一个name
属性。按以下方式探索这些属性:
# Show the shape of DataFrame
print("Shape:", df.shape)
Output: Shape: (202, 9)
让我们查看一下 DataFrame 的列列表:
# Check the column list of DataFrame
print("List of Columns:", df.columns)
Output:List of Columns: Index(['Country', 'CountryID', 'Continent', 'Adolescent fertility rate (%)',
'Adult literacy rate (%)',
'Gross national income per capita (PPP international $)',
'Net primary school enrolment ratio female (%)',
'Net primary school enrolment ratio male (%)',
'Population (in thousands) total'],
dtype='object')
让我们检查一下 DataFrame 列的数据类型:
# Show the datatypes of columns
print("Data types:", df.dtypes)
Output: Data types: Country object
CountryID int64
Continent int64
Adolescent fertility rate (%) float64
Adult literacy rate (%) float64
Gross national income per capita (PPP international $) float64
Net primary school enrolment ratio female (%) float64
Net primary school enrolment ratio male (%) float64
Population (in thousands) total float64
dtype: object
- 让我们看看
pandas
Series 的切片操作:
# Pandas Series Slicing
country_series[-5:]
Output:
197 Vietnam
198 West Bank and Gaza
199 Yemen
200 Zambia
201 Zimbabwe
Name: Country, dtype: object
现在我们知道如何使用 pandas Series,接下来让我们使用 Quandl 来处理数据库。
读取和查询 Quandl 数据
在上一部分,我们看到了pandas
DataFrame,它们具有类似关系数据库的表格结构。它们提供类似的查询操作。在本节中,我们将重点介绍 Quandl。Quandl 是一家总部位于加拿大的公司,提供用于投资数据分析师的商业和替代金融数据。Quandl 理解投资和金融定量分析师的需求。它通过 API、R、Python 或 Excel 提供数据。
在本节中,我们将从 Quandl 中检索 Sunspot 数据集。我们可以使用 API 或手动下载 CSV 格式的数据。
首先,我们使用pip
安装 Quandl 包:
$ pip3 install Quandl
如果您想安装 API,可以通过从pypi.python.org/pypi/Quandl
下载安装包,或通过运行上述命令来安装。
使用该 API 是免费的,但每天限 50 次 API 调用。如果需要更多的 API 调用,您需要请求一个认证密钥。本教程中的代码没有使用密钥。应该很容易修改代码以使用密钥或读取已下载的 CSV 文件。如果遇到困难,请参考第一章中的如何获取帮助和参考资料部分,或浏览 Python 文档:docs.python.org/2/
。
让我们看看如何在pandas
DataFrame 中查询数据:
- 第一步,显然我们需要下载数据。在导入 Quandl API 后,按照以下方式获取数据:
import quandl
sunspots = quandl.get("SIDC/SUNSPOTS_A")
head()
和tail()
方法的功能类似于 Unix 中具有相同名称的命令。选择 DataFrame 的前n和后n条记录,其中n是一个整数参数:
sunspots.head()
这将产生以下输出:
让我们按照如下方式查看tail
函数:
sunspots.tail()
这将产生以下输出:
head()
和 tail()
方法分别返回 Sunspot 数据的前五行和最后五行。
- 筛选列:
pandas
提供了选择列的功能。我们来选择pandas
DataFrame 中的列:
# Select columns
sunspots_filtered=sunspots[['Yearly Mean Total Sunspot Number','Definitive/Provisional Indicator']]
# Show top 5 records
sunspots_filtered.head()
这将产生以下输出:
- 筛选行:
pandas
提供了选择行的功能。我们来选择pandas
DataFrame 中的行:
# Select rows using index
sunspots["20020101": "20131231"]
这将产生以下输出:
- 布尔过滤:我们可以使用类似于 SQL 中
WHERE
子句的布尔条件查询数据。我们来筛选出大于算术平均值的数据:
# Boolean Filter
sunspots[sunspots['Yearly Mean Total Sunspot Number'] > sunspots['Yearly Mean Total Sunspot Number'].mean()]
这将产生以下输出:
描述 pandas
DataFrame
pandas
DataFrame 具有十几种统计方法。下表列出了这些方法,并简要描述了每种方法:
方法 | 描述 |
---|---|
describes |
此方法返回一个包含描述性统计信息的小表格。 |
count |
此方法返回非 NaN 项的数量。 |
mad |
此方法计算均值绝对偏差,这是类似于标准差的稳健度量。 |
median |
此方法返回中位数。它等于第 50^(th) 百分位数的值。 |
min |
此方法返回最小值。 |
max |
此方法返回最大值。 |
mode |
此方法返回众数,即最频繁出现的值。 |
std |
此方法返回标准差,用于衡量数据的离散程度。它是方差的平方根。 |
var |
此方法返回方差。 |
skew |
此方法返回偏度。偏度表示分布的对称性。 |
kurt |
此方法返回峰度。峰度表示分布的形状。 |
使用上一节相同的数据,我们将展示这些统计方法:
# Describe the dataset
df.describe()
这将产生以下输出:
describe()
方法将展示所有列的绝大多数描述性统计量:
# Count number of observation
df.count()
这将产生以下输出:
count()
方法计算每一列中的观察值数量。它帮助我们检查数据集中的缺失值。除了最初的三列,其他列都有缺失值。类似地,你可以计算中位数、标准差、均值绝对偏差、方差、偏度和峰度:
# Compute median of all the columns
df.median()
这将产生以下输出:
我们可以按如下方式计算所有列的偏差:
# Compute the standard deviation of all the columns
df.std()
这将产生以下输出:
上述代码示例正在计算每个数字列的标准差。
对 pandas
DataFrame 的分组与连接
分组是数据聚合操作的一种。分组术语来源于关系型数据库。关系型数据库软件使用 group by
关键字将列中相似的值进行分组。我们可以对分组应用聚合函数,例如均值、最小值、最大值、计数和求和。pandas
DataFrame 也提供了类似的功能。分组操作基于拆分-应用-合并策略。它首先将数据分成组,并对每个组应用聚合操作,如均值、最小值、最大值、计数和求和,然后将每个组的结果合并:
# Group By DataFrame on the basis of Continent column
df.groupby('Continent').mean()
这将产生以下输出:
现在,让我们根据文盲率对 DataFrame 进行分组:
# Group By DataFrame on the basis of continent and select adult literacy rate(%)
df.groupby('Continent').mean()['Adult literacy rate (%)']
这将产生以下输出:
在上面的例子中,我们计算了按大洲划分的成人文盲率百分比。你也可以通过将多个列的列表传递给 groupby()
函数来进行多列分组。
连接是一种用于表格数据库的合并操作。连接概念源自关系型数据库。在关系型数据库中,表被规范化或拆分以减少冗余和不一致性,连接用于从多个表中选择信息。数据分析师需要将来自多个来源的数据进行合并。pandas
也提供了通过 merge()
函数连接多个 DataFrame 的功能。
为了理解连接操作,我们将以一个出租车公司为例。我们使用两个文件:dest.csv
和 tips.csv
。每当司机将乘客送到目的地时,我们会将一条记录(员工编号和目的地)插入 dest.csv
文件中。每当司机获得小费时,我们会将记录(员工编号和小费金额)插入 tips.csv
文件中。你可以从以下 GitHub 链接下载这两个文件:github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Python-Data-Analysis-Third-Edition/Ch2
:
# Import pandas
import pandas as pd
# Load data using read_csv()
dest = pd.read_csv("dest.csv")
# Show DataFrame
dest.head()
这将产生以下输出:
在前面的代码块中,我们使用 read_csv()
方法读取了 dest.csv
文件:
# Load data using read_csv()
tips = pd.read_csv("tips.csv")
# Show DataFrame
tips.head()
这将产生以下输出:
在前面的代码块中,我们使用 read_csv()
方法读取了 tips.csv
文件。接下来,我们将查看不同类型的连接操作:
- 内连接:内连接相当于集合的交集操作。它只会选择两个 DataFrame 中的共同记录。要执行内连接,使用
merge()
函数,指定两个 DataFrame 和公共属性作为参数,并设置how
为 "inner" 来展示参数。on
参数用于提供连接所依据的公共属性,how
定义连接类型:
# Join DataFrames using Inner Join
df_inner= pd.merge(dest, tips, on='EmpNr', how='inner')
df_inner.head()
这会产生以下输出:
- 全外连接:外连接相当于集合的并操作。它将合并左右两个 DataFrame。它会包含两个 DataFrame 中的所有记录,并在没有匹配的地方填充 NaN:
# Join DataFrames using Outer Join
df_outer= pd.merge(dest, tips, on='EmpNr', how='outer')
df_outer.head()
这会产生以下输出:
- 右外连接:在右外连接中,所有来自 DataFrame 右侧的记录都会被选中。如果在左侧 DataFrame 中找不到匹配的记录,则用 NaN 填充:
# Join DataFrames using Right Outer Join
df_right= pd.merge(dest, tips, on='EmpNr', how='right')
df_right.head()
这会产生以下输出:
- 左外连接:在左外连接中,所有来自 DataFrame 左侧的记录都会被选中。如果在右侧 DataFrame 中找不到匹配的记录,则用 NaN 填充:
# Join DataFrames using Left Outer Join
df_left= pd.merge(dest, tips, on='EmpNr', how='left')
df_left.head()
这会产生以下输出:
我们现在将继续检查数据集中的缺失值。
处理缺失值
大多数现实世界中的数据集都是杂乱无章且充满噪声的。由于其杂乱和噪声,许多值要么是错误的,要么是缺失的。pandas
提供了许多内置函数来处理 DataFrame 中的缺失值:
- 检查 DataFrame 中的缺失值:
pandas
的isnull()
函数检查是否存在空值,并返回True
或False
,其中True
表示空值,False
表示非空值。sum()
函数会将所有True
值相加,返回缺失值的数量。我们尝试了两种方法来计算缺失值,两者显示的输出相同:
# Count missing values in DataFrame
pd.isnull(df).sum()
以下是第二种方法:
df.isnull().sum()
这会产生以下输出:
- 删除缺失值:一种非常简单的处理缺失值的方法是删除它们以便进行分析。
pandas
提供了dropna()
函数来删除 DataFrame 中的这些观察值。在这里,inplace=True
属性会在原始 DataFrame 中进行更改:
# Drop all the missing values
df.dropna(inplace=True)
df.info()
这会产生以下输出:
在这里,观察值的数量从 202 减少到 118。
- 填充缺失值:另一种方法是用零、均值、中位数或常数值填充缺失值:
# Fill missing values with 0
df.fillna(0,inplace=True)
df.info()
这会产生以下输出:
在这里,我们用 0 填充了缺失值。这就是处理缺失值的全部内容。
在下一节中,我们将重点讨论数据透视表。
创建数据透视表
数据透视表是一个汇总表格,它是 Excel 中最流行的概念。大多数数据分析师将其作为一个方便的工具来总结他们的结果。pandas
提供了 pivot_table()
函数来汇总 DataFrame。DataFrame 使用聚合函数进行汇总,例如均值、最小值、最大值或总和。你可以从以下 GitHub 链接下载数据集:github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Python-Data-Analysis-Third-Edition/Ch2
:
# Import pandas
import pandas as pd
# Load data using read_csv()
purchase = pd.read_csv("purchase.csv")
# Show initial 10 records
purchase.head(10)
这将产生以下输出:
在前面的代码块中,我们使用 read_csv()
方法读取了 purchase.csv
文件。
现在,我们将使用以下代码汇总 DataFrame:
# Summarise dataframe using pivot table
pd.pivot_table(purchase,values='Number', index=['Weather',],
columns=['Food'], aggfunc=np.sum)
这将产生以下输出:
在前面的示例中,purchase
DataFrame 被汇总。这里,index
是 Weather
列,columns
是 Food
列,values
是 Number
列的聚合总和。aggfun
被初始化为 np.sum
参数。现在是时候学习如何在 pandas
DataFrame 中处理日期了。
处理日期
处理日期是繁琐而复杂的。你可以回想起 Y2K 问题、即将到来的 2038 问题以及时区处理的不同问题。在时间序列数据集中,我们会遇到日期。pandas
提供了日期范围,重新采样时间序列数据,并执行日期算术操作。
创建一个从 2020 年 1 月 1 日开始的日期范围,持续 45 天,如下所示:
pd.date_range('01-01-2000', periods=45, freq='D')
Output: DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-03', '2000-01-04',
'2000-01-05', '2000-01-06', '2000-01-07', '2000-01-08',
'2000-01-09', '2000-01-10', '2000-01-11', '2000-01-12',
'2000-01-13', '2000-01-14', '2000-01-15', '2000-01-16',
'2000-01-17', '2000-01-18', '2000-01-19', '2000-01-20',
'2000-01-21', '2000-01-22', '2000-01-23', '2000-01-24',
'2000-01-25', '2000-01-26', '2000-01-27', '2000-01-28',
'2000-01-29', '2000-01-30', '2000-01-31', '2000-02-01',
'2000-02-02', '2000-02-03', '2000-02-04', '2000-02-05',
'2000-02-06', '2000-02-07', '2000-02-08', '2000-02-09',
'2000-02-10', '2000-02-11', '2000-02-12', '2000-02-13',
'2000-02-14'],
dtype='datetime64[ns]', freq='D')
1 月份的天数少于 45 天,因此结束日期落在了 2 月份,你可以自己检查。
date_range()
的 freq 参数可以取值,例如 B
表示工作日频率,W
表示每周频率,H
表示每小时频率,M
表示每分钟频率,S
表示每秒频率,L
表示每毫秒频率,U
表示每微秒频率。更多详情,请参考官方文档:pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
。
- pandas 日期范围:
date_range()
函数生成具有固定频率间隔的日期和时间序列:
# Date range function
pd.date_range('01-01-2000', periods=45, freq='D')
这将产生以下输出:
to_datetime()
:to_datetime()
将时间戳字符串转换为 datetime:
# Convert argument to datetime
pd.to_datetime('1/1/1970')
Output: Timestamp('1970-01-01 00:00:00')
- 我们可以将时间戳字符串转换为指定格式的 datetime 对象:
# Convert argument to datetime in specified format
pd.to_datetime(['20200101', '20200102'], format='%Y%m%d')
Output: DatetimeIndex(['2020-01-01', '2020-01-02'], dtype='datetime64[ns]', freq=None)
- 处理未知格式字符串: 未知的输入格式可能会导致值错误。我们可以通过使用带有
coerce
的errors
参数来处理这种情况。coerce
将无效的字符串设置为 NaT:
# Value Error
pd.to_datetime(['20200101', 'not a date'])
Output: ValueError: ('Unknown string format:', 'not a date')
# Handle value error
pd.to_datetime(['20200101', 'not a date'], errors='coerce')
Output: DatetimeIndex(['2020-01-01', 'NaT'], dtype='datetime64[ns]', freq=None)
在前面的示例中,第二个日期仍然无效,无法转换为 datetime 对象。errors
参数帮助我们通过输入值 NaT
(不是时间)来处理此类错误。
摘要
在本章中,我们探讨了 NumPy 和 pandas 库。这两个库都用于处理数组和 DataFrame。NumPy 数组能够处理 n 维数组。我们学习了各种数组属性和操作。我们的主要关注点是数据类型、数据类型作为对象、重塑、堆叠、拆分、切片和索引。
我们还专注于用于 Python 数据分析的pandas
库。我们看到了pandas
如何模拟关系数据库表的功能。它提供了查询、聚合、操作和高效连接数据的功能。
NumPy 和pandas
作为工具协同工作,使得执行基本的数据分析成为可能。此时,您可能会认为pandas
是我们进行数据分析所需的一切。然而,数据分析远不止于此。
在掌握了基础知识后,是时候进入使用第三章中常用统计函数进行数据分析的环节,统计学。这包括了统计概念的应用。
鼓励您阅读参考文献部分中提到的书籍,深入探索 NumPy 和pandas
。
参考文献
-
Ivan Idris, NumPy Cookbook – 第二版,Packt Publishing,2015 年。
-
Ivan Idris, 学习 NumPy 数组,Packt Publishing,2014 年。
-
Ivan Idris, NumPy:初学者指南 – 第三版,Packt Publishing,2015 年。
-
L. (L.-H.) Chin 和 T. Dutta,NumPy 基础,Packt Publishing,2016 年。
-
T. Petrou, pandas Cookbook,Packt Publishing,2017 年。
-
F. Anthony, 精通 pandas,Packt Publishing,2015 年。
-
M. Heydt, 金融领域的 pandas 精通,Packt Publishing,2015 年。
-
T. Hauck, 使用 pandas 进行数据密集型应用的操作指南,Packt Publishing,2013 年。
-
M. Heydt, 学习 pandas,Packt Publishing,2015 年。
第三章:统计学
探索性数据分析 (EDA) 是数据分析和构建机器学习模型的第一步。统计学提供了探索性或描述性数据分析的基础知识和一套工具。本章旨在使您具备处理真实世界数据的能力,这些数据通常具有噪声、缺失值,并从各种来源收集而来。
在进行任何预处理和分析之前,您需要熟悉当前的数据,统计学是唯一能帮助您的工具。这使得统计学成为数据专业人士的一项主要且非常必要的技能,帮助他们获得初步见解和对数据的理解。例如,员工每月工作小时的算术平均数可以帮助我们了解组织中员工的工作负荷。类似地,每月工作小时的标准差可以帮助我们推断工作小时的范围。血压和患者年龄之间的相关性可以帮助我们理解血压和年龄之间的关系。抽样方法在任何类型的主数据收集中都可能有用。我们还可以执行参数和非参数假设检验以推断有关总体的事实。
在本章中,我们将涵盖以下主题:
-
理解属性及其类型
-
测量中心趋势
-
测量离散度
-
偏度和峰度
-
使用协方差和相关系数理解关系
-
中心极限定理
-
收集样本
-
执行参数检验
-
执行非参数检验
技术要求
对于本章,以下技术信息可用:
-
您可以在以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter03
. -
所有代码块均在
ch3.ipynb
中可用。 -
在本章中,我们将使用 Python 的 NumPy、Pandas 和 SciPy 库。
理解属性及其类型
数据是原始事实和统计数据的集合,如数字、文字和观察结果。属性是表示对象特征的列、数据字段或系列,也称为变量、特征或维度。统计学家使用术语变量,而机器学习工程师更喜欢术语特征。数据仓库中使用术语维度,而数据库专业人员使用术语属性。
属性类型
属性的数据类型对数据分析更为关键,因为某些情况需要特定的数据类型。属性的数据类型帮助分析人员选择正确的数据分析和可视化绘图方法。以下列表显示了各种属性:
-
名义属性: 名义是指类别变量的名称或标签。名义属性的值可以是项目的符号或名称。这些值是类别性的、定性的,且没有顺序性,如产品名称、品牌名称、邮政编码、州、性别和婚姻状况。对于定性和类别性的值,计算均值和中位数是没有意义的,但数据分析师可以计算众数,即出现频率最高的值。
-
顺序属性: 顺序是指具有有意义顺序或排名的名称或标签,但值的大小并不明确。这些类型的属性仅用于衡量主观特质。这就是为什么它们被广泛用于客户满意度调查、产品评级和电影评分等领域。客户满意度评级按以下顺序出现:
-
1: 非常不满意
-
2: 有点不满意
-
3: 中立
-
4: 满意
-
5: 非常满意
-
另一个例子可能是饮料的大小:小号、中号或大号。顺序属性只能通过众数和中位数来衡量。由于顺序属性具有定性的特点,因此无法计算均值。顺序属性也可以通过将定量变量离散化,将其值划分为有限数字范围来重新创建。
- 数值属性: 数值属性以整数或实数值定量表示。数值属性可以分为两种类型:区间尺度或比例尺度。
区间尺度属性是在有序的等大小单位尺度上进行测量的。区间尺度属性两个值之间的有意义差异可以被计算出来,这使得可以进行两者的比较——例如,出生年份和摄氏温度。区间尺度属性值的主要问题是它们没有“真实的零点”——例如,摄氏温度为 0 时并不意味着温度不存在。区间尺度数据可以进行加法和减法运算,但不能进行乘法和除法,因为没有真实零点。我们还可以计算区间尺度属性的均值,除了中位数和众数之外。
比例尺度属性是按等大小单位的有序尺度进行测量的,类似于具有固有零点的区间尺度。比例尺度属性的例子包括身高、体重、纬度、经度、工作年限和文档中的单词数量。我们可以进行乘法和除法运算,并计算比例尺度值之间的差异。我们还可以计算集中趋势度量,如均值、中位数和众数。摄氏度和华氏度温标是按区间尺度进行测量的,而开尔文温标则按比例尺度进行测量,因为它具有真实的零点。
离散和连续属性
有多种方式可以对属性进行分类。在前一个小节中,我们已经见过了名义型、顺序型和数值型属性。在本小节中,我们将讨论另一种属性分类方法。这里我们将讲解离散型或连续型属性。离散变量只接受有限个可数的数值,例如班级中有多少学生、卖出多少辆车、出版多少本书。这些数值可以通过计数得到。连续变量则接受无限多个可能的数值,例如学生的体重和身高。这些数值可以通过测量得到。
离散变量接受整数值,而连续变量接受实数值。换句话说,我们可以说离散变量接受的值的分数没有意义,而连续变量接受的值的分数是有意义的。离散属性使用有限数量的值,而连续属性则使用无限数量的值。
在理解了属性及其类型后,接下来我们要关注基本的统计描述,如集中趋势测量。
测量集中趋势
集中趋势是值围绕均值、众数和中位数等平均值聚集的趋势。集中趋势的主要目的是计算观测值的中心导向值。集中趋势决定了描述性总结,并提供了关于一组观测值的定量信息。它有能力代表整个观测集。接下来,我们将在各个小节中详细探讨每种集中趋势度量方法。
均值
均值是算术平均数或平均值,通过将观测值的总和除以观测次数来计算。它对异常值和噪声非常敏感,因此每当把不常见或异常的值添加到一组数据中时,其均值就会偏离典型的中心值。假设 x[1], x[²], . . . , x [N] 是 N 次观测值。这些值的均值公式如下所示:
让我们使用 pandas
库来计算沟通技能得分列的均值,如下所示:
# Import pandas library
import pandas as pd
# Create dataframe
sample_data = {'name': ['John', 'Alia', 'Ananya', 'Steve', 'Ben'],
'gender': ['M', 'F', 'F', 'M', 'M'],
'communication_skill_score': [40, 45, 23, 39, 39],
'quantitative_skill_score': [38, 41, 42, 48, 32]}
data = pd.DataFrame(sample_data, columns = ['name', 'gender', 'communcation_skill_score', 'quantitative_skill_score'])
# find mean of communication_skill_score column
data['communcation_skill_score'].mean(axis=0)
Output:
37.2
在前面的代码块中,我们创建了一个名为 data
的 DataFrame,它有四列(name
、gender
、communication_skill_score
和 quantitative_skill_score
),并使用 mean(axis=0)
函数计算了均值。这里,axis=0
表示按行计算均值。
众数
众数是观测值中出现频率最高的项。众数值在数据中出现频繁,主要用于分类值。如果一组数据中的所有值都是独一无二的或不重复的,那么该组数据就没有众数。也有可能有多个值的出现频率相同,在这种情况下,可以有多个众数。
让我们使用 pandas
库来计算沟通技能得分列的众数值,如下所示:
# find mode of communication_skill_score column
data['communcation_skill_score'].mode()
Output:
39
在前面的代码块中,我们通过使用mode()
函数计算了沟通技巧得分列的众数。让我们计算另一个集中趋势度量:中位数。
中位数
中位数是一个观察组中的中点或中间值。它也被称为第 50 百分位数。中位数比均值更不容易受异常值和噪声的影响,这也是它被认为是更适合报告的统计量度量的原因。它通常接近典型的中央值。让我们使用pandas
库计算沟通技巧得分列的中位数值,如下所示:
# find median of communication_skill_score column
data['communcation_skill_score'].median()
Output:
39.0
在前面的代码块中,我们通过使用median()
函数计算了沟通技巧得分列的中位数。让我们在下一节中了解离散度量并计算它们。
测量离散程度
正如我们所见,集中趋势展示了观察组的中间值,但并未提供观察的整体图像。离散度量衡量的是观察值的偏差。最常见的离散度量有范围、四分位间距(IQR)、方差和标准差。这些离散度量关注的是观察值的变异性或观察值的分布。让我们详细了解每个离散度量,如下所示:
- 范围: 范围是观察值的最大值和最小值之间的差异。它易于计算且易于理解。它的单位与观察值的单位相同。让我们计算沟通技巧得分的范围,如下所示:
column_range=data['communcation_skill_score'].max()-data['communcation_skill_score'].min()
print(column_range)
Output:
22
在前面的代码块中,我们通过计算最大值和最小值之间的差异,得出了沟通技巧得分的范围。最大值和最小值是通过使用max()
和min()
函数计算得出的。
- IQR: IQR 是第三四分位数和第一四分位数之间的差值。它易于计算且易于理解。它的单位与观察值的单位相同。它衡量了观察值的中间 50%。它表示大多数观察值所在的范围。IQR 也称为中间差距或中间 50%,或者 H-差距。让我们计算沟通技巧得分的 IQR,如下所示:
# First Quartile
q1 = data['communcation_skill_score'].quantile(.25)
# Third Quartile
q3 = data['communcation_skill_score'].quantile(.75)
# Inter Quartile Ratio
iqr=q3-q1
print(iqr)
Output:
1.0
在前面的代码块中,我们通过计算得分的第一和第三四分位数之间的差异,得出了沟通技巧得分的 IQR。第一和第三四分位数的得分是通过使用quantile(.25)
和quantile(.75)
函数计算得出的。
- 方差: 方差衡量的是观察值与均值之间的偏差。它是观察值与均值之间平方差的平均值。方差的主要问题在于其测量单位,因为它是观察值与均值之间差值的平方。假设 x[1]、x[²]、...、x[N]是N个观察值,那么这些值的方差公式将如下所示:
让我们计算沟通技巧得分的方差,如下所示:
# Variance of communication_skill_score
data['communcation_skill_score'].var()
Output:
69.2
在前面的代码块中,我们使用var()
函数计算了沟通技能评分的方差。
- 标准差: 这是方差的平方根。它的单位与原始观测值相同。这使得分析师更容易评估数据点与均值的偏差。标准差值越小,表示数据点距离均值越近;也就是说,数据分布较集中。标准差值越大,表示数据点距离均值越远——也就是说,数据分布较分散。标准差在数学上用希腊字母 sigma (Σ) 表示。假设 x[1]、x[²]、...、x[N] 是 N 个观测值。以下是这些值的标准差公式:
让我们计算沟通技能评分的标准差,如下所示:
# Standard deviation of communication_skill_score
data['communcation_skill_score'].std()
Output:
8.318653737234168
在前面的代码块中,我们使用std()
函数计算了沟通技能评分的标准差。
我们还可以尝试描述这个函数,以便通过一个命令获取所有的摘要统计信息。describe()
函数返回每个数值型列的计数、均值、标准差、第一四分位数、中位数、第三四分位数、最小值和最大值,具体如以下代码块所示:
# Describe dataframe
data.describe()
Output:
communcation_skill_score quantitative_skill_score
count 5.000000 5.000000
mean 37.200000 40.200000
std 8.318654 5.848077
min 23.000000 32.000000
25% 39.000000 38.000000
50% 39.000000 41.000000
75% 40.000000 42.000000
max 45.000000 48.000000
在前面的代码块中,我们使用describe()
方法生成了数据的描述性统计摘要。
偏度和峰度
偏度衡量分布的对称性。它显示分布偏离正态分布的程度。其值可以是零、正值或负值。零值表示完全正态的分布形状。正偏度表现为尾部指向右侧——即异常值偏向右侧,数据堆积在左侧。负偏度表现为尾部指向左侧——即异常值偏向左侧,数据堆积在右侧。当均值大于中位数和众数时,会出现正偏度;当均值小于中位数和众数时,会出现负偏度。让我们在以下代码块中计算偏度:
# skewness of communication_skill_score column
data['communcation_skill_score'].skew()
Output:
-1.704679180800373
在前面的代码块中,我们使用skew()
方法计算了沟通技能评分列的偏度。
峰度衡量与正态分布相比的尾部(尾部的厚度)。高峰度表示重尾分布,这意味着数据中有更多的异常值;低峰度表示轻尾分布,这意味着数据中异常值较少。峰度有三种形状:中峰、平峰和尖峰。我们逐一定义它们,如下所示:
-
具有零峰度的正态分布被称为中峰分布。
-
平峰分布具有负的峰度值,并且相比于正态分布,它的尾部较薄。
-
尖峰峰度分布的峰度值大于 3,相较于正态分布,它呈现肥尾特征。
让我们看看下图中不同的峰度形态类型:
直方图是展示偏度和峰度的有效方式。让我们计算一下沟通技能分数列的峰度,如下所示:
# kurtosis of communication_skill_score column
data['communcation_skill_score'].kurtosis()
Output:
3.6010641852384015
在前面的代码块中,我们使用kurtosis()
方法计算了沟通技能分数列的峰度。
使用协方差和相关系数理解关系
测量变量之间的关系将有助于数据分析师理解变量之间的动态关系——例如,人力资源经理需要了解员工绩效分数与满意度分数之间关系的强度。统计学提供了两种度量协方差和相关性来理解变量之间的关系。协方差衡量一对变量之间的关系。它显示变量的变化程度——即一个变量的变化如何影响另一个变量。其值范围从负无穷到正无穷。协方差的问题在于,它没有提供有效的结论,因为它没有标准化。让我们使用协方差来找出沟通技能和定量技能分数之间的关系,如下所示:
# Covariance between columns of dataframe
data.cov()
这将产生以下输出:
在前面的代码块中,协方差是通过cov()
方法计算的。这里,该方法的输出是协方差矩阵。
皮尔逊相关系数
相关性显示了变量之间的相关程度。与协方差相比,相关性提供了更好的理解,并且是协方差的标准化版本。相关性的范围是从-1 到 1。负值表示一个变量的增加导致另一个变量的减少,或者变量朝同一方向变化。正值表示一个变量的增加导致另一个变量的增加,或者一个变量的减少导致另一个变量的减少。零值表示变量之间没有关系,或者变量是独立的。请看下面的代码片段:
# Correlation between columns of dataframe
data.corr(method ='pearson')
这将产生以下输出:
'method'
参数可以取以下三种参数之一:
-
pearson
: 标准相关系数 -
kendall
: 肯德尔的 tau 相关系数 -
spearman
: 斯皮尔曼等级相关系数
斯皮尔曼等级相关系数
斯皮尔曼等级相关系数是皮尔逊相关系数在观察值排名上的应用。它是一个非参数的等级相关度量,用于评估两个有序变量之间关联的强度。排名变量是顺序数值,按顺序排列。首先,我们对观察值进行排名,然后计算排名的相关性。它可以应用于连续型和离散型有序变量。当数据分布偏斜或受到异常值影响时,使用斯皮尔曼等级相关性而不是皮尔逊相关性,因为它不对数据分布做任何假设。
肯德尔等级相关系数
肯德尔等级相关系数或肯德尔 tau 系数是一个非参数统计量,用于衡量两个有序变量之间的关联性。它是等级相关的一种类型,衡量两个变量之间的相似性或不相似性。如果两个变量都是二元的,则皮尔逊相关系数=斯皮尔曼相关系数=肯德尔 tau 系数。
到目前为止,我们已经学习了描述性统计的主题,例如集中趋势度量、离散度量、分布度量和变量关系度量。现在是时候转向推断统计的主题,如中心极限定理、抽样技术,以及参数和非参数检验。
中心极限定理
数据分析方法包括假设检验和置信区间的确定。所有统计检验都假设总体呈正态分布。中心极限定理是假设检验的核心。根据该定理,随着样本量的增加,抽样分布趋近于正态分布。此外,样本的均值会更接近总体均值,样本的标准差会减小。这个定理对于推断统计学至关重要,它帮助数据分析师理解如何通过样本来获取关于总体的见解。
它是否能回答诸如“应该抽取多大样本”或“哪个样本量能准确代表总体”等问题?你可以通过以下图示来理解这一点:
在上面的图示中,你可以看到针对不同样本量(50、100、200 和 500)的四个直方图。如果你观察,会发现随着样本量的增加,直方图趋近于正态曲线。接下来让我们学习抽样技术。
收集样本
样本是用于数据分析的小规模群体。抽样是从各个来源收集样本数据的一个方法或过程,它是数据收集过程中至关重要的部分。实验的成功与否取决于数据收集的质量。如果抽样出现问题,将对最终的解读产生巨大影响。此外,收集整个群体的数据几乎是不可能的。抽样帮助研究人员从样本推断总体,并减少收集和管理数据的调查成本和工作量。有许多抽样技术可供选择,适用于不同目的。这些技术可以分为两类:概率抽样和非概率抽样,下面将详细描述这两类抽样方法:
-
概率抽样: 这种抽样方法对每个受访者进行随机选择,每个样本被选中的机会相等。这类抽样技术更耗时且成本较高,具体包括以下几种:
-
简单随机抽样: 采用这种技术,每个受访者都是通过随机方式选出的,这意味着每个受访者都有相等的机会被选中。这是一种简单直接的方法——例如,从 500 个产品中随机选出 20 个产品进行质量检测。
-
分层抽样: 采用这种技术时,整个群体被划分为小组,称为“层”,这些层是基于某些相似标准划分的。这些层的大小可能不相等。该技术通过减少选择偏差来提高准确性。
-
系统抽样: 采用这种技术时,受访者会按照规律性的间隔被选出。换句话说,可以说受访者是按系统顺序从目标群体中选出的,例如每隔n个受访者选出一个。
-
整群抽样: 这种抽样技术将整个群体划分为若干个群体或小组。群体的划分通常基于性别、地点、职业等因素。整个群体将用于抽样,而不是单独选择某一受访者。
-
-
非概率抽样: 这种抽样方法是非随机地从整个群体中选取每个受访者,选中的样本有不等的机会。其结果可能会存在偏差。这类抽样技术成本较低且更方便,具体包括以下几种:
-
方便抽样: 这是最简单的收集数据的方法。它根据受访者的可用性和参与意愿来选取受访者。统计学家通常偏爱这种方法进行初步调查,因为其成本低且数据收集速度快,但结果容易受到偏差的影响。
-
目的性抽样: 这种方法也称为判断抽样,因为它依赖于统计学家的判断。在实际操作中,统计学家根据一些预先定义的特征来决定谁将参与调查。新闻记者也常使用这种方法,选择他们希望获取意见的人。
-
配额抽样: 该技术预先定义了样本层次和比例的属性。样本受访者被选择,直到满足特定比例为止。它与分层抽样在选择策略上有所不同;它通过随机抽样选择各个层次中的项目。
-
滚雪球抽样: 该技术用于在样本人群稀少且难以追踪的情况下,如非法移民或艾滋病等领域。统计学家联系志愿者,帮助联系受害者。此方法也被称为推荐抽样,因为最初参与调查的人会推荐另一位符合样本描述的人。
-
在本节中,我们已了解了抽样方法及其类型:概率抽样和非概率抽样。接下来,我们将进入假设检验技术。在接下来的章节中,我们将重点讨论参数假设检验和非参数假设检验。
执行参数检验
假设是推断统计的核心主题。在本节中,我们将专注于参数检验。参数检验的基本假设是潜在的统计分布。大多数基础统计方法都是参数性质的。参数检验用于定量和连续数据。参数是代表整个总体的数字量。参数检验比非参数检验更强大、更可靠。假设是建立在总体分布参数上的。以下是一些参数检验的示例:
- t 检验是一种参数检验,用于检查两个相关组的均值是否存在显著差异。它是最常用的推断统计量,遵循正态分布。t 检验有两种类型:单样本 t 检验和双样本 t 检验。单样本 t 检验用于检查样本与假设总体均值之间是否存在显著差异。我们可以通过 t 检验,检查 10 名学生的平均体重是否为 68 公斤,如下所示:
import numpy as np
from scipy.stats import ttest_1samp
# Create data
data=np.array([63, 75, 84, 58, 52, 96, 63, 55, 76, 83])
# Find mean
mean_value = np.mean(data)
print("Mean:",mean_value)
Output:
Mean: 70.5
在前面的代码块中,我们创建了一个包含 10 名学生体重的数组,并使用numpy.mean()
计算了其算术平均值。
让我们进行一个单样本 t 检验,如下所示:
# Perform one-sample t-test
t_test_value, p_value = ttest_1samp(data, 68)
print("P Value:",p_value)
print("t-test Value:",t_test_value)
# 0.05 or 5% is significance level or alpha.
if p_value < 0.05:
print("Hypothesis Rejected")
else:
print("Hypothesis Accepted")
Output:
P Value: 0.5986851106160134
t-test Value: 0.5454725779039431
Hypothesis Accepted
在前面的代码块中,我们使用ttest_1samp()
检验了原假设(10 名学生的平均体重为 68 公斤)。输出结果显示,在 95%的置信区间内,原假设被接受,这意味着 10 名学生的平均体重是 68 公斤。
- 双样本 t 检验用于比较两组独立样本之间的显著差异。该检验也被称为独立样本 t 检验。我们可以比较两组独立学生的平均体重,如下所示:
原假设 H[0]: 样本均值相等—μ [1] = μ [2]
备择假设 H[a]: 样本均值不相等—μ [1] > μ [2] 或 μ [2] > μ [1]
看一下以下代码块:
from scipy.stats import ttest_ind
# Create numpy arrays
data1=np.array([63, 75, 84, 58, 52, 96, 63, 55, 76, 83])
data2=np.array([53, 43, 31, 113, 33, 57, 27, 23, 24, 43])
在前面的代码块中,我们创建了两组包含 10 名学生体重的数组。
接下来,我们进行双样本 t 检验,如下所示:
# Compare samples
stat, p = ttest_ind(data1, data2)
print("p-values:",p)
print("t-test:",stat)
# 0.05 or 5% is significance level or alpha.
if p < 0.05:
print("Hypothesis Rejected")
else:
print("Hypothesis Accepted")
Output:
p-values: 0.015170931362451255
t-test: 2.6835879913819185
Hypothesis Rejected
在前面的代码块中,我们使用 ttest_ind()
方法测试了两个组的平均体重的假设,结果显示原假设在 95%的置信区间内被拒绝,这意味着样本均值之间存在差异。
- 配对样本 t 检验是一种依赖样本 t 检验,用于判断同一组的两次观察值之间的均值差异是否为零——例如,比较患者群体在某种药物治疗前后的血压变化。这相当于单样本 t 检验,也称为依赖样本 t 检验。让我们进行配对 t 检验,以评估减重治疗的效果。我们已经收集了患者治疗前后的体重数据。可以通过以下假设表示:
原假设 H[0]: 两个依赖样本之间的均值差异为 0。
备择假设 H[a]: 两个依赖样本之间的均值差异不为 0。
看一下以下代码块:
# paired test
from scipy.stats import ttest_rel
# Weights before treatment
data1=np.array([63, 75, 84, 58, 52, 96, 63, 65, 76, 83])
# Weights after treatment
data2=np.array([53, 43, 67, 59, 48, 57, 65, 58, 64, 72])
在前面的代码块中,我们创建了两组包含 10 名患者治疗前后体重的数组。接下来,我们进行配对样本 t 检验,如下所示:
# Compare weights
stat, p = ttest_rel(data1, data2)
print("p-values:",p)
print("t-test:",stat)
# 0.05 or 5% is the significance level or alpha.
if p < 0.05:
print("Hypothesis Rejected")
else:
print("Hypothesis Accepted")
Output:
p-values: 0.013685575312467715
t-test: 3.0548295044306903
Hypothesis Rejected
在前面的代码块中,我们使用 ttest_rel()
方法测试了两个组的平均体重在治疗前后的假设。结果表明,原假设在 95%的置信区间内被拒绝,这意味着减重治疗对患者体重有显著影响。
-
方差分析(ANOVA):t 检验只适用于两组数据,但有时我们需要同时比较两组以上的数据。方差分析(ANOVA)(ANalysis Of VAriance)是一种统计推断检验,用于比较多个组之间的差异。它分析多个组之间和组内的方差,并同时检验多个原假设。通常,它用于比较两组以上的数据并检查统计显著性。我们可以通过三种方式使用方差分析:单因素方差分析、双因素方差分析和多因素方差分析。
-
使用单因素方差分析(ANOVA)方法,我们基于一个自变量比较多个组——例如,一家 IT 公司想根据绩效分数比较多个员工组或团队的生产力。在我们的例子中,我们正在比较三个位于不同地点的 IT 公司员工的绩效:孟买、芝加哥和伦敦。接下来,我们将进行单因素方差分析检验并检查绩效是否存在显著差异。我们先定义原假设和备择假设,如下所示:
原假设 H[0]: 多个地点的平均绩效得分没有差异。
备择假设 H[a]: 多个地点的平均绩效得分存在差异。
看一下以下的代码块:
from scipy.stats import f_oneway
# Performance scores of Mumbai location
mumbai=[0.14730927, 0.59168541, 0.85677052, 0.27315387, 0.78591207,0.52426114, 0.05007655, 0.64405363, 0.9825853 , 0.62667439]
# Performance scores of Chicago location
chicago=[0.99140754, 0.76960782, 0.51370154, 0.85041028, 0.19485391,0.25269917, 0.19925735, 0.80048387, 0.98381235, 0.5864963 ]
# Performance scores of London location
london=[0.40382226, 0.51613408, 0.39374473, 0.0689976 , 0.28035865,0.56326686, 0.66735357, 0.06786065, 0.21013306, 0.86503358]
在前面的代码块中,我们为三个地点:孟买、芝加哥和伦敦创建了三个员工表现分数的列表。
让我们进行一个单因素方差分析(ANOVA)检验,如下所示:
# Compare results using Oneway ANOVA
stat, p = f_oneway(mumbai, chicago, london)
print("p-values:", p)
print("ANOVA:", stat)
if p < 0.05:
print("Hypothesis Rejected")
else:
print("Hypothesis Accepted")
Output:
p-values: 0.27667556390705783
ANOVA: 1.3480446381965452
Hypothesis Accepted
在前面的代码块中,我们使用 f_oneway()
方法检验了不同地点之间平均表现分数没有差异的假设。前述结果显示,在 95% 的置信区间下,接受了原假设,这意味着各地点的平均表现分数之间没有显著差异。
-
通过双因素方差分析方法,我们基于两个独立变量比较多个组——例如,如果一家 IT 公司想要根据工作时长和项目复杂度来比较多个员工组或团队的生产力。
-
在 N 维方差分析中,我们基于 N 个独立变量比较多个组——例如,如果一家 IT 公司想要根据工作时长、项目复杂度、员工培训以及其他员工福利设施来比较多个员工组或团队的生产力。
在本节中,我们详细探讨了参数检验,如 t 检验和方差分析(ANOVA)检验。接下来让我们进入非参数假设检验部分。
执行非参数检验
非参数检验不依赖于任何统计分布,因此被称为“无分布”假设检验。非参数检验没有总体的参数。这类检验用于观察的顺序和排名,并需要特殊的排名和计数方法。以下是一些非参数检验的例子:
- 卡方检验 是通过单一总体中两个类别变量之间的显著差异或关系来确定的。通常,这个检验评估类别变量的分布是否相互不同。它也被称为卡方拟合优度检验或卡方独立性检验。卡方统计量的较小值意味着观察数据与期望数据吻合,而卡方统计量的较大值则意味着观察数据与期望数据不符。例如,性别对投票偏好的影响,或公司规模对健康保险覆盖范围的影响,可以通过卡方检验来评估:
这里,O 是观察值,E 是期望值,"i" 是列联表中的 "i^(th)" 位置。
让我们通过一个例子来理解卡方检验。假设我们在一家拥有 200 名员工的公司进行了调查,询问他们的最高学历,如高中、高中以上、大专、研究生,并将其与表现水平(如普通和优秀)进行对比。以下是假设和列联标准:
原假设 H[0]: 两个类别变量是独立的——即员工表现与最高学历水平是独立的。
备择假设 H[a]: 两个分类变量不是独立的——也就是说,员工表现与最高学历水平之间并非独立。
列联表可以表示如下:
高中 | 高等中学 | 本科 | 研究生 | |
---|---|---|---|---|
平均值 | 20 | 16 | 13 | 7 |
优秀 | 31 | 40 | 50 | 13 |
让我们进行卡方检验,并检查变量之间的关联是否存在显著差异,具体如下:
from scipy.stats import chi2_contingency
# Average performing employees
average=[20, 16, 13, 7]
# Outstanding performing employees
outstanding=[31, 40, 60, 13]
# contingency table
contingency_table= [average, outstanding]
在上面的代码块中,我们创建了两组员工表现的列表,并构建了一个列联表。
让我们进行卡方检验,具体如下:
# Apply Test
stat, p, dof, expected = chi2_contingency(contingency_table)
print("p-values:",p)
if p < 0.05:
print("Hypothesis Rejected")
else:
print("Hypothesis Accepted")
Output:
p-values: 0.059155602774381234
Hypothesis Accepted
在上面的代码块中,我们检验了员工表现是否与最高学历水平独立的假设。结果表明,在 95% 的置信区间内接受了原假设,这意味着员工表现与最高学历水平之间是独立的。
- Mann-Whitney U 检验是 t 检验的非参数对应检验,适用于两个样本。它不假设样本之间的差异符合正态分布。当观察值为序数且 t 检验的假设条件不成立时(例如,比较两组电影评分偏好),可以使用 Mann-Whitney U 检验。让我们通过以下标准比较两组电影评分:
原假设 H[0]: 两个样本分布之间没有差异。
备择假设 H[a]: 两个样本分布之间存在差异。
请查看以下代码块:
from scipy.stats import mannwhitneyu
# Sample1
data1=[7,8,4,9,8]
# Sample2
data2=[3,4,2,1,1]
在上面的代码块中,我们创建了两个数据列表。
让我们进行 Mann-Whitney U 检验,具体如下:
# Apply Test
stat, p = mannwhitneyu(data1, data2)
print("p-values:",p)
# 0.01 or 1% is significance level or alpha.
if p < 0.01:
print("Hypothesis Rejected")
else:
print("Hypothesis Accepted")
Output:
p-values: 0.007666581056801412
Hypothesis Rejected
在上面的代码块中,我们使用 mannwhitneyu()
方法检验了“两个电影评分组之间是否存在差异”的假设。结果表明,原假设在 99% 的置信区间内被拒绝,意味着两个电影评分组之间存在显著差异。
- Wilcoxon 符号秩检验用于比较两个配对样本。它是配对 t 检验的非参数对应检验。它检验的原假设是两个配对样本是否属于同一分布——例如,比较多组治疗观察的差异。让我们通过以下标准比较两组治疗观察的差异:
原假设 H[0]: 两个依赖样本分布之间没有差异。
备择假设 H[a]: 依赖样本分布之间存在差异。
请查看以下代码块:
from scipy.stats import wilcoxon
# Sample-1
data1 = [1, 3, 5, 7, 9]
# Sample-2 after treatement
data2 = [2, 4, 6, 8, 10]
在上面的代码块中,我们创建了两个数据列表。
让我们进行 Wilcoxon 符号秩检验,具体如下:
# Apply
stat, p = wilcoxon(data1, data2)
print("p-values:",p)
# 0.01 or 1% is significance level or alpha.
if p < 0.01:
print("Hypothesis Rejected")
else:
print("Hypothesis Accepted")
Output:
p-values: 0.025347318677468252
Hypothesis Accepted
在前面的代码块中,我们使用 wilcoxon()
方法检验了治疗前后组间分布是否有差异的假设。前面的结果表明,在 99%的置信区间内接受了原假设,这意味着治疗前后组之间没有显著差异。
- Kruskal-Wallis 检验是单因素方差分析(ANOVA)的非参数版本,用于评估样本是否来自相同的分布。它比较两个或更多独立样本。它扩展了 Mann-Whitney U 检验的限制,后者仅比较两组样本。我们通过以下代码来比较三组样本:
from scipy.stats import kruskal
# Data sample-1
x = [38, 18, 39, 83, 15, 38, 63, 1, 34, 50]
# Data sample-2
y = [78, 32, 58, 59, 74, 77, 29, 77, 54, 59]
# Data sample-3
z = [117, 92, 42, 79, 58, 117, 46, 114, 86, 26]
在前面的代码块中,我们创建了三组数据列表。接下来,我们进行 Kruskal-Wallis 检验,如下所示:
# Apply kruskal-wallis test
stat, p = kruskal(x,y,z)
print("p-values:",p)
# 0.01 or 1% is significance level or alpha.
if p < 0.01:
print("Hypothesis Rejected")
else:
print("Hypothesis Accepted")
Output:
p-values: 0.01997922369138151
Hypothesis Accepted
在前面的代码块中,我们使用 kruskal()
方法检验了三组样本间是否有差异的假设。前面的结果表明,在 99%的置信区间内接受了原假设,这意味着三组样本之间没有差异。接下来,我们将比较参数检验和非参数检验,如下所示:
特征 | 参数检验 | 非参数检验 |
---|---|---|
检验统计量 | 分布 | 任意分布或“无分布假设” |
属性类型 | 数值型 | 名义型和有序型 |
集中趋势测量 | 均值 | 中位数 |
相关性检验 | Pearson 相关性 | Spearman 相关性 |
关于总体的信息 | 完整信息 | 无信息 |
在前面的表格中,您看到了基于多种特征(如检验统计量、属性类型、集中趋势测量、相关性检验和总体信息)进行的参数检验和非参数检验的示例。最后,您完成了这一章节。在本章节中,我们探讨了描述性统计学和推断性统计学的基础知识,并结合 Python 进行了实践。
总结
统计学的核心基础将为数据分析提供基础,帮助我们理解数据的描述和意义。在本章节中,您学习了统计学的基础知识,如属性及其不同类型(如名义型、顺序型和数值型)。您还学习了用于衡量集中趋势的均值、中位数和众数。范围、四分位距、方差和标准差用于估计数据的变异性;偏度和峰度用于理解数据分布;协方差和相关性用于理解变量间的关系。您还了解了推断性统计学的相关主题,如中心极限定理、样本收集以及参数检验和非参数检验。您还使用 pandas
和 scipy.stats
库在统计学概念上进行了动手实践。
下一章,第四章,线性代数,将帮助我们学习如何解线性方程组,求解特征值和特征向量,并使用 Python 包 NumPy 和 SciPy 学习二项分布和正态分布、正态性检验以及掩蔽数组。
第四章:线性代数
线性代数和统计学是任何数据分析活动的基础。统计学帮助我们获得初步的描述性理解,并从数据中做出推断。在上一章中,我们已经理解了数据分析的描述性和推断性统计度量。另一方面,线性代数是数据专业人员的核心基础之一。线性代数对于处理向量和矩阵非常有用。大多数数据以向量或矩阵的形式存在。深入理解线性代数有助于数据分析师和数据科学家理解机器学习和深度学习算法的工作流程,使他们能够根据业务需求灵活地设计和修改算法。例如,如果你想使用主成分分析(PCA),你必须了解特征值和特征向量的基础知识;或者如果你想开发一个推荐系统,你必须了解奇异值分解(SVD)。扎实的数学和统计学背景将有助于更顺利地过渡到数据分析领域。
本章主要关注线性代数的核心概念,如多项式、行列式、矩阵逆;解线性方程;特征值和特征向量;SVD;随机数;二项分布和正态分布;正态性检验;以及掩码数组。我们还可以使用 Python 的 NumPy 和 SciPy 包来执行这些操作。NumPy 和 SciPy 都提供了用于线性代数运算的 linalg
包。
本章将涵盖以下主题:
-
使用 NumPy 拟合多项式
-
行列式
-
查找矩阵的秩
-
使用 NumPy 计算矩阵逆
-
使用 NumPy 解线性方程
-
使用 SVD 分解矩阵
-
使用 NumPy 计算特征向量和特征值
-
生成随机数
-
二项分布
-
正态分布
-
使用 SciPy 检验数据的正态性
-
使用
numpy.ma
子包创建掩码数组
技术要求
本章的以下技术信息可供参考:
-
你可以在以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter04
。 -
所有的代码块都可以在
ch4.ipynb
中找到。 -
本章将使用 NumPy、SciPy、Matplotlib 和 Seaborn Python 库。
使用 NumPy 拟合多项式
多项式是具有非负策略的数学表达式。多项式函数的例子包括线性函数、二次函数、立方函数和四次函数。NumPy 提供了 polyfit()
函数来通过最小二乘法生成多项式。该函数接受 x 坐标、y 坐标和阶数作为参数,并返回一个多项式系数列表。
NumPy 还提供了 polyval()
函数来在给定的值上评估多项式。该函数接受多项式的系数和点的数组,并返回多项式的结果值。另一个函数是 linspace()
,它生成一系列等间隔的值。它接受起始值、终止值和起止范围之间的值的个数,并返回闭区间内等间隔的值。
让我们看一个例子,使用 NumPy 生成并评估多项式,如下所示:
# Import required libraries NumPy, polynomial and matplotlib
import numpy as np
import matplotlib.pyplot as plt
# Generate two random vectors
v1=np.random.rand(10)
v2=np.random.rand(10)
# Creates a sequence of equally separated values
sequence = np.linspace(v1.min(),v1.max(), num=len(v1)*10)
# Fit the data to polynomial fit data with 4 degrees of the polynomial
coefs = np.polyfit(v1, v2, 3)
# Evaluate polynomial on given sequence
polynomial_sequence = np.polyval(coefs,sequence)
# plot the polynomial curve
plt.plot(sequence, polynomial_sequence)
# Show plot
plt.show()
这将产生以下输出:
在上面的截图中显示的图形将在每次迭代中发生变化,使用的是之前编写的程序。这种波动的原因是向量的随机值生成。
让我们进入下一个话题:行列式。我们将使用 numpy.linalg
子包执行大部分的线性代数运算。NumPy 提供了 linalg
子包来进行线性代数运算。我们可以利用线性代数进行矩阵操作,如求逆、秩、特征值、特征向量、求解线性方程组以及执行线性回归。
行列式
行列式是线性代数中最基本的概念。它是一个标量值,由方阵计算得出。行列式是一个基础运算,它帮助我们进行逆矩阵的计算以及解线性方程组。行列式仅适用于方阵,方阵的行数和列数相等。numpy.linalg
子包提供了 det()
函数,用于计算给定输入矩阵的行列式。让我们在下面的代码块中计算行列式:
# Import numpy
import numpy as np
# Create matrix using NumPy
mat=np.mat([[2,4],[5,7]])
print("Matrix:\n",mat)
# Calculate determinant
print("Determinant:",np.linalg.det(mat))
这将产生以下输出:
Matrix:
[[2 4]
[5 7]]
Determinant: -5.999999999999998
在上面的代码块中,我们使用 np.linalg.det()
方法计算了给定矩阵的行列式。现在让我们理解另一个线性代数的概念——秩,并使用 numpy.linalg
子包计算它。
寻找矩阵的秩
秩是解决线性方程组时一个非常重要的概念。矩阵的秩表示矩阵中保留的信息量。秩越低表示信息越少,秩越高表示信息量越大。秩可以定义为矩阵中独立行或列的数量。numpy.linalg
子包提供了 matrix_rank()
函数。matrix_rank()
函数接受矩阵作为输入,并返回计算出的矩阵秩。让我们通过下面的代码块来看一个 matrix_rank()
函数的例子:
# import required libraries
import numpy as np
from numpy.linalg import matrix_rank
# Create a matrix
mat=np.array([[5, 3, 1],[5, 3, 1],[1, 0, 5]])
# Compute rank of matrix
print("Matrix: \n", mat)
print("Rank:",matrix_rank(mat))
这将产生以下输出:
Matrix:
[[5 3 1]
[5 3 1]
[1 0 5]]
Rank: 2
在上面的代码块中,numpy.linalg
的 matrix_rank()
函数用于生成矩阵的秩。现在让我们来看线性代数中的另一个重要概念:矩阵逆。
使用 NumPy 计算矩阵的逆
矩阵是由行和列组织的数字、表达式和符号的矩形序列。一个方阵与其逆矩阵相乘的结果是单位矩阵 I。我们可以用以下方程表示:
AA^(-1) = I
numpy.linalg
子包提供了一个用于求逆的函数:inv()
函数。让我们使用numpy.linalg
子包来求矩阵的逆。首先,我们使用mat()
函数创建一个矩阵,然后使用inv()
函数求矩阵的逆,如下面的代码块所示:
# Import numpy
import numpy as np
# Create matrix using NumPy
mat=np.mat([[2,4],[5,7]])
print("Input Matrix:\n",mat)
# Find matrix inverse
inverse = np.linalg.inv(mat)
print("Inverse:\n",inverse)
这将产生以下输出:
Input Matrix:
[[2 4]
[5 7]]
Inverse:
[[-1.16666667 0.66666667]
[ 0.83333333 -0.33333333]]
在前面的代码块中,我们使用numpy.linalg
子包的inv()
函数计算了矩阵的逆。
如果给定的输入矩阵不是方阵或是奇异矩阵,它将引发LinAlgError
错误。如果你愿意,可以手动测试inv()
函数。我将把这个任务留给你自己完成。
使用 NumPy 求解线性方程
矩阵操作可以将一个向量转换为另一个向量。这些操作将帮助我们找到线性方程的解。NumPy 提供了solve()
函数来求解形式为 Ax = B 的线性方程,其中 A 是 n*n 矩阵,B 是一个一维数组,x 是未知的一维向量。我们还将使用dot()
函数来计算两个浮点数数组的点积。
让我们来解一个线性方程的例子,如下所示:
- 为给定方程创建矩阵 A 和数组 B,如下所示:
x1 + x2 = 200
3x1 + 2x2 = 450
这在下面的代码块中进行了说明
# Create matrix A and Vector B using NumPy
A=np.mat([[1,1],[3,2]])
print("Matrix A:\n",A)
B = np.array([200,450])
print("Vector B:", B)
这将产生以下输出:
Matrix A:
[[1 1]
[3 2]]
Vector B: [200 450]
在前面的代码块中,我们创建了一个 2*2 矩阵和一个向量。
- 使用
solve()
函数求解线性方程,如下所示:
# Solve linear equations
solution = np.linalg.solve(A, B)
print("Solution vector x:", solution)
这将产生以下输出:
Solution vector x: [ 50\. 150.]
在前面的代码块中,我们使用numpy.linalg
子包的solve()
函数求解了一个线性方程。
- 使用
dot()
函数检查解,如下所示:
# Check the solution
print("Result:",np.dot(A,solution))
这将产生以下输出:
Result: [[200\. 450.]]
在前面的代码块中,我们使用dot()
函数评估了解决方案。你可以看到,A 和解的点积等于 B。到目前为止,我们已经看到了行列式、秩、逆矩阵以及如何解线性方程。接下来,我们将讨论用于矩阵分解的 SVD。
使用 SVD 分解矩阵
矩阵分解是将一个矩阵拆分为多个部分的过程,也称为矩阵因式分解。常见的矩阵分解方法包括下三角-上三角(LU)分解、QR分解(其中Q是正交矩阵,R是上三角矩阵)、Cholesky 分解和 SVD。
特征分析将矩阵分解为向量和值。SVD 将矩阵分解为以下几部分:奇异向量和奇异值。SVD 在信号处理、计算机视觉、自然语言处理(NLP)和机器学习中被广泛应用——例如在主题建模和推荐系统中,SVD 在现实商业解决方案中被广泛接受和实现。请看以下内容:
这里,A是一个m x n的左奇异矩阵,Σ是一个n x n的对角矩阵,V是一个m x n的右奇异矩阵,V^T是 V 的转置矩阵。numpy.linalg
子包提供了svd()
函数来分解矩阵。我们来看一个 SVD 的例子,如下所示:
# import required libraries
import numpy as np
from scipy.linalg import svd
# Create a matrix
mat=np.array([[5, 3, 1],[5, 3, 0],[1, 0, 5]])
# Perform matrix decomposition using SVD
U, Sigma, V_transpose = svd(mat)
print("Left Singular Matrix:",U)
print("Diagonal Matrix: ", Sigma)
print("Right Singular Matrix:", V_transpose)
这将产生以下输出:
Left Singular Matrix: [[-0.70097269 -0.06420281 -0.7102924 ]
[-0.6748668 -0.26235919 0.68972636]
[-0.23063411 0.9628321 0.14057828]]
Diagonal Matrix: [8.42757145 4.89599358 0.07270729]
Right Singular Matrix: [[-0.84363943 -0.48976369 -0.2200092]
[-0.13684207 -0.20009952 0.97017237]
[ 0.51917893 -0.84858218 -0.10179157]]
在前面的代码块中,我们使用scipy.linalg
子包的svd()
函数将给定的矩阵分解成三个部分:左奇异矩阵
、对角矩阵
和右奇异矩阵
。
使用 NumPy 计算特征向量和特征值
特征向量和特征值是理解线性映射和变换所需的工具。特征值是方程 Ax = λx 的解,其中 A 是方阵,x 是特征向量,λ是特征值。numpy.linalg
子包提供了两个函数,eig()
和eigvals()
。eig()
函数返回特征值和特征向量的元组,而eigvals()
返回特征值。
特征向量和特征值是线性代数的核心基础。特征向量和特征值被广泛应用于 SVD、谱聚类和 PCA。
让我们计算一个矩阵的特征向量和特征值,如下所示:
- 使用 NumPy 的
mat()
函数创建矩阵,如下所示:
# Import numpy
import numpy as np
# Create matrix using NumPy
mat=np.mat([[2,4],[5,7]])
print("Matrix:\n",mat)
这将产生以下输出:
Matrix: [[2 4]
[5 7]]
- 使用
eig()
函数计算特征向量和特征值,如下所示:
# Calculate the eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(mat)
print("Eigenvalues:", eigenvalues)
print("Eigenvectors:", eigenvectors)
这将产生以下输出:
Eigenvalues: [-0.62347538 9.62347538]
Eigenvectors: [[-0.83619408 -0.46462222]
[ 0.54843365 -0.885509 ]]
在前面的两个代码块中,我们创建了一个 2*2 的矩阵,并使用numpy.linalg
子包的eig()
函数计算了特征向量和特征值。
- 使用
eigvals()
函数计算特征值,如下所示:
# Compute eigenvalues
eigenvalues= np.linalg.eigvals(mat)
print("Eigenvalues:", eigenvalues)
这将产生以下输出:
Eigenvalues: [-0.62347538 9.62347538]
在前面的代码片段中,我们使用numpy.linalg
子包的eigvals()
函数计算了特征值。完成特征分解后,我们将看到如何生成随机数和矩阵。
生成随机数
随机数在许多应用中都有使用,比如蒙特卡罗模拟、加密学、密码初始化和随机过程。生成真正的随机数并不容易,因此实际上大多数应用程序使用伪随机数。伪随机数对于大多数目的都是足够的,除非遇到一些极少数的特殊情况。随机数可以从离散和连续数据中生成。numpy.random()
函数将为给定输入矩阵的大小生成一个随机数矩阵。
核心随机数生成器基于梅森旋转算法(参考 en.wikipedia.org/wiki/Mersenne_twister
)。
让我们看一个生成随机数的示例,如下所示:
# Import numpy
import numpy as np
# Create an array with random values
random_mat=np.random.random((3,3))
print("Random Matrix: \n",random_mat)
这将产生以下输出:
Random Matrix: [[0.90613234 0.83146869 0.90874706]
[0.59459996 0.46961249 0.61380679]
[0.89453322 0.93890312 0.56903598]]
在前面的示例中,我们使用 numpy.random.random()
函数生成了一个 3*3 的随机矩阵。让我们尝试其他分布来生成随机数,例如二项分布和正态分布。
二项分布
二项分布模型描述了在每次试验中都有相同概率的重复试验的次数。在这里,每次试验都是独立的,且每次试验有两个可能的结果——成功和失败——这些结果可能出现在每个客户上。以下公式表示二项分布:
在这里,p 和 q 分别是成功和失败的概率,n 是试验次数,X 是期望的输出数量。
numpy.random
子包提供了一个 binomial()
函数,用于基于二项分布生成样本,该分布需要指定参数:试验次数和成功概率。
让我们考虑一个 17 世纪的赌博馆,你可以在这里对八个投掷物和九个硬币的翻转进行下注。如果你得到五个或更多的正面朝上,则你获胜,否则你将失败。让我们编写代码,模拟 1,000 个硬币的投掷,使用 binomial()
函数,如下所示:
# Import required libraries
import numpy as np
import matplotlib.pyplot as plt
# Create an numpy vector of size 5000 with value 0
cash_balance = np.zeros(5000)
cash_balance[0] = 500
# Generate random numbers using Binomial
samples = np.random.binomial(9, 0.5, size=len(cash_balance))
# Update the cash balance
for i in range(1, len(cash_balance)):
if samples[i] < 5:
cash_balance[i] = cash_balance[i - 1] - 1
else:
cash_balance[i] = cash_balance[i - 1] + 1
# Plot the updated cash balance
plt.plot(np.arange(len(cash_balance)), cash_balance)
plt.show()
这将产生以下输出:
在前面的代码块中,我们首先创建了一个大小为 500 且初始值为 0 的 cash_balance
数组,并将第一个值更新为 500。然后,我们使用 binomial()
函数生成了 0 到 9 之间的值。之后,我们根据掷硬币的结果更新了 cash_balance
数组,并使用 Matplotlib 库绘制了现金余额。
在每次执行中,代码将生成不同的结果或随机行走。如果你希望行走保持一致,你需要在 binomial()
函数中使用种子值。让我们尝试另一种形式的分布来生成随机数:正态分布。
正态分布
正态分布在现实生活中经常出现。正态分布也被称为钟形曲线,因为它的特征形状。概率密度函数描述了连续分布。numpy.random
子包提供了许多连续分布,如贝塔分布、伽马分布、逻辑分布、指数分布、多元正态分布和正态分布。normal()
函数可以从高斯分布或正态分布中生成样本。
让我们编写代码,通过 normal()
函数可视化正态分布,如下所示:
# Import required library
import numpy as np
import matplotlib.pyplot as plt
sample_size=225000
# Generate random values sample using normal distribution
sample = np.random.normal(size=sample_size)
# Create Histogram
n, bins, patch_list = plt.hist(sample, int(np.sqrt(sample_size)), density=True)
# Set parameters
mu, sigma=0,1
x= bins
y= 1/(sigma * np.sqrt(2 * np.pi)) * np.exp( - (bins - mu)**2 / (2 * sigma**2) )
# Plot line plot(or bell curve)
plt.plot(x,y,color='red',lw=2)
plt.show()
这将产生以下输出:
在这里,我们使用numpy.random
子包的normal()
函数生成随机值,并使用直方图和线性图或钟形曲线(即理论上的概率密度函数(PDF))将值进行展示,均值为 0,标准差为 1。
使用 SciPy 进行数据的正态性检验
正态分布在科学和统计操作中广泛应用。根据中心极限定理,随着样本量的增加,样本分布趋近于正态分布。正态分布因其已知且易于使用而广为人知。在大多数情况下,建议确认数据的正态性,特别是在假设数据符合高斯分布的参数方法中。文献中存在许多正态性检验方法,如 Shapiro-Wilk 检验、Anderson-Darling 检验和 D'Agostino-Pearson 检验。scipy.stats
包提供了大多数正态性检验方法。
在本节中,我们将学习如何对数据应用正态性检验。我们使用三种样本:小型样本、中型样本和大型样本的随机数据。我们将使用normal()
函数为这三种样本生成数据,如下所示:
# Import required library
import numpy as np
# create small, medium, and large samples for normality test
small_sample = np.random.normal(loc=100, scale=60, size=15)
medium_sample = np.random.normal(loc=100, scale=60, size=100)
large_sample = np.random.normal(loc=100, scale=60, size=1000)
我们现在将探索多种方法来检查数据的正态性:
- 使用直方图: 直方图是检查数据正态性的最简单和最快捷的方法。它将数据划分为多个区间,并统计每个区间中的观测值。最后,它将数据可视化。在这里,我们使用来自
seaborn
库的distplot()
来绘制直方图和核密度估计。让我们来看一个关于小型样本的直方图示例,如下所示:
# Histogram for small
import seaborn as sns
import matplotlib.pyplot as plt
# Create distribution plot
sns.distplot(small_sample)
sns.distplot(small_sample)
plt.show()
这将产生以下输出:
让我们来看一个关于中型样本的直方图示例,如下所示:
# Histogram for medium
import seaborn as sns
import matplotlib.pyplot as plt
# Create distribution plot
sns.distplot(medium_sample)
plt.show()
这将产生以下输出:
让我们来看一个关于大型样本的直方图示例,如下所示:
# Histogram for large
import seaborn as sns
import matplotlib.pyplot as plt
# Create distribution plot
sns.distplot(large_sample)
plt.show()
这将产生以下输出:
在前面的三个图中,我们可以观察到,随着样本量的增加,曲线逐渐变成正态分布曲线。直方图可以是检验数据正态性的一种有效工具。
- Shapiro-Wilk 检验: 此检验用于评估数据的正态性。在 Python 中,可以使用
scipy.stats
子包中的shapiro()
函数来进行正态性检验。shapiro()
函数将返回两个值的元组:检验统计量和 p 值。让我们来看以下示例:
# Import shapiro function
from scipy.stats import shapiro
# Apply Shapiro-Wilk Test
print("Shapiro-Wilk Test for Small Sample: ",shapiro(small_sample))
print("Shapiro-Wilk Test for Medium Sample: ",shapiro(medium_sample))
print("Shapiro-Wilk Test for Large Sample: ",shapiro(large_sample))
这将产生以下输出:
Shapiro-Wilk Test for Small Sample: (0.9081739783287048, 0.2686822712421417)
Shapiro-Wilk Test for Medium Sample: (0.9661878347396851, 0.011379175819456577)
Shapiro-Wilk Test for Large Sample: (0.9991633892059326, 0.9433153867721558)
在前面的代码块中,你可以看到小型和大型数据集的 p 值都大于 0.05,因此由于原假设未被拒绝,这意味着样本看起来像是高斯分布或正态分布;而对于中型数据集,p 值小于 0.05,因此原假设被拒绝,这意味着样本看起来不像高斯分布或正态分布。
同样,我们可以使用 scipy.stats
子包中的 anderson()
和 normaltest()
函数,尝试 Anderson-Darling 检验和 D'Agostino-Pearson 正态性检验。我将这个留给你作为练习。在可视化方面,我们也可以尝试箱线图和 分位数-分位数 (QQ) 图技术来评估数据的正态性。我们将在接下来的章节 第五章,数据可视化 中学习箱线图技术。接下来我们将讨论掩码数组的概念。
使用 numpy.ma
子包创建掩码数组
在大多数情况下,现实数据是嘈杂且杂乱的。数据中通常会有很多空缺或缺失的字符。掩码数组在这种情况下非常有用,能够处理这个问题。掩码数组可能包含无效和缺失值。numpy.ma
子包提供了所有掩码数组所需的功能。在本章的这一部分中,我们将使用面部图像作为原始图像源,并执行对数掩码操作。
看一下以下的代码块:
# Import required library
import numpy as np
from scipy.misc import face
import matplotlib.pyplot as plt
face_image = face()
mask_random_array = np.random.randint(0, 3, size=face_image.shape)
fig, ax = plt.subplots(nrows=2, ncols=2)
# Display the Original Image
plt.subplot(2,2,1)
plt.imshow(face_image)
plt.title("Original Image")
plt.axis('off')
# Display masked array
masked_array = np.ma.array(face_image, mask=mask_random_array)
plt.subplot(2,2,2)
plt.title("Masked Array")
plt.imshow(masked_array)
plt.axis('off')
# Log operation on original image
plt.subplot(2,2,3)
plt.title("Log Operation on Original")
plt.imshow(np.ma.log(face_image).astype('uint8'))
plt.axis('off')
# Log operation on masked array
plt.subplot(2,2,4)
plt.title("Log Operation on Masked")
plt.imshow(np.ma.log(masked_array).astype('uint8'))
plt.axis('off')
# Display the subplots
plt.show()
这将产生以下输出:
在前面的代码块中,我们首先从 scipy.misc
子包加载了面部图像,并使用 randint()
函数创建了一个随机掩码。然后,我们将随机掩码应用于面部图像。之后,我们对原始面部图像和掩码面部图像进行了对数操作。最后,我们将所有图像显示在 2*2 的子图中。你还可以尝试对图像进行一系列来自 numpy.ma
子包的掩码操作。这里,我们只关注掩码数组的对数操作。这就是基本线性代数概念的全部内容。接下来是时候进入数据可视化的概念了,我们将在下一章学习。
总结
最后,我们可以得出结论,像线性代数这样的数学学科是所有机器学习算法的核心支柱。在本章中,我们专注于基础的线性代数概念,旨在提升你作为数据专业人士的能力。在这一章中,你学习了许多使用 NumPy 和 SciPy 子包的线性代数概念。我们主要关注了多项式、行列式、矩阵求逆;解线性方程;特征值和特征向量;奇异值分解(SVD);随机数;二项分布和正态分布;正态性检验;以及掩码数组。
下一章,第五章,数据可视化,将介绍如何使用 Python 进行数据可视化这一重要主题。数据可视化是我们在开始分析数据时经常进行的操作,它有助于展示数据中变量之间的关系。通过数据可视化,我们还可以更好地了解数据的统计属性。
第五章:第二节:探索性数据分析与数据清理
本节的主要目标是培养学习者的探索性数据分析(EDA)和数据清理技能。这些技能包括数据可视化、数据提取和预处理。本节将主要使用 matplotlib、seaborn、Bokeh、pandas、scikit-learn、NumPy 和 SciPy,并重点关注信号处理和时间序列分析。
本节包括以下章节:
-
第五章,数据可视化
-
第六章,数据的获取、处理和存储
-
第七章,清理杂乱数据
-
第八章,信号处理与时间序列
第六章:数据可视化
数据可视化是数据分析系统中帮助轻松理解和传达信息的初步步骤。它通过使用图表、图形、图表和地图等可视化元素将信息和数据以图形形式表示。这有助于分析师理解模式、趋势、异常值、分布和关系。数据可视化是一种有效处理大量数据集的方式。
Python 提供了多种用于数据可视化的库,如 Matplotlib、Seaborn 和 Bokeh。在本章中,我们将首先关注 Matplotlib,它是用于可视化的基础 Python 库。然后我们将探讨 Seaborn,Seaborn 基于 Matplotlib,并提供了高级的统计图表。最后,我们将使用 Bokeh 进行交互式数据可视化。我们还将探讨 pandas
绘图。以下是本章将要涵盖的主题列表:
-
使用 Matplotlib 进行可视化
-
使用 Seaborn 包进行高级可视化
-
使用 Bokeh 进行交互式可视化
技术要求
本章具有以下技术要求:
-
你可以在以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter05
。 -
所有代码块都可以在
ch5.ipynb
文件中找到。 -
本章仅使用一个 CSV 文件(
HR_comma_sep.csv
)进行练习。 -
在本章中,我们将使用 Matplotlib、
pandas
、Seaborn 和 Bokeh Python 库。
使用 Matplotlib 进行可视化
正如我们所知道的,一图胜千言。人类对视觉信息的理解更好。可视化有助于向任何类型的观众展示事物,并可以用通俗的语言轻松解释复杂的现象。Python 提供了几种可视化库,如 Matplotlib、Seaborn 和 Bokeh。
Matplotlib 是最流行的 Python 数据可视化模块。它是大多数高级 Python 可视化模块(如 Seaborn)的基础库。它提供了灵活且易于使用的内置函数,用于创建图形和图表。
在 Anaconda 中,Matplotlib 已经安装。如果你仍然遇到错误,可以通过以下方式安装它。
我们可以通过 pip
安装 Matplotlib,方法如下:
pip install matplotlib
对于 Python 3,我们可以使用以下命令:
pip3 install matplotlib
你也可以通过终端或命令提示符使用以下命令简单地安装 Matplotlib:
conda install matplotlib
要在 Matplotlib 中创建一个非常基础的图表,我们需要调用 plot()
函数,该函数位于 matplotlib.pyplot
子包中。此函数为已知 x 和 y 坐标的单个列表或多个列表的点生成一个二维图表。
以下示例代码位于本书代码包中的 ch5.ipynb
文件中,您可以通过以下 GitHub 链接找到该文件:github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/blob/master/Chapter05/Ch5.ipynb
。
让我们看看一个小的示例代码,用于可视化折线图:
# Add the essential library matplotlib
import matplotlib.pyplot as plt
import numpy as np
# create the data
a = np.linspace(0, 20)
# Draw the plot
plt.plot(a, a + 0.5, label='linear')
# Display the chart
plt.show()
这将产生以下输出:
在前面的代码块中,首先,我们导入了 Matplotlib 和 NumPy 模块。之后,我们使用 NumPy 的 linespace()
函数创建数据,并使用 Matplotlib 的 plot()
函数绘制这些数据。最后,我们使用 show()
函数显示图形。
图表有两个基本组件:图形和坐标轴。图形是一个容器,所有内容都绘制在上面。它包含如图表、子图、坐标轴、标题和图例等组件。在下一节中,我们将重点介绍这些组件,它们就像图表的配件。
图表的配件
在 matplotlib
模块中,我们可以向图表添加标题和坐标轴标签。我们可以使用 plt.title()
添加标题,使用 plt.xlabel()
和 plt.ylabel()
添加标签。
多个图形意味着多个对象,如折线、柱状图和散点图。不同系列的点可以显示在同一个图表上。图例或图表系列反映 y 轴。图例是一个框,通常出现在图表的右侧或左侧,显示每个图形元素的含义。让我们看看一个示例,展示如何在图表中使用这些配件:
# Add the required libraries
import matplotlib.pyplot as plt
# Create the data
x = [1,3,5,7,9,11]
y = [10,25,35,33,41,59]
# Let's plot the data
plt.plot(x, y,label='Series-1', color='blue')
# Create the data
x = [2,4,6,8,10,12]
y = [15,29,32,33,38,55]
# Plot the data
plt.plot(x, y, label='Series-2', color='red')
# Add X Label on X-axis
plt.xlabel("X-label")
# Add X Label on X-axis
plt.ylabel("Y-label")
# Append the title to graph
plt.title("Multiple Python Line Graph")
# Add legend to graph
plt.legend()
# Display the plot
plt.show()
这将产生以下输出:
在前面的图表中,两个折线显示在同一个图表上。我们在 plot()
函数中使用了两个额外的参数 —— label
和 color
。label
参数定义了系列的名称,color
定义了折线的颜色。在接下来的章节中,我们将重点介绍不同类型的图表。我们将在下一节中探讨散点图。
散点图
散点图使用笛卡尔坐标绘制数据点,显示数值的大小。它们还表示两个数值之间的关系。我们可以使用 Matplotlib 中的 scatter()
函数创建散点图,如下所示:
# Add the essential library matplotlib
import matplotlib.pyplot as plt
# create the data
x = [1,3,5,7,9,11]
y = [10,25,35,33,41,59]
# Draw the scatter chart
plt.scatter(x, y, c='blue', marker='*',alpha=0.5)
# Append the label on X-axis
plt.xlabel("X-label")
# Append the label on X-axis
plt.ylabel("Y-label")
# Add the title to graph
plt.title("Scatter Chart Sample")
# Display the chart
plt.show()
这将产生以下输出:
在前面的散点图中,scatter()
函数接受 x 轴和 y 轴的值。在我们的示例中,我们绘制了两个列表:x
和 y
。我们还可以使用一些可选参数,例如 c
来设置颜色,alpha
来设置标记的透明度(范围从 0 到 1),以及 marker
来设置散点图中点的形状,如 *
、o
或任何其他符号。在下一节中,我们将重点介绍折线图。
折线图
折线图是一种显示两变量之间关系的图表,它由一系列数据点连接而成:
# Add the essential library matplotlib
import matplotlib.pyplot as plt
# create the data
x = [1,3,5,7,9,11]
y = [10,25,35,33,41,59]
# Draw the line chart
plt.plot(x, y)
# Append the label on X-axis
plt.xlabel("X-label")
# Append the label on X-axis
plt.ylabel("Y-label")
# Append the title to chart
plt.title("Line Chart Sample")
# Display the chart
plt.show()
这会生成如下输出:
在之前的折线图程序中,plot()
函数接受 x 轴和 y 轴的数值。在接下来的章节中,我们将学习如何绘制饼图。
饼图
饼图是一个圆形图表,被分成楔形的部分。每一部分的大小与它所代表的数值成正比。饼图的总值为 100 百分比:
# Add the essential library matplotlib
import matplotlib.pyplot as plt
# create the data
subjects = ["Mathematics","Science","Communication Skills","Computer Application"]
scores = [85,62,57,92]
# Plot the pie plot
plt.pie(scores,
labels=subjects,
colors=['r','g','b','y'],
startangle=90,
shadow= True,
explode=(0,0.1,0,0),
autopct='%1.1f%%')
# Add title to graph
plt.title("Student Performance")
# Draw the chart
plt.show()
这会生成如下输出:
在之前的饼图代码中,我们指定了 values
、labels
、colors
、startangle
、shadow
、explode
和 autopct
。在我们的例子中,values
是学生在四个科目中的成绩,labels
是科目名称的列表。我们还可以为每个科目的成绩指定颜色列表。startangle
参数指定第一个值的角度,默认为 90 度;这意味着第一个线条是垂直的。
可选地,我们还可以使用 shadow
参数来指定饼图切片的阴影,使用 explode
参数来突出显示某些饼图切片的二进制值。如果我们想突出显示第二个饼图切片,则值的元组为 (0, 0.1, 0, 0)。接下来我们跳到条形图部分。
条形图
条形图是一种比较不同组值的可视化工具,可以水平或垂直绘制。我们可以使用 bar()
函数创建条形图:
# Add the essential library matplotlib
import matplotlib.pyplot as plt
# create the data
movie_ratings = [1,2,3,4,5]
rating_counts = [21,45,72,89,42]
# Plot the data
plt.bar(movie_ratings, rating_counts, color='blue')
# Add X Label on X-axis
plt.xlabel("Movie Ratings")
# Add X Label on X-axis
plt.ylabel("Rating Frequency")
# Add a title to graph
plt.title("Movie Rating Distribution")
# Show the plot
plt.show()
这会生成如下输出:
在之前的条形图程序中,bar()
函数接受 x 轴值、y 轴值和颜色。在我们的例子中,我们绘制的是电影评分及其频率。电影评分位于 x 轴上,评分频率位于 y 轴上。我们还可以使用 color
参数为条形图的条形指定颜色。接下来,让我们看看条形图的另一种变体。
直方图
直方图显示的是一个数值变量的分布情况。我们使用 hist()
方法创建直方图,它显示的是连续变量的概率分布。直方图只适用于单一变量,而条形图适用于两个变量:
# Add the essential library
import matplotlib.pyplot as plt
# Create the data
employee_age = [21,28,32,34,35,35,37,42,47,55]
# Create bins for histogram
bins = [20,30,40,50,60]
# Plot the histogram
plt.hist(employee_age, bins, rwidth=0.6)
# Add X Label on X-axis
plt.xlabel("Employee Age")
# Add X Label on X-axis
plt.ylabel("Frequency")
# Add title to graph
plt.title("Employee Age Distribution")
# Show the plot
plt.show()
这会生成如下输出:
在之前的直方图中,hist()
函数接受 values
、bins
和 rwidth
。在我们的例子中,我们绘制的是员工的年龄,并使用 10 年的区间。我们从 20 岁到 60 岁开始,每个区间的大小为 10 年。我们使用 0.6 的相对条形宽度,但你可以选择任何大小来调整宽度的粗细。现在,我们将跳转到气泡图,它可以在二维图中处理多个变量。
气泡图
气泡图是一种散点图。它不仅使用笛卡尔坐标绘制数据点,还在数据点上创建气泡。气泡显示了图表的第三维度。它展示了三个数值:两个数值位于 x 和 y 轴上,第三个数值则是数据点(或气泡)的大小:
# Import the required modules
import matplotlib.pyplot as plt
import numpy as np
# Set figure size
plt.figure(figsize=(8,5))
# Create the data
countries = ['Qatar','Luxembourg','Singapore','Brunei','Ireland','Norway','UAE','Kuwait']
populations = [2781682, 604245,5757499,428963,4818690,5337962,9630959,4137312]
gdp_per_capita = [130475, 106705, 100345, 79530, 78785, 74356,69382, 67000]
# scale GDP per capita income to shoot the bubbles in the graph
scaled_gdp_per_capita = np.divide(gdp_per_capita, 80)
colors = np.random.rand(8)
# Draw the scatter diagram
plt.scatter(countries, populations, s=scaled_gdp_per_capita, c=colors, cmap="Blues",edgecolors="grey", alpha=0.5)
# Add X Label on X-axis
plt.xlabel("Countries")
# Add Y Label on X-axis
plt.ylabel("Population")
# Add title to graph
plt.title("Bubble Chart")
# rotate x label for clear visualization
plt.xticks(rotation=45)
# Show the plot
plt.show()
这将产生以下输出:
在前面的图表中,使用散点图函数创建了一个气泡图。这里,重要的是散点图函数的 s
(大小)参数。我们将第三个变量 scaled_gdp_per_capita
分配给了 size
参数。在前面的气泡图中,国家位于 x 轴上,人口位于 y 轴上,而人均 GDP 通过散点或气泡的大小来展示。我们还为气泡分配了随机颜色,以使其更具吸引力并易于理解。从气泡的大小,可以很容易看出卡塔尔的人均 GDP 最高,而科威特的人均 GDP 最低。在前面的所有部分中,我们集中讨论了大部分 Matplotlib 图表。现在,我们将看看如何使用 pandas
模块绘制图表。
pandas 绘图
pandas
库提供了 plot()
方法,它是 Matplotlib 库的封装。plot()
方法允许我们直接在 pandas
DataFrame 上创建图表。以下是用于创建图表的 plot()
方法参数:
-
kind
: 用于图表类型的字符串参数,例如:line、bar、barh、hist、box、KDE、pie、area 或 scatter。 -
figsize
: 这定义了图表的尺寸,采用 (宽度, 高度) 的元组形式。 -
title
: 这定义了图表的标题。 -
grid
: 布尔参数,表示轴的网格线。 -
legend
: 这定义了图例。 -
xticks
: 这定义了 x 轴刻度的序列。 -
yticks
: 这定义了 y 轴刻度的序列。
让我们使用 pandas plot()
函数创建一个散点图:
# Import the required modules
import pandas as pd
import matplotlib.pyplot as plt
# Let's create a Dataframe
df = pd.DataFrame({
'name':['Ajay','Malala','Abhijeet','Yming','Desilva','Lisa'],
'age':[22,72,25,19,42,38],
'gender':['M','F','M','M','M','F'],
'country':['India','Pakistan','Bangladesh','China','Srilanka','UK'],
'income':[2500,3800,3300,2100,4500,5500]
})
# Create a scatter plot
df.plot(kind='scatter', x='age', y='income', color='red', title='Age Vs Income')
# Show figure
plt.show()
这将产生以下输出:
在前面的图表中,plot()
函数使用了 kind
、x
、y
、color
和 title
参数。在我们的示例中,我们使用 kind
参数为 'scatter'
来绘制年龄与收入之间的散点图。age
和 income
列分别分配给 x
和 y
参数。散点的颜色和图表的标题被分配给 color
和 title
参数:
import matplotlib.pyplot as plt
import pandas as pd
# Create bar plot
df.plot(kind='bar',x='name', y='age', color='blue')
# Show figure
plt.show()
这将产生以下输出:
在前面的图表中,plot()
函数使用 kind
、x
、y
、color
和 title
参数。在我们的示例中,我们使用 kind
参数为 'bar'
来绘制年龄与收入之间的柱状图。name
和 age
列分别分配给 x
和 y
参数。散点的颜色被分配给 color
参数。这就是关于 pandas
绘图的内容。从下一部分开始,我们将看到如何使用 Seaborn 库可视化数据。
使用 Seaborn 包进行高级可视化
可视化有助于轻松理解复杂的模式和概念。它以图像的形式表示洞察。在前面的部分中,我们学习了如何使用 Matplotlib 进行可视化。现在,我们将探索新的 Seaborn 库,它可以进行高级统计图表的绘制。Seaborn 是一个开源的 Python 库,用于高阶互动性和吸引人的统计可视化。Seaborn 以 Matplotlib 为基础库,提供了更简单、易懂、互动性强且美观的可视化效果。
在 Anaconda 软件包中,您可以通过以下方式安装 Seaborn 库:
使用 pip
安装 Seaborn:
pip install seaborn
对于 Python 3,请使用以下命令:
pip3 install seaborn
您可以通过终端或命令提示符使用以下命令轻松安装 Seaborn:
conda install seaborn
如果您是在 Jupyter Notebook 中安装,则需要在 pip
命令前加上 !
符号。以下是示例:
!pip install seaborn
让我们跳转到 Seaborn 的 lm
图。
lm 图
lm
图绘制了散点图并在其上拟合回归模型。散点图是理解两个变量之间关系的最佳方式。它的输出可视化表示了两个变量的联合分布。lmplot()
接受两个列名——x
和 y
——作为字符串和 DataFrame 变量。让我们来看以下示例:
# Import the required libraries
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# Create DataFrame
df=pd.DataFrame({'x':[1,3,5,7,9,11],'y':[10,25,35,33,41,59]})
# Create lmplot
sns.lmplot(x='x', y='y', data=df)
# Show figure
plt.show()
这将产生以下输出:
默认情况下,lmplot()
会拟合回归线。我们也可以通过将 fit_reg
参数设置为 False
来移除回归线:
# Create lmplot
sns.lmplot(x='x', y='y', data=df, fit_reg=False)
# Show figure
plt.show()
这将产生以下输出:
让我们使用 HR Analytics 数据集并尝试绘制 lmplot()
:
# Load the dataset
df=pd.read_csv("HR_comma_sep.csv")
# Create lmplot
sns.lmplot(x='satisfaction_level', y='last_evaluation', data=df, fit_reg=False, hue='left')
# Show figure
plt.show()
这将产生以下输出:
在前面的示例中,last_evaluation
是员工的评估表现,satisfaction_level
是员工在公司的满意度,left
表示员工是否离开公司。satisfaction_level
和 last_evaluation
分别绘制在 x 和 y 轴上。第三个变量 left
被传递到 hue
参数中。hue
属性用于颜色阴影。我们将 left
变量作为 hue
。从图表中我们可以清楚地看到,已经离职的员工分散成三组。接下来我们来看条形图。
条形图
barplot()
显示了分类变量和连续变量之间的关系。它使用不同长度的矩形条形图:
# Import the required libraries
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# Create DataFrame
df=pd.DataFrame({'x':['P','Q','R','S','T','U'],'y':[10,25,35,33,41,59]})
# Create lmplot
sns.barplot(x='x', y='y', data=df)
# Show figure
plt.show()
这将产生以下输出:
在前面的示例中,条形图是通过 bar()
函数创建的。它接受两个列——x
和 y
——以及一个 DataFrame 作为输入。在接下来的部分中,我们将看到如何绘制分布图。
分布图
这绘制了一个单变量的分布图。它是默认箱体大小的直方图与核密度估计(KDE)图的结合。在我们的示例中,distplot()
将接受satisfaction_level
输入列并绘制其分布。在这里,satisfaction_level
的分布有两个峰值:
# Create a distribution plot (also known as Histogram)
sns.distplot(df.satisfaction_level)
# Show figure
plt.show()
这将产生以下输出:
在前面的代码块中,我们使用distplot()
创建了分布图。现在是时候跳转到箱型图了。
箱型图
箱型图,又称箱形须图,是理解每个变量分布及其四分位数的最佳图表之一。它可以是水平的也可以是垂直的。它通过一个箱子显示四分位数分布,该箱子被称为须。它还显示数据的最小值、最大值和异常值。我们可以使用 Seaborn 轻松创建箱型图:
# Create boxplot
sns.boxplot(data=df[['satisfaction_level','last_evaluation']])
# Show figure
plt.show()
这将产生以下输出:
在前面的示例中,我们使用了两个变量来绘制箱型图。这里,箱型图表明satisfaction_level
的范围高于last_evaluation
(表现)。接下来,我们来看看 Seaborn 中的 KDE 图。
KDE 图
kde()
函数绘制给定连续变量的概率密度估计。这是一种非参数估计方法。在我们的示例中,kde()
函数接受一个参数满意度
并绘制 KDE:
# Create density plot
sns.kdeplot(df.satisfaction_level)
# Show figure
plt.show()
这将产生以下输出:
在前面的代码块中,我们使用kdeplot()
创建了一个密度图。在下一节中,我们将看到另一种分布图,它是密度图和箱型图的结合,称为小提琴图。
小提琴图
小提琴图是箱型图和 KDE 的结合形式,能够提供易于理解的分布分析:
# Create violin plot
sns.violinplot(data=df[['satisfaction_level','last_evaluation']])
# Show figure
plt.show()
这将产生以下输出:
在前面的示例中,我们使用了两个变量来绘制小提琴图。在这里,我们可以得出结论,satisfaction_level
的范围高于last_evaluation
(表现),且这两个变量的分布中都有两个峰值。完成分布图的分析后,我们将看到如何将groupby
操作和箱型图结合成一个图表,使用计数图来展示。
计数图
countplot()
是一个特殊类型的条形图。它显示每个分类变量的频率。它也被称为分类变量的直方图。与 Matplotlib 相比,它简化了操作。在 Matplotlib 中,要创建一个计数图,首先需要按类别列进行分组,并计算每个类别的频率。之后,Matplotlib 的条形图会使用这个计数。然而,Seaborn 的计数图只需要一行代码就可以绘制分布:
# Create count plot (also known as Histogram)
sns.countplot(x='salary', data=df)
# Show figure
plt.show()
这将产生以下输出:
在上面的示例中,我们正在统计salary
变量。count()
函数接受一个单列和 DataFrame。因此,从图表中我们可以很容易地得出结论,大多数员工的工资都在低到中等范围内。我们还可以将hue
作为第二个变量。让我们看看以下示例:
# Create count plot (also known as Histogram)
sns.countplot(x='salary', data=df, hue='left')
# Show figure
plt.show()
这将产生以下输出:
在上述示例中,我们可以看到left
被用作色调或颜色阴影。这表明大多数薪资最低的员工离开了公司。接下来,我们来看另一个用于可视化两个变量关系和分布的重要图表。
联合图
联合图是一种多面板可视化,显示了双变量关系以及单个变量的分布。我们还可以使用jointplot()
的kind
参数绘制 KDE 图。通过将kind
参数设置为"kde"
,我们可以绘制 KDE 图。让我们看以下示例:
# Create joint plot using kernel density estimation(kde)
sns.jointplot(x='satisfaction_level', y='last_evaluation', data=df, kind="kde")
# Show figure
plt.show()
这将产生以下输出:
在上述图中,我们使用jointplot()
创建了联合图,并通过kind
参数设置为"kde"
来添加了kde
图。接下来让我们来看热力图,进行更丰富的可视化。
热力图
热力图提供了二维网格表示。网格的每个单元格包含矩阵的一个值。热力图功能还提供了对每个单元格的注释:
# Import required library
import seaborn as sns
# Read iris data using load_dataset() function
data = sns.load_dataset("iris")
# Find correlation
cor_matrix=data.corr()
# Create heatmap
sns.heatmap(cor_matrix, annot=True)
# Show figure
plt.show()
这将产生以下输出:
在上述示例中,使用load_dataset()
加载了 Iris 数据集,并通过corr()
函数计算了相关性。corr()
函数返回相关性矩阵。然后,使用heatmap()
函数绘制相关性矩阵的网格视图。它接受两个参数:相关性矩阵和annot
。annot
参数设置为True
。在图中,我们可以看到一个对称矩阵,所有对角线上的值都是 1,这表示一个变量与自身的完美相关性。我们还可以通过cmap
参数设置新的颜色映射以实现不同的颜色:
# Create heatmap
sns.heatmap(cor_matrix, annot=True, cmap="YlGnBu")
# Show figure
plt.show()
这将产生以下输出:
在上述热力图中,我们通过cmap
参数为不同的颜色设置了颜色映射。这里,我们使用了YlGnBu
(黄色、绿色和蓝色)组合作为cmap
。现在,我们将进入对角矩阵图(pair plot)以进行更快速的探索性分析。
对角矩阵图
Seaborn 提供了快速的探索性数据分析,通过对角矩阵图展示变量间的关系以及单独的分布。对角矩阵图使用直方图显示单一分布,并通过散点图展示联合分布:
# Load iris data using load_dataset() function
data = sns.load_dataset("iris")
# Create a pair plot
sns.pairplot(data)
# Show figure
plt.show()
这将产生以下输出:
在前面的示例中,Iris 数据集是通过load_dataset()
加载的,并将该数据集传递给pairplot()
函数。在图表中,它创建了一个n乘n的矩阵或图形网格。对角线展示了各列的分布,网格的非对角元素展示了散点图,以便理解所有变量之间的关系。
在前面的几个部分中,我们已经看到了如何使用 Seaborn 绘图。现在,我们将转到另一个重要的可视化库——Bokeh。在接下来的部分中,我们将使用 Bokeh 库绘制互动和多功能的图表。
使用 Bokeh 进行互动式可视化
Bokeh 是一个互动性强、高质量、功能多样、专注且更强大的可视化库,适用于大数据量和流式数据。它为现代 Web 浏览器提供互动丰富的图表、图形、布局和仪表板。其输出可以映射到笔记本、HTML 或服务器。
Matplotlib 和 Bokeh 库有不同的用途。Matplotlib 专注于静态、简单和快速的可视化,而 Bokeh 则专注于高度互动、动态、基于 Web 的高质量可视化。Matplotlib 通常用于出版物中的图像,而 Bokeh 则面向 Web 用户。在本章的后续部分,我们将学习如何使用 Bokeh 进行基础绘图。我们可以利用 Bokeh 为数据探索创建更多互动的可视化效果。
安装 Bokeh 库的最简单方法是通过 Anaconda 发行包。要安装 Bokeh,请使用以下命令:
conda install bokeh
我们还可以使用pip
来安装它。要使用pip
安装 Bokeh,请使用以下命令:
pip install bokeh
绘制一个简单的图形
让我们用 Bokeh 绘制一个简单的图形。首先,我们需要导入基本的bokeh.plotting
模块。output_notebook()
函数定义了图表将在 Jupyter Notebook 上渲染。figure
对象是绘制图表和图形的核心对象之一。figure
对象关注图表的标题、大小、标签、网格和样式。figure
对象还处理图表样式、标题、坐标轴标签、坐标轴、网格以及添加数据的各种方法:
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
# Create the data
x = [1,3,5,7,9,11]
y = [10,25,35,33,41,59]
# Output to notebook
output_notebook()
# Instantiate a figure
fig= figure(plot_width = 500, plot_height = 350)
# Create scatter circle marker plot by rendering the circles
fig.circle(x, y, size = 10, color = "red", alpha = 0.7)
# Show the plot
show(fig)
这将产生如下输出:
在设置完figure
对象后,我们将使用 circle 函数创建一个散点圆形标记图。circle()
函数将接受x
和y
值。它还接受大小、颜色和透明度(alpha)参数。最后,show()
函数将在所有特性和数据添加到图表后,绘制输出结果。
符号
Bokeh 使用视觉符号(glyph),它指的是圆圈、线条、三角形、正方形、条形、菱形以及其他形状的图形。符号是一种独特的标记,用于以图像形式传达信息。让我们使用line()
函数创建一条线性图:
# Import the required modules
from bokeh.plotting import figure, output_notebook, show
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
# Create the data
x_values = [1,3,5,7,9,11]
y_values = [10,25,35,33,41,59]
# Output to notebook
output_notebook()
# Instantiate a figure
p = figure(plot_width = 500, plot_height = 350)
# create a line plot
p.line(x_values, y_values, line_width = 1, color = "blue")
# Show the plot
show(p)
这将产生如下输出:
在上面的示例中,line()
函数接受x和y轴值。它还接受线的line_width
和color
值。在下一节中,我们将重点介绍多图表的布局。
布局
Bokeh 提供了用于组织图和小部件的布局。布局将多个图表组织在单个面板中,用于交互式可视化。它们还允许根据面板大小设置调整图表和小部件的大小调整模式。布局可以是以下类型之一:
-
行布局:这将所有图表以行或水平方式组织。
-
列布局:这将所有图表以列或垂直方式组织。
-
嵌套布局:这是行和列布局的组合。
-
网格布局:这为您提供了一个网格矩阵,用于排列图表。
让我们看一个行布局的例子:
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook, show
from bokeh.layouts import row, column
# Import iris flower dataset as pandas DataFrame
from bokeh.sampledata.iris import flowers as df
# Output to notebook
output_notebook()
# Instantiate a figure
fig1 = figure(plot_width = 300, plot_height = 300)
fig2 = figure(plot_width = 300, plot_height = 300)
fig3 = figure(plot_width = 300, plot_height = 300)
# Create scatter marker plot by render the circles
fig1.circle(df['petal_length'], df['sepal_length'], size=8, color = "green", alpha = 0.5)
fig2.circle(df['petal_length'], df['sepal_width'], size=8, color = "blue", alpha = 0.5)
fig3.circle(df['petal_length'], df['petal_width'], size=8, color = "red", alpha = 0.5)
# Create row layout
row_layout = row(fig1, fig2, fig3)
# Show the plot
show(row_layout)
这导致以下输出:
在此布局图中,我们导入了行和列布局,从 Bokeh 示例数据加载了鸢尾花数据,用图表宽度和高度实例化了三个 figure
对象,在每个图表对象上创建了三个散点圆圈标记,并创建了行布局。此行布局将以 figure
对象作为输入,并使用 show()
函数绘制。我们也可以通过创建列布局来创建列布局,如下所示:
# Create column layout
col_layout = column(fig1, fig2, fig3)
# Show the plot
show(col_layout)
这导致以下输出:
在前面的图中,我们创建了三个图表的列布局。让我们跳转到嵌套布局,以进行更强大的可视化。
使用行和列布局进行嵌套布局
嵌套布局是多行和列布局的组合。让我们看看这里给出的示例,以便更好地实际理解:
# Import the required modules
from bokeh.plotting import figure, output_notebook, show
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
from bokeh.layouts import row, column
# Import iris flower dataset as pandas DataFrame
from bokeh.sampledata.iris import flowers as df
# Output to notebook
output_notebook()
# Instantiate a figure
fig1 = figure(plot_width = 300, plot_height = 300)
fig2 = figure(plot_width = 300, plot_height = 300)
fig3 = figure(plot_width = 300, plot_height = 300)
# Create scatter marker plot by render the circles
fig1.circle(df['petal_length'], df['sepal_length'], size=8, color = "green", alpha = 0.5)
fig2.circle(df['petal_length'], df['sepal_width'], size=8, color = "blue", alpha = 0.5)
fig3.circle(df['petal_length'], df['petal_width'], size=8, color = "red", alpha = 0.5)
# Create nested layout
nasted_layout = row(fig1, column(fig2, fig3))
# Show the plot
show(nasted_layout)
这导致以下输出:
在这里,您可以看到行布局有两行。在第一行中,分配了 fig1
,第二行有 fig2
和 fig3
的列布局。因此,此布局变为 2*2 布局,第一列仅有一个组件,第二列有两个组件。
多个图表
还可以使用网格布局创建多个图表和对象。网格布局以行列矩阵方式排列图表和小部件对象。它接受每行的图形对象列表。我们还可以使用 None
作为占位符:
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
from bokeh.layouts import gridplot
# Import iris flower dataset as pandas DataFrame
from bokeh.sampledata.iris import flowers as df
# Output to notebook
output_notebook()
# Instantiate a figure
fig1 = figure(plot_width = 300, plot_height = 300)
fig2 = figure(plot_width = 300, plot_height = 300)
fig3 = figure(plot_width = 300, plot_height = 300)
# Create scatter marker plot by render the circles
fig1.circle(df['petal_length'], df['sepal_length'], size=8, color = "green", alpha = 0.5)
fig2.circle(df['petal_length'], df['sepal_width'], size=8, color = "blue", alpha = 0.5)
fig3.circle(df['petal_length'], df['petal_width'], size=8, color = "red", alpha = 0.5)
# Create a grid layout
grid_layout = gridplot([[fig1, fig2], [None,fig3]])
# Show the plot
show(grid_layout)
这导致以下输出:
前面的布局类似于嵌套布局。这里,我们导入了 gridplot()
。它将组件排列在行和列中。网格图采用一列行图。列表中的第一项是 fig1
和 fig2
。第二项是 None
和 fig3
。每个项目都是网格矩阵中的一行。None
占位符用于使单元格保持空白或没有组件。
尺寸模式可以帮助我们配置具有可调整大小选项的图形。Bokeh 提供了以下尺寸模式:
-
fixed
:保持原始宽度和高度不变。 -
stretch_width
:根据其他组件的类型,拉伸到可用宽度。它不保持纵横比。 -
stretch_height
:根据其他组件的类型,拉伸到可用高度。它不保持纵横比。 -
stretch_both
:根据其他组件的类型,拉伸宽度和高度,同时不保持原始纵横比。 -
scale_width
:根据其他组件的类型,拉伸到可用宽度,同时保持原始纵横比。 -
scale_height
:根据其他组件的类型,拉伸到可用高度,同时保持原始纵横比。 -
scale_both
:根据其他组件的类型,拉伸宽度和高度,同时保持原始纵横比。
在了解了布局和多个图表之后,接下来是学习互动可视化的交互。
交互
Bokeh 提供了交互式图例,用于运行时可操作的图表。可以通过点击符号图表来隐藏或静音图例。我们可以通过激活click_policy
属性并点击图例项来启用这些模式。
隐藏点击策略
隐藏点击策略通过点击图例项来隐藏所需的符号。让我们看一个隐藏点击策略的例子:
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
from bokeh.models import CategoricalColorMapper
# Import iris flower dataset as pandas DataFrame
from bokeh.sampledata.iris import flowers as df
# Output to notebook
output_notebook()
# Instantiate a figure object
fig = figure(plot_width = 500, plot_height = 350, title="Petal length Vs. Petal Width",
x_axis_label='petal_length', y_axis_label='petal_width')
# Create scatter marker plot by render the circles
for specie, color in zip(['setosa', 'virginica','versicolor'], ['blue', 'green', 'red']):
data = df[df.species==specie]
fig.circle('petal_length', 'petal_width', size=8, color=color, alpha = 0.7, legend_label=specie, source=data)
# Set the legend location and click policy
fig.legend.location = 'top_left'
fig.legend.click_policy="hide"
# Show the plot
show(fig)
这将产生以下输出:
在这里,我们可以通过figure
对象的legend.click_policy
参数设置点击策略。此外,我们需要对每种类型的符号或图例元素运行for
循环,在这些元素上进行点击。在我们的示例中,我们正在为物种和颜色类型运行for
循环。在点击图例中的任何物种时,它会过滤数据并隐藏该符号。
静音点击策略
静音点击策略通过点击图例项来静音符号。在这里,以下代码显示了高强度的所需符号,而不感兴趣的符号则使用较低强度,而不是隐藏整个符号。让我们看一个静音点击策略的例子:
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
from bokeh.models import CategoricalColorMapper
# Import iris flower dataset as pandas DataFrame
from bokeh.sampledata.iris import flowers as df
# Output to notebook
output_notebook()
# Instantiate a figure object
fig = figure(plot_width = 500, plot_height = 350, title="Petal length Vs. Petal Width",
x_axis_label='petal_length', y_axis_label='petal_width')
# Create scatter marker plot by render the circles
for specie, color in zip(['setosa', 'virginica','versicolor'], ['blue', 'green', 'red']):
data = df[df.species==specie]
fig.circle('petal_length', 'petal_width', size=8, color=color, alpha = 0.7,legend_label=specie,source=data,
muted_color=color, muted_alpha=0.2)
# Set the legend location and click policy
fig.legend.location = 'top_left'
fig.legend.click_policy="mute"
# Show the plot
show(fig)
这将产生以下输出:
在这里,我们可以通过legend.click_policy
参数设置静音点击策略来静音图形对象。此外,我们需要对每种类型的符号或图例元素运行for
循环,在这些元素上进行点击。在我们的示例中,我们正在为物种和颜色类型运行for
循环。在点击图例中的任何物种时,它会过滤数据并隐藏该符号。此外,我们需要为散点圆形标记添加muted_color
和muted_alpha
参数。
注解
Bokeh 提供了多种注解,用于为可视化提供补充信息。它通过添加以下信息来帮助查看者:
-
标题:这个注释为图表提供一个名称。
-
轴标签:这个注释为轴提供标签,帮助我们理解x轴和y轴表示的内容。
-
图例:这个注释通过颜色或形状表示第三个变量,并帮助我们将特征链接在一起,便于解释。
-
颜色条:颜色条是通过使用 ColorMapper 和颜色调色板创建的。
让我们看一个注释的例子:
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
from bokeh.models import CategoricalColorMapper
# Import iris flower dataset as pandas DataFrame
from bokeh.sampledata.iris import flowers as df
# Output to notebook
output_notebook()
# Create color mapper for categorical column
color_mapper = CategoricalColorMapper(factors=['setosa', 'virginica', 'versicolor'], palette=['blue', 'green', 'red'])
color_dict={'field': 'species','transform': color_mapper }
# Instantiate a figure object
p = figure(plot_width = 500, plot_height = 350, title="Petal length Vs. Petal Width",
x_axis_label='petal_length', y_axis_label='petal_width')
# Create scatter marker plot by render the circles
p.circle('petal_length', 'petal_width', size=8, color=color_dict, alpha = 0.5, legend_group='species', source=df)
# Set the legend location
p.legend.location = 'top_left'
# Show the plot
show(p)
这将生成以下输出:
在前面的例子中,导入了CategoricalColorMapper
并通过定义鸢尾花品种中的因子或唯一项及其相应的颜色来创建对象。通过为映射器定义field
和transform
参数来创建颜色字典。我们需要定义图表的标题;x_axis_label
和y_axis_label
是在figure
对象中定义的。图例是在圆形散点标记函数中定义的,使用品种列并通过figure
对象的top_left
位置属性定义其位置。
悬停工具
悬停工具会在鼠标指针悬停在特定区域时显示相关信息。让我们看一些例子来理解悬浮图:
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
from bokeh.models import CategoricalColorMapper
from bokeh.models import HoverTool
# Import iris flower dataset as pandas DataFrame
from bokeh.sampledata.iris import flowers as df
# Output to notebook
output_notebook()
# Create color mapper for categorical column
mapper = CategoricalColorMapper(factors=['setosa', 'virginica', 'versicolor'],
palette=['blue', 'green', 'red'])
color_dict={'field': 'species','transform': mapper}
# Create hovertool and specify the hovering information
hover = HoverTool(tooltips=[('Species type','@species'),
('IRIS Petal Length','@petal_length'),
('IRIS Petal Width', '@petal_width')])
# Instantiate a figure object
p = figure(plot_width = 500, plot_height = 350, title="Petal length Vs. Petal Width",
x_axis_label='petal_length', y_axis_label='petal_width',
tools=[hover, 'pan', 'wheel_zoom'])
# Create scatter marker plot by render the circles
p.circle('petal_length', 'petal_width', size=8, color=color_dict, alpha = 0.5,legend_group='species',source=df)
# Set the legend location
p.legend.location = 'top_left'
# Show the plot
show(p)
这将生成以下输出:
在前面的例子中,我们从bokeh.models
导入了HoverTool
并通过定义在鼠标悬停时显示的信息来创建它的对象。在我们的例子中,我们在元组列表中定义了信息。每个元组有两个参数,第一个是字符串标签,第二个是实际值(前面加上@
)。这个悬停对象被传递到figure
对象的tools
参数中。
小部件
小部件提供了前端的实时交互。小部件能够在运行时修改和更新图表。它们可以运行 Bokeh 服务器或独立的 HTML 应用程序。使用小部件时,您需要指定功能。它可以嵌套在布局中。将小部件功能添加到程序中有两种方法:
-
CustomJS 回调
-
使用 Bokeh 服务器和设置事件处理程序,如
onclick
或onchange
事件
标签面板
标签面板使我们能够在一个窗口中创建多个图表和布局。让我们看一个标签面板的例子:
# Import the required modules
from bokeh.plotting import figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
from bokeh.models.widgets import Tabs
from bokeh.models.widgets import Panel
# Import iris flower dataset as pandas DataFrame
from bokeh.sampledata.iris import flowers as df
# Output to notebook
output_notebook()
# Instantiate a figure
fig1 = figure(plot_width = 300, plot_height = 300)
fig2 = figure(plot_width = 300, plot_height = 300)
# Create scatter marker plot by render the circles
fig1.circle(df['petal_length'], df['sepal_length'], size=8, color = "green", alpha = 0.5)
fig2.circle(df['petal_length'], df['sepal_width'], size=8, color = "blue", alpha = 0.5)
# Create panels
tab1 = Panel(child=fig1, title='tab1')
tab2 = Panel(child=fig2, title='tab2')
# Create tab by putting panels into it
tab_layout = Tabs(tabs=[tab1,tab2])
# Show the plot
show(tab_layout)
这将生成以下输出:
在前面的代码中,我们通过将figure
对象传递给child
参数,并将title
传递给title
参数,创建了两个面板。两个面板被组合成一个列表,并传递给Tabs
布局对象。通过show()
函数显示此Tabs
对象。您只需点击标签即可更改标签。
滑块
滑块是一个图形化的轨迹条,它通过在水平刻度上移动来控制值。我们可以在不影响图表格式的情况下更改图表的值。让我们来看一个滑块的例子:
# Import the required modules
from bokeh.plotting import Figure
from bokeh.plotting import output_notebook
from bokeh.plotting import show
from bokeh.models import CustomJS
from bokeh.models import ColumnDataSource
from bokeh.models import Slider
from bokeh.layouts import column
# Show output in notebook
output_notebook()
# Create list of data
x = [x for x in range(0, 100)]
y = x
# Create a DataFrame
df = ColumnDataSource(data={"x_values":x, "y_values":y})
# Instantiate the Figure object
fig = Figure(plot_width=350, plot_height=350)
# Create a line plot
fig.line('x_values', 'y_values', source=df, line_width=2.5, line_alpha=0.8)
# Create a callback using CustomJS
callback = CustomJS(args=dict(source=df), code="""
var data = source.data;
var f = cb_obj.value
var x_values = data['x_values']
var y_values = data['y_values']
for (var i = 0; i < x_values.length; i++) {
y_values[i] = Math.pow(x_values[i], f)
}
source.change.emit();
""")
slider_widget = Slider(start=0.0, end=10, value=1, step=.1, title="Display power of x")
slider_widget.js_on_change('value', callback)
# Create layout
slider_widget_layout = column(fig,slider_widget)
# Display the layout
show(slider_widget_layout)
这将产生以下输出:
在前面的代码中,Bokeh 的slider()
函数接受start
、end
、value
、step
、title
和 CustomJS 回调作为输入。在我们的例子中,我们创建了一个折线图,并通过滑动条根据x
变量的幂来改变其y
值。我们可以通过将start
、end
、value
、step
、title
和 CustomJS 回调传递给Slider
对象来创建滑块。我们需要关注 CustomJS 回调。它获取源数据框,通过cb_obj.value
获取滑块的值,并使用change.emit()
函数更新其值。我们在for
循环中更新y_value
,通过找到其幂次来使用滑块值。
总结
在本章中,我们讨论了如何使用 Matplotlib、pandas
、Seaborn 和 Bokeh 进行数据可视化。我们介绍了各种图表类型,如折线图、饼图、条形图、直方图、散点图、箱线图、气泡图、热图、KDE 图、提琴图、计数图、联合图和配对图。我们重点介绍了图表的附加功能,如标题、标签、图例、布局、子图和注释。此外,我们还学习了使用 Bokeh 布局、交互、悬停工具和小部件进行交互式可视化。
下一章,第六章,数据的检索、处理与存储,将教授我们如何从各种来源(如文件、对象、关系型数据库和 NoSQL 数据库)读取和写入数据的技能。虽然有些人认为这些技能不适用于数据分析,但独立或辅助的数据分析师必须了解如何从不同的文件格式和数据库中获取数据用于分析。
第七章:检索、处理和存储数据
数据无处不在,呈现各种形态和形式。我们可以从网络、物联网传感器、电子邮件、FTP 和数据库中获取数据。我们还可以通过实验室实验、选举民调、市场调查和社会调查等方式收集数据。作为数据专业人员,你应该知道如何处理各种数据集,因为这是一个非常重要的技能。本章将讨论如何检索、处理和存储不同类型的数据。本章概述了如何获取不同格式的数据,如 CSV、Excel、JSON、HDF5、Parquet 和 pickle
。
有时候,我们需要在数据分析之前或之后存储或保存数据。我们还将学习如何访问来自关系型和NoSQL(非关系型数据库)数据库的数据,如 sqlite3
、MySQL、MongoDB、Cassandra 和 Redis。在 21 世纪的网络世界中,NoSQL 数据库在大数据和 Web 应用程序中正在经历快速增长。它们提供了更灵活、更快速、无需架构的数据库。NoSQL 数据库可以存储各种格式的数据,如文档风格、列式存储、对象、图形、元组或它们的组合。
本章涵盖的主题如下:
-
使用 NumPy 读取和写入 CSV 文件
-
使用 pandas 读取和写入 CSV 文件
-
从 Excel 读取和写入数据
-
从 JSON 读取和写入数据
-
从 HDF5 读取和写入数据
-
从 HTML 表格读取和写入数据
-
从 Parquet 读取和写入数据
-
从
pickle pandas
对象读取和写入数据 -
使用
sqlite3
进行轻量级访问 -
从 MySQL 读取和写入数据
-
从 MongoDB 读取和写入数据
-
从 Cassandra 读取和写入数据
-
从 Redis 读取和写入数据
-
PonyORM
技术要求
本章有以下技术要求:
-
你可以在以下 GitHub 链接中找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter06
。 -
所有代码块都可以在
ch6.ipynb
文件中找到。 -
本章使用以下文件进行实践:CSV 文件(
demo.csv
、product.csv
、demo_sample_df.csv
、my_first_demo.csv
和employee.csv
)、Excel 文件(employee.xlsx
、employee_performance.xlsx
和new_employee_details.xlsx
)、JSON 文件(employee.json
和employee_demo.json
)、HTML 文件(country.html
)、pickle
文件(demo_obj.pkl
)、HDF5 文件(employee.h5
)和 Parquet 文件(employee.parquet
)。 -
本章将使用以下 Python 库:
pandas
、pickle
、pyarrow
、sqlite3
、pymysql
、mysql-connector
、pymongo
、cassandra-driver
和redis
。
使用 NumPy 读取和写入 CSV 文件
在第二章,《NumPy 和 pandas》中,我们详细研究了 NumPy 库并探索了许多功能。NumPy 也有读取和写入 CSV 文件的功能,并能以 NumPy 数组的形式输出结果。genfromtxt()
函数将帮助我们读取数据,而savetxt()
函数将帮助我们将数据写入文件。由于其两阶段操作,genfromtxt()
函数比其他函数慢。第一阶段,它将数据以字符串类型读取,第二阶段,它将字符串类型转换为适当的数据类型。genfromtxt()
具有以下参数:
-
fname
:字符串;文件名或文件路径。 -
delimiter
:字符串;可选,分隔字符串值。默认情况下,它采用连续的空白字符。 -
skip_header
:整数;可选,表示要跳过的文件开头的行数。
让我们看一个读取和写入 CSV 文件的例子:
# import genfromtxt function
from numpy import genfromtxt
# Read comma separated file
product_data = genfromtxt('demo.csv', delimiter=',')
# display initial 5 records
print(product_data)
这将产生以下输出:
[[14\. 32\. 33.]
[24\. 45\. 26.]
[27\. 38\. 39.]]
在前面的代码示例中,我们使用 NumPy 模块的genfromtxt()
方法读取了demo.csv
文件:
# import numpy
import numpy as np
# Create a sample array
sample_array = np.asarray([ [1,2,3], [4,5,6], [7,8,9] ])
# Write sample array to CSV file
np.savetxt("my_first_demo.csv", sample_array, delimiter=",")
在前面的代码示例中,我们使用 NumPy 模块的savetxt()
方法写入了my_first_demo.csv
文件。
让我们在下一节中看看如何使用pandas
模块读取 CSV 文件。
使用 pandas 读取和写入 CSV 文件
pandas
库提供了多种文件读取和写入选项。在本节中,我们将学习如何读取和写入 CSV 文件。为了读取 CSV 文件,我们将使用read_csv()
方法。让我们看一个例子:
# import pandas
import pandas as pd
# Read CSV file
sample_df=pd.read_csv('demo.csv', sep=',' , header=None)
# display initial 5 records
sample_df.head()
这将产生以下输出:
现在我们可以使用以下代码将数据框保存为 CSV 文件:
# Save DataFrame to CSV file
sample_df.to_csv('demo_sample_df.csv')
在前面的示例代码中,我们使用pandas
模块的read_csv()
和to_csv(0)
方法读取和保存了 CSV 文件。
read_csv()
方法有以下重要参数:
-
filepath_or_buffer
:提供作为字符串的文件路径或 URL,用于读取文件。 -
sep
:提供字符串中的分隔符,例如逗号为','
,分号为';'
。默认分隔符是逗号','
。 -
delim_whitespace
:用于空白分隔符的备用参数。它是一个布尔变量。delim_whitespace
的默认值为False
。 -
header
:用于标识列名。默认值为infer
。 -
names
:可以传递一个列名列表。names
的默认值为None
。
在pandas
中,可以使用to_csv()
方法将 DataFrame 导出为 CSV 文件。CSV 文件是逗号分隔值文件。此方法可以仅使用一个参数(文件名作为字符串)运行:
-
path_or_buf
:文件将导出的文件路径或位置。 -
sep
:用于输出文件的分隔符。 -
header
:包含列名或列别名列表(默认值:True
)。 -
index
:将索引写入文件(默认值:True
)。
更多参数和详细描述,请访问pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_csv.html
。接下来,我们将看看如何使用pandas
模块读取 Excel 文件。
从 Excel 读取和写入数据
Excel 文件是商业领域中广泛使用的文件类型。在 Python 中,Excel 文件可以通过pandas
的read_excel()
函数轻松读取。read_excel()
函数需要传入文件路径和sheet_name
参数来读取数据:
# Read excel file
df=pd.read_excel('employee.xlsx',sheet_name='performance')
# display initial 5 records
df.head()
这将产生以下输出:
DataFrame 对象可以写入 Excel 工作表中。我们可以使用to_excel()
函数将 DataFrame 对象导出到 Excel 工作表中。通常,to_excel()
函数的参数与to_csv()
相同,除了sheet_name
参数:
df.to_excel('employee_performance.xlsx')
在前面的代码示例中,我们将一个单独的 DataFrame 导出到了 Excel 工作表中。我们还可以将多个 DataFrame 导出到一个文件中,并使用不同的工作表名称。我们还可以使用ExcelWriter
在一个 Excel 文件中写入多个 DataFrame(每个 DataFrame 在不同的工作表上),如下所示:
# Read excel file
emp_df=pd.read_excel('employee.xlsx',sheet_name='employee_details')
# write multiple dataframes to single excel file
with pd.ExcelWriter('new_employee_details.xlsx') as writer:
emp_df.to_excel(writer, sheet_name='employee')
df.to_excel(writer, sheet_name='perfromance')
在前面的代码示例中,我们将多个 DataFrame 写入了一个 Excel 文件。这里,每个 DataFrame 存储在不同的工作表上,使用了ExcelWriter
函数。接下来,我们将看看如何使用pandas
模块读取 JSON 文件。
从 JSON 读取和写入数据
JSON(JavaScript 对象表示法)文件是用于 Web 应用和服务器之间交换数据的广泛使用的格式。它作为数据交换格式,比 XML 更易读。pandas
提供了read_json
函数来读取 JSON 数据,以及to_json()
函数来写入 JSON 数据:
# Reading JSON file
df=pd.read_json('employee.json')
# display initial 5 records
df.head()
这将产生以下输出:
在前面的代码示例中,我们使用read_json()
方法读取了 JSON 文件。接下来,我们将看看如何写入一个 JSON 文件:
# Writing DataFrame to JSON file
df.to_json('employee_demo.json',orient="columns")
在前面的代码示例中,我们使用to_json()
方法写入了 JSON 文件。在to_json()
方法中,orient
参数用于处理输出字符串格式。orient
提供了 record、column、index 和 value 等格式。你可以在官方网页上查看更多详细信息,网址是:pandas.pydata.org/pandas-docs/version/0.24.2/reference/api/pandas.DataFrame.to_json.html
。接下来,我们将进入 HDF5 文件部分,在下一节中,我们将学习如何使用pandas
模块读取和写入 HDF5 文件。
从 HDF5 读取和写入数据
HDF 代表 层次化数据格式。HDF 旨在以高性能存储和管理大量数据。它提供了快速的 I/O 处理和异构数据的存储。有多种 HDF 文件格式可用,如 HDF4 和 HDF5。HDF5 与读取和写入 pandas
DataFrame 的字典对象相同。它使用 PyTables 库的 read_hdf()
函数来读取 HDF5 文件,使用 to_hdf()
函数来写入:
# Write DataFrame to hdf5
df.to_hdf('employee.h5', 'table', append=True)
在前面的代码示例中,我们使用 to_hdf()
方法写入了 HDF 文件格式。'table'
是用于表格格式的格式参数。表格格式可能会较慢,但提供了更灵活的操作,例如搜索和选择。append
参数用于将输入数据追加到现有数据文件中:
# Read a hdf5 file
df=pd.read_hdf('employee.h5', 'table')
# display initial 5 records
df.head()
这将生成以下输出:
在前面的代码示例中,我们使用 read_hdf()
方法读取了 HDF 文件格式。接下来让我们看看如何从网站读取和写入 HTML 表格。
从 HTML 表格读取和写入数据
HTML 表格将行存储在 <tr>...</tr>
标签中,每行都有相应的 <td>...</td>
单元格来存储值。在 pandas
中,我们还可以从文件或 URL 中读取 HTML 表格。read_html()
函数从文件或 URL 中读取 HTML 表格,并将 HTML 表格返回为 pandas
DataFrame 列表:
# Reading HTML table from given URL
table_url = 'https://en.wikipedia.org/wiki/List_of_sovereign_states_and_dependent_territories_in_North_America'
df_list = pd.read_html(table_url)
print("Number of DataFrames:",len(df_list))
这将生成以下输出:
Number of DataFrames: 7
在前面的代码示例中,我们使用 read_html()
方法从给定的网页读取了 HTML 表格。read_html()
将返回所有表格,作为 DataFrame 列表。接下来让我们检查列表中的一个 DataFrame:
# Check first DataFrame
df_list[0].head()
这将生成以下输出:
在前面的代码示例中,我们展示了给定网页上第一个表格的前五条记录。类似地,我们还可以使用 to_html()
将 DataFrame 对象写入 HTML 表格。to_html()
将内容渲染为 HTML 表格:
# Write DataFrame to raw HTML
df_list[1].to_html('country.html')
通过前面的代码示例,我们可以将任何 DataFrame 转换为一个包含 DataFrame 作为表格的 HTML 页面。
从 Parquet 读取和写入数据
Parquet 文件格式为 pandas
DataFrame 提供了列式序列化。它在存储和性能方面高效地读取和写入 DataFrame,并且能够在分布式系统之间共享数据而不丢失信息。Parquet 文件格式不支持重复列和数值列。
pandas
使用两个引擎来读取和写入 Parquet 文件:pyarrow
和 fastparquet
引擎。pandas
的默认 Parquet 引擎是 pyarrow
;如果 pyarrow
不可用,则使用 fastparquet
。在我们的示例中,我们使用的是 pyarrow
。让我们使用 pip
安装 pyarrow
:
pip install pyarrow
你还可以通过在 pip
关键词前加上 !
来在 Jupyter Notebook 中安装 pyarrow
引擎。以下是一个示例:
!pip install pyarrow
让我们使用 pyarrow
引擎写入一个文件:
# Write to a parquet file.
df.to_parquet('employee.parquet', engine='pyarrow')
在前面的代码示例中,我们使用to_parquet()
Parquet 文件和pyarrow
引擎进行写入:
# Read parquet file
employee_df = pd.read_parquet('employee.parquet', engine='pyarrow')
# display initial 5 records
employee_df.head()
这将产生以下输出:
在前面的代码示例中,我们使用read_parquet()
和pyarrow
引擎读取了 Parquet 文件。read_parquet()
有助于读取 Parquet 文件格式。接下来,我们将在下一部分了解如何读取和写入pickle
文件中的数据。
从 Pickle pandas 对象中读取和写入数据
在数据准备步骤中,我们将使用各种数据结构,如字典、列表、数组或 DataFrame。有时,我们可能希望将它们保存以备将来参考,或将它们发送给其他人。在这种情况下,pickle
对象就派上用场了。pickle
将对象序列化以保存它们,并可以随时加载。pandas
提供了两个函数:read_pickle()
用于加载pandas
对象,to_pickle()
用于保存 Python 对象:
# import pandas
import pandas as pd
# Read CSV file
df=pd.read_csv('demo.csv', sep=',' , header=None)
# Save DataFrame object in pickle file
df.to_pickle('demo_obj.pkl')
在前面的代码中,我们使用read_csv()
方法读取了demo.csv
文件,并设置了sep
和header
参数。在这里,我们将sep
设置为逗号,将header
设置为None
。最后,我们使用to_pickle()
方法将数据集写入pickle
对象。接下来,让我们看看如何使用pandas
库读取pickle
对象:
#Read DataFrame object from pickle file
pickle_obj=pd.read_pickle('demo_obj.pkl')
# display initial 5 records
pickle_obj.head()
这将产生以下输出:
在前面的代码中,我们使用read_pickle()
方法读取了pickle
对象。
使用 sqllite3 轻量级访问
SQLite 是一个开源数据库引擎。它提供了多种特性,如更快的执行速度、轻量级处理、无服务器架构、ACID 兼容性、更少的管理工作、高稳定性和可靠的事务。它是移动设备和计算机世界中最流行、最广泛部署的数据库。它也被称为嵌入式关系型数据库,因为它作为应用程序的一部分运行。SQLite 是一个更轻量的数据库,功能上没有完整的功能。它主要用于存储和处理小型数据,比如移动应用和桌面应用。SQLite 的主要优点是易于使用、高效、轻便,并且可以嵌入到应用程序中。
我们可以使用sqlite3
模块在 Python 中读取和写入数据。我们不需要下载和安装sqlite3
,因为它已经包含在所有标准 Python 发行版中。使用sqlite3
,我们可以将数据库存储在文件中或保存在内存中。sqlite3
允许我们使用 SQL 编写任何数据库,而不需要第三方应用程序服务器。让我们通过以下示例了解数据库连接:
# Import sqlite3
import sqlite3
# Create connection. This will create the connection with employee database. If the database does not exist it will create the database
conn = sqlite3.connect('employee.db')
# Create cursor
cur = conn.cursor()
# Execute SQL query and create the database table
cur.execute("create table emp(eid int,salary int)")
# Execute SQL query and Write the data into database
cur.execute("insert into emp values(105, 57000)")
# commit the transaction
con.commit()
# Execute SQL query and Read the data from the database
cur.execute('select * from emp')
# Fetch records
print(cur.fetchall())
# Close the Database connection
conn.close()
Output: [(105, 57000)]
这里,我们使用sqlite3
模块。首先,我们导入该模块并使用connect()
方法创建一个连接。connect()
方法将接收数据库名称和路径;如果数据库不存在,它将使用给定的名称和路径创建该数据库。一旦与数据库建立连接,您需要创建Cursor
对象,并使用execute()
方法执行 SQL 查询。我们可以在execute()
方法中创建表,例如在员工数据库中创建的emp
表。类似地,我们可以使用execute()
方法和insert
查询参数写入数据,并使用commit()
方法将数据提交到数据库。也可以通过execute()
方法传递select
查询作为参数来提取数据,使用fetchall()
和fetchone()
方法进行提取。fetchone()
提取一条记录,fetchall()
提取数据库表中的所有记录。
从 MySQL 读取和写入数据
MySQL 是一个快速、开源、易于使用的关系型数据库,适用于小型和大型商业应用。它与数据库驱动的 Web 开发应用非常兼容。Python 中有很多方式可以访问 MySQL 中的数据。像 MySQLdb、mysqlconnector
和pymysql
等连接器可用于 MySQL 数据库连接。为了实现这种连接,您需要安装 MySQL 关系型数据库和mysql-python
连接器。MySQL 的安装详情可以在其官网上找到:www.mysql.com/downloads/
。
您可以使用pymysql
连接器作为客户端库,可以通过pip
安装:
pip install pymysql
我们可以通过以下步骤建立连接:
-
导入库。
-
创建数据库连接。
-
创建游标对象。
-
执行 SQL 查询。
-
获取记录或响应以更新或插入记录。
-
关闭连接。
在我们的示例中,我们尝试使用mysqlconnecter
和pymysql
进行数据库连接。在运行数据库连接脚本之前,第一步是设计并创建一个数据库,然后在 MySQL 中创建一个表。
让我们使用以下查询来创建一个数据库:
>> create database employee
将数据库更改为员工数据库:
>> use employee
在数据库中创建表:
>> create table emp(eid int, salary int);
现在我们可以插入和获取 MySQL 表中的记录。让我们看一下以下示例来理解数据库连接:
# import pymysql connector module
import pymysql
# Create a connection object using connect() method
connection = pymysql.connect(host='localhost', # IP address of the MySQL database server
user='root', # user name
password='root',# password
db='emp', # database name
charset='utf8mb4', # character set
cursorclass=pymysql.cursors.DictCursor) # cursor type
try:
with connection.cursor() as cur:
# Inject a record in database
sql_query = "INSERT INTO `emp` (`eid`, `salary`) VALUES (%s, %s)"
cur.execute(sql_query, (104,43000))
# Commit the record insertion explicitly.
connection.commit()
with connection.cursor() as cur:
# Read records from employee table
sql_query = "SELECT * FROM `emp`"
cur.execute(sql_query )
table_data = cur.fetchall()
print(table_data)
except:
print("Exception Occurred")
finally:
connection.close()
这里,我们使用pymysql
模块。首先,我们导入该模块并创建一个连接。connect()
函数将接收主机地址,在我们的案例中是localhost
(我们也可以使用远程数据库的 IP 地址)、用户名、密码、数据库名称、字符集和游标类。
在建立连接之后,我们可以读取或写入数据。在我们的例子中,我们使用 insert
SQL 查询写入数据,并使用 select
查询检索数据。在插入查询中,我们执行查询并传递要插入数据库的参数,然后使用 commit()
方法将结果提交到数据库。当我们使用选择查询读取记录时,将得到一些记录。我们可以使用 fetchone()
和 fetchall()
函数提取这些记录。fetchone()
方法仅提取单条记录,而 fetchall()
方法则提取数据库表中的多条记录。
还有一件事;在这里,所有的读写操作都在 try
块中执行,连接则在最终块中关闭。我们还可以尝试另一个模块 mysql.connector
来实现 MySQL 和 Python 的连接。它可以通过 pip
安装:
pip install mysql-connector-python
让我们看一个例子来理解数据库连接:
# Import the required connector
import mysql.connector
import pandas as pd
# Establish a database connection to mysql
connection=mysql.connector.connect(user='root',password='root',host='localhost',database='emp')
# Create a cursor
cur=connection.cursor()
# Running sql query
cur.execute("select * from emp")
# Fetch all the records and print it one by one
records=cur.fetchall()
for i in records:
print(i)
# Create a DataFrame from fetched records.
df = pd.DataFrame(records)
# Assign column names to DataFrame
df.columns = [i[0] for i in cur.description]
# close the connection
connection.close()
在前面的代码示例中,我们使用 mysql.connector
模块连接 Python 和 MySQL 数据库,提取数据的方式和步骤与使用 pymysql
模块相同。我们还通过将提取的记录传递到 pandas
DataFrame 对象中,并从游标描述中分配列名来写入数据。
将整个 DataFrame 插入数据库
在前面的程序中,使用 insert
命令插入单个记录。如果我们想要插入多个记录,则需要运行一个循环,将多个记录插入数据库。我们还可以使用 to_sql()
函数在一行代码中插入多个记录:
# Import the sqlalchemy engine
from sqlalchemy import create_engine
# Instantiate engine object
en = create_engine("mysql+pymysql://{user}:{pw}@localhost/{db}"
.format(user="root",
pw="root",
db="emp"))
# Insert the whole dataframe into the database
df.to_sql('emp', con=en, if_exists='append',chunksize=1000, index= False)
在前面的代码示例中,我们将为数据库连接创建一个引擎,包含用户名、密码和数据库参数。to_sql()
函数将多个记录从 DataFrame 写入 SQL 数据库。它需要表名、con
参数(用于连接引擎对象)、if_exists
参数(检查数据是追加到新表还是替换为新表),以及 chunksize
(用于批量写入数据)。
从 MongoDB 读取和写入数据
MongoDB 是一个面向文档的非关系型(NoSQL)数据库。它使用类似 JSON 的符号,BSON(二进制对象符号)来存储数据。MongoDB 提供以下功能:
-
它是一个免费的、开源的、跨平台的数据库软件。
-
它易于学习,能够构建更快的应用程序,支持灵活的模式,处理多种数据类型,并且具备在分布式环境中扩展的能力。
-
它基于文档的概念工作。
-
它包含数据库、集合、文档、字段和主键。
我们可以通过pymongo
连接器在 Python 中从 MongoDB 读取和写入数据。为了实现这个连接,我们需要安装 MongoDB 和pymongo
连接器。你可以从其官方网站下载 MongoDB:www.mongodb.com/download-center/community
。PyMongo 是一个纯 Python 的 MongoDB 客户端库,可以通过pip
安装:
pip install pymongo
让我们尝试使用pymongo
进行数据库连接:
# Import pymongo
import pymongo
# Create mongo client
client = pymongo.MongoClient()
# Get database
db = client.employee
# Get the collection from database
collection = db.emp
# Write the data using insert_one() method
employee_salary = {"eid":114, "salary":25000}
collection.insert_one(employee_salary)
# Create a dataframe with fetched data
data = pd.DataFrame(list(collection.find()))
在这里,我们通过创建 Mongo 客户端、插入数据、提取集合详情并将其分配给 DataFrame,尝试从 MongoDB 的数据库集合中提取数据。接下来,我们将展示如何在下一节中使用列式数据库 Cassandra 建立数据库连接。
从 Cassandra 读取和写入数据
Cassandra 是一个可扩展、高可用、耐久和容错的列式数据库,具有较低的管理开销、更快的读写速度,并且能够提供强大的弹性。它容易学习和配置,能够为复杂问题提供解决方案,并且支持跨多个数据中心的复制。许多大型公司,例如苹果、eBay 和 Netflix,都在使用 Cassandra。
我们可以通过cassandra-driver
连接器在 Python 中从 Cassandra 读取和写入数据。为了实现这个连接,我们需要安装 Cassandra 和cassandra-driver
连接器。你可以从其官方网站下载 Cassandra:cassandra.apache.org/download/
。cassandra-driver
是一个纯 Python 的 Cassandra 客户端库,可以通过pip
安装:
pip install cassandra-driver
让我们尝试使用cassandra-driver
进行数据库连接:
# Import the cluster
from cassandra.cluster import Cluster
# Creating a cluster object
cluster = Cluster()
# Create connections by calling Cluster.connect():
conn = cluster.connect()
# Execute the insert query
conn.execute("""INSERT INTO employee.emp_details (eid, ename, age) VALUES (%(eid)s, %(ename)s, %(age)s)""", {'eid':101, 'ename': "Steve Smith", 'age': 42})
# Execute the select query
rows = conn.execute('SELECT * FROM employee.emp_details')
# Print the results
for emp_row in rows:
print(emp_row.eid, emp_row.ename, emp_row.age)
# Create a dataframe with fetched data
data = pd.DataFrame(rows)
在这里,我们通过创建一个集群对象、使用connect()
方法建立连接、执行插入操作以及选择查询数据,尝试从 Cassandra 数据库中提取数据。运行查询后,我们打印结果并将提取的记录分配给pandas
DataFrame。接下来,让我们进入另一个 NoSQL 数据库:Redis。
从 Redis 读取和写入数据
Redis 是一个开源的 NoSQL 数据库。它是一个键值数据库,存储在内存中,速度极快,并且高可用。它还可以用作缓存或消息代理。内存存储意味着它使用 RAM 来存储数据,并通过虚拟内存处理更大的数据。Redis 提供缓存服务或永久存储。Redis 支持多种数据结构,如字符串、集合、列表、位图、地理空间索引和超日志。Redis 能够处理地理空间、流式数据和时间序列数据。它还可以与云服务如 AWS 和 Google Cloud 一起使用。
我们可以通过 Redis 连接器在 Python 中从 Redis 读取和写入数据。为了实现这个连接,我们需要安装 Redis 和 Redis 连接器。你可以通过以下链接下载 Redis:github.com/rgl/redis/downloads
。Redis 是一个纯 Python 的 Redis 客户端库,可以通过pip
安装:
pip install redis
让我们尝试使用 Redis 进行数据库连接:
# Import module
import redis
# Create connection
r = redis.Redis(host='localhost', port=6379, db=0)
# Setting key-value pair
r.set('eid', '101')
# Get value for given key
value=r.get('eid')
# Print the value
print(value)
这里,我们尝试从 Redis 键值数据库中提取数据。首先,我们与数据库建立了连接。我们使用 set()
方法将键值对设置到 Redis 数据库中,并且通过给定的键参数使用 get()
方法提取值。
最后,进入本章的最后一个主题,即用于对象关系映射(ORM)的 PonyORM。
PonyORM
PonyORM 是一个强大的 ORM 包,使用纯 Python 编写。它快速、易于使用,并且能以最小的努力执行操作。它提供自动查询优化和图形界面数据库模式编辑器。它还支持自动事务管理、自动缓存和复合键。PonyORM 使用 Python 生成器表达式,这些表达式会被翻译成 SQL。我们可以通过 pip
安装它:
$ pip install pony
让我们来看一个使用 pony
的 ORM 示例:
# Import pony module
from pony.orm import *
# Create database
db = Database()
# Define entities
class Emp(db.Entity):
eid = PrimaryKey(int,auto=True)
salary = Required(int)
# Check entity definition
show(Emp)
# Bind entities to MySQL database
db.bind('mysql', host='localhost', user='root', passwd='12345', db='employee')
# Generate required mappings for entities
db.generate_mapping(create_tables=True)
# turn on the debug mode
sql_debug(True)
# Select the records from Emp entities or emp table
select(e for e in Emp)[:]
# Show the values of all the attribute
select(e for e in Emp)[:].show()
Output:
eid|salary
---+------
104|43000
104|43000
在前面的代码示例中,我们正在执行 ORM。首先,我们创建了一个 Database
对象,并使用 Emp
类定义了实体。接着,我们通过 db.bind()
方法将实体绑定到数据库。我们可以将其绑定到四个数据库:sqlite
、mysql
、postgresql
和 oracle
。在我们的示例中,我们使用 MySQL,并传递其凭证信息,如用户名、密码和数据库名称。我们可以使用 generate_mapping()
来执行实体与数据的映射。create_tables=True
参数在表格不存在时会创建表。sql_debug(True)
会启用调试模式。select()
函数将 Python 生成器翻译成 SQL 查询,并返回一个 pony
对象。这个 pony
对象将通过切片操作符([:]
)转换为实体列表,show()
函数将以表格形式显示所有记录。
总结
在这一章中,我们学习了如何以不同格式检索、处理和存储数据。我们查看了如何从各种文件格式和来源读取和写入数据,如 CSV、Excel、JSON、HDF5、HTML、pickle
、表格和 Parquet 文件。我们还学习了如何从各种关系型和 NoSQL 数据库中读取和写入数据,如 SQLite3、MySQL、MongoDB、Cassandra 和 Redis。
下一章,第七章,清理混乱数据,讲述了数据预处理和特征工程在 Python 中的关键主题。本章从探索性数据分析开始,接着介绍了数据过滤、处理缺失值和异常值。清理之后,将重点讨论数据转换,如编码、缩放和拆分。
第八章:清理杂乱的数据
数据分析师和科学家大部分时间都在清理数据和预处理杂乱的数据集。虽然这项活动较少被讨论,但它是最常执行的活动之一,也是任何数据专业人士必须具备的重要技能。掌握数据清理技巧是每个有抱负的数据科学家必备的技能。数据清理和预处理是识别、更新和删除损坏或不正确数据的过程。清理和预处理的结果是高质量的数据,能够支持强大且无误的分析。优质数据能够击败复杂的算法,并超越简单和不复杂的算法。在这个背景下,高质量意味着准确、完整和一致的数据。数据清理包括处理缺失值、移除异常值、特征编码、缩放、转换和拆分等一系列活动。
本章重点讲解数据清理、处理和整理。数据准备、处理、整理和清洗是同一件事情的不同说法,主要目标是清理数据,以便获得有价值的洞察。我们将从探索员工数据开始,然后过滤数据并处理缺失值和异常值。清理完数据后,我们将集中进行数据转换操作,如编码、缩放和拆分。本章大部分内容将使用pandas
和scikit-learn
。
在本章中,我们将涵盖以下主题:
-
探索数据
-
过滤数据以剔除噪声
-
处理缺失值
-
处理异常值
-
特征编码技术
-
特征缩放
-
特征转换
-
特征拆分
让我们开始吧!
技术要求
以下是本章的技术要求:
-
你可以在本书的 GitHub 仓库中找到本章将使用的代码和数据集,链接为
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter07
。 -
所有代码都可以在
ch7.ipynb
文件中找到。 -
本章仅使用一个 CSV 文件(
employee.csv
)进行练习。 -
在本章中,我们将使用
pandas
和scikit-learn
这两个 Python 库,因此请确保你已安装它们。
探索数据
在本节中,我们将通过进行探索性数据分析(EDA)来探索数据。EDA 是数据分析过程中最关键和最重要的组成部分。EDA 带来以下好处:
-
它提供了数据及其背景的初步了解。
-
它可以快速捕捉洞察,并识别出数据中可能影响预测分析的潜在驱动因素。它可以发现那些有助于决策的查询和问题。
-
它评估数据质量,帮助我们为数据清理和预处理制定路线图。
-
它可以找到缺失值、异常值,以及分析中各特征的重要性。
-
EDA(探索性数据分析)使用描述性统计和可视化技术来探索数据。
在 EDA 中,第一步是读取数据集。我们可以使用pandas
来读取数据集。pandas
库提供了多种读取数据的选项。它可以读取多种格式的文件,如 CSV、Excel、JSON、parquet、HTML 和 pickle。所有这些方法在前一章节中都有讲解。读取数据后,我们可以对数据进行探索。这一初步探索将帮助我们理解数据并获得一些领域洞察。让我们开始 EDA 过程吧。
首先,我们将读取employee.csv
文件(你可以在本书 GitHub 仓库的Chapter-7
文件夹中找到该文件,链接为github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/blob/master/Chapter07/employee.csv
):
# import pandas
import pandas as pd
# Read the data using csv
data=pd.read_csv('employee.csv')
让我们使用head()
方法查看文件中的前五条记录:
# See initial 5 records
data.head()
这将产生以下输出:
同样,让我们使用head()
方法查看文件中的最后五条记录:
# See last 5 records
data.tail()
这将产生以下输出:
我们可以通过使用columns
属性查看列的列表:
# Print list of columns in the data
print(data.columns)
这将产生以下输出:
Index(['name', 'age', 'income', 'gender', 'department', 'grade',
'performance_score'], dtype='object')
让我们通过使用shape
属性查看数据框的列列表:
# Print the shape of a DataFrame
print(data.shape)
这将产生以下输出:
(9, 7)
如我们所见,数据集包含9
行和7
列。
我们可以通过以下代码检查数据框的表模式、列、行、数据类型和缺失值:
# Check the information of DataFrame
data.info()
这将产生以下输出:
在前面的输出中,你可以看到数据中有 7 列。在这 7 列中,有 3 列(年龄、收入和性别)包含缺失值。这 7 列中,4 列是对象类型,2 列是浮动类型,1 列是整数类型。
现在,让我们通过使用describe
函数查看数据的描述性统计信息。该函数将描述数值型数据。在我们的例子中,年龄、收入和表现分数将描述计数、均值、标准差、最小值和最大值,以及第一个、第二个和第三个四分位数:
# Check the descriptive statistics
data.describe()
这将产生以下输出:
在前面的代码块中,我们使用describe()
函数检查了数据的描述性统计值。从这些结果我们可以解读出员工的年龄范围是从 23 岁到 54 岁。在这里,平均年龄是 40 岁,中位年龄是 45 岁。同样,我们也可以对收入和表现分数得出类似的结论。现在我们已经描述了数据,让我们来学习如何从数据中过滤噪声。
过滤数据以剔除噪声
在过去的二十年里,随着数字化的发展,企业和政府机构的数据量不断增加。这也导致了一致性、错误和缺失值的增加。数据过滤负责处理这些问题,并优化它们以用于管理、报告和预测。过滤过程通过处理脏数据、杂乱无章的数据或粗糙数据集,提升了数据的准确性、相关性、完整性、一致性和质量。这是任何数据管理中非常关键的步骤,因为它能够决定一个企业的竞争优势。数据科学家需要掌握数据过滤的技巧。不同类型的数据需要不同的处理方法。正因如此,数据过滤需要采取系统化的方法。
在上一节中,我们学习了数据探索,而在本节中,我们将学习数据过滤。数据可以按列或按行过滤。我们一一探讨这两种方法。
按列过滤
在这一小节中,我们将学习如何过滤按列排列的数据。我们可以使用filter()
方法来过滤列数据。slicing []. filter()
方法在列作为列列表传递时选择这些列。看看下面的例子:
# Filter columns
data.filter(['name', 'department'])
这将产生如下输出:
类似地,我们也可以通过切片来过滤列数据。在切片中,单个列不需要列表,但是当我们要过滤多个列时,它们应放在一个列表中。单列的输出是一个 pandas Series。如果我们希望输出为 DataFrame,则需要将单个列的名称放入列表中。看看下面的例子:
# Filter column "name"
data['name']
0 Allen Smith
1 S Kumar
2 Jack Morgan
3 Ying Chin
4 Dheeraj Patel
5 Satyam Sharma
6 James Authur
7 Josh Wills
8 Leo Duck
Name: name, dtype: object
在前面的例子中,我们选择了单个列而没有将其放入列表中,输出是一个 pandas Series。
现在,让我们使用 Python 列表选择单个列:
# Filter column "name"
data[['name']]
这将产生如下输出:
如你所见,单个列可以使用 Python 列表进行选择。此过滤的输出是一个包含单列的 pandas DataFrame。
现在,让我们从 pandas DataFrame 中过滤多个列:
# Filter two columns: name and department
data[['name','department']]
这将产生如下输出:
如你所见,我们在没有使用filter()
函数的情况下对两列数据进行了过滤。
按行过滤
现在,让我们来过滤按行排列的数据。我们可以通过索引、切片和条件进行数据过滤。在索引中,你需要传递记录的索引,而在切片中,我们需要传递切片范围。看看下面的例子:
# Select rows for the specific index
data.filter([0,1,2],axis=0)
这将产生如下输出:
在前面的例子中,我们是基于索引来过滤数据的。
以下是通过切片过滤数据的例子:
# Filter data using slicing
data[2:5]
这将产生如下输出:
在基于条件的筛选中,我们必须将一些条件传递给方括号[ ]
或圆括号( )
。对于单一值,我们使用==
(双等于)条件,而对于多个值,我们使用isin()
函数并传递值的列表。让我们看一下以下示例:
# Filter data for specific value
data[data.department=='Sales']
这将得到以下输出:
在前面的代码中,我们在代码的第一行使用==
(双等于)作为条件筛选了销售部门。现在,让我们使用isin()
函数筛选多个列:
# Select data for multiple values
data[data.department.isin(['Sales','Finance'])]
这将得到以下输出:
在前面的例子中,我们使用isin()
函数筛选了销售部门和财务部门。
现在,让我们来看一下>=
和<=
条件,适用于连续变量。我们可以使用单个或多个条件。让我们看一下以下示例:
# Filter employee who has more than 700 performance score
data[(data.performance_score >=700)]
这将得到以下输出:
在前面的例子中,我们根据员工的绩效评分(performance_score >=700)进行了筛选。现在,让我们使用多个条件来筛选数据:
# Filter employee who has more than 500 and less than 700 performance score
data[(data.performance_score >=500) & (data.performance_score < 700)]
这将得到以下输出:
我们还可以尝试使用query()
方法。这个方法使用布尔表达式查询列。让我们看一个例子:
# Filter employee who has performance score of less than 500
data.query('performance_score<500')
这将得到以下输出:
在前面的例子中,我们筛选了绩效分数低于 500 的员工。现在,让我们学习如何处理缺失值。
处理缺失值
缺失值是数据中缺失的值。缺失值可能由于人为错误、隐私问题,或调查填写者未填写该值而出现。这是数据科学中最常见的问题,也是数据预处理的第一步。缺失值会影响机器学习模型的表现。处理缺失值的方法有以下几种:
-
删除缺失值记录。
-
手动填充缺失值。
-
使用集中趋势的测量值填充缺失值,例如均值、中位数和众数。均值用于填充数值特征,中位数用于填充序数特征,而众数或最高频次值用于填充分类特征。
-
使用机器学习模型,如回归、决策树、KNN,填充最可能的值。
需要理解的是,在某些情况下,缺失值不会影响数据。例如,驾驶执照号码、社会保障号码或任何其他唯一的身份证号码不会影响机器学习模型,因为它们无法作为模型中的特征使用。
在接下来的子章节中,我们将更详细地了解如何处理缺失值。首先,我们将学习如何删除缺失值。
删除缺失值
在 Python 中,缺失值可以使用 dropna()
函数删除。dropna
接受一个参数:how
。how
可以取两个值:all
或 any
。any
会删除包含 NAN 或缺失值的某些行,而 all
会删除包含 NAN 或缺失值的所有行:
# Drop missing value rows using dropna() function
# Read the data
data=pd.read_csv('employee.csv')
data=data.dropna()
data
这会产生以下输出:
这将数据集总结为一个数据框。
填充缺失值
在 Python 中,缺失值可以使用 fillna()
函数填充。fillna()
函数接受我们希望填充的值来替代缺失值。我们可以使用均值、中位数和众数来填充缺失值:
# Read the data
data=pd.read_csv('employee.csv')
# Fill all the missing values in the age column with mean of the age column
data['age']=data.age.fillna(data.age.mean())
data
这会产生以下输出:
在前面的示例中,年龄列的缺失值已经使用年龄列的均值填充。接下来我们将学习如何使用中位数填充缺失值:
# Fill all the missing values in the income column with a median of the income column
data['income']=data.income.fillna(data.income.median())
data
这会产生以下输出:
在前面的示例中,收入列的缺失值已经使用收入列的中位数填充。接下来我们将学习如何使用众数填充缺失值:
# Fill all the missing values in the gender column(category column) with the mode of the gender column
data['gender']=data['gender'].fillna(data['gender'].mode()[0])
data
这会产生以下输出:
在前面的代码示例中,性别列的缺失值已经使用性别列的众数进行了填充。正如你所见,均值、中位数和众数帮助我们在 pandas DataFrame 中处理缺失值。在接下来的部分,我们将重点讨论如何处理异常值。
处理异常值
异常值是那些与大多数相似数据点相距较远的数据点——换句话说,我们可以说,异常值是与大多数数据不同的实体。当建立预测模型时,异常值会引发问题,例如训练时间过长、准确度差、误差方差增大、正态性下降,以及统计检验的效能降低。
异常值有两种类型:单变量异常值和多变量异常值。单变量异常值可以在单一变量分布中找到,而多变量异常值可以在 n 维空间中找到。我们可以通过以下方法检测和处理异常值:
-
箱型图:我们可以使用箱型图通过四分位数生成一组数据点。它将数据点分组在第一四分位数和第三四分位数之间,形成一个矩形框。箱型图还通过四分位距显示异常值作为单独的点。
-
散点图:散点图在二维图表上展示点(或两个变量)。一个变量放在 x 轴上,另一个变量放在 y 轴上。
-
Z-Score:Z-Score 是一种检测异常值的参数方法。它假设数据服从正态分布。异常值位于正态曲线分布的尾部,远离均值:
- 四分位距 (IQR):IQR 是一种稳健的统计数据离散度量。它是第三四分位数与第一四分位数之间的差值。这些四分位数可以通过箱线图可视化。这也被称为中间离差、中间 50% 或 H-离差:
- 百分位数:百分位数是一种统计度量,将数据分为 100 个相等大小的组。它的值表示该值以下的人群百分比。例如,第 95 百分位意味着 95% 的人处于此类别下。
让我们通过标准差和均值来去除一些异常值:
# Dropping the outliers using Standard Deviation
# Read the data
data=pd.read_csv('employee.csv')
# Dropping the outliers using Standard Deviation
upper_limit= data['performance_score'].mean () + 3 * data['performance_score'].std ()
lower_limit = data['performance_score'].mean () - 3 * data['performance_score'].std ()
data = data[(data['performance_score'] < upper_limit) & (data['performance_score'] > lower_limit)]
data
这将产生以下输出:
在上面的例子中,我们使用标准差和均值来处理异常值。我们使用 作为上限,并使用
作为下限来筛选异常值。我们也可以尝试使用百分位数来去除异常值。让我们看一下以下示例:
# Read the data
data=pd.read_csv('employee.csv')
# Drop the outlier observations using Percentiles
upper_limit = data['performance_score'].quantile(.99)
lower_limit = data['performance_score'].quantile(.01)
data = data[(data['performance_score'] < upper_limit) & (data['performance_score'] > lower_limit)]
data
这将产生以下输出:
在上面的代码示例中,我们通过使用百分位数来处理异常值。我们通过使用 1 的百分位数作为下限,99 的百分位数作为上限来去除异常值。这有助于我们处理 pandas DataFrame 中的异常值。在接下来的部分,我们将重点介绍如何进行特征编码。
特征编码技术
机器学习模型是数学模型,需要数值型和整数型数据进行计算。此类模型无法处理类别特征。因此,我们通常需要将类别特征转换为数值型特征。机器学习模型的性能受我们使用的编码技术的影响。类别值的范围从 0 到 N-1 类。
One-hot 编码
One-hot 编码将类别列转换为标签,并将该列拆分为多个列。数字会被二进制值如 1 或 0 替代。例如,假设在 color
变量中有三类,即 red
、green
和 blue
。这三类将被标记并编码为二进制列,如下图所示:
One-hot 编码也可以使用 get_dummies()
函数进行。我们以 get_dummies()
函数为例:
# Read the data
data=pd.read_csv('employee.csv')
# Dummy encoding
encoded_data = pd.get_dummies(data['gender'])
# Join the encoded _data with original dataframe
data = data.join(encoded_data)
# Check the top-5 records of the dataframe
data.head()
这将产生以下输出:
在这里,我们可以看到两个额外的列,F 和 M。这两列是布尔编码器添加的虚拟列。我们也可以使用 scikit-learn
模块中的 OneHotEncoder
来执行相同的任务。让我们看一下使用 OneHotEncoder
的示例:
# Import one hot encoder
from sklearn.preprocessing import OneHotEncoder
# Initialize the one-hot encoder object
onehotencoder = OneHotEncoder()
# Fill all the missing values in income column(category column) with mode of age column
data['gender']=data['gender'].fillna(data['gender'].mode()[0])
# Fit and transforms the gender column
onehotencoder.fit_transform(data[['gender']]).toarray()
这将产生以下输出:
array([[1., 0.],
[1., 0.],
[0., 1.],
[1., 0.],
[1., 0.],
[1., 0.],
[1., 0.],
[1., 0.],
[0., 1.]])
在前面的代码示例中,我们导入了OneHotEncoder
,初始化了它的对象,然后对性别列进行了拟合和转换。我们可以看到,输出数组为女性和男性员工各有一列。
标签编码
标签编码也被称为整数编码。整数编码将分类值替换为数值。在这里,变量中的唯一值被替换为一系列整数值。例如,假设有三个类别:红色、绿色和蓝色。这三个类别已经用整数值进行了编码;也就是说,red
是 0,green
是 1,blue
是 2。
让我们来看一个标签编码的例子:
# Import pandas
import pandas as pd
# Read the data
data=pd.read_csv('employee.csv')
# Import LabelEncoder
from sklearn.preprocessing import LabelEncoder
# Instantiate the Label Encoder Object
label_encoder = LabelEncoder()
# Fit and transform the column
encoded_data = label_encoder.fit_transform(data['department'])
# Print the encoded
print(encoded_data)
这将产生以下输出:
[2 1 0 0 2 1 2 1 0 2]
在前面的例子中,我们进行了简单的标签编码。
在以下例子中,我们使用LabelEncoder
类对部门列进行编码。首先,我们必须导入并初始化LabelEncoder
对象,然后拟合并转换我们想要编码的列。让我们对已编码的标签执行反向转换:
# Perform inverse encoding
inverse_encode=label_encoder.inverse_transform([0, 0, 1, 2])
# Print inverse encode
print(inverse_encode)
这将产生以下输出:
['Finance' 'Finance' 'Operations' 'Sales']
在前面的例子中,我们使用inverse_transformation()
方法反转了已编码值的编码。我们还可以对数值变量使用独热编码。在这里,每个唯一的数值都被编码为一个等效的二进制变量。
序数编码器
序数编码与标签编码类似,唯一的区别是编码是有顺序的。输出编码从 0 开始,到类别数量减 1 为止。我们来看一个包含员工等级的例子,例如 G0、G1、G2、G3 和 G4。这五个等级已经用序数整数值进行了编码;也就是说,G0
是 0,G1
是 1,G2
是 2,G3
是 3,G4
是 4。我们可以将值的顺序定义为一个列表,并将其传递给类别参数。序数编码器使用整数或数值来进行编码。在这里,整数和数值是有序的。这种编码有助于机器学习算法利用这种序数关系。
让我们来看一个OrdinalEncoder
的例子:
# Import pandas and OrdinalEncoder
import pandas as pd
from sklearn.preprocessing import OrdinalEncoder
# Load the data
data=pd.read_csv('employee.csv')
# Initialize OrdinalEncoder with order
order_encoder=OrdinalEncoder(categories=['G0','G1','G2','G3','G4'])
# fit and transform the grade
data['grade_encoded'] = label_encoder.fit_transform(data['grade'])
# Check top-5 records of the dataframe
data.head()
这将产生以下输出:
前面的例子与LabelEncoder
的例子类似,唯一的区别是在初始化OrdinalEncoder
对象时传递的值的顺序。在这个例子中,categories
参数与grade
顺序一起在初始化时传递。
特征缩放
在现实生活中,大多数特征有不同的范围、大小和单位,比如年龄在 0 到 200 之间,薪资从几千到几百万不等。从数据分析师或数据科学家的角度来看,当这些特征处于不同的尺度时,我们如何进行比较呢?大规模的特征会在机器学习模型中比小规模的特征权重更大。幸运的是,特征缩放或特征归一化可以解决这些问题。
特征缩放将所有特征的量级调整到相同水平。这对于所有类型的算法并非强制性的;有些算法显然需要缩放数据,例如那些依赖于欧几里得距离度量的算法,如 K 近邻算法和 K-means 聚类算法。
特征缩放方法
现在,让我们来看一下我们可以使用的各种特征缩放方法:
- 标准化缩放或 Z 得分归一化:该方法通过使用特征的均值和标准差来计算特征的缩放值。它最适用于正态分布的数据。假设![]是特征列的均值,![]是标准差。这将导致以下公式:
让我们来看一下以下的标准化缩放示例:
# Import StandardScaler(or z-score normalization)
from sklearn.preprocessing import StandardScaler
# Initialize the StandardScaler
scaler = StandardScaler()
# To scale data
scaler.fit(data['performance_score'].values.reshape(-1,1))
data['performance_std_scaler']=scaler.transform(data['performance_score'].values.reshape(-1,1))
data.head()
这将产生以下输出:
在这里,我们需要导入并初始化StandardScaler
对象。初始化后,我们必须对要缩放的列执行拟合和转换操作。
- 最小-最大缩放:该方法将原始数据线性转换为给定范围。它保留了缩放数据与原始数据之间的关系。如果分布不是正态分布,并且标准差值非常小,则最小-最大缩放器效果更好,因为它对异常值更为敏感。假设![]是特征列的最小值,![]是最大值,而
和
是新的最小值和最大值。这将导致以下公式:
让我们来看一下以下的最小-最大缩放示例:
# Import MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
# Initialise the MinMaxScaler
scaler = MinMaxScaler()
# To scale data
scaler.fit(data['performance_score'].values.reshape(-1,1))
data['performance_minmax_scaler']=scaler.transform(data['performance_score'].values.reshape(-1,1))
data.head()
这将产生以下输出:
在这里,我们需要导入并初始化MinMaxScaler
对象。初始化后,我们必须对要缩放的列执行拟合和转换操作。
- 鲁棒缩放:该方法类似于最小-最大缩放方法。与最小-最大不同,使用的是四分位数范围。因此,它对异常值具有鲁棒性。假设
和
是 x 列的第一四分位数和第三四分位数。这将导致以下公式:
让我们来看一下以下的鲁棒缩放示例:
# Import RobustScaler
from sklearn.preprocessing import RobustScaler
# Initialise the RobustScaler
scaler = RobustScaler()
# To scale data
scaler.fit(data['performance_score'].values.reshape(-1,1))
data['performance_robust_scaler']=scaler.transform(data['performance_score'].values.reshape(-1,1))
# See initial 5 records
data.head()
这将产生以下输出:
在这里,我们需要导入并初始化RobustScaler
对象。初始化后,我们必须对要缩放的列进行拟合和转换操作。
特征变换
特征变换改变特征的形式,使其符合要求。它还减少了异常值的影响,处理了偏态数据,并使模型更加稳健。以下是不同类型的特征变换:
-
对数变换是最常用的数学变换,用于将偏态数据转换为正态分布。在应用对数变换之前,确保所有数据值仅包含正值;否则,这会抛出异常或错误信息。
-
平方和立方变换对分布形状有中等的影响。它可以用于减少左偏。
-
平方和立方根变换对分布形状有相当强的变换效果,但其效果比对数变换弱。它可以应用于右偏数据。
-
离散化也可以用来转换数字列或属性。例如,一组候选人的年龄可以分组为 0-10 岁、11-20 岁等区间。我们还可以使用离散化来分配概念标签,而不是区间,例如青年、成人和老年。
如果特征是右偏或正偏,或者在较低值处聚集,那么我们可以应用平方根、立方根和对数变换;而如果特征是左偏或负偏,或者在较高值处聚集,那么我们可以应用立方、平方等变换。
让我们看一个离散化变换的例子:
# Read the data
data=pd.read_csv('employee.csv')
# Create performance grade function
def performance_grade(score):
if score>=700:
return 'A'
elif score<700 and score >= 500:
return 'B'
else:
return 'C'
# Apply performance grade function on whole DataFrame using apply() function.
data['performance_grade']=data.performance_score.apply(performance_grade)
# See initial 5 records
data.head()
这会产生以下输出:
在前面的例子中,我们加载了数据集并创建了performance_grade()
函数。performance_grade()
函数接受表现分数并将其转换为等级;即A
、B
和C
。
特征拆分
特征拆分帮助数据分析师和数据科学家为建模创造更多的新特征。它使机器学习算法能够理解特征并揭示决策过程中的潜在信息;例如,将姓名特征拆分为名、中间名和姓,或将地址拆分为门牌号、地区、地标、区域、城市、国家和邮政编码。
诸如字符串和日期列之类的复合特征违反了整洁数据原则。如果你希望从复合特征中生成更多特征,特征拆分是一个不错的选择。我们可以利用列的组件来做到这一点。例如,从一个日期对象中,我们可以轻松获得年份、月份和星期几。这些特征可能直接影响预测模型。拆分特征成组件时没有固定规则;这取决于特征的具体特点:
# Split the name column in first and last name
data['first_name']=data.name.str.split(" ").map(lambda var: var[0])
data['last_name']=data.name.str.split(" ").map(lambda var: var[1])
# Check top-5 records
data.head()
这会产生以下输出:
在前面的示例中,我们使用 split()
和 map()
函数拆分了姓名列。split()
函数使用空格拆分姓名列,而 map()
函数将拆分后的第一个字符串分配给名字,将第二个字符串分配给姓氏。
总结
在本章中,我们探讨了使用 Python 进行数据预处理和特征工程。这帮助你获得了数据分析中重要的技能。本章的主要重点是清理和过滤脏数据。我们从 EDA(探索性数据分析)开始,讨论了数据过滤、处理缺失值和异常值。之后,我们重点介绍了特征工程任务,如数据变换、特征编码、特征缩放和特征分割。接着,我们探索了可以用于特征工程的各种方法和技术。
在下一章,第八章,信号处理与时间序列,我们将重点讨论信号处理和时间序列数据在 Python 中的重要性。我们将从分析时间序列数据开始,讨论移动平均、自相关、回归模型和 ARMA 模型。然后,我们将研究信号处理,讨论傅里叶变换、谱变换以及信号的滤波。
第九章:信号处理与时间序列
信号处理是电气工程和应用数学的一个子领域。它涵盖了时间相关变量或随时间变化的变量的分析和处理,例如模拟信号和数字信号。模拟信号是非数字化的信号,例如广播或电话信号。数字信号是数字化的、离散的、按时间采样的信号,例如计算机和数字设备信号。时间序列分析是信号处理的一个类别,处理的是有序或按顺序排列的观察列表。这些数据可以按小时、日、周、月或年排序。时间序列中的时间因素非常重要。我们需要提取数据中所有与时间相关的关系。与时间序列分析相关的例子有很多,例如产品的生产和销售、按小时或每天预测股价、经济预测和人口普查分析。
在本章中,我们的主要关注点是使用 NumPy、SciPy、pandas
和statsmodels
库进行信号处理和时间序列操作。本章将对数据分析师理解趋势和模式,以及预测销售、股价、生产、人口、降水量和气温等有所帮助。
本章将涵盖以下主题:
-
statsmodels
模块 -
移动平均
-
窗口函数
-
定义协整
-
STL 分解
-
自相关
-
自回归模型
-
ARMA 模型
-
生成周期信号
-
傅里叶分析
-
谱分析滤波
技术要求
本章有以下技术要求:
-
你可以在以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter08
。 -
所有的代码块都在
Ch8.ipynb
文件中。 -
本章使用两个 CSV 文件(
beer_production.csv
和sales.csv
)进行实践。 -
本章将使用
pandas
和 Scikit-learn Python 库。
statsmodels 模块
statsmodels
是一个开源的 Python 模块,提供了多种统计操作的功能,例如中心值(均值、众数和中位数)、离散度量(标准差和方差)、相关性和假设检验。
让我们使用pip
安装statsmodels
并运行以下命令:
pip3 install statsmodels
statsmodels
提供了statsmodels.tsa
子模块用于时间序列操作。statsmodels.tsa
提供了有用的时间序列方法和技术,例如自回归、 autocorrelation、部分自相关、移动平均、简单指数平滑、霍尔特线性法、霍尔特-温特斯法、ARMA、ARIMA、向量自回归(VAR)模型和许多助手函数,我们将在接下来的章节中探讨这些。
移动平均
移动平均或滚动均值是时间序列滤波器,通过对一组或窗口的观测值进行平均,滤除冲动响应。它使用窗口大小的概念,并计算每个周期连续窗口滑动的平均值。简单移动平均可以表示如下:
有多种类型的移动平均可用,例如居中、双重和加权移动平均。让我们使用rolling()
函数找到移动平均值,但在此之前,我们将首先加载数据并可视化:
# import needful libraries
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
# Read dataset
sales_data = pd.read_csv('sales.csv')
# Setting figure size
plt.figure(figsize=(10,6))
# Plot original sales data
plt.plot(sales_data['Time'], sales_data['Sales'], label="Sales-Original")
# Rotate xlabels
plt.xticks(rotation=60)
# Add legends
plt.legend()
#display the plot
plt.show()
这将产生以下输出:
在前面的代码中,我们读取了 2017 年 1 月到 2019 年 12 月 36 个月的销售数据,并使用 Matplotlib 绘制了图表。现在,我们将使用滚动函数计算移动平均值:
# Moving average with window 3
sales_data['3MA']=sales_data['Sales'].rolling(window=3).mean()
# Moving average with window 5
sales_data['5MA']=sales_data['Sales'].rolling(window=5).mean()
# Setting figure size
plt.figure(figsize=(10,6))
# Plot original sales data
plt.plot(sales_data['Time'], sales_data['Sales'], label="Sales-Original", color="blue")
# Plot 3-Moving Average of sales data
plt.plot(sales_data['Time'], sales_data['3MA'], label="3-Moving Average(3MA)", color="green")
# Plot 5-Moving Average of sales data
plt.plot(sales_data['Time'], sales_data['5MA'], label="5-Moving Average(5MA)", color="red")
# Rotate xlabels
plt.xticks(rotation=60)
# Add legends
plt.legend()
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码中,我们使用滚动均值计算了 3 和 5 的移动平均值,并使用 Matplotlib 显示了线形图。现在,让我们在下一节中看看不同类型的移动平均窗口函数。
窗口函数
NumPy 提供了多种窗口选项,可以计算滚动窗口中的权重,正如我们在前一节中所做的那样。
窗口函数用于光谱分析和滤波器设计的间隔(更多背景信息,请参见en.wikipedia.org/wiki/Window_function
)。箱型窗口是一个矩形窗口,其公式如下:
w(n) = 1
三角窗口的形状像一个三角形,其公式如下:
在这里,L可以等于N、N+1 或N–1。
如果L的值为N–1,则称为 Bartlett 窗口,其公式如下:
在pandas
模块中,DataFrame.rolling()
函数使用win_type
参数提供相同的功能,以实现不同的窗口函数。另一个参数是窗口,用于定义窗口的大小,如前一节所示,这非常容易设置。让我们使用win_type
参数并尝试不同的窗口函数:
# import needful libraries
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
# Read dataset
sales_data = pd.read_csv('sales.csv', index_col ="Time")
# Apply all the windows on given DataFrame
sales_data['boxcar']=sales_data.Sales.rolling(3, win_type ='boxcar').mean()
sales_data['triang']=sales_data.Sales.rolling(3, win_type ='triang').mean()
sales_data['hamming']=sales_data.Sales.rolling(3, win_type ='hamming').mean()
sales_data['blackman']=sales_data.Sales.rolling(3, win_type ='blackman').mean()
#Plot the rolling mean of all the windows
sales_data.plot(kind='line',figsize=(10,6))
这将产生以下输出:
在前面的代码块中,我们使用rolling()
函数中的win_type
参数绘制了不同窗口函数的滚动均值,如箱型、三角形、汉明窗口和布莱克曼窗口。现在,让我们学习如何使用协整找到两个时间序列之间的相关性。
定义协整
协整就像是相关性,可以看作是定义两个时间序列相关性的更优指标。协整是两个时间序列线性组合的平稳行为。这样,以下方程的趋势必须是平稳的:
y(t) - a x(t)
想象一个醉汉带着他的狗在散步。相关性告诉我们他们是否朝同一方向走。协整则告诉我们男人与狗之间的距离随时间的变化。我们将使用随机生成的时间序列和实际数据来展示协整。增强型迪基-福勒(ADF)检验用于检验时间序列中的单位根,并可用来判断时间序列的平稳性。
让我们通过一个例子来理解两个时间序列的协整。
你可以通过以下 GitHub 链接查看完整的代码:github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/blob/master/Chapter08/Ch8.ipynb
让我们开始协整演示:
- 导入所需的库并定义以下函数来计算 ADF 统计量:
# Import required library
import statsmodels.api as sm
import pandas as pd
import statsmodels.tsa.stattools as ts
import numpy as np
# Calculate ADF function
def calc_adf(x, y):
result = sm.OLS(x, y).fit()
return ts.adfuller(result.resid)
- 将太阳黑子数据加载到一个 NumPy 数组中:
# Read the Dataset
data = sm.datasets.sunspots.load_pandas().data.values
N = len(data)
- 生成一个正弦波并计算正弦与自身的协整:
# Create Sine wave and apply ADF test
t = np.linspace(-2 * np.pi, 2 * np.pi, N)
sine = np.sin(np.sin(t))
print("Self ADF", calc_adf(sine, sine))
代码应该打印以下内容:
Self ADF (-5.0383000037165746e-16, 0.95853208606005591, 0, 308,
{'5%': -2.8709700936076912, '1%': -3.4517611601803702, '10%':
-2.5717944160060719}, -21533.113655477719)
在打印的结果中,第一个值代表 ADF 指标,第二个值代表 p 值。如你所见,p 值非常高。接下来的值为滞后和样本大小。最后的字典给出了此样本大小对应的 t 分布值。
- 现在,给正弦波加入噪声,以演示噪声如何影响信号:
# Apply ADF test on Sine and Sine with noise
noise = np.random.normal(0, .01, N)
print("ADF sine with noise", calc_adf(sine, sine + noise))
有了噪声,我们得到以下结果:
ADF sine with noise (-7.4535502402193075, 5.5885761455106898e- 11, 3, 305, {'5%': -2.8710633193086648, '1%': -3.4519735736206991, '10%': -2.5718441306100512}, -1855.0243977703672)
p 值已经大幅下降。这里的 ADF 指标-7.45
低于字典中的所有临界值。所有这些都是强有力的理由来拒绝协整。
- 让我们生成一个幅度更大的余弦并进行偏移。再次,加入噪声:
# Apply ADF test on Sine and Cosine with noise
cosine = 100 * np.cos(t) + 10
print("ADF sine vs cosine with noise", calc_adf(sine, cosine + noise))
以下值将被打印出来:
ADF sine vs cosine with noise (-17.927224617871534, 2.8918612252729532e-30, 16, 292, {'5%': -2.8714895534256861, '1%': -3.4529449243622383, '10%': -2.5720714378870331}, -11017.837238220782)
同样,我们有充分的理由拒绝协整。检查正弦与太阳黑子之间的协整,得到以下输出:
print("Sine vs sunspots", calc_adf(sine, data))
以下值将被打印出来:
Sine vs sunspots (-6.7242691810701016, 3.4210811915549028e-09, 16, 292,
{'5%': -2.8714895534256861, '1%': -3.4529449243622383,
'10%': -2.5720714378870331}, -1102.5867415291168)
这里使用的配对的置信水平大致相同,因为它们依赖于数据点的数量,而这一点变化不大。结果总结在以下表格中:
配对 | 统计量 | p 值 | 5% | 1% | 10% | 拒绝 |
---|---|---|---|---|---|---|
自身的正弦 | -5.03E-16 | 0.95 | -2.87 | -3.45 | -2.57 | 否 |
噪声影响下的正弦与正弦 | -7.45 | 5.58E-11 | -2.87 | -3.45 | -2.57 | 是 |
噪声影响下的正弦与余弦 | -17.92 | 2.89E-30 | -2.87 | -3.45 | -2.57 | 是 |
正弦与太阳黑子 | -6.72 | 3.42E-09 | -2.87 | -3.45 | -2.57 | 是 |
在前面的表格中,汇总了所有四个正弦波的结果,并讨论了它们的显著性水平以及拒绝/接受的标准。现在让我们进入本章的另一个重要话题,即任何时间序列的 STL 分解。
STL 分解
STL 代表 季节性 和趋势分解 使用 LOESS。STL 是一种时间序列分解方法,可以将观测信号分解为趋势、季节性和残差。它能够估计非线性关系并处理任何类型的季节性。statsmodels.tsa.seasonal
子包提供了seasonal_decompose
方法,用于将给定的输入信号分解为趋势、季节性和残差。
让我们看以下示例来理解 STL 分解:
# import needful libraries
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose
# Read the dataset
data = pd.read_csv('beer_production.csv')
data.columns= ['date','data']
# Change datatype to pandas datetime
data['date'] = pd.to_datetime(data['date'])
data=data.set_index('date')
# Decompose the data
decomposed_data = seasonal_decompose(data, model='multiplicative')
# Plot decomposed data
decomposed_data.plot()
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码块中,给定的时间序列信号通过statsmodels
模块的seasonal_decompose()
函数被分解为趋势、季节性和残差组件。现在让我们跳到自相关,理解时间序列与其滞后序列之间的关系。
自相关
自相关,或滞后相关,是时间序列与其滞后序列之间的相关性。它指示数据集中的趋势。自相关公式可以定义如下:
我们可以使用 NumPy 的correlate()
函数来计算日冕周期的实际自相关。我们还可以直接使用autocorrelation_plot()
函数可视化自相关图。让我们计算自相关并可视化它:
# import needful libraries
import pandas as pd
import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt
# Read the dataset
data = sm.datasets.sunspots.load_pandas().data
# Calculate autocorrelation using numpy
dy = data.SUNACTIVITY - np.mean(data.SUNACTIVITY)
dy_square = np.sum(dy ** 2)
# Cross-correlation
sun_correlated = np.correlate(dy, dy, mode='full')/dy_square
result = sun_correlated[int(len(sun_correlated)/2):]
# Diplay the Chart
plt.plot(result)
# Display grid
plt.grid(True)
# Add labels
plt.xlabel("Lag")
plt.ylabel("Autocorrelation")
# Display the chart
plt.show()
这将产生以下输出:
在前面的代码块中,我们使用 NumPy 模块看到了一个自相关示例。现在让我们计算由pandas
生成的自相关图:
from pandas.plotting import autocorrelation_plot
# Plot using pandas function
autocorrelation_plot(data.SUNACTIVITY)
这将产生以下输出:
在前面的代码块中,我们使用pandas
库的autocorrelation_plot()
函数生成了自相关图。与 NumPy 库相比,使用pandas
库更容易绘制自相关图。现在让我们跳到自回归模型进行时间序列预测。
自回归模型
自回归模型是用于预测未来事件的时间序列模型。以下公式展示了这一点:
在前面的公式中,c是常数,最后一项是随机成分,也称为白噪声。
让我们使用statsmodels.tsa
子包构建自回归模型:
- 导入库并读取数据集:
# import needful libraries
from statsmodels.tsa.ar_model import AR
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
import statsmodels.api as sm
from math import sqrt
# Read the dataset
data = sm.datasets.sunspots.load_pandas().data
- 将日冕数据拆分为训练集和测试集:
# Split data into train and test set
train_ratio=0.8
train=data[:int(train_ratio*len(data))]
test=data[int(train_ratio*len(data)):]
- 训练并拟合自回归模型:
# AutoRegression Model training
ar_model = AR(train.SUNACTIVITY)
ar_model = ar_model.fit()
# print lags and
print("Number of Lags:", ar_model.k_ar)
print("Model Coefficients:\n", ar_model.params)
这将产生以下输出:
Number of Lags: 15
Model Coefficients:
const 9.382322
L1.SUNACTIVITY 1.225684
L2.SUNACTIVITY -0.512193
L3.SUNACTIVITY -0.130695
L4.SUNACTIVITY 0.193492
L5.SUNACTIVITY -0.168907
L6.SUNACTIVITY 0.054594
L7.SUNACTIVITY -0.056725
L8.SUNACTIVITY 0.109404
L9.SUNACTIVITY 0.108993
L10.SUNACTIVITY -0.117063
L11.SUNACTIVITY 0.200454
L12.SUNACTIVITY -0.075111
L13.SUNACTIVITY -0.114437
L14.SUNACTIVITY 0.177516
L15.SUNACTIVITY -0.091978
dtype: float64
在前面的代码中,我们读取了 Sunspot 数据集并将其分成了两部分:训练集和测试集。然后,我们通过创建实例并拟合模型构建了自回归模型。接下来,我们进行预测并评估模型的性能。
- 进行预测并评估模型:
# make predictions
start_point = len(train)
end_point = start_point + len(test)-1
pred = ar_model.predict(start=start_point, end=end_point, dynamic=False)
# Calculate errors
mae = mean_absolute_error(test.SUNACTIVITY, pred)
mse = mean_squared_error(test.SUNACTIVITY, pred)
rmse = sqrt(mse)
print("MAE:",mae)
print("MSE:",mse)
print("RMSE:",rmse)
这将产生以下输出:
MAE: 31.17846098350052
MSE: 1776.9463826165913
RMSE: 42.15384184883498
在前面的代码块中,我们对测试数据集进行了预测,并使用平均绝对误差(MAE)、均方误差(MSE)和均方根误差(RMSE)评估了模型的性能。接下来,我们绘制原始序列和预测序列的折线图。
- 让我们绘制预测序列和原始序列,以便更好地理解预测结果:
# Setting figure size
plt.figure(figsize=(10,6))
# Plot test data
plt.plot(test.SUNACTIVITY, label='Original-Series')
# Plot predictions
plt.plot(pred, color='red', label='Predicted Series')
# Add legends
plt.legend()
# Display the plot
plt.show()
这将产生以下输出:
在前面的图中,我们可以看到使用自回归模型得到的原始序列和预测序列。在生成了自回归模型后,我们需要进入时间序列预测的另一个高级方法——自回归移动平均(ARMA)。
ARMA 模型
ARMA 模型结合了自回归和移动平均。ARMA 模型通常称为 ARMA(p,q),其中p是自回归部分的阶数,q是移动平均部分的阶数:
在前面的公式中,就像在自回归模型公式中一样,我们有一个常数项和一个白噪声分量;但是,我们还尝试拟合滞后的噪声分量:
- 导入库并读取数据集:
# import needful libraries
import statsmodels.api as sm
from statsmodels.tsa.arima_model import ARMA
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
from math import sqrt
# Read the dataset
data = sm.datasets.sunspots.load_pandas().data
data.drop('YEAR',axis=1,inplace=True)
- 将 Sunspot 数据集分成训练集和测试集:
# Split data into train and test set
train_ratio=0.8
train=data[:int(train_ratio*len(data))]
test=data[int(train_ratio*len(data)):]
- 训练并拟合自回归模型:
# AutoRegression Model training
arma_model = ARMA(train, order=(10,1))
arma_model = arma_model.fit()
- 进行预测并评估模型:
# make predictions
start_point = len(train)
end_point = start_point + len(test)-1
pred = arma_model.predict(start_point,end_point)
# Calculate errors
mae = mean_absolute_error(test.SUNACTIVITY, pred)
mse = mean_squared_error(test.SUNACTIVITY, pred)
rmse = sqrt(mse)
print("MAE:",mae)
print("MSE:",mse)
print("EMSE:",rmse)
这将产生以下输出:
MAE: 33.95457845540467
MSE: 2041.3857010355755
EMSE: 45.18169652675268
- 让我们绘制预测序列和原始序列,以便更好地理解预测结果:
# Setting figure size
plt.figure(figsize=(10,6))
# Plot test data
plt.plot(test, label='Original-Series')
# Plot predictions
plt.plot(pred, color='red', label='Predicted Series')
# Add legends
plt.legend()
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码中,我们读取了 Sunspot 数据集并将其分成了两部分:训练集和测试集。然后,我们通过创建实例并拟合模型构建了 ARMA 模型。我们对测试数据集进行了预测,并使用 MAE、MSE 和 RMSE 评估了模型性能。最后,我们查看了原始序列和预测序列的折线图。接下来,我们将讨论另一个重要主题——生成周期性信号。
生成周期性信号
许多自然现象是规律和可靠的,比如准确的时钟。有些现象展现出看似规律的模式。一组科学家发现,利用 Hilbert-Huang 变换(参见en.wikipedia.org/wiki/Hilbert%E2%80%93Huang_transform
)可以发现太阳黑子活动有三个周期,分别为大约 11 年、22 年和 100 年。通常,我们会使用三角函数,如正弦函数,来模拟周期信号。你可能记得高中时学过一点三角学。对于这个例子来说,这些知识就足够了。因为我们有三个周期,所以看起来合理的是创建一个由三个正弦函数线性组合而成的模型。这只需要对自回归模型的代码进行一点小的调整:
- 创建模型、误差和拟合函数:
# Import required libraries
import numpy as np
import statsmodels.api as sm
from scipy.optimize import leastsq
import matplotlib.pyplot as plt
# Create model function
def model(p, t):
C, p1, f1, phi1 , p2, f2, phi2, p3, f3, phi3 = p
return C + p1 * np.sin(f1 * t + phi1) + p2 * np.sin(f2 * t + phi2) +p3 * np.sin(f3 * t + phi3)
# Create error function
def error(p, y, t):
return y - model(p, t)
# Create fit function
def fit(y, t):
p0 = [y.mean(), 0, 2 * np.pi/11, 0, 0, 2 * np.pi/22, 0, 0, 2 * np.pi/100, 0]
params = leastsq(error, p0, args=(y, t))[0]
return params
- 让我们加载数据集:
# Load the dataset
data_loader = sm.datasets.sunspots.load_pandas()
sunspots = data_loader.data["SUNACTIVITY"].values
years = data_loader.data["YEAR"].values
- 应用并拟合模型:
# Apply and fit the model
cutoff = int(.9 * len(sunspots))
params = fit(sunspots[:cutoff], years[:cutoff])
print("Params", params)
pred = model(params, years[cutoff:])
actual = sunspots[cutoff:]
- 打印结果:
print("Root mean square error", np.sqrt(np.mean((actual - pred) ** 2)))
print("Mean absolute error", np.mean(np.abs(actual - pred)))
print("Mean absolute percentage error", 100 *
np.mean(np.abs(actual - pred)/actual))
mid = (actual + pred)/2
print("Symmetric Mean absolute percentage error", 100 *
np.mean(np.abs(actual - pred)/mid))
print("Coefficient of determination", 1 - ((actual - pred)
**2).sum()/ ((actual - actual.mean()) ** 2).sum())
这将产生以下输出:
Params [47.1880006 28.89947462 0.56827279 6.51178464 4.55214564
0.29372076 -14.30924768 -18.16524123 0.06574835 -4.37789476]
Root mean square error 59.56205597915569
Mean absolute error 44.58158470150657
Mean absolute percentage error 65.16458348768887
Symmetric Mean absolute percentage error 78.4480696873044
Coefficient of determination -0.3635315489903188
第一行显示了我们尝试的模型的系数。我们的 MAE 是 44,意味着我们平均偏离这个值无论是正向还是反向。我们还希望决定系数尽可能接近 1,这样拟合效果才好。然而,我们得到的是一个负值,这是不理想的。让我们绘制一张图表,详细了解这些结果。
- 绘制原始数据和预测数据:
year_range = data_loader.data["YEAR"].values[cutoff:]
# Plot the actual and predicted data points
plt.plot(year_range, actual, 'o', label="Sunspots")
plt.plot(year_range, pred, 'x', label="Prediction")
plt.grid(True)
# Add labels
plt.xlabel("YEAR")
plt.ylabel("SUNACTIVITY")
# Add legend
plt.legend()
# Display the chart
plt.show()
这将产生以下输出:
从前面的图表可以得出结论,模型未能捕捉到序列的实际模式。这就是为什么我们得到负的决定系数或 R 平方值的原因。现在,我们将看另一种重要的时间序列分析技术——傅里叶分析。
傅里叶分析
傅里叶分析使用了数学家约瑟夫·傅里叶提出的傅里叶级数概念。傅里叶级数是一种将函数表示为正弦和余弦项的无限级数的数学方法。这里的函数可以是实数值或复数值的:
对于傅里叶分析,最有效的算法是快速 傅里叶变换(FFT)。FFT 将信号分解为不同频率的信号。这意味着它生成了给定信号的频谱。SciPy 和 NumPy 库提供了 FFT 的相关函数。
rfft()
函数对实值数据执行 FFT。我们也可以使用fft()
函数,但在这个太阳黑子数据集上会给出警告。fftshift()
函数将零频率成分移动到频谱的中间。
让我们通过以下示例来理解 FFT:
- 导入库并读取数据集:
# Import required library
import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt
from scipy.fftpack import rfft
from scipy.fftpack import fftshift
# Read the dataset
data = sm.datasets.sunspots.load_pandas().data
# Create Sine wave
t = np.linspace(-2 * np.pi, 2 * np.pi, len(data.SUNACTIVITY.values))
mid = np.ptp(data.SUNACTIVITY.values)/2
sine = mid + mid * np.sin(np.sin(t))
2. 计算正弦波和太阳黑子的 FFT:
# Compute FFT for Sine wave
sine_fft = np.abs(fftshift(rfft(sine)))
print("Index of max sine FFT", np.argsort(sine_fft)[-5:])
# Compute FFT for sunspots dataset
transformed = np.abs(fftshift(rfft(data.SUNACTIVITY.values)))
print("Indices of max sunspots FFT", np.argsort(transformed)[-5:])
- 创建子图:
# Create subplots
fig, axs = plt.subplots(3,figsize=(12,6),sharex=True)
fig.suptitle('Power Specturm')
axs[0].plot(data.SUNACTIVITY.values, label="Sunspots")
axs[0].plot(sine, lw=2, label="Sine")
axs[0].legend() # Set legends
axs[1].plot(transformed, label="Transformed Sunspots")
axs[1].legend() # Set legends
axs[2].plot(sine_fft, lw=2, label="Transformed Sine")
axs[2].legend() # Set legends
# Display the chart
plt.show()
这将产生以下输出:
在前面的代码中,首先,我们读取了太阳黑子数据集并创建了正弦波。之后,我们计算了正弦波和SUNACTIVITY
列的 FFT。最后,我们绘制了原始序列、正弦波以及变换后的太阳黑子和正弦波的三张图。
谱分析滤波
在前一节中,我们讨论了数据集的幅度谱。现在是时候探索功率谱了。任何物理信号的功率谱都可以显示信号的能量分布。我们可以通过以下语法轻松地修改代码并通过对变换信号进行平方来显示功率谱:
plt.plot(transformed ** 2, label="Power Spectrum")
我们还可以使用以下 Python 语法绘制相位谱:
plt.plot(np.angle(transformed), label="Phase Spectrum")
让我们看一下完整的代码,用于计算太阳黑子数据集的功率谱和相位谱:
- 导入库并读取数据集:
# Import required library
import numpy as np
import statsmodels.api as sm
from scipy.fftpack import rfft
from scipy.fftpack import fftshift
import matplotlib.pyplot as plt
# Read the dataset
data = sm.datasets.sunspots.load_pandas().data
- 计算
FFT
、Spectrum
和Phase
:
# Compute FFT
transformed = fftshift(rfft(data.SUNACTIVITY.values))
# Compute Power Spectrum
power=transformed ** 2
# Compute Phase
phase=np.angle(transformed)
- 创建子图:
# Create subplots
fig, axs = plt.subplots(3,figsize=(12,6),sharex=True)
fig.suptitle('Power Specturm')
axs[0].plot(data.SUNACTIVITY.values, label="Sunspots")
axs[0].legend() # Set legends
axs[1].plot(power, label="Power Spectrum")
axs[1].legend() # Set legends
axs[2].plot(phase, label="Phase Spectrum")
axs[2].legend() # Set legends
# Display the chart
plt.show()
这将产生以下输出:
在前面的代码中,首先,我们读取了太阳黑子数据集并计算了 SUNACTIVITY
列的 FFT。然后,我们计算了变换后 FFT 的功率谱和相位谱。最后,我们使用子图绘制了原始序列、功率谱和相位谱的三张图。
总结
在本章中,我们使用的时间序列示例包括年度太阳黑子周期数据、销售数据和啤酒生产数据。我们学到,在同一时间序列中,尝试推导一个值与另一个数据点或一组数据点在过去固定周期内的关系是常见的做法。我们了解到,移动平均通过使用窗口大小将随机变化趋势转换为平滑趋势。我们还学到了 DataFrame.rolling()
函数如何为不同的窗口函数提供 win_type
字符串参数。协整与相关性类似,是用来定义两个时间序列之间关系的度量指标。我们还重点讲解了 STL 分解、自相关、自动回归、ARMA 模型、傅里叶分析和谱分析滤波。
下一章,第九章,监督学习 – 回归分析,将重点讲解回归分析和逻辑回归在 Python 中的重要主题。章节首先介绍了多元线性回归、多重共线性、虚拟变量和模型评估指标。在后面的部分,将重点讨论逻辑回归。
第三部分:深入了解机器学习
本节的主要目标是深入研究机器学习算法并开发预测模型。本节重点讲解回归分析、分类、主成分分析(PCA)和聚类方法。本节将主要使用 pandas 和 scikit-learn。
本节包含以下章节:
-
第九章,有监督学习 – 回归分析
-
第十章,有监督学习 – 分类技术
-
第十一章,无监督学习 – 主成分分析与聚类
第十章:监督学习 - 回归分析
回归是统计学和机器学习中最受欢迎的算法。在机器学习和数据科学领域,回归分析是监督式机器学习领域的一个成员,它帮助我们预测连续变量,如股价、房价、销售额、降雨量和温度。例如,作为一家电子商店的销售经理,假设你需要预测未来几周所有类型产品的销售情况,如电视、空调、笔记本电脑、冰箱等。许多因素可能影响你的销售情况,如天气条件、节假日、促销策略、竞争对手的优惠等。回归分析是帮助你识别这些因素的重要性,从而做出商店决策的工具之一。
回归分析用于识别因变量如何依赖于自变量。例如,假设作为一名教育官员,你希望识别体育活动、智能课堂、师生比、额外课程和教师培训对学生成绩的影响。普通最小二乘法 (OLS) 通过最小化平方误差和(或误差方差)来找出最佳拟合函数。它预测在给定条件下最可能的结果。本章的主要目标是学习多重线性回归 (MLR) 的基础知识、多重共线性、虚拟变量、回归以及模型评估指标,如 R 平方、均方误差 (MSE)、平均绝对误差 (MAE) 和均方根误差 (RMSE)。另一个目标是创建一个逻辑回归分类模型。
本章涵盖的主题如下:
-
线性回归
-
理解多重共线性
-
虚拟变量
-
开发线性回归模型
-
评估回归模型性能
-
拟合多项式回归
-
用于分类的回归模型
-
逻辑回归
-
使用 scikit-learn 实现逻辑回归
技术要求
本章有以下技术要求:
-
你可以在以下 GitHub 链接中找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter09
。 -
所有代码块都可以在
ch9.ipynb
文件中找到。 -
本章使用三个 CSV 文件(
Advertising.csv
、bloodpress.txt
和diabetes.csv
)用于练习。 -
在本章中,我们将使用 Matplotlib、
pandas
、Seaborn 和 scikit-learn Python 库。
线性回归
线性回归是一种曲线拟合和预测算法,用于发现因变量(或目标列)与一个或多个自变量(或预测变量)之间的线性关系。这种关系是确定性的,意味着它以一定的误差预测因变量。在回归分析中,因变量是连续的,而自变量可以是连续的或离散的。线性回归已广泛应用于各种商业和科学问题,如股票价格、原油价格、销售额、房产价格和 GDP 增长率的预测。在下图中,我们可以看到线性回归如何在二维空间中拟合数据:
主要目标是找到最佳拟合线,以最小的误差理解变量之间的关系。回归中的误差是预测值与实际值之间的差异。回归系数是通过 OLS 方法估计的。OLS 试图最小化残差平方和。我们来看回归模型的方程:
这里,x 是自变量,y 是因变量。 截距是 x 的系数,
(希腊字母发音为 epsilon)是作为随机变量的误差项。
线性回归的参数是通过 OLS 估计的。OLS 是一种广泛用于估计回归截距和系数的方法。它通过减少残差平方和(或误差)来优化,这个误差是预测值与实际值之间的差异。
在了解了线性回归之后,现在是时候学习 MLR 了。
多重线性回归
多重线性回归(MLR)是简单线性回归的推广形式。它是一种统计方法,用于根据多个特征或解释变量预测连续的目标变量。MLR 的主要目标是估计多个特征与目标变量之间的线性关系。MLR 在现实生活中的应用非常广泛。MLR 模型可以表示为数学方程:
这里,![] 是自变量, 是因变量。
截距是 x 的系数,
(希腊字母发音为 epsilon)是作为随机变量的误差项。
现在我们知道什么是线性回归,让我们继续讨论多重共线性。
理解多重共线性
多重共线性表示自变量(或预测变量)之间高度的相互相关性或相互关系。
多重共线性发生在多元回归分析中的自变量彼此高度相关时。这种关联是由于自变量之间的高相关性造成的。这种高相关性会导致线性回归模型预测结果出现问题。在进行线性回归分析时,避免多重共线性是基本假设,以获得更好的结果:
-
它是由于虚拟变量的不当使用而发生的。
-
它还可能是由于相似变量的重复出现引起的。
-
它还可能是由于从数据中的其他变量合成的变量引起的。
-
它可能是由于变量之间的高相关性引起的。
多重共线性会导致以下问题:
-
它会导致回归系数的精确估计变得困难,并且系数对模型中的微小变化变得更加敏感。
-
它还可能导致系数的符号和大小发生变化。
-
它使得评估自变量相对重要性变得困难。
移除多重共线性
可以使用以下方法检测多重共线性:
-
自变量之间的相关系数(或相关矩阵)
-
方差膨胀因子(VIF)
-
特征值
相关系数或相关矩阵有助于我们识别自变量之间的高度相关性。通过相关系数,我们可以通过检查相关系数的大小来轻松检测多重共线性:
# Import pandas
import pandas as pd
# Read the blood pressure dataset
data = pd.read_csv("bloodpress.txt",sep='\t')
# See the top records in the data
data.head()
这会导致以下输出:
在前面的代码块中,我们使用read_csv()
函数读取了bloodpress.txt
数据。我们还检查了数据集的初始记录。这个数据集包含了BP
、Age
、Weight
、BSA
、Dur
、Pulse
和Stress
字段。让我们使用相关矩阵来检查数据集中的多重共线性:
# Import seaborn and matplotlib
import seaborn as sns
import matplotlib.pyplot as plt
# Correlation matrix
corr=data.corr()
# Plot Heatmap on correlation matrix
sns.heatmap(corr, annot=True, cmap='YlGnBu')
# display the plot
plt.show()
这会导致以下输出:
在前面的示例中,我们使用相关矩阵查找多个变量之间的相关性。我们加载了bloodpress.txt
文件,并使用corr()
函数找到了相关性。最后,我们使用heatmap()
函数可视化了相关矩阵。
这里,BP(血压)是因变量或目标变量,其他列是自变量或特征。我们可以看到,Weight和BSA(体表面积)之间有很高的相关性。我们需要移除其中一个变量(Weight或BSA),以消除多重共线性。在我们的例子中,体重比体表面积更容易测量,因此专家会选择体重并移除体表面积。
虚拟变量
虚拟变量是回归分析中使用的分类自变量。它也被称为布尔值、指示变量、定性变量、分类变量和二元变量。虚拟变量将具有N个不同值的分类变量转换为N–1 个虚拟变量。它只取 1 和 0 这两个二进制值,分别代表存在和不存在。
pandas
提供了get_dummies()
函数来生成虚拟值。让我们通过一个例子来理解get_dummies()
函数:
# Import pandas module
import pandas as pd
# Create pandas DataFrame
data=pd.DataFrame({'Gender':['F','M','M','F','M']})
# Check the top-5 records
data.head()
这将产生以下输出:
Gender | |
---|---|
0 | F |
1 | M |
2 | M |
3 | F |
4 | M |
在前面的代码块中,我们创建了包含Gender
列的 DataFrame,并使用get_dummies()
函数生成了虚拟变量。让我们通过以下代码看一个例子:
# Dummy encoding
encoded_data = pd.get_dummies(data['Gender'])
# Check the top-5 records of the dataframe
encoded_data.head()
这将产生以下输出:
F M
0 1 0
1 0 1
2 0 1
3 1 0
4 0 1
在前面的例子中,get_dummies()
函数生成了两列,这意味着每个值都有一个单独的列。
我们可以通过使用drop_first=True
参数,去掉一列,避免共线性问题,首先删除N个类别中的第一列虚拟变量:
# Dummy encoding
encoded_data = pd.get_dummies(data['Gender'], drop_first=True)
# Check the top-5 records of the dataframe
encoded_data.head()
这将产生以下输出:
M
0 0
1 1
2 1
3 0
4 1
在前面的代码块中,我们使用get_dummies()
函数并设置drop_first=True
参数,为Gender
列创建了虚拟变量。这将删除第一列,剩下N–1 列。现在,让我们学习如何使用scikit-learn
库实现线性回归模型。
开发线性回归模型
在理解回归分析、多重共线性和虚拟变量的概念之后,是时候通过实际操作体验回归分析了。让我们学习如何使用科学机器学习工具包(scikit-learn)构建回归模型:
- 我们将首先使用
read_csv()
函数加载数据集:
# Import pandas
import pandas as pd
# Read the dataset using read_csv method
df = pd.read_csv("Advertising.csv")
# See the top-5 records in the data
df.head()
这将产生以下输出:
现在我们已经使用read_csv()
加载了Advertising.csv
数据集,并使用head()
函数查看了初始记录,我们将数据拆分为两部分:因变量或目标变量和自变量或特征变量。
- 在这一步,我们将数据拆分两次:
-
将数据分为两部分:因变量或目标变量与自变量或特征变量。
-
将数据拆分为训练集和测试集。这可以使用以下代码完成:
# Independent variables or Features
X = df[['TV', 'Radio', 'Newspaper']]
# Dependent or Target variable
y = df.Sales
在将列分为因变量和自变量部分之后,我们将使用train_test_split()
函数按 75:25 的比例将数据拆分为训练集和测试集。这个比例可以通过test_size
参数指定,random_state
用作种子值,确保每次拆分的数据相同。如果random_state
为None
,则每次都会随机拆分记录,可能会得到不同的性能度量:
# Lets import the train_test_split method
from sklearn.model_selection import train_test_split
# Distribute the features(X) and labels(y) into two parts training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
在之前的代码块中,我们已经将数据分为两部分——训练集和测试集——比例为 75:25 或 3:1。
- 让我们导入
LinearRegression
模型,创建它的对象,并将其拟合到训练数据集(X_train
,y_train
)。在拟合模型后,我们可以预测测试数据(X_test
)的值。通过intercept_
和coef_
属性,我们可以看到回归方程的截距和系数:
# Import linear regression model
from sklearn.linear_model import LinearRegression
# Create linear regression model
lin_reg = LinearRegression()
# Fit the linear regression model
lin_reg.fit(X_train, y_train)
# Predict the values given test set
predictions = lin_reg.predict(X_test)
# Print the intercept and coefficients
print("Intercept:",lin_reg.intercept_)
print("Coefficients:",lin_reg.coef_)
这将产生以下输出:
Intercept: 2.8925700511511483
Coefficients: [0.04416235 0.19900368 0.00116268]
在之前的代码中,我们已经准备了线性回归模型,对测试集进行了预测,并显示了截距和系数。在接下来的部分,我们将使用回归评估指标(如 R 平方和误差函数)评估回归模型的性能。
评估回归模型性能
在这一部分,我们将回顾回归模型评估指标,以了解回归模型的性能水平。模型评估是任何机器学习模型构建过程中的关键方面之一。它帮助我们评估当模型投入生产时的表现。我们将使用以下指标进行模型评估:
-
R 平方
-
MSE
-
MAE
-
RMSE
R 平方
R 平方(或决定系数)是一个统计学模型评估指标,用于评估回归模型的拟合优度。它帮助数据分析师解释模型相较于基准模型的表现。其值介于 0 和 1 之间。接近 0 的值表示模型较差,而接近 1 的值表示完美拟合。有时,R 平方结果可能为负值,这意味着你的模型比平均基准模型还差。我们可以通过以下公式解释 R 平方:
让我们逐一理解所有组件:
-
回归平方和(SSR):这是预测值与数据均值之间差异的估算。
-
误差平方和(SSE):这是原始值与预测值之间变化的估算。
-
总平方和(SST):这是原始值与数据均值之间的变化。
MSE
MSE 是均方误差(Mean Squared Error)的缩写。它的定义为原始值与预测值之间变化的平方,以及所有值之间的平均值:
这里, 是原始值,
是预测值。
MAE
MAE 是均绝对误差(Mean Absolute Error)的缩写。它的定义为原始值与预测值之间的绝对变化,以及所有值之间的平均值:
这里, 是原始值,
是预测值。
RMSE
RMSE 是均方根误差的缩写。它可以解释为 MSE 的平方根:
让我们在测试数据集上评估模型性能。在前一部分,我们预测了测试集的值。现在,我们将预测值与测试集的实际值(y_test
)进行比较。scikit-learn 提供了 metrics
类来评估模型。对于回归模型评估,我们有 R 平方值、MSE、MAE 和 RMSE 的评估方法。每个方法都需要两个输入:测试集的实际值和预测值(y_test
和 y_pred
)。让我们评估线性回归模型的表现:
# Import the required libraries
import numpy as np
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
# Evaluate mean absolute error
print('Mean Absolute Error(MAE):', mean_absolute_error(y_test,predictions))
# Evaluate mean squared error
print("Mean Squared Error(MSE):", mean_squared_error(y_test, predictions))
# Evaluate root mean squared error
print("Root Mean Squared Error(RMSE):", np.sqrt(mean_squared_error(y_test, predictions)))
# Evaluate R-square
print("R-Square:",r2_score(y_test, predictions))
这将产生以下输出:
Mean Absolute Error(MAE): 1.300032091923545
Mean Squared Error(MSE): 4.0124975229171
Root Mean Squared Error(RMSE): 2.003121944095541
R-Square: 0.8576396745320893
在这个例子中,我们通过 MAE、MSE、RMSE 和 R 平方值评估了线性回归模型。在这里,R 平方值为 0.85,表示该模型解释了数据 85% 的变异性。
拟合多项式回归
多项式回归是一种回归分析方法,用于适应因变量与自变量之间的非线性关系。在这种回归类型中,变量被建模为 n 次多项式的形式。它用于理解各种现象的增长率,如流行病爆发和销售增长。让我们理解一下多项式回归的方程:
在这里, 是自变量,
是因变量。
截距,
...
是 x 的系数,
(这个希腊字母发音为 epsilon)是误差项,作为随机变量起作用。
让我们看一个例子来详细理解多项式概念:
# import libraries
import matplotlib.pyplot as plt
import numpy as np
# Create X and Y lists
X=[1,2,3,4,5,6,7,8,9,10]
y=[9,10,12,16,22,28,40,58,102,200]
# Plot scatter diagram
plt.scatter(X,y, color = 'red')
plt.title('Polynomial Regression')
plt.xlabel('X-Axis')
plt.ylabel('y-Axis')
这将产生以下输出:
在前面的代码中,我们展示了一个具有多项式关系的数据集。让我们来看一下如何在回归分析中映射这个关系:
# import libraries
import pandas as pd
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
# Prepare dataset
data = pd.DataFrame({"X":[1,2,3,4,5,6,7,8,9,10],
"y":[9,10,12,16,22,28,40,58,102,200]})
X = data[['X']] y = data[['y']]
# Apply Polynomial Features
polynomial_reg = PolynomialFeatures(degree = 6)
X_polynomial = polynomial_reg.fit_transform(X)
# Apply Linear Regression Model
linear_reg = LinearRegression()
linear_reg.fit(X_polynomial, y) predictions=linear_reg.predict(X_polynomial)
# Plot the results
plt.scatter(X,y, color = 'red')
plt.plot(X, predictions, color = 'red')
plt.title('Polynomial Regression')
plt.xlabel('X-Axis')
plt.ylabel('y-Axis')
这将产生以下输出:
在前面的代码中,我们读取了多项式关系数据集,使用 PolynomialFeatures()
将 X 列转换为多项式 n 次方列,然后对 X_polynomial
和 label
应用了线性回归。前面的输出图显示了结果模型的表现。现在,轮到我们转向另一种回归模型,这种模型可以用于分类目的。
用于分类的回归模型
分类是机器学习和统计学习领域中使用最多的技术。大多数机器学习问题都是分类问题,例如,垃圾邮件检测、金融风险分析、客户流失分析以及潜在客户发现等。
分类可以分为两种类型:二分类和多分类。二分类目标变量只有两个值:0 和 1,或者是是和否。例如,二分类的实例包括:客户是否会购买某商品,客户是否会转向其他品牌或流失,垃圾邮件检测,疾病预测,以及贷款申请者是否会违约等。多分类则有多个类别,例如,新闻文章的类别可以包括体育、政治、商业等多个类。
逻辑回归是其中一种分类方法,尽管它的名字以“回归”结尾。它是一种常用的二分类方法,是解决各种分类问题的基础机器学习算法。它找出因变量(或目标变量)与一组自变量(或特征)之间的关联。在接下来的部分中,我们将详细介绍逻辑回归。
逻辑回归
逻辑回归是一种有监督的机器学习算法,用于预测二元结果并进行观察分类。其因变量是一个二元变量,具有两个类别:0 或 1。例如,它可以用来检测贷款申请者是否会违约。它是一种特殊类型的回归,其中因变量是二元的。它计算目标变量的赔率比的对数,表示某事件发生的概率,例如,一个人患糖尿病的概率。
逻辑回归是一种简单的线性回归,其中因变量或目标变量是类别型的。它在线性回归的预测结果上使用了 sigmoid 函数。我们还可以使用逻辑回归算法来处理多个目标类别。在多类别问题中,它被称为多项逻辑回归。多项逻辑回归是对逻辑回归的一种修改;它使用 softmax 函数代替 sigmoid 激活函数:
Sigmoid 函数也叫做逻辑函数或 S 形曲线。它将输入值映射到 0 到 1 之间,这表示某事件发生的概率。如果曲线向正无穷延伸,则结果变为 1;如果曲线向负无穷延伸,则结果变为 0。让我们看看 Sigmoid 函数和逻辑回归方程的公式:
以下公式展示了逻辑回归方程:
log()
函数中的项称为赔率比或“赔率”。赔率比是事件发生的概率与事件不发生的概率之比。在下图中,您可以看到逻辑回归输出的表现:
我们可以看到这里的比率大致在 0.5 左右。接下来我们将在下面的小节中进一步探讨逻辑回归。
逻辑回归模型的特点
在本小节中,我们将重点讨论逻辑回归的基本特征和假设。让我们了解以下特征:
-
因变量或目标变量应为二元类型。
-
自变量之间不应存在多重共线性。
-
系数通过最大似然法估计。
-
逻辑回归遵循伯努利分布。
-
该模型没有 R 平方值用于评估。模型通过一致性和 KS 统计量进行评估。
逻辑回归算法类型
不同的逻辑回归算法适用于不同的使用场景和情境。在本节中,我们将重点讨论二项逻辑回归、多项逻辑回归和顺序逻辑回归。让我们逐一了解这些模型,并理解它们的应用场景:
- 二项逻辑回归模型:
在二项逻辑回归模型中,因变量或目标列只有两个取值,例如贷款是否违约、电子邮件是否为垃圾邮件,或者患者是否为糖尿病患者。
- 多项逻辑回归模型:
在多项逻辑回归模型中,因变量或目标列有三个或更多的取值,例如预测鸢尾花的种类,或预测新闻文章的类别,如政治、商业和体育。
- 顺序逻辑回归:
在顺序逻辑回归模型中,因变量将具有顺序或序列类别,例如电影和酒店评分。
逻辑回归的优缺点
逻辑回归模型不仅提供预测(0 或 1),还给出结果的概率,这有助于我们理解预测的置信度。它易于实现和理解,并且具有可解释性。
如果自变量数量过多,将增加解释的方差,这会导致模型过拟合。逻辑回归无法处理非线性关系,并且在特征变量(或自变量)高度相关时表现不佳。
使用 scikit-learn 实现逻辑回归
现在您已经了解了逻辑回归的所有内容,接下来让我们使用 scikit-learn
库在 Python 中实现它。我们将使用朴素贝叶斯分类来创建模型。我们将通过以下步骤进行:
- 我们将首先导入数据集和所需的库,使用以下代码:
# Import libraries
import pandas as pd
# read the dataset
diabetes = pd.read_csv("diabetes.csv")
# Show top 5-records
diabetes.head()
这将产生以下输出:
在前面的示例中,我们正在读取 Pima Indians 糖尿病数据集。此数据集没有列名,因此我们需要手动添加列名。
- 在
read_csv()
函数中,我们将header
设置为None
,并将之前创建的列名列表传递给names
参数:
# Split dataset in two parts: feature set and target label
feature_set = ['pregnant', 'insulin', 'bmi', 'age','glucose','bp','pedigree']
features = diabetes[feature_set]
target = diabetes.label
# Partition data into training and testing set
from sklearn.model_selection import train_test_split
feature_train, feature_test, target_train, target_test = train_test_split(features, target, test_size=0.3, random_state=1)
加载数据集后,我们需要将数据集分为独立(特征集)列特征和依赖(或标签)列目标。之后,数据集将被划分为训练集和测试集。现在,依赖列和独立列将使用train_test_split()
函数分为训练集和测试集(feature_train
、feature_test
、target_train
和target_test
)。train_test_split()
接受依赖和独立的 DataFrame,test_size
和random_state
。其中,test_size
决定训练-测试数据集的划分比例(例如,test_size
为0.3
表示 30%的数据用于测试集,剩余的 70%用于训练集),而random_state
用作种子值,以确保每次划分的数据集相同。如果random_state
为None
,则每次都会随机划分记录,可能会得到不同的性能指标:
# import logistic regression scikit-learn model
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# instantiate the model
logreg = LogisticRegression(solver='lbfgs')
# fit the model with data
logreg.fit(feature_train,target_train)
# Forecast the target variable for given test dataset
predictions = logreg.predict(feature_test)
# Assess model performance using accuracy measure
print("Logistic Regression Model Accuracy:",accuracy_score(target_test, predictions))
结果如下所示:
Logistic Regression Model Accuracy: 0.7835497835497836
现在,我们准备好创建一个逻辑回归模型。首先,我们将导入LogisticRegression
类并创建其对象或模型。该模型将在训练数据集(X_train
和y_train
)上进行训练。训练完成后,模型即可使用predict()
方法进行预测。scikit-learn 的metrics
类提供了多种性能评估方法,如准确率。accuracy_score()
方法将接受实际标签(y_test
)和预测标签(y_pred
)。
总结
在本章中,我们探讨了回归分析算法。这将帮助你获得进行预测数据分析的重要技能。你已经掌握了回归分析、多重共线性、虚拟变量、回归评估指标和逻辑回归等概念。本章从简单线性回归和多重回归开始,之后我们的主要重点是多重共线性、模型开发和模型评估指标。在后续部分,我们重点介绍了逻辑回归、回归的特点、回归类型及其实现。
下一章,第十章,监督学习 – 分类技术,将重点讨论分类及其技术、训练-测试数据集划分策略和性能评估指标。在后续部分,我们将重点介绍数据划分、混淆矩阵以及性能评估指标,如准确率、精确度、召回率、F1 分数、ROC 和 AUC。
第十一章:监督学习 - 分类技术
大多数现实世界中的机器学习问题都使用监督学习。在监督学习中,模型将从一个带标签的训练数据集中学习。标签是我们想要预测的目标变量。它是一个额外的信息,帮助做出决策或预测,例如,哪个贷款申请是安全的或有风险的,患者是否患有某种疾病,房价,以及信用资格分数。这些标签作为学习过程中的监督者或老师。监督学习算法可以分为两种类型:分类或回归。分类问题有一个分类目标变量,例如,贷款申请状态是安全的还是有风险的,患者是否患有“疾病”或“无疾病”,或顾客是否是“潜在的”或“非潜在的”。
本章重点介绍监督学习,特别是分类技术。本章将主要使用 scikit-learn。将深入讨论分类的基本技术,如朴素贝叶斯、支持向量机(SVM)、K-最近邻(KNN)和决策树。此外,还重点介绍训练集与测试集的划分策略以及模型评估方法和参数。
本章的主题如下:
-
分类
-
朴素贝叶斯分类
-
决策树分类
-
KNN 分类
-
SVM 分类
-
划分训练集和测试集
-
评估分类模型的表现
-
ROC 曲线与 AUC
技术要求
本章的技术要求如下:
-
你可以在以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter10
。 -
所有代码块都在
ch10.ipynb
文件中。 -
本章仅使用一个 CSV 文件(
diabetes.csv
)进行练习。 -
在本章中,我们将使用
pandas
和scikit-learn
这两个 Python 库。
分类
作为一名医疗数据分析师,你的工作是识别那些患某种特定疾病(例如糖尿病或癌症)可能性较高的患者或病人。这些预测将帮助你在疾病发生之前对患者进行治疗。同样,销售和市场经理也希望预测那些有更高可能性购买产品的潜在客户。这就是将客户分类为两个或多个类别的过程,称为分类。分类模型预测类别标签,例如客户是否是潜在客户。在分类过程中,模型基于现有数据进行训练,做出预测,并评估模型的表现。开发出来的模型称为分类器。这意味着分类模型有三个阶段:训练、预测和评估。训练好的模型通过准确率、精确度、召回率、F1 分数和曲线下面积(AUC)等参数来评估。分类在许多领域中有广泛的应用,如银行、金融、公共服务、医疗保健、文本分析、图像识别和物体检测等。
作为分析师,你首先需要定义你想通过分类解决的问题,然后确定能够准确预测标签的潜在特征。特征是负责预测的列或属性。在糖尿病预测问题中,健康分析师会收集患者的信息,例如年龄、锻炼习惯、垃圾食品摄入习惯、酒精消费和吸烟习惯等特征。这些特征将用于预测患者是否会患上糖尿病。你可以在下面的图示中看到,如何通过一条线将数据分类为两个类别:
机器学习和数据挖掘过程有多个步骤:数据收集、数据预处理、训练-测试拆分、模型生成和评估。我们已经看到了像 KDD、SEMMA 和 CRISP-DM 这样的数据分析模型。在分类中,我们只关注训练-测试拆分、模型生成和评估这几个步骤。
分类模型有三个阶段:训练-测试拆分、模型生成和模型评估。在训练-测试拆分阶段,数据被分为两部分:训练集和测试集。在训练阶段,使用训练集来生成模型,而在评估阶段,使用测试集来评估模型的表现,通过准确率、误差、精确度和召回率等评估指标。你可以在以下图示中看到分类过程:
在上面的图示中,展示了分类过程的各个步骤。现在我们已经理解了分类过程,接下来是学习分类技术。在下一节中,我们将重点介绍朴素贝叶斯分类算法。
朴素贝叶斯分类
朴素贝叶斯是一种基于贝叶斯定理的分类方法。贝叶斯定理以其发明者、统计学家托马斯·贝叶斯命名。它是一种快速、准确、稳健、易于理解和解释的技术。它还可以在大数据集上更快速地工作。朴素贝叶斯广泛应用于文本挖掘,如文档分类、客户评论情感预测和垃圾邮件过滤等。
朴素贝叶斯分类器被称为“朴素”,因为它假设类条件独立。类条件独立意味着每个特征列独立于其他特征。例如,在确定一个人是否患有糖尿病时,可能依赖于他们的饮食习惯、锻炼习惯、职业性质和生活方式。即使特征之间存在相关性或依赖关系,朴素贝叶斯仍然假设它们是独立的。让我们理解贝叶斯定理的公式:
这里,y 是目标,X 是特征集。p(y) 和 p(X) 是不考虑证据的先验概率。这意味着在看到证据之前事件的概率。p(y|X) 是在看到证据 X 后事件 X 的后验概率。它是给定证据 X 的 y 的概率。p(X|y) 是在看到证据后事件 y 的后验概率。它是给定证据 y 的 X 的概率。我们来看看前面的方程示例:
在这里,我们使用贝叶斯定理找出一个患者是否会因吸烟频率而患糖尿病的概率。
让我们看看朴素贝叶斯分类算法的工作原理。假设数据集 D 具有 X 特征和标签 y。特征可以是 n 维的,X = X1, X2, X3... Xn。标签 y 可能有 m 个类,C1, C2, C3... Cm。它将按以下方式工作:
-
计算给定类标签的先验概率,
和
。
-
计算后验概率,
和
,每个类的每个属性:
- 乘以相同类的后验概率,
:
-
如果属性是分类的,那么应该有多个类
在具有
值的记录中,除以数据集中的
记录数。
-
如果属性是连续的,则使用高斯分布进行计算:
- 将先验概率 p(y) 乘以步骤 3中的后验概率:
- 找到给定输入特征集的最大概率类。这个类将作为我们的最终预测。
现在,让我们使用 Python 中的朴素贝叶斯分类创建一个模型:
- 使用以下代码行加载 Pima 印度糖尿病数据集(
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/blob/master/Chapter09/diabetes.csv
):
# Import libraries
import pandas as pd
# read the dataset
diabetes = pd.read_csv("diabetes.csv")
# Show top 5-records
diabetes.head()
这将产生以下输出:
因此,我们已经导入了pandas
并读取了数据集。在之前的示例中,我们正在读取 Pima 印度糖尿病数据集。
- 我们现在将数据集拆分成两部分,如下所示:
# split dataset in two parts: feature set and target label
feature_set = ['pregnant', 'insulin', 'bmi', 'age','glucose','bp','pedigree'] features = diabetes[feature_set]
target = diabetes.label
# partition data into training and testing set
from sklearn.model_selection import train_test_split
feature_train,feature_test, target_train, target_test = \
train_test_split(features, target, test_size=0.3, random_state=1)
加载数据集后,我们将数据集分为依赖列或标签列(target
)和独立列或特征列(feature_set
)。之后,数据集将被拆分为训练集和测试集。现在,依赖列和独立列将通过train_test_split()
被拆分为训练集和测试集(feature_train
、feature_test
、target_train
和target_test
)。train_test_split()
接受依赖和独立的 DataFrame,test_size
和random_state
。其中,test_size
决定了训练集和测试集的比例(例如,test_size 0.3
意味着 30%是测试集,剩余的 70%数据是训练集),random_state
用作种子值,用于每次重新生成相同的数据拆分。如果random_state
为None
,则每次都会随机拆分记录,这会导致不同的性能度量。
- 我们现在将构建朴素贝叶斯分类模型:
# Import Gaussian Naive Bayes model
from sklearn.naive_bayes import GaussianNB
# Create a Gaussian Classifier
model = GaussianNB()
# Train the model using the training sets
model.fit(feature_train,target_train)
# Forecast the target variable for given test dataset
predictions = model.predict(feature_test)
在这里,我们创建了一个朴素贝叶斯模型。首先,我们将导入GaussianNB
类并创建其对象或模型。这个模型将根据训练数据集(feature_train
、target_train
)进行拟合。训练完成后,模型准备好使用predict()
方法进行预测。
- 最后,我们将评估模型的性能:
# Import metrics module for performance evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# Calculate model accuracy
print("Accuracy:",accuracy_score(target_test, predictions))
# Calculate model precision
print("Precision:",precision_score(target_test, predictions))
# Calculate model recall
print("Recall:",recall_score(target_test, predictions))
# Calculate model f1 score
print("F1-Score:",f1_score(target_test, predictions))
这将产生以下输出:
Accuracy: 0.7748917748917749
Precision: 0.7391304347826086
Recall: 0.6
F1-Score: 0.6623376623376623
scikit-learn 的metrics
类提供了多种性能评估方法,例如准确率、精确率、召回率和 F1 分数。这些方法将使用实际目标标签(target_test
)和预测标签(predictions
)。我们将在评估分类模型性能部分详细了解这些指标。
朴素贝叶斯是一种简单、快速、准确且易于理解的预测方法。它具有较低的计算成本,可以处理大型数据集。朴素贝叶斯还可以用于多类分类问题。当数据具有类独立性假设时,朴素贝叶斯分类器比逻辑回归表现更好。
朴素贝叶斯存在 零频率问题。零频率意味着如果某个特征的类别缺失,则该类别的频率计数为零。这个问题可以通过拉普拉斯修正来解决。拉普拉斯修正(或拉普拉斯变换)是一种平滑技术,会为每个类别添加一条记录,使得缺失类别的频率计数变为 1,从而不会影响贝叶斯定理中的概率。朴素贝叶斯的另一个问题是其假设类条件独立性,因为在实际情况中,所有预测因子完全独立几乎是不可能的。在这一部分,我们已经学习了朴素贝叶斯分类法。现在是时候学习决策树分类算法了。
决策树分类
决策树是最著名的分类技术之一。它可以应用于两种类型的监督学习问题(分类问题和回归问题)。决策树是一种类似流程图的树形结构,模仿人类思维方式,这使得它更容易理解和解释。与支持向量机(SVM)和神经网络等“黑箱”算法不同,决策树能让你看到预测背后的逻辑。
决策树有三个基本组件:内部节点、分支和叶子节点。在这里,每个终端节点代表一个特征,链接代表决策规则或分割规则,叶子节点提供预测结果。树中的第一个起始节点或主节点是根节点。它根据特征或属性值来划分数据。在这里,我们分割数据,然后递归地再次划分剩余数据,直到所有项属于同一类别或没有剩余列。决策树可以应用于分类和回归问题。市面上有许多决策树算法,例如 CART、ID3、C4.5 和 CHAID。但在这里,我们主要关注 CART 和 ID3,因为在 scikit-learn 中,这两种算法是可用的。让我们在下图中看看决策树分类器的生成过程:
CART 代表 分类与回归树。CART 使用基尼指数来选择最佳列。基尼指数是每个类别的概率平方和与 1 的差值。具有最小基尼指数值的特征或列被选为分割或划分特征。基尼指数的值范围在 0 和 1 之间。如果基尼指数值为 0,表示所有项属于同一类;如果基尼指数值为 1,则表示所有元素是随机分布的。基尼指数值为 0.5 表示项在多个类别中均匀分布:
ID3 代表 Iterative Dichotomiser 3(迭代二分法 3)。它使用信息增益或熵作为属性选择度量。熵由香农提出,用于衡量数据集中的杂乱度或随机性。信息增益衡量在特定列的分割前后,熵的变化量。具有最大信息增益值的特征或属性将被选择为分裂特征或属性。如果熵为 0,表示只有一个类;如果熵为 1,则表示项目均匀分布:
决策树非常直观,易于理解、解释并向利益相关者讲解。无需对特征进行标准化,并且是无分布假设的算法。决策树也可以用来预测缺失值。它们具备捕捉非线性模式的能力。决策树可能会过拟合,并且对噪声数据敏感。决策树在数据不平衡时有偏差,这也是在应用决策树之前,我们需要平衡数据集的原因。决策树在时间和复杂度上更为昂贵。
让我们使用 scikit-learn 构建决策树并执行预测数据集。之后,我们将准备好构建模型:
-
首先,您需要导入
pandas
并使用我们在上一部分中看到的read_csv()
方法加载 Pimas 数据集。 -
接下来,我们需要将数据集划分为训练集和测试集,类似于我们在前一部分中执行的操作。
-
现在,我们将构建决策树分类模型:
# Import Decision Tree model
from sklearn.tree import DecisionTreeClassifier
# Create a Decision Tree classifier object
clf = DecisionTreeClassifier()
# Train the model using training dataset
clf = clf.fit(feature_train,target_train)
# Predict the response for test dataset
predictions = clf.predict(feature_test)
这里,我们创建了一个决策树模型。首先,我们将导入 DecisionTreeClassifier
类并创建其对象或模型。该模型将在训练数据集(feature_train
,target_train
)上进行拟合。训练完成后,模型已准备好使用 predict()
方法进行预测。
- 现在,我们将评估模型的性能:
# Import metrics module for performance evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# Calculate model accuracy
print("Accuracy:",accuracy_score(target_test, predictions))
# Calculate model precision
print("Precision:",precision_score(target_test, predictions))
# Calculate model recall
print("Recall:",recall_score(target_test, predictions))
# Calculate model f1 score
print("F1-Score:",f1_score(target_test, predictions))
这将输出以下结果:
Accuracy: 0.7229437229437229
Precision: 0.6438356164383562
Recall: 0.5529411764705883
F1-Score: 0.5949367088607594
在上述示例中,模型性能通过准确度、精确度、召回率和 F1 分数进行评估。
在完全理解决策树之后,让我们继续进行 KNN 分类。
KNN 分类
KNN 是一种简单、易于理解和实现的分类算法。它也可以用于回归问题。KNN 可用于许多应用场景,如项目推荐和分类问题。具体来说,它可以在 Netflix 上推荐电影,在 Medium 上推荐文章,在 naukari.com 上推荐候选人,在 eBay 上推荐产品,在 YouTube 上推荐视频。在分类中,它可以用于分类实例,例如银行机构可以对风险较大的贷款候选人进行分类,或者政治学家可以对潜在选民进行分类。
KNN 有三个基本属性,分别是非参数性、懒惰学习者和基于实例的学习。非参数性意味着算法不依赖于分布,因此不需要像均值和标准差这样的参数。懒惰学习者意味着 KNN 不进行模型训练;也就是说,模型是在测试阶段进行训练的。这使得训练速度更快,但测试速度较慢,并且也更加耗时和占用内存。基于实例的学习意味着预测结果是基于与其最近邻的相似度。它不会为预测创建抽象的方程或规则,而是存储所有数据并查询每个记录。
KNN 分类算法从训练数据集中找到 k 个最相似的实例,且多数决定了给定输入特征的预测标签。KNN 分类器将执行以下步骤来进行预测:
-
计算输入观察与训练数据集中所有观察的距离。
-
通过按距离对所有实例进行升序排序,找到 K 个最接近的邻居。
-
对 K 个最接近的邻居进行投票,并预测获得最多票数的标签。
这一过程可以通过以下图示来更好地展示:
让我们使用 scikit-learn 构建一个 KNN 分类器,并对数据集进行预测:
- 加载皮马印第安糖尿病数据集。
首先,您需要导入 pandas
并使用我们在 朴素贝叶斯分类 课程中已经看到的 read_csv()
方法加载数据集。
- 划分数据集。
之后,我们需要将数据集分成两部分——训练集和测试集——就像我们在 朴素贝叶斯分类 部分所做的那样。
- 构建 KNN 分类模型。
现在,我们准备开始构建模型:
# Import KNN model
from sklearn.neighbors import KNeighborsClassifier
# Create a KNN classifier object
model = KNeighborsClassifier(n_neighbors=3)
# Train the model using the training dataset
model.fit(feature_train,target_train)
# Predict the target variable for test dataset
predictions = model.predict(feature_test)
在前面的代码块中,我们导入了 KNeighborsClassifier
类并创建了它的对象或模型。在这里,我们将 3 个邻居作为模型的输入参数。如果我们没有指定邻居的数量,模型将默认选择 5 个邻居。该模型将拟合训练数据集(feature_train
, target_train
)。训练完成后,模型已准备好使用 predict()
方法进行预测。
- 评估模型性能:
# Import metrics module for performance evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# Calculate model accuracy
print("Accuracy:",accuracy_score(target_test, predictions))
# Calculate model precision
print("Precision:",precision_score(target_test, predictions))
# Calculate model recall
print("Recall:",recall_score(target_test, predictions))
# Calculate model f1 score
print("F1-Score:",f1_score(target_test, predictions))
这将产生以下输出:
Accuracy: 0.7532467532467533
Precision: 0.7058823529411765
Recall: 0.5647058823529412
F1-Score: 0.6274509803921569
在前面的示例中,模型性能通过准确率、精确度、召回率和 F1 分数进行评估。
在理解了 KNN 分类算法之后,接下来是学习 SVM 分类算法。
SVM 分类
由于其较少的计算资源需求和较高的准确性,SVM 是许多数据科学家最喜欢的机器学习算法。它们被用于回归和分类问题,并提供了核技巧来建模非线性关系。SVM 有多种应用场景,例如入侵检测、文本分类、人脸识别和手写识别。
SVM 是一个判别模型,它在 n 维空间中生成具有大间隔的最优超平面来分隔数据点。其基本思想是发现最大间隔超平面(MMH),该超平面完美地将数据分成不同类别。最大间隔意味着两类数据点之间的最大距离。
术语
现在,我们将探讨一些与 SVM 分类相关的术语:
-
超平面:超平面是用来区分两类的决策边界。超平面的维度由特征的数量决定。它也被称为决策平面。
-
支持向量:支持向量是距离超平面最近的点,它们通过最大化间隔来帮助确定超平面的方向。
-
间隔:间隔是距离最近点的最大距离。间隔越大,分类效果越好。间隔可以通过从支持向量线到超平面的垂直距离来计算。
SVM 的核心目标是选择具有最大可能边界的超平面,边界由支持向量之间的距离决定。SVM 通过以下两个阶段来找到 MMH:
-
创建超平面,以最佳方式分隔数据点。
-
选择具有最大间隔的超平面:
与朴素贝叶斯相比,SVM 算法是一种更快、更准确的分类器。它在较大的分隔间隔下表现更好。SVM 不适用于大规模数据集。其性能也依赖于所使用的核函数类型。对于重叠类,它的表现较差。
让我们使用scikit-learn
进行支持向量分类器的工作,并执行一个预测数据集。完成后,我们将数据集划分为训练集和测试集,就像我们在朴素贝叶斯分类部分所做的那样。接下来,我们准备好进行模型构建:
- 加载皮马印第安人糖尿病数据集。
首先,你需要导入pandas
并使用我们在朴素贝叶斯分类环节中已经看到的read_csv()
方法加载数据集。
- 划分数据集。
完成后,我们需要将数据集拆分为两个集合——训练集和测试集——就像我们在朴素贝叶斯分类部分所做的那样。
- 构建 SVM 分类模型。
现在,我们已经准备好开始模型构建:
# Import SVM model
from sklearn import svm
# Create a SVM classifier object
clf = svm.SVC(kernel='linear')
# Train the model using the training sets
clf.fit(feature_train,target_train)
# Predict the target variable for test dataset
predictions = clf.predict(feature_test)
在上述代码块中,我们将导入 svm
模块并创建其 svm.SVC()
对象或模型。 这里我们传递了 linear
核。 您还可以传递另一个核,例如 poly
,rbf
或 sigmoid
。 如果我们不指定核心,则默认选择 rbf
作为核心。 线性核将创建一个线性超平面来区分糖尿病患者和非糖尿病患者。 该模型将适合于训练数据集(feature_train
,target_train
)。 训练后,模型已准备使用 predict()
方法进行预测。
- 评估模型的性能:
# Import metrics module for performance evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# Calculate model accuracy
print("Accuracy:",accuracy_score(target_test, predictions))
# Calculate model precision
print("Precision:",precision_score(target_test, predictions))
# Calculate model recall
print("Recall:",recall_score(target_test, predictions))
# Calculate model f1 score
print("F1-Score:",f1_score(target_test, predictions))
这将导致以下输出:
Accuracy: 0.7835497835497836
Precision: 0.7868852459016393
Recall: 0.5647058823529412
F1-Score: 0.6575342465753424
在上述示例中,将使用准确性、精确度、召回率和 F1 分数等指标评估模型的性能。 理解所有这些分类器后,现在是时候看看训练和测试集分割策略了。
分割训练和测试集
数据科学家需要评估模型的性能,克服过拟合并调整超参数。 所有这些任务都需要一些未在模型开发阶段使用的隐藏数据记录。 在模型开发之前,数据需要分成一些部分,例如训练、测试和验证集。 训练数据集用于构建模型。 测试数据集用于评估在训练集上训练的模型的性能。 验证集用于找到超参数。 让我们看看下面的训练-测试分割策略:
-
保留样本法
-
K 折交叉验证
-
Bootstrap 方法
保留样本法
在此方法中,数据集被随机分成两部分:训练集和测试集。 通常,这个比例是 2:1,即 2/3 用于训练,1/3 用于测试。 我们还可以将其分割成不同的比例,如 6:4、7:3 和 8:2:
# partition data into training and testing set
from sklearn.model_selection import train_test_split
# split train and test set
feature_train, feature_test, target_train, target_test = train_test_split(features, target, test_size=0.3, random_state=1)
在上述示例中,test_size=0.3
表示测试集占 30%,训练集占 70%。 train_test_split()
将数据集分割为 7:3。
K 折交叉验证
在此方法中,数据分为大致相同大小的 k 个分区。 它将训练 k 个模型,并使用每个分区进行评估。 在每次迭代中,一个分区将保留用于测试,其余的 k 个分区将集体用于训练。 分类准确度将是所有准确度的平均值。 它还确保模型不会过拟合:
在分层交叉验证中,k 个分区大致相同的类分布。 这意味着它保留每个分区中每个类的百分比。
Bootstrap 方法
自助法是一种重采样技术。它从数据集中反复进行有放回的抽样。有放回的抽样会进行随机选择。它需要样本的大小和迭代次数。在每次迭代中,它会均匀选择记录。每条记录被选择的机会相同。未被选择的样本称为“袋外”样本。我们可以通过以下图表来理解自助法:
在上面的图表中,我们可以看到每个元素在每次自助采样中都有相同的选择机会。接下来,我们将跳到分类的另一个重要话题——分类模型评估。下一个话题帮助我们评估分类模型的表现。
评估分类模型的表现
到目前为止,我们已经学习了如何创建分类模型。创建机器学习分类模型还不够;作为业务或数据分析师,你还需要评估它的表现,以便可以将其部署到实际项目中。
scikit-learn 提供了各种度量标准,如混淆矩阵、准确率、精确度、召回率和 F1 分数,用于评估模型的表现。
混淆矩阵
混淆矩阵是一种简要说明二分类和多分类问题预测结果的方法。假设我们要找出一个人是否患有糖尿病。混淆矩阵的概念是找出正确预测和错误预测的数量,然后将它们进一步总结并分开到每个类别中。它阐明了与我们分类模型表现相关的所有混淆信息。这个 2x2 矩阵不仅显示了分类器所犯的错误,还展示了犯的是什么类型的错误。混淆矩阵用于更快速地完成统计数据分析,并通过清晰的数据可视化使结果更具可读性和易理解性。它包含两行两列,如下所示。让我们理解混淆矩阵的基本术语:
-
真阳性 (TP):这代表预测为
是
,且实际情况也是是
的案例;例如,我们预测它们为欺诈案例,实际上它们确实是欺诈案例。 -
真阴性 (TN):这代表预测为
不是
,且实际情况也是不是
的案例;例如,我们预测它们为非欺诈案例,实际上它们确实是非欺诈案例。 -
假阳性 (FP):这代表预测为
是
,但实际情况是不是
的案例;例如,我们预测它们为欺诈案例,但实际上它们不是欺诈案例。这种类型的事件类别表示类型 I 错误。 -
假阴性(FN):这表示那些预测为
否
,但实际上是否
的案例;例如,我们预测它们为非欺诈案件,实际上它们是欺诈案件。这种类型的事件类别表示的是第二类错误。
让我们以一个欺诈检测问题为例:
在前面的例子中,我们考虑了两类欺诈行为:是和否。是表示欺诈行为,否表示非欺诈行为。预测记录的总数为 825,这意味着测试了 825 笔交易。在这 825 个案例中,模型或分类器预测了 550 次是,275 次否。实际上,实际的欺诈案件为 525 个,非欺诈案件为 300 个。
让我们使用 scikit-learn 在 Python 中创建一个混淆矩阵:
# Import libraries
import pandas as pd
# read the dataset
diabetes = pd.read_csv("diabetes.csv")
# split dataset in two parts: feature set and target label
feature_set = ['pregnant', 'insulin', 'bmi', 'age','glucose','bp','pedigree']
features = diabetes[feature_set]
target = diabetes.label
# partition data into training and testing set
from sklearn.model_selection import train_test_split
feature_train, feature_test, target_train, target_test = train_test_split(features, target, test_size=0.3, random_state=1)
# import logistic regression scikit-learn model
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score # for performance evaluation
# instantiate the model
logreg = LogisticRegression(solver='lbfgs')
# fit the model with data
logreg.fit(feature_train,target_train)
# Forecast the target variable for given test dataset
predictions = logreg.predict(feature_test)
# Get prediction probability
predictions_prob = logreg.predict_proba(feature_test)[::,1]
# Import the confusion matrix
from sklearn.metrics import plot_confusion_matrix
# Plot Confusion matrix
plot_confusion_matrix(logreg , feature_test, target_test, values_format='d')
这将产生以下输出:
在前面的例子中,我们加载了数据并将其分为两个部分:训练集和测试集。之后,我们使用逻辑回归进行了模型训练,正如在前一章中所做的那样。在这里,为了绘制混淆矩阵,我们使用了plot_confusion_matrix()
方法,传入了模型对象、测试特征集、测试标签集和values_format
参数。
准确率
现在,我们将计算从混淆矩阵得出的模型准确率。它告诉我们预测模型的准确性:
精确度
当模型预测为是
时,它的正确率有多高?这是数据集中总预测案例中正类案例的百分比。简而言之,我们可以将精确度理解为“当模型说它正确时,它到底有多正确”:
召回率
当实际为是
时,模型预测是
的频率是多少?这也被称为灵敏度。它是数据集中所有实际正类案例中,预测为正类的比例:
F-measure(F 值)
F-measure 被认为是评估模型的较好方式之一。在数据科学的许多领域,竞争模型的性能都是通过 F-measure 来评估的。它是精确度和召回率的调和均值。F1 分数的值越高,模型越好。F1 分数为精确度和召回率赋予相等的权重,这意味着它表示两者之间的平衡:
F-measure 的一个缺点是它对精确度和召回率赋予相同的权重,但在某些情况下,需要其中一个高于另一个,这也是为什么 F1 分数可能不是一个准确的度量标准。
在前面的章节中,我们看到了诸如朴素贝叶斯、决策树、KNN 和 SVM 等分类算法。我们通过使用 scikit-learn 的 accuracy_score()
来评估模型准确率,precision_score()
来评估模型精度,recall_score()
来评估模型召回率,以及 f1_score()
来评估模型的 F1 分数。
我们还可以打印分类报告,深入了解分类模型的细节。让我们创建混淆报告:
# import classification report
from sklearn.metrics import classification_report
# Create classification report
print(classification_report(target_test, predictions, target_names=['Yes(1)','No(0)']))
这将产生以下输出:
在前面的代码中,我们使用 confusion_report()
方法打印了混淆矩阵报告,传入了测试集标签、预测集或预测标签以及目标值列表参数。
ROC 曲线和 AUC
AUC-ROC 曲线是衡量和评估分类模型性能的工具。ROC(接收者操作特性)是模型性能的图示化表示。它绘制了 FP 率(或 1-特异性)与 TP 率(或灵敏度)之间的二维概率图。我们还可以使用 AUC 用一个数字表示模型所覆盖的区域:
让我们使用 scikit-learn 模块创建 ROC 曲线:
# import plot_roc_curve
from sklearn.metrics import plot_roc_curve
plot_roc_curve(logreg , feature_test, target_test)
这将产生以下输出:
在前面的示例中,我们使用模型对象、测试特征集和测试标签集参数绘制了 plot_roc_curve()
方法的 ROC 图。
在 ROC 曲线中,AUC 是一种可分性度量。它告诉我们模型的类区分能力。AUC 值越高,模型在区分“欺诈”和“非欺诈”方面就越好。对于理想的分类器,AUC 等于 1:
让我们计算 AUC 分数,如下所示:
# import ROC AUC score
from sklearn.metrics import roc_auc_score
# Compute the area under ROC curve
auc = roc_auc_score(target_test, predictions_prob)
# Print auc value
print("Area Under Curve:",auc)
这将产生以下输出:
Area Under Curve: 0.8628525382755843
scikit-learn 的 metrics
类提供了 AUC 性能评估度量。roc_auc_score()
方法将接受实际标签(y_test
)和预测概率(y_pred_prob
)。
总结
在本章中,我们学习了分类及其技术、训练-测试划分策略和性能评估指标。这将帮助你掌握预测数据分析的重要技能。你已经学会了如何使用 scikit-learn 开发线性和非线性分类器进行预测分析。在本章的前面部分,你了解了分类基础和机器学习算法,例如朴素贝叶斯分类、决策树分类、KNN 和 SVM。后续章节中,你看到了数据划分方法和模型性能评估指标,例如准确度分数、精度分数、召回率、F1 分数、ROC 曲线和 AUC 分数。
下一章,第十一章,无监督学习——主成分分析与聚类,将重点讲解无监督机器学习技术和降维技术在 Python 中的应用。章节开始介绍降维和主成分分析。在接下来的部分中,将聚焦于聚类方法,如 k-means、层次聚类、DBSCAN 和谱聚类。
第十二章:无监督学习 - PCA 和聚类
无监督学习是机器学习中最重要的分支之一。它使我们能够在没有目标标签的情况下进行预测。在无监督学习中,模型仅通过特征进行学习,因为数据集没有目标标签列。大多数机器学习问题从某些能够自动化过程的事物开始。例如,当你想要开发一个预测模型来检测糖尿病患者时,你需要为数据集中的每个患者设置目标标签。在初期阶段,为任何机器学习问题安排目标标签并非易事,因为这需要改变业务流程来获得标签,无论是通过手动内部标注还是再次收集带标签的数据。
在本章中,我们的重点是学习无监督学习技术,这些技术可以处理没有目标标签的情况。我们将特别介绍降维技术和聚类技术。当我们有大量特征时,降维技术将被使用,以减少这些特征的数量。这将减少模型复杂性和训练成本,因为这意味着我们可以仅通过少量特征就能实现我们想要的结果。
聚类技术根据相似性在数据中找到组。这些组本质上代表了无监督分类。在聚类中,特征观察的类或标签是以无监督的方式找到的。聚类在各种业务操作中非常有用,例如认知搜索、推荐、细分和文档聚类。
本章的主题如下:
-
无监督学习
-
降低数据的维度
-
主成分分析
-
聚类
-
使用 K-means 聚类对数据进行划分
-
层次聚类
-
DBSCAN 聚类
-
谱聚类
-
评估聚类性能
技术要求
本章有以下技术要求:
-
你可以通过以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter11
。 -
所有代码块都可以在
ch11.ipynb
文件中找到。 -
本章仅使用一个 CSV 文件(
diabetes.csv
)进行实践。 -
在本章中,我们将使用 pandas 和 scikit-learn Python 库。
无监督学习
无监督学习意味着通过观察学习,而不是通过示例学习。这种学习类型适用于无标签的数据。降维和聚类就是这种学习的例子。降维用于将大量特征减少到只有少数几个特征,但能产生相同的结果。有几种方法可以减少数据的维度,例如主成分分析(PCA)、t-SNE、小波变换和特征子集选择。
术语“聚类”指的是一组相似的项目,它们彼此密切相关。聚类是一种生成相似单元或项目组的方法。此相似性是基于项目的某些特征或特性计算的。我们可以说,聚类是一组数据点,它们与其聚类中的其他数据点相似,并且与其他聚类的数据点不相似。聚类具有许多应用,例如搜索文档、业务智能、信息安全和推荐系统:
在上图中,我们可以看到聚类如何将数据记录或观察结果分成少数几组,而降维则减少了特征或属性的数量。让我们在接下来的部分详细讨论每个主题。
减少数据的维度
减少维度,即降维,意味着将大量属性或列(特征)缩减为较少数量的属性。该技术的主要目标是获得最佳的特征数用于分类、回归和其他无监督方法。在机器学习中,我们面临一个称为维度灾难的问题。这意味着有大量属性或特征。这意味着更多的数据,导致复杂的模型和过拟合问题。
降低数据的维度有助于应对维度灾难。它可以线性和非线性地转换数据。线性转换技术包括 PCA、线性判别分析和因子分析。非线性转换包括 t-SNE、Hessian 特征映射、谱嵌入和等距特征映射等技术。降维提供以下好处:
-
过滤冗余和不重要的特征。
-
减少模型复杂性,使用较少维度的数据。
-
减少模型生成的内存和计算成本。
-
它可视化高维数据。
在接下来的部分中,我们将专注于一种重要且流行的降维技术之一,PCA。
PCA
在机器学习中,认为拥有大量数据意味着拥有预测模型的高质量,但大型数据集也带来了更高维度的挑战(或维度灾难)。由于属性数量众多,这导致了预测模型复杂度的增加。PCA 是最常用的降维方法,帮助我们识别原始数据集中的模式和相关性,将其转换为一个低维数据集,同时不丢失信息。
PCA 的主要概念是发现原始数据集中属性之间未见的关系和关联。高度相关的属性是如此相似,以至于它们是冗余的。因此,PCA 去除了这些冗余的属性。例如,如果我们的数据中有 200 个属性或列,那么面对这么多属性时,我们将难以继续处理。在这种情况下,我们需要将这些属性的数量减少到 10 或 20 个变量。PCA 的另一个目标是减少维度,同时不影响重要信息。对于p维数据,PCA 的方程可以写成如下:
主成分是所有属性的加权和。这里,是原始数据集中的属性,
是属性的权重。
让我们举个例子。假设我们将一个城市的街道作为属性,并且假设你想参观这个城市。那么问题是,你会参观多少条街道?显然,你会想参观城市中的热门或主要街道,假设这些街道是 50 条中的 10 条。这 10 条街道将为你提供对这座城市的最佳了解。这些街道就是主成分,因为它们解释了数据(城市街道)中的大部分方差。
执行 PCA
让我们从头开始在 Python 中执行 PCA:
-
计算给定数据集的相关或协方差矩阵。
-
求解相关或协方差矩阵的特征值和特征向量。
-
将特征向量矩阵与原始数据集相乘,你将得到主成分矩阵。
让我们从头开始实现 PCA:
- 我们将首先导入库并定义数据集:
# Import numpy
import numpy as np
# Import linear algebra module
from scipy import linalg as la
# Create dataset
data=np.array([[7., 4., 3.],
[4., 1., 8.],
[6., 3., 5.],
[8., 6., 1.],
[8., 5., 7.],
[7., 2., 9.],
[5., 3., 3.],
[9., 5., 8.],
[7., 4., 5.],
[8., 2., 2.]])
- 计算协方差矩阵:
# Calculate the covariance matrix
# Center your data
data -= data.mean(axis=0)
cov = np.cov(data, rowvar=False)
- 计算协方差矩阵的特征值和特征向量:
# Calculate eigenvalues and eigenvector of the covariance matrix
evals, evecs = la.eig(cov)
- 将原始数据矩阵与特征向量矩阵相乘:
# Multiply the original data matrix with Eigenvector matrix.
# Sort the Eigen values and vector and select components
num_components=2
sorted_key = np.argsort(evals)[::-1][:num_components]
evals, evecs = evals[sorted_key], evecs[:, sorted_key]
print("Eigenvalues:", evals)
print("Eigenvector:", evecs)
print("Sorted and Selected Eigen Values:", evals)
print("Sorted and Selected Eigen Vector:", evecs)
# Multiply original data and Eigen vector
principal_components=np.dot(data,evecs)
print("Principal Components:", principal_components)
这将产生如下输出:
Eigenvalues: [0.74992815+0.j 3.67612927+0.j 8.27394258+0.j]
Eigenvector: [[-0.70172743 0.69903712 -0.1375708 ]
[ 0.70745703 0.66088917 -0.25045969]
[ 0.08416157 0.27307986 0.95830278]]
Sorted and Selected Eigen Values: [8.27394258+0.j 3.67612927+0.j]
Sorted and Selected Eigen Vector: [[-0.1375708 0.69903712]
[-0.25045969 0.66088917]
[ 0.95830278 0.27307986]]
Principal Components: [[-2.15142276 -0.17311941]
[ 3.80418259 -2.88749898]
[ 0.15321328 -0.98688598]
[-4.7065185 1.30153634]
[ 1.29375788 2.27912632]
[ 4.0993133 0.1435814 ]
[-1.62582148 -2.23208282]
[ 2.11448986 3.2512433 ]
[-0.2348172 0.37304031]
[-2.74637697 -1.06894049]]
在这里,我们从头开始计算了主成分矩阵。首先,我们对数据进行了中心化并计算了协方差矩阵。计算协方差矩阵后,我们求得了特征值和特征向量。最后,我们选择了两个主成分(主成分的数量应等于特征值大于 1 的数量),并将原始数据与排序和选择的特征向量相乘。我们也可以使用 scikit-learn 库来执行 PCA。
让我们在 Python 中使用 scikit-learn 进行 PCA:
# Import pandas and PCA
import pandas as pd
# Import principal component analysis
from sklearn.decomposition import PCA
# Create dataset
data=np.array([[7., 4., 3.],
[4., 1., 8.],
[6., 3., 5.],
[8., 6., 1.],
[8., 5., 7.],
[7., 2., 9.],
[5., 3., 3.],
[9., 5., 8.],
[7., 4., 5.],
[8., 2., 2.]])
# Create and fit_transformed PCA Model
pca_model = PCA(n_components=2)
components = pca_model.fit_transform(data)
components_df = pd.DataFrame(data = components,
columns = ['principal_component_1', 'principal_component_2'])
print(components_df)
这将产生如下输出:
principal_component_1 principal_component_2
0 2.151423 -0.173119
1 -3.804183 -2.887499
2 -0.153213 -0.986886
3 4.706518 1.301536
4 -1.293758 2.279126
5 -4.099313 0.143581
6 1.625821 -2.232083
7 -2.114490 3.251243
8 0.234817 0.373040
9 2.746377 -1.068940
在上面的代码中,我们使用 scikit-learn 库执行了 PCA。首先,我们创建了数据集并实例化了 PCA 对象。之后,我们执行了fit_transform()
并生成了主成分。
以上就是 PCA 的内容。现在是时候了解另一个无监督学习的概念——聚类。
聚类
聚类是将相似的项目进行分组。将相似的产品分组、将相似的文章或文档分组、以及将相似的客户进行市场细分,都是聚类的例子。聚类的核心原理是最小化簇内距离并最大化簇间距离。簇内距离是同一组内数据项之间的距离,而簇间距离是不同组之间的距离。由于数据点没有标签,因此聚类是一个无监督问题。聚类有多种方法,每种方法使用不同的方式将数据点分组。下图展示了如何使用聚类将数据观察值分组:
当我们将相似的数据点进行合并时,出现的问题是如何找到两个数据点之间的相似性,从而将相似的数据对象归为同一个簇。为了衡量数据点之间的相似性或差异性,我们可以使用距离度量,如欧几里得距离、曼哈顿距离和闵可夫斯基距离:
在这里,距离公式计算的是两个 k 维向量 x[i]和 y[i]之间的距离。
现在我们知道了什么是聚类,但最重要的问题是,在对数据进行分组时,应该考虑多少个簇?这是大多数聚类算法面临的最大挑战。有很多方法可以决定簇的数量。让我们在接下来的章节中讨论这些方法。
查找簇数
在本节中,我们将重点讨论聚类算法中的最基本问题,即发现数据集中的簇数——这个问题没有明确的答案。然而,并非所有的聚类算法都需要预定义簇数。在层次聚类和 DBSCAN 聚类中,不需要定义簇数,但在 k-means、k-medoids 和谱聚类中,我们需要定义簇数。选择簇数的正确值是非常棘手的,因此我们来看看几种确定最佳簇数的方法:
-
肘部法则
-
轮廓法
让我们详细了解这些方法。
肘部法则
肘部法则是一种广为人知的确定最佳簇数的方法。在这种方法中,我们关注不同簇数下的方差百分比。该方法的核心概念是选择一个簇数,使得再增加一个簇时,方差不会发生巨大的变化。我们可以使用簇数绘制一个图,表示簇内平方和,以找到最佳值。簇内平方和也被称为簇内平方和(WCSS)或惯性:
如图所示,在 k = 3 时,图形开始显著变平,因此我们会选择 3 作为聚类数。
让我们使用肘部法则在 Python 中找到最佳聚类数:
# import pandas
import pandas as pd
# import matplotlib
import matplotlib.pyplot as plt
# import K-means
from sklearn.cluster import KMeans
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
wcss_list = []
# Run a loop for different value of number of cluster
for i in range(1, 6):
# Create and fit the KMeans model
kmeans_model = KMeans(n_clusters = i, random_state = 123)
kmeans_model.fit(data)
# Add the WCSS or inertia of the clusters to the score_list
wcss_list.append(kmeans_model.inertia_)
# Plot the inertia(WCSS) and number of clusters
plt.plot(range(1, 6), wcss_list, marker='*')
# set title of the plot
plt.title('Selecting Optimum Number of Clusters using Elbow Method')
# Set x-axis label
plt.xlabel('Number of Clusters K')
# Set y-axis label
plt.ylabel('Within-Cluster Sum of the Squares(Inertia)')
# Display plot
plt.show()
这将产生以下输出:
在前面的示例中,我们创建了一个包含 X
和 Y
两列的 DataFrame。我们使用 K-means
生成了聚类并计算了 WCSS。之后,我们绘制了聚类数量和惯性图。如图所示,在 k = 2 时,图形开始显著变平,因此我们会选择 2 作为最佳聚类数。
轮廓法
轮廓法评估并验证聚类数据。它找出每个数据点的分类效果。轮廓分数的图表帮助我们可视化和解释数据点在各自聚类内部的紧密程度,以及与其他聚类的分离程度。它帮助我们评估聚类的数量。其得分范围从 -1 到 +1。正值表示聚类分离良好,负值表示数据点被错误地分配。值越正,数据点与最近的聚类的距离越远;零值表示数据点位于两个聚类之间的分隔线上。让我们看看轮廓分数的公式:
a[i] 是第 i 个数据点与聚类内其他点的平均距离。
b[i] 是第 i 个数据点与其他聚类点的平均距离。
这意味着我们可以轻松地说 S(i) 的值将在[-1, 1]之间。所以,S(i) 要接近 1,a[i] 必须相较于 b[i,即]e,a[i] << b[i]。
让我们使用轮廓分数在 Python 中找到最佳聚类数:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# import k-means for performing clustering
from sklearn.cluster import KMeans
# import silhouette score
from sklearn.metrics import silhouette_score
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
score_list = []
# Run a loop for different value of number of cluster
for i in range(2, 6):
# Create and fit the KMeans model
kmeans_model = KMeans(n_clusters = i, random_state = 123)
kmeans_model.fit(data)
# Make predictions
pred=kmeans_model.predict(data)
# Calculate the Silhouette Score
score = silhouette_score (data, pred, metric='euclidean')
# Add the Silhouette score of the clusters to the score_list
score_list.append(score)
# Plot the Silhouette score and number of cluster
plt.bar(range(2, 6), score_list)
# Set title of the plot
plt.title('Silhouette Score Plot')
# Set x-axis label
plt.xlabel('Number of Clusters K')
# Set y-axis label
plt.ylabel('Silhouette Scores')
# Display plot
plt.show()
这将产生以下输出:
在前面的示例中,我们创建了一个包含 X
和 Y
两列的 DataFrame。我们使用 K-means
在创建的 DataFrame 上生成了不同数量的聚类并计算了轮廓分数。之后,我们使用条形图绘制了聚类数量和轮廓分数。如图所示,在 k = 2 时,轮廓分数达到最高值,因此我们会选择 2 个聚类。接下来,我们进入 k-means 聚类技术。
使用 k-means 聚类对数据进行分区
k-means 是最简单、最流行且最著名的聚类算法之一。它是一种划分聚类方法。它通过定义一个基于给定聚类数目的随机初始聚类中心来划分输入数据。在下一次迭代中,它使用欧氏距离将数据项与最近的聚类中心关联。在该算法中,初始聚类中心可以手动选择或随机选择。k-means 以数据和聚类数目作为输入,执行以下步骤:
-
选择k个随机数据项作为聚类的初始中心。
-
将数据项分配到最近的聚类中心。
-
通过计算其他聚类项的平均值来选择新的聚类中心。
-
重复步骤 2 和 3,直到聚类不再发生变化。
该算法旨在最小化平方误差之和:
k-means 是同类算法中最快且最稳健的算法之一。它在数据集具有明显且分离的数据项时效果最佳。它生成球形聚类。k-means 在开始时需要聚类数作为输入。如果数据项高度重叠,它的表现不佳。它捕捉到的是平方误差函数的局部最优解。它在处理噪声和非线性数据时表现不佳。它也不适用于非球形聚类。让我们使用 k-means 聚类创建一个聚类模型:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import K-means
from sklearn.cluster import KMeans
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
# Define number of clusters
num_clusters = 2
# Create and fit the KMeans model
km = KMeans(n_clusters=num_clusters)
km.fit(data)
# Predict the target variable
pred=km.predict(data)
# Plot the Clusters
plt.scatter(data.X,data.Y,c=pred, marker="o", cmap="bwr_r")
# Set title of the plot
plt.title('K-Means Clustering')
# Set x-axis label
plt.xlabel('X-Axis Values')
# Set y-axis label
plt.ylabel('Y-Axis Values')
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码示例中,我们导入了KMeans
类并创建了它的对象或模型。该模型将在数据集(无标签)上进行拟合。训练后,模型已经准备好使用predict()
方法进行预测。在预测结果后,我们使用散点图绘制了聚类结果。在本节中,我们已经了解了 k-means 的工作原理以及如何使用 scikit-learn 库实现它。在下一节中,我们将介绍层次聚类。
层次聚类
层次聚类根据不同的层次结构级别对数据项进行分组。它使用自顶向下或自底向上的策略,根据不同的层次结构级别将数据项组合成组。根据所使用的策略,层次聚类可以分为两种类型——凝聚型或分裂型:
-
聚合型层次聚类是最广泛使用的层次聚类技术。它基于相似性将相似的数据项分组成层次结构。这种方法也叫做聚合嵌套(AGNES)。该算法从将每个数据项视为单独的聚类开始,并根据相似性合并聚类。它迭代地收集小的聚类,并将其合并为一个大的聚类。该算法的结果以树形结构呈现。它以自底向上的方式工作;也就是说,每个项最初被视为一个单独的元素聚类,在算法的每次迭代中,两个最相似的聚类会被合并,形成一个更大的聚类。
-
分裂式层次聚类是一种自上而下的策略算法。它也被称为分裂分析(DIANA)。它从将所有数据项视为一个大的聚类开始,并递归地进行划分。在每次迭代中,聚类被分成两个非相似或异质的子聚类:
为了决定哪些聚类应该被组合或拆分,我们使用各种距离和链接标准,例如单链、全链、平均链和质心链。这些标准决定了聚类的形状。两种类型的层次聚类(聚合型和分裂型层次聚类)都需要预定义的聚类数量或距离阈值作为输入,以终止递归过程。由于很难确定距离阈值,因此最简单的选项是通过树状图检查聚类数量。树状图帮助我们理解层次聚类的过程。让我们来看一下如何使用scipy
库创建树状图:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import dendrogram
from scipy.cluster.hierarchy import dendrogram
from scipy.cluster.hierarchy import linkage
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
# create dendrogram using ward linkage
dendrogram_plot = dendrogram(linkage(data, method = 'ward'))
# Set title of the plot
plt.title('Hierarchical Clustering: Dendrogram')
# Set x-axis label
plt.xlabel('Data Items')
# Set y-axis label
plt.ylabel('Distance')
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码示例中,我们创建了数据集,并使用 ward 链接生成了树状图。对于树状图,我们使用了scipy.cluster.hierarchy
模块。为了设置图表标题和轴标签,我们使用了matplotlib
。为了选择聚类数量,我们需要画一条横线,使其不与聚类相交,并计算垂直线的数量以找出聚类数。让我们使用聚合型聚类创建一个聚类模型:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import Agglomerative Clustering
from sklearn.cluster import AgglomerativeClustering
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
# Specify number of clusters
num_clusters = 2
# Create agglomerative clustering model
ac = AgglomerativeClustering(n_clusters = num_clusters, linkage='ward')
# Fit the Agglomerative Clustering model
ac.fit(data)
# Predict the target variable
pred=ac.labels_
# Plot the Clusters
plt.scatter(data.X,data.Y,c=pred, marker="o")
# Set title of the plot
plt.title('Agglomerative Clustering')
# Set x-axis label
plt.xlabel('X-Axis Values')
# Set y-axis label
plt.ylabel('Y-Axis Values')
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码示例中,我们导入了AgglomerativeClustering
类,并创建了它的对象或模型。该模型将适应没有标签的数据集。训练后,模型准备好使用predict()
方法进行预测。预测结果之后,我们使用散点图绘制了聚类结果。在这一部分中,我们已经看到层次聚类的工作原理及其使用scipy
和 scikit-learn 库的实现。在下一部分中,我们将介绍基于密度的聚类。
DBSCAN 聚类
分区聚类方法,如 k-means,和层次聚类方法,如凝聚聚类,适用于发现球形或凸形的簇。这些算法对噪声或离群点较为敏感,并且适用于分离良好的簇:
直观地说,我们可以认为基于密度的聚类方法最类似于我们人类可能本能地对物品进行分组的方式。在所有前面的图中,我们可以通过物品的密度快速看到不同组或簇的数量。
基于密度的空间聚类法(带噪声) (DBSCAN) 基于群组和噪声的思想。其核心思想是每个群组或簇中的每个数据项在给定半径内都有最少数量的数据项。
DBSCAN 的主要目标是发现密集区域,该区域可以通过最少数量的对象(minPoints
)和给定半径(eps
)来计算。DBSCAN 能够生成随机形状的簇,并处理数据集中的噪声。此外,DBSCAN 不需要输入簇的数量。它会自动识别数据中的簇数。
让我们使用 Python 中的 DBSCAN 聚类方法来创建一个聚类模型:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import DBSCAN clustering model
from sklearn.cluster import DBSCAN
# import make_moons dataset
from sklearn.datasets import make_moons
# Generate some random moon data
features, label = make_moons(n_samples = 2000)
# Create DBSCAN clustering model
db = DBSCAN()
# Fit the Spectral Clustering model
db.fit(features)
# Predict the target variable
pred_label=db.labels_
# Plot the Clusters
plt.scatter(features[:, 0], features[:, 1], c=pred_label,
marker="o",cmap="bwr_r")
# Set title of the plot
plt.title('DBSCAN Clustering')
# Set x-axis label
plt.xlabel('X-Axis Values')
# Set y-axis label
plt.ylabel('Y-Axis Values')
# Display the plot
plt.show()
这将生成以下输出:
首先,我们导入 DBSCAN
类并创建月亮数据集。之后,我们创建 DBSCAN 模型并将其拟合到数据集上。DBSCAN 不需要簇的数量。训练完成后,模型可以使用 predict()
方法进行预测。预测结果后,我们通过散点图绘制了聚类结果。在本节中,我们了解了 DBSCAN 聚类的工作原理及其在 scikit-learn 库中的实现。下一节中,我们将介绍谱聚类技术。
谱聚类
谱聚类是一种利用相似度矩阵谱的方法。矩阵的谱代表其特征值的集合,而相似度矩阵由每个数据点之间的相似度分数组成。它在聚类前进行数据的降维。换句话说,谱聚类创建一个数据点图,这些点被映射到较低的维度,并分成不同的簇。
相似度矩阵将数据转换,以克服分布中缺乏凸性的难题。对于任何数据集,数据点可能是n维的,且这里可能有m个数据点。从这m个点中,我们可以创建一个图,其中点是节点,边则是节点之间的相似度加权。定义相似度的一种常见方式是使用高斯核,它是欧几里得距离的非线性函数:
这个函数的距离范围从 0 到 1。它被限制在零和一之间是一个很好的性质。欧几里得距离中的绝对距离(它可以是任何值)可能导致不稳定性和建模困难。你可以将高斯核视为欧几里得距离的归一化函数。
在获得图形后,创建一个邻接矩阵,并在矩阵的每个单元格中填入边的权重 。这是一个对称矩阵。我们将邻接矩阵称为 A。我们还可以创建一个“度数”对角矩阵 D,在每个
元素中填入与节点 i 相连的所有边的权重之和。我们将这个矩阵称为 D。对于一个给定的图 G,假设它有 n 个顶点,它的 n×n 拉普拉斯矩阵可以定义如下:
这里 D 是度数矩阵,A 是图的邻接矩阵。
现在我们有了图(G)的拉普拉斯矩阵。我们可以计算矩阵的特征向量谱。如果我们取 k 个最不显著的特征向量,就可以得到一个 k 维度的表示。最不显著的特征向量与最小的特征值相关。每个特征向量提供有关图的连通性的信息。
谱聚类的思想是使用这 k 个特征向量来对点进行聚类。所以,你取 k 个最不显著的特征向量,你就有了 k 维度下的 m 个点。然后,你运行一个聚类算法,比如 k-means,最后得到结果。在谱聚类中,这个 k 与高斯核 k-means 密切相关。你也可以将其看作是一种聚类方法,其中你的点被投影到一个无限维的空间中,在那里进行聚类,然后你使用这些结果作为聚类点的结果。
当 k-means 聚类效果不好时,通常使用谱聚类,因为原始空间中的簇无法线性区分。我们还可以尝试其他聚类方法,如层次聚类或基于密度的聚类,来解决这个问题。让我们在 Python 中使用谱聚类创建一个聚类模型:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import Spectral Clustering
from sklearn.cluster import SpectralClustering
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
# Specify number of clusters
num_clusters = 2
# Create Spectral Clustering model
sc=SpectralClustering(num_clusters, affinity='rbf', n_init=100, assign_labels='discretize')
# Fit the Spectral Clustering model
sc.fit(data)
# Predict the target variable
pred=sc.labels_
# Plot the Clusters
plt.scatter(data.X,data.Y,c=pred, marker="o")
# Set title of the plot
plt.title('Spectral Clustering')
# Set x-axis label
plt.xlabel('X-Axis Values')
# Set y-axis label
plt.ylabel('Y-Axis Values')
# Display the plot
plt.show()
这会产生以下输出:
在前面的代码示例中,我们导入了 SpectralClustering
类,并使用 pandas 创建了一个虚拟数据集。之后,我们创建了模型并将其拟合到数据集上。训练完成后,模型已经准备好使用 predict()
方法进行预测。在本节中,我们已经了解了谱聚类的工作原理及其在 scikit-learn 库中的实现。在下一节中,我们将学习如何评估聚类算法的性能。
评估聚类性能
评估聚类性能是评估聚类算法在给定数据集上强度的必要步骤。在无监督环境中评估性能并非易事,但文献中有许多可用的方法。我们可以将这些方法分为两大类:内部评估和外部评估。让我们详细了解这两类评估方法。
内部性能评估
在内部性能评估中,聚类是仅基于特征数据进行评估的。这种方法不使用任何目标标签信息。这些评估指标为生成良好分离聚类的聚类方法分配更高的分数。在这里,高分数并不保证有效的聚类结果。
内部性能评估有助于我们比较多个聚类算法,但这并不意味着得分更高的算法会比其他算法生成更好的结果。以下内部性能评估指标可以用来估计生成聚类的质量:
Davies-Bouldin 指数
Davies-Bouldin 指数 (BDI) 是聚类内距离与聚类间距离的比值。较低的 DBI 值意味着更好的聚类结果。计算公式如下:
在此,以下公式适用:
-
n:聚类的数量
-
c[i]:聚类 i 的质心
-
σ[i]:聚类内距离或所有聚类项到质心 c[i]的平均距离
-
d(c[i], c[j]):两个聚类质心 c[i]和 c[j]之间的聚类间距离
轮廓系数
轮廓系数用于找出一个项目与其自身聚类项目和其他最近聚类之间的相似度。它也用于确定聚类的数量,如我们在本章其他部分看到的那样。较高的轮廓系数意味着更好的聚类结果。计算公式如下:
a[i] 是第 i 个数据点与聚类内其他点的平均距离。
b[i] 是第 i 个数据点与其他聚类点之间的平均距离。
所以,我们可以说 S(i) 的值会在[-1, 1]之间。为了让 S(i) 接近 1,a[i]必须比 b[i]小很多,即 a[i] << b[i]。
外部性能评估
在外部性能评估中,生成的聚类是通过与聚类过程中未使用的实际标签进行评估的。这类似于监督学习评估过程;也就是说,我们可以使用相同的混淆矩阵来评估性能。以下外部评估指标用于评估生成聚类的质量。
Rand 得分
Rand 得分表示一个聚类与基准分类的相似度,并计算正确决策的百分比。较低的值更可取,因为这表示聚类更加分明。计算公式如下:
这里,以下内容适用:
-
TP:真阳性的总数
-
TN:真阴性的总数
-
FP:假阳性的总数
-
FN:假阴性的总数
Jaccard 分数
Jaccard 分数计算两个数据集之间的相似性。它的取值范围是 0 到 1,1 表示数据集完全相同,0 表示数据集没有共同元素。较低的值更可取,因为它表示簇之间的区别。这可以按以下方式计算:
这里 A 和 B 是两个数据集。
F-度量或 F1 分数
F-度量是精度和召回率值的调和平均数。它衡量了聚类算法的精度和鲁棒性。它还试图通过β的值平衡假阴性的参与。可以按以下方式计算:
这里β是非负值。β=1 表示精度和召回率赋予相等的权重,β=0.5 表示精度的权重是召回率的两倍,而β=0 则表示不考虑召回率。
Fowlkes-Mallows 分数
Fowlkes-Mallows 分数是精度和召回率的几何平均数。较高的值表示簇之间相似。可以按以下方式计算:
让我们使用 k-means 聚类创建一个聚类模型,并使用 Python 中的内部和外部评估指标评估性能,使用 Pima Indian Diabetes 数据集(https://github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/blob/master/Chapter11/diabetes.csv):
# Import libraries
import pandas as pd
# read the dataset
diabetes = pd.read_csv("diabetes.csv")
# Show top 5-records
diabetes.head()
这将得到以下输出:
首先,我们需要导入 pandas 并读取数据集。在前面的例子中,我们读取的是 Pima Indian Diabetes 数据集:
# split dataset in two parts: feature set and target label
feature_set = ['pregnant', 'insulin', 'bmi', 'age','glucose','bp','pedigree']
features = diabetes[feature_set]
target = diabetes.label
在加载数据集后,我们需要将数据集分为依赖/标签列(目标)和独立/特征列(feature_set
)。之后,数据集将被拆分为训练集和测试集。现在,依赖列和独立列会通过train_test_split()
拆分成训练集和测试集(feature_train
、feature_test
、target_train
和 target_test
)。我们来拆分数据集为训练集和测试集:
# partition data into training and testing set
from sklearn.model_selection import train_test_split
feature_train, feature_test, target_train, target_test = train_test_split(features, target, test_size=0.3, random_state=1)
这里,train_test_split()
接收依赖和独立的 DataFrame、test_size
和random_state
。其中,test_size
决定了训练集和测试集的比例(test_size
值为0.3
意味着 30%的数据将分配给测试集,其余 70%将作为训练集),而random_state
则作为种子值,用于每次生成相同的数据分割。如果random_state
为None
,那么每次会随机分割记录,这将导致不同的性能评估结果:
# Import K-means Clustering
from sklearn.cluster import KMeans
# Import metrics module for performance evaluation
from sklearn.metrics import davies_bouldin_score
from sklearn.metrics import silhouette_score
from sklearn.metrics import adjusted_rand_score
from sklearn.metrics import jaccard_score
from sklearn.metrics import f1_score
from sklearn.metrics import fowlkes_mallows_score
# Specify the number of clusters
num_clusters = 2
# Create and fit the KMeans model
km = KMeans(n_clusters=num_clusters)
km.fit(feature_train)
# Predict the target variable
predictions = km.predict(feature_test)
# Calculate internal performance evaluation measures
print("Davies-Bouldin Index:", davies_bouldin_score(feature_test, predictions))
print("Silhouette Coefficient:", silhouette_score(feature_test, predictions))
# Calculate External performance evaluation measures
print("Adjusted Rand Score:", adjusted_rand_score(target_test, predictions))
print("Jaccard Score:", jaccard_score(target_test, predictions))
print("F-Measure(F1-Score):", f1_score(target_test, predictions))
print("Fowlkes Mallows Score:", fowlkes_mallows_score(target_test, predictions))
这将导致以下输出:
Davies-Bouldin Index: 0.7916877512521091
Silhouette Coefficient: 0.5365443098840619
Adjusted Rand Score: 0.03789319261940484
Jaccard Score: 0.22321428571428573
F-Measure(F1-Score): 0.36496350364963503
Fowlkes Mallows Score: 0.6041244457314743
首先,我们导入了KMeans
和metrics
模块。我们创建了一个 k-means 对象或模型,并将其拟合到训练数据集(没有标签)。训练完成后,模型进行预测,并使用内部评估指标(如 DBI 和轮廓系数)和外部评估指标(如 Rand 得分、Jaccard 得分、F-Measure 和 Fowlkes-Mallows 得分)来评估这些预测。
摘要
在本章中,我们探讨了无监督学习及其技术,如降维和聚类。主要内容包括 PCA(主成分分析)降维技术以及几种聚类方法,如 k-means 聚类、层次聚类、DBSCAN 和谱聚类。本章从降维和 PCA 开始,PCA 之后,我们的主要焦点是聚类技术及如何确定聚类的数量。在后续章节中,我们介绍了聚类性能评估指标,如 DBI 和轮廓系数,这些是内部评估指标。接着,我们又探讨了外部评估指标,如 Rand 得分、Jaccard 得分、F-Measure 和 Fowlkes-Mallows 指数。
下一章,第十二章,文本数据分析,将专注于文本分析,包括使用 NLTK、SpaCy 和 scikit-learn 进行文本预处理和文本分类。本章首先探讨文本数据的基本操作,如通过分词、去除停用词、词干提取和词形还原、词性标注、实体识别、依存句法分析以及词云等进行文本标准化。在后续章节中,重点将放在特征工程方法上,如词袋模型、术语存在、TF-IDF、情感分析、文本分类和文本相似度。
第四部分:自然语言处理、图像分析与并行计算
本节的主要目标是概述自然语言处理(NLP)、图像分析和并行计算。NLP 技能包括使用 NLTK 和 SpaCy 进行文本预处理、情感分析和文本相似度计算。图像分析则涉及使用 OpenCV 进行图像处理和人脸检测。本节还重点讨论 DataFrame、数组和机器学习算法的并行计算。
本节包括以下章节:
-
第十二章,文本数据分析
-
第十三章,图像数据分析
-
第十四章,使用 Dask 进行并行计算
第十三章:分析文本数据
在信息化时代,数据以惊人的速度和体量产生。产生的数据不仅限于结构化或表格类型,也可以是多种非结构化类型,如文本数据、图像或图形数据、语音数据和视频数据。文本是非常常见且丰富的数据类型。文章、博客、教程、社交媒体帖子和网站内容都会生成非结构化的文本数据。每分钟都会有成千上万的电子邮件、消息、评论和推文发送出去。如此大量的文本数据需要被挖掘。文本分析为商业人士提供了大量机会;例如,亚马逊可以解读关于特定产品的客户反馈,新闻分析师可以分析 Twitter 上的新闻趋势和最新问题,Netflix 也可以解读每部电影和网络剧集的评论。商业分析师可以通过 NLP 和文本分析解读客户活动、评论、反馈和情感,从而有效推动业务发展。
本章将从基本的文本分析操作开始,如分词、移除停用词、词干提取、词形还原、词性标注和实体识别。之后,我们将学习如何使用 WordCloud 可视化文本分析结果。我们将看到如何基于评论,通过情感分析找出客户对某个产品的看法。在这里,我们将使用文本分类进行情感分析,并通过准确率、精确度、召回率和 F1 分数评估模型的性能。最后,我们将重点关注通过 Jaccard 和余弦相似度衡量两句话之间的文本相似性。
本章的主题如下所示:
-
安装 NLTK 和 SpaCy
-
文本规范化
-
分词
-
移除停用词
-
词干提取与词形还原
-
词性标注
-
实体识别
-
依存句法分析
-
创建词云
-
词袋模型
-
TF-IDF
-
使用文本分类进行情感分析
-
文本相似度
技术要求
本章的技术要求如下:
-
你可以通过以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter12
-
所有代码块都可以在
ch12.ipynb
文件中找到。 -
本章仅使用一个 TSV 文件(
amazon_alexa.tsv
)进行练习。 -
在本章中,我们将使用 NLTK、SpaCy、WordCloud、matplotlib、seaborn 和 scikit-learn Python 库。
安装 NLTK 和 SpaCy
NLTK 是一个流行且重要的 Python 自然语言处理库。它提供所有基础和高级的 NLP 操作。它包括常见的算法,如分词、词干提取、词形还原、词性标注和命名实体识别。NLTK 库的主要特点是开源、易于学习、易于使用、拥有活跃的社区以及完善的文档。NLTK 库可以通过在命令行中运行以下pip install
命令来安装:
pip install nltk
NLTK 不是 Anaconda 中预安装的库。我们可以直接在 Jupyter Notebook 中安装nltk
。我们可以在单元格中的命令前加上感叹号 (!):
!pip install nltk
SpaCy 是另一个重要且强大的 Python NLP 库。它提供了常见的 NLP 算法和高级功能。它旨在用于生产目的,并为大规模数据开发应用程序。可以使用以下命令在命令行中通过pip install
安装 SpaCy 库:
pip install spacy
安装完 spaCy 后,我们需要安装一个spacy
英语语言模型。我们可以使用以下命令安装:
python -m spacy download en
SpaCy 及其英语模型没有预安装在 Anaconda 中。我们可以使用以下代码直接安装spacy
。我们可以在单元格中的命令前加上感叹号 (!):
!pip install spacy
!python -m spacy download en
使用上述语法,我们可以在 Jupyter Notebook 中安装spacy
及其英语模型。
文本标准化
文本标准化将文本转换为标准或规范形式。它确保一致性,并有助于处理和分析。标准化过程没有单一的方法。标准化的第一步是将所有文本转换为小写。这是最简单、最适用且最有效的文本预处理方法。另一种方法可能是处理拼写错误的单词、缩写词、简写和使用超出词汇表的单词;例如,“super”,“superb”和“superrrr”可以转换为“super”。文本标准化处理测试数据中的噪声和干扰,准备无噪声数据。我们还会应用词干提取和词形还原来标准化文本中存在的单词。
让我们通过将文本转换为小写来执行基本的标准化操作:
# Input text
paragraph="""Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Converting paragraph in lowercase
print(paragraph.lower())
这将输出以下内容:
taj mahal is one of the beautiful monuments. it is one of the wonders of the world. it was built by shah jahan in 1631 in memory of his third beloved wife mumtaj mahal.
在前面的代码块中,我们通过使用lower()
方法将给定的输入段落转换为小写。
在 NLP 中,文本标准化处理随机性,并将文本转换为标准形式,从而提高 NLP 解决方案的整体性能。它还通过将单词转换为根词来减少文档词项矩阵的大小。在接下来的部分中,我们将重点介绍基本的文本预处理操作。
分词
分词是文本分析中的初步步骤。分词被定义为将文本段落分解成更小的部分或词元,如句子或单词,并忽略标点符号。分词可以分为两种类型:句子分词和单词分词。句子分词器将段落拆分为句子,而单词分词器将文本拆分为单词或词元。
让我们使用 NLTK 和 spaCy 对段落进行分词:
- 在分词之前,导入 NLTK 并下载所需的文件:
# Loading NLTK module
import nltk
# downloading punkt
nltk.download('punkt')
# downloading stopwords
nltk.download('stopwords')
# downloading wordnet
nltk.download('wordnet')
# downloading average_perception_tagger
nltk.download('averaged_perceptron_tagger')
- 现在,我们将使用 NLTK 的
sent_tokenize()
方法将段落分割成句子:
# Sentence Tokenization
from nltk.tokenize import sent_tokenize
paragraph="""Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
tokenized_sentences=sent_tokenize(paragraph)
print(tokenized_sentences)
这将产生以下输出:
['Taj Mahal is one of the beautiful monument.', 'It is one of the wonders of the world.', 'It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal.']
在前面的例子中,我们将段落作为参数传递给 sent_tokenize()
方法。此方法的输出将是一个句子列表。
让我们使用 spaCy 将段落分割成句子:
# Import spacy
import spacy
# Loading english language model
nlp = spacy.load("en")
# Build the nlp pipe using 'sentencizer'
sent_pipe = nlp.create_pipe('sentencizer')
# Append the sentencizer pipe to the nlp pipeline
nlp.add_pipe(sent_pipe)
paragraph = """Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Create nlp Object to handle linguistic annotations in a documents.
nlp_doc = nlp(paragraph)
# Generate list of tokenized sentence
tokenized_sentences = []
for sentence in nlp_doc.sents:
tokenized_sentences.append(sentence.text)
print(tokenized_sentences)
这将产生以下输出:
['Taj Mahal is one of the beautiful monument.', 'It is one of the wonders of the world.', 'It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal.']
在前面的例子中,我们首先导入了英语语言模型并实例化它。之后,我们使用 sentencizer
创建了 NLP 管道,并将其添加到管道中。最后,我们创建了 NLP 对象,并通过迭代 NLP 对象的 sents
属性来生成一个分词后的句子列表。
让我们使用 NLTK 的 word_tokenize()
函数将段落分词:
# Import nltk word_tokenize method
from nltk.tokenize import word_tokenize
# Split paragraph into words
tokenized_words=word_tokenize(paragraph)
print(tokenized_words)
这将产生以下输出:
['Taj', 'Mahal', 'is', 'one', 'of', 'the', 'beautiful', 'monument', '.', 'It', 'is', 'one', 'of', 'the', 'wonders', 'of', 'the', 'world', '.', 'It', 'was', 'built', 'by', 'Shah', 'Jahan', 'in', '1631', 'in', 'memory', 'of', 'his', 'third', 'beloved', 'wife', 'Mumtaj', 'Mahal', '.']
在前面的例子中,我们将段落作为参数传递给 word_tokenize()
方法。此方法的输出将是一个单词列表。
让我们使用 spaCy 将段落分词为单词:
# Import spacy
import spacy
# Loading english language model
nlp = spacy.load("en")
paragraph = """Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Create nlp Object to handle linguistic annotations in a documents.
my_doc = nlp(paragraph)
# tokenize paragraph into words
tokenized_words = []
for token in my_doc:
tokenized_words.append(token.text)
print(tokenized_words)
这将产生以下输出:
['Taj', 'Mahal', 'is', 'one', 'of', 'the', 'beautiful', 'monument', '.', 'It', 'is', 'one', 'of', 'the', 'wonders', 'of', 'the', 'world', '.', 'It', 'was', 'built', 'by', 'Shah', 'Jahan', 'in', '1631', 'in', 'memory', 'of', 'his', 'third', 'beloved', 'wife', 'Mumtaj', 'Mahal', '.']
在前面的例子中,我们首先导入了英语语言模型并实例化它。之后,我们创建了一个文本段落。最后,我们使用文本段落创建了一个 NLP 对象,并通过迭代它来生成一个分词后的单词列表。
让我们创建分词后的单词的频率分布:
# Import frequency distribution
from nltk.probability import FreqDist
# Find frequency distribution of paragraph
fdist = FreqDist(tokenized_words)
# Check top 5 common words
fdist.most_common(5)
这将产生以下输出:
[('of', 4), ('the', 3), ('.', 3), ('Mahal', 2), ('is', 2)]
让我们使用 matplotlib 创建一个频率分布图:
# Import matplotlib
import matplotlib.pyplot as plt
# Plot Frequency Distribution
fdist.plot(20, cumulative=False)
plt.show()
这将产生以下输出:
在前面的例子中,我们使用 FreqDist
类生成了词元的频率分布。在句子和单词分词后,我们将学习如何从给定文本中移除停用词。
移除停用词
停用词被视为文本分析中的噪声。任何文本段落中都必须包含动词、冠词和介词。这些都被视为停用词。停用词对于人类对话是必要的,但在文本分析中贡献不大。从文本中移除停用词称为噪声消除。
让我们看看如何使用 NLTK 移除停用词:
# import the nltk stopwords
from nltk.corpus import stopwords
# Load english stopwords list
stopwords_set=set(stopwords.words("english"))
# Removing stopwords from text
filtered_word_list=[]
for word in tokenized_words:
# filter stopwords
if word not in stopwords_set:
filtered_word_list.append(word)
# print tokenized words
print("Tokenized Word List:", tokenized_words)
# print filtered words
print("Filtered Word List:", filtered_word_list)
这将产生以下输出:
Tokenized Word List: ['Taj', 'Mahal', 'is', 'one', 'of', 'the', 'beautiful', 'monuments', '.', 'It', 'is', 'one', 'of', 'the', 'wonders', 'of', 'the', 'world', '.', 'It', 'was', 'built', 'by', 'Shah', 'Jahan', 'in', '1631', 'in', 'memory', 'of', 'his', 'third', 'beloved', 'wife', 'Mumtaj', 'Mahal', '.']
Filtered Word List: ['Taj', 'Mahal', 'one', 'beautiful', 'monuments', '.', 'It', 'one', 'wonders', 'world', '.', 'It', 'built', 'Shah', 'Jahan', '1631', 'memory', 'third', 'beloved', 'wife', 'Mumtaj', 'Mahal', '.']
在前面的示例中,首先,我们导入了停用词并加载了英文单词列表。之后,我们使用for
循环迭代在上一节中生成的分词单词列表,并使用if
条件从停用词列表中过滤分词单词。我们将过滤后的单词保存在fltered_word_list
列表对象中。
让我们看看如何使用 spaCy 去除停用词:
# Import spacy
import spacy
# Loading english language model
nlp = spacy.load("en")
# text paragraph
paragraph = """Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Create nlp Object to handle linguistic annotations in a documents.
my_doc = nlp(paragraph)
# Removing stopwords from text
filtered_token_list = []
for token in my_doc:
# filter stopwords
if token.is_stop==False:
filtered_token_list.append(token)
print("Filtered Word List:",filtered_token_list)
这会产生以下输出:
Filtered Sentence: [Taj, Mahal, beautiful, monument, ., wonders, world, ., built, Shah, Jahan, 1631, memory, beloved, wife, Mumtaj, Mahal, .]
在前面的示例中,首先,我们导入了停用词并将英文单词列表加载到停用词变量中。之后,我们使用for
循环迭代 NLP 对象,并通过if
条件从停用词列表中过滤每个单词,使用属性"is_stop"
。我们将过滤后的单词附加到fltered_token_list
列表对象中。在这一部分,我们了解了如何去除停用词。现在,轮到我们学习词干提取和词形还原,找到根词了。
词干提取和词形还原
词干提取(Stemming)是文本分析中语言层面的另一步规范化。词干提取过程将单词替换为其词根。它会去除单词的前缀和后缀。例如,单词“connect”是“connecting”、“connected”和“connection”的词根。所有这些单词都有一个共同的词根:connect。这种单词拼写的差异使得文本数据分析变得困难。
词形还原(Lemmatization)是另一种词汇规范化方法,它将一个单词转换为其词根。它与词干提取(stemming)密切相关。主要区别在于,词形还原在执行规范化时考虑了单词的上下文,而词干提取则没有考虑单词的上下文知识。词形还原比词干提取更为复杂。例如,单词“geese”经过词形还原后变成“goose”。词形还原通过使用词典将单词简化为有效的词元。词形还原会根据单词的词性进行规范化;这就是为什么它难以实现且速度较慢,而词干提取则容易实现且速度较快,但准确度较低的原因。
让我们看看如何使用 NLTK 进行词干提取和词形还原:
# Import Lemmatizer
from nltk.stem.wordnet import WordNetLemmatizer
# Import Porter Stemmer
from nltk.stem.porter import PorterStemmer
# Create lemmatizer object
lemmatizer = WordNetLemmatizer()
# Create stemmer object
stemmer = PorterStemmer()
# take a sample word
sample_word = "crying"
print("Lemmatized Sample Word:", lemmatizer.lemmatize(sample_word, "v"))
print("Stemmed Sample Word:", stemmer.stem(sample_word))
这会产生以下输出:
Lemmatized Sample Word: cry
Stemmed Sample Word: cri
在前面的示例中,首先,我们导入了WordNetLemmatizer
进行词形还原并实例化了它的对象。类似地,我们导入了PorterStemmer
进行词干提取并实例化了它的对象。之后,我们通过lemmatize()
函数获得了词形,还通过stem()
函数得到了词干。
让我们看看如何使用 spaCy 进行词形还原:
# Import english language model
import spacy
# Loading english language model
nlp = spacy.load("en")
# Create nlp Object to handle linguistic annotations in documents.
words = nlp("cry cries crying")
# Find lemmatized word
for w in words:
print('Original Word: ', w.text)
print('Lemmatized Word: ',w.lemma_)
这会产生以下输出:
Original Word: cry
Lemmatized Word: cry
Original Word: cries
Lemmatized Word: cry
Original Word: crying
Lemmatized Word: cry
在前面的示例中,首先,我们导入了英文语言模型并实例化了它。之后,我们创建了 NLP 对象并使用for
循环对其进行迭代。在循环中,我们通过text
和lemma_
属性获取了文本值及其词形还原值。在这一部分,我们学习了词干提取和词形还原。现在,我们将学习给定文档中的词性标注(PoS tagging)。
词性标注(POS tagging)
PoS 代表词性。PoS 标签的主要目标是发现单词的句法类型,如名词、代词、形容词、动词、副词和介词。PoS 标签能够找到句子中单词之间的关系。
让我们看看如何使用 NLTK 获取单词的 PoS 标签:
# import Word Tokenizer and PoS Tagger
from nltk.tokenize import word_tokenize
from nltk import pos_tag
# Sample sentence
sentence = "Taj Mahal is one of the beautiful monument."
# Tokenize the sentence
sent_tokens = word_tokenize(sentence)
# Create PoS tags
sent_pos = pos_tag(sent_tokens)
# Print tokens with PoS
print(sent_pos)
这将产生以下输出:
[('Taj', 'NNP'), ('Mahal', 'NNP'), ('is', 'VBZ'), ('one', 'CD'), ('of', 'IN'), ('the', 'DT'), ('beautiful', 'JJ'), ('monument', 'NN'), ('.', '.')]
在前面的示例中,首先,我们导入了word_tokenize
和pos_tag
。接着,我们获取了一段文本并将其作为参数传递给word_tokenize()
方法。此方法的输出将是一个单词列表。接下来,使用pos_tag()
函数为每个标记生成 PoS 标签。
让我们看看如何使用 spaCy 获取单词的 PoS 标签:
# Import spacy
import spacy
# Loading small english language model
nlp = spacy.load("en_core_web_sm")
# Create nlp Object to handle linguistic annotations in a documents.
sentence = nlp(u"Taj Mahal is one of the beautiful monument.")
for token in sentence:
print(token.text, token.pos_)
这将产生以下输出:
Taj PROPN
Mahal PROPN
is VERB
one NUM
of ADP
the DET
beautiful ADJ
monument NOUN
. PUNCT
在前面的示例中,首先,我们导入了英语语言模型并实例化了它。接着,我们创建了 NLP 对象,并使用for
循环对其进行迭代。在循环中,我们通过text
和pos_
属性获取文本值及其词干值。在这一部分中,我们已经研究了 PoS 标签。现在,是时候跳转到识别文本中的命名实体了。
识别实体
实体识别是指从给定文本中提取或检测实体。它也被称为命名实体识别(NER)。实体可以定义为一个对象,如地点、人物、组织或日期。实体识别是 NLP 的高级主题之一,它用于从文本中提取重要信息。
让我们看看如何使用 spaCy 从文本中提取实体:
# Import spacy
import spacy
# Load English model for tokenizer, tagger, parser, and NER
nlp = spacy.load('en')
# Sample paragraph
paragraph = """Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Create nlp Object to handle linguistic annotations in documents.
docs=nlp(paragraph)
entities=[(i.text, i.label_) for i in docs.ents]
print(entities)
这将产生以下输出:
[('Taj Mahal', 'PERSON'), ('Shah Jahan', 'PERSON'), ('1631', 'DATE'), ('third', 'ORDINAL'), ('Mumtaj Mahal', 'PERSON')]
在前面的示例中,首先,我们导入了 spaCy 并加载了英语语言模型。接着,我们创建了 NLP 对象,并使用for
循环对其进行迭代。在循环中,我们通过text
和label_
属性获取文本值及其实体类型值。现在,让我们使用 spaCy 的display
类来可视化文本中的实体:
# Import display for visualizing the Entities
from spacy import displacy
# Visualize the entities using render function
displacy.render(docs, style = "ent",jupyter = True)
这将产生以下输出:
在前面的示例中,我们导入了display
类,并使用一个 NLP 文本对象调用了它的render()
方法,style
设置为ent
,jupyter
设置为True
。
依存解析
依存解析用于发现单词之间的关系——即单词是如何彼此关联的。它帮助计算机理解句子以便进行分析;例如,“泰姬陵是最美丽的纪念碑之一。”我们不能仅通过分析单词来理解这个句子。我们需要深入分析并理解单词顺序、句子结构以及词性:
# Import spacy
import spacy
# Load English model for tokenizer, tagger, parser, and NER
nlp = spacy.load('en')
# Create nlp Object to handle linguistic annotations in a documents.
docs=nlp(sentence)
# Visualize the using render function
displacy.render(docs, style="dep", jupyter= True, options={'distance': 150})
这将产生以下输出:
在前面的示例中,我们导入了display
类,并使用一个 NLP 文本对象调用了它的render()
方法,style
设置为'dep'
,jupyter
设置为True
,并将options
设置为一个包含distance
键和值 150 的字典。接下来,我们将看看如何根据文本中单词的频率,使用词云可视化文本数据。
创建词云
作为数据分析师,你需要识别最常见的单词,并以图形形式向高层管理层呈现它们。词云用于表示单词频率图。它通过单词的大小来表示频率,也就是说,频率越高的单词看起来越大,频率较低的单词看起来越小。它也被称为标签云。我们可以使用 Python 中的wordcloud
库来创建词云。我们可以使用以下命令安装它:
pip install wordcloud
或者,也可以使用这个:
conda install -c conda-forge wordcloud
让我们学习如何创建词云:
- 导入库并加载停用词列表:
# importing all necessary modules
from wordcloud import WordCloud
from wordcloud import STOPWORDS
import matplotlib.pyplot as plt
stopword_list = set(STOPWORDS)
paragraph="""Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
在前面的示例中,我们导入了WordCloud
、STOPWORDS
和matplotlib.pyplot
类。我们还创建了停用词集合并定义了段落文本。
- 创建并生成词云:
word_cloud = WordCloud(width = 550, height = 550,
background_color ='white',
stopwords = stopword_list,
min_font_size = 10).generate(paragraph)
之后,创建并生成一个WordCloud
对象,设置width
、height
、background_color
、stopwords
和min_font_size
等参数,并在段落文本字符串上生成词云。
- 可视化词云:
0.# Visualize the WordCloud Plot
# Set wordcloud figure size
plt.figure(figsize = (8, 6))
# Show image
plt.imshow(word_cloud)
# Remove Axis
plt.axis("off")
# show plot
plt.show()
这将产生以下输出:
在前面的示例中,我们使用matplotlib.pyplot
可视化了词云。让我们学习如何使用词袋模型将文本文档转换为数值向量。
词袋模型
词袋模型(BoW)是最基本、最简单和最流行的特征工程技术之一,用于将文本转换为数值向量。它分为两个步骤:收集词汇表中的单词,并计算它们在文本中的出现频率或存在。它不考虑文档结构和上下文信息。让我们以以下三个文档为例,理解 BoW:
文档 1:我喜欢披萨。
文档 2:我不喜欢汉堡。
文档 3:披萨和汉堡都属于垃圾食品。
现在,我们将创建文档词矩阵(DTM)。该矩阵由文档(行)、单词(列)以及单词频率(单元格值)组成。
I | like | pizza | do | not | burgers | and | both | are | junk | food | |
---|---|---|---|---|---|---|---|---|---|---|---|
Doc-1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Doc-2 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
Doc-3 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
在前面的示例中,我们使用了单一的关键词生成了 DTM,这称为单元法(unigram)。我们还可以使用连续的两个关键词组合,这被称为二元模型(bigram),或者三个关键词的组合,称为三元模型(trigram)。这种通用形式被称为 n 元模型(n-gram)。
在 Python 中,scikit-learn 提供了CountVectorizer
来生成 BoW 的文档词矩阵(DTM)。我们将在使用文本分类进行情感分析章节中看到如何使用 scikit-learn 生成它。
TF-IDF
TF-IDF 代表 词频-逆文档频率。它有两个部分:词频 (TF) 和 逆文档频率 (IDF)。TF 仅计算每个文档中单词的出现次数,等同于 BoW(词袋模型)。TF 不考虑单词的上下文,且偏向较长的文档。IDF 计算值,表示某个词保留的信息量。
TF-IDF 是 TF 和 IDF 两个部分的点积。TF-IDF 会对文档权重进行归一化处理。TF-IDF 值越高,表示某个词在该文档中的出现频率越高。让我们看看以下三个文档:
文档 1:我喜欢比萨。
文档 2:我不喜欢汉堡。
文档 3:比萨和汉堡都是垃圾食品。
现在,我们将创建 DTM(文档-词项矩阵)。该矩阵的行头是文档名称,列头是词汇,单元格中则是 TF-IDF 值:
I | like | pizza | do | not | burgers | and | both | are | junk | food | |
---|---|---|---|---|---|---|---|---|---|---|---|
Doc-1 | 0.58 | 0.58 | 0.58 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Doc-2 | 0.58 | 0.58 | 0 | 1.58 | 1.58 | 0.58 | 0 | 0 | 0 | 0 | 0 |
Doc-3 | 0 | 0 | 0.58 | 0 | 0 | 0.58 | 1.58 | 1.58 | 1.58 | 1.58 | 1.58 |
在 Python 中,scikit-learn 提供了 TfidfVectorizer
用于生成 TF-IDF DTM。我们将在接下来的章节中看到如何使用 scikit-learn 来生成它。
使用文本分类进行情感分析
一个业务或数据分析师需要了解客户对某一特定产品的反馈和评价。客户喜欢或不喜欢什么?销售情况如何?作为一名业务分析师,你需要以合理的准确性分析这些内容,并量化客户的评论、反馈、意见和推文,从而了解目标受众。情感分析提取文本中的核心信息,并提供人们对产品、服务、品牌以及政治和社会话题的看法。情感分析用于了解客户和人们的心态。它不仅应用于营销领域,我们还可以在政治、公共管理、政策制定、信息安全和研究中使用它。它帮助我们理解人们反馈的极性。情感分析还涵盖了词汇、语气和写作风格。
文本分类可以作为情感分析的一种方法。它是一种监督学习方法,用于检测网页内容、新闻文章、博客、推文和情感类别。文本分类在营销、金融、电子商务和安全等领域有着广泛的应用。首先,我们对文本进行预处理,然后找出预处理文本的特征,再将这些特征和标签输入机器学习算法进行分类。以下图解展示了使用文本分类进行情感分析的完整过程:
让我们对 Amazon Alexa 产品评论进行情感分类。我们可以从 Kaggle 网站获取数据(www.kaggle.com/sid321axn/amazon-alexa-reviews
)。
Alexa 产品评论数据是一个制表符分隔的值文件(TSV 文件)。该数据有五列或属性——评分,日期,变体,已验证评论,和 反馈。
评分
列表示用户对 Alexa 产品的评分。日期列是用户给出评论的日期。变体
列表示产品型号。已验证评论
包含用户对产品的实际评论。
评分表示每个用户对产品给出的评分。日期表示评论的日期,变体描述了型号名称。已验证评论
包含用户撰写的文本评论,反馈列表示情感分数,其中 1 表示正面情感,0 表示负面情感。
使用 BoW 进行分类
在本小节中,我们将基于 BoW 执行情感分析和文本分类。这里,我们使用 scikit-learn
库生成词袋。接下来,让我们看看如何在以下步骤中使用 BoW 特征执行情感分析:
- 加载数据集:
构建机器学习模型的第一步是加载数据集。让我们首先使用 pandas 的 read_csv()
函数读取数据:
# Import libraries
import pandas as pd
# read the dataset
df=pd.read_csv('amazon_alexa.tsv', sep='\t')
# Show top 5-records
df.head()
这将生成以下输出:
在之前的输出数据框中,我们看到 Alexa 评论数据集有五个列:评分,日期,变体,已验证评论,和 反馈。
- 探索数据集。
让我们绘制 反馈 列的计数,以查看数据集中的正面和负面评论数量:
# Import seaborn
import seaborn as sns
import matplotlib.pyplot as plt
# Count plot
sns.countplot(x='feedback', data=df)
# Set X-axis and Y-axis labels
plt.xlabel('Sentiment Score')
plt.ylabel('Number of Records')
# Show the plot using show() function
plt.show()
这将生成以下输出:
在前面的代码中,我们使用 seaborn 的 countplot()
函数绘制了反馈列的条形图。该函数会计算并绘制 反馈 列的值。在此图中,我们可以看到 2,900 条评论为正面评论,250 条评论为负面评论。
- 使用
CountVectorizer
生成特征:
让我们使用 scikit-learn 的 CountVectorizer
为客户评论生成一个 BoW 矩阵:
# Import CountVectorizer and RegexTokenizer
from nltk.tokenize import RegexpTokenizer
from sklearn.feature_extraction.text import CountVectorizer
# Create Regex tokenizer for removing special symbols and numeric values
regex_tokenizer = RegexpTokenizer(r'[a-zA-Z]+')
# Initialize CountVectorizer object
count_vectorizer = CountVectorizer(lowercase=True,
stop_words='english',
ngram_range = (1,1),
tokenizer = regex_tokenizer.tokenize)
# Fit and transform the dataset
count_vectors = count_vectorizer.fit_transform( df['verified_reviews'])
在前面的代码中,我们创建了一个RegexTokenizer
对象,并使用输入的正则表达式来去除特殊字符和符号。之后,创建了CountVectorizer
对象,并对已验证的评论执行了 fit 和 transform 操作。这里,CountVectorizer
接受一些参数,如lowercase
用于将关键字转换为小写,stop_words
用于指定特定语言的停用词列表,ngram_range
用于指定单词一元组、二元组或三元组,而tokenizer
则用来传递tokenizer
对象。RegexTokenizer
对象被传递给tokenizer
参数。最后,我们调用了fit_transform()
函数,该函数根据指定的参数将文本评论转换为文档-词项矩阵(DTM)。
- 分割训练集和测试集:
让我们使用train_test_split()
将特征集和目标列分割为feature_train
、feature_test
、target_train
和target_test
。train_test_split()
接受依赖和独立数据帧、test_size
和random_state
作为参数。这里,test_size
决定了训练集与测试集的比例(即,test_size 0.3
表示 30%的数据作为测试集,剩下的 70%作为训练集),而random_state
则用作种子值,以便每次能重复相同的数据分割。如果random_state
为None
,则会每次随机分割记录,这会导致不同的性能度量:
# Import train_test_split
from sklearn.model_selection import train_test_split
# Partition data into training and testing set
feature_train, feature_test, target_train, target_test = train_test_split(count_vectors, df['feedback'], test_size=0.3, random_state=1)
在前面的代码中,我们使用train_test_split()
方法将特征集和目标列分割为feature_train
、feature_test
、target_train
和target_test
。
- 使用逻辑回归构建分类模型:
在这一部分,我们将构建逻辑回归模型,通过 BoW(或CountVectorizer
)对评论情感进行分类。让我们创建逻辑回归模型:
# import logistic regression scikit-learn model
from sklearn.linear_model import LogisticRegression
# Create logistic regression model object
logreg = LogisticRegression(solver='lbfgs')
# fit the model with data
logreg.fit(feature_train,target_train)
# Forecast the target variable for given test dataset
predictions = logreg.predict(feature_test)
在前面的代码中,我们导入了LogisticRegression
并创建了LogisticRegression
对象。创建模型对象后,我们对训练数据执行了fit()
操作,并使用predict()
对测试数据集进行情感预测。
- 评估分类模型:
让我们使用metrics
类及其方法——accuracy_score
、precision_score
和recall_score
来评估分类模型:
# Import metrics module for performance evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# Assess model performance using accuracy measure
print("Logistic Regression Model Accuracy:",accuracy_score(target_test, predictions))
# Calculate model precision
print("Logistic Regression Model Precision:",precision_score(target_test, predictions))
# Calculate model recall
print("Logistic Regression Model Recall:",recall_score(target_test, predictions))
# Calculate model f1 score
print("Logistic Regression Model F1-Score:",f1_score(target_test, predictions))
这会产生以下输出:
Logistic Regression Model Accuracy: 0.9428571428571428
Logistic Regression Model Precision: 0.952433628318584
Logistic Regression Model Recall: 0.9873853211009175
Logistic Regression Model F1-Score: 0.9695945945945945
在前面的代码中,我们使用scikit-learn metrics
函数评估了模型的性能,指标包括准确率、精确度、召回率和 F1 值。所有这些度量都大于 94%,所以我们可以说我们的模型表现良好,能够准确分类情感级别,并且精确度和召回率都很高。
使用 TF-IDF 进行分类
在这一小节中,我们将基于 TF-IDF 执行情感分析和文本分类。这里,TF-IDF 是通过scikit-learn
库生成的。让我们看看如何使用 TF-IDF 特征执行情感分析,具体步骤如下:
- 加载数据集:
构建机器学习模型的第一步是加载数据集。
首先,使用 pandas 的read_csv()
函数读取数据:
# Import libraries
import pandas as pd
# read the dataset
df=pd.read_csv('amazon_alexa.tsv', sep='\t')
# Show top 5-records
df.head()
这将产生以下输出:
在前面的输出数据帧中,我们看到 Alexa 评论数据集包含五列:rating、date、variation、verified_reviews和feedback。
- 使用
TfidfVectorizer
生成特征:
让我们使用 scikit-learn 的TfidfVectorizer
生成客户评论的 TF-IDF 矩阵:
# Import TfidfVectorizer and RegexTokenizer
from nltk.tokenize import RegexpTokenizer
from sklearn.feature_extraction.text import TfidfVectorizer
# Create Regex tokenizer for removing special symbols and numeric values
regex_tokenizer = RegexpTokenizer(r'[a-zA-Z]+')
# Initialize TfidfVectorizer object
tfidf = TfidfVectorizer(lowercase=True, stop_words ='english',ngram_range = (1,1),tokenizer = regex_tokenizer.tokenize)
# Fit and transform the dataset
text_tfidf = tfidf.fit_transform(df['verified_reviews'])
在前面的代码中,我们创建了一个RegexTokenizer
对象,并使用一个输入的正则表达式来去除特殊字符和符号。之后,我们创建了TfidfVectorizer
对象,并对已验证的评论进行了拟合和转换操作。在这里,TfidfVectorizer
接受一些参数,比如lowercase
,用于将关键词转换为小写;stop_words
,指定语言特定的停用词列表;ngram_range
,指定单词组(unigram)、双词组(bigram)或三词组(trigram);tokenizer
则用于传递tokenizer
对象。RegexTokenizer
对象被传递给tokenizer
参数。最后,我们调用了fit_transform()
函数,根据指定的参数将文本评论转换为文档-词项矩阵(DTM)。
- 分割训练集和测试集:
我们使用train_test_split()
将特征集和目标列分割成feature_train
、feature_test
、target_train
和target_test
。train_test_split()
接受依赖变量和独立数据帧、test_size
和random_state
作为参数。让我们将数据集分割为训练集和测试集:
# Import train_test_split
from sklearn.model_selection import train_test_split
# Partition data into training and testing set
from sklearn.model_selection import train_test_split
feature_train, feature_test, target_train, target_test = train_test_split(text_tfidf, df['feedback'], test_size=0.3, random_state=1)
在前面的代码中,我们使用train_test_split()
方法将特征集和目标列分割成feature_train
、feature_test
、target_train
和target_test
。
- 使用逻辑回归构建分类模型:
在本节中,我们将构建逻辑回归模型,以使用 TF-IDF 对评论情感进行分类。让我们创建逻辑回归模型:
# import logistic regression scikit-learn model
from sklearn.linear_model import LogisticRegression
# instantiate the model
logreg = LogisticRegression(solver='lbfgs')
# fit the model with data
logreg.fit(feature_train,target_train)
# Forecast the target variable for given test dataset
predictions = logreg.predict(feature_test)
在前面的代码中,我们导入了LogisticRegression
并创建了LogisticRegression
对象。在创建模型对象之后,我们对训练数据进行了fit()
操作,并使用predict()
对测试数据集进行情感预测。
- 评估分类模型:
让我们使用metrics
类及其方法——accuracy_score
、precision_score
和recall_score
来评估分类模型:
# Import metrics module for performance evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# Assess model performance using accuracy measure
print("Logistic Regression Model Accuracy:",accuracy_score(target_test, predictions))
# Calculate model precision
print("Logistic Regression Model Precision:",precision_score(target_test, predictions))
# Calculate model recall
print("Logistic Regression Model Recall:",recall_score(target_test, predictions))
# Calculate model f1 score
print("Logistic Regression Model F1-Score:",f1_score(target_test, predictions))
这将产生以下输出:
Logistic Regression Model Accuracy: 0.9238095238095239
Logistic Regression Model Precision: 0.923728813559322
Logistic Regression Model Recall: 1.0
Logistic Regression Model F1-Score: 0.960352422907489
在前面的代码中,我们使用 scikit-learn 的metrics
函数,通过准确率、精确率、召回率和 F1 分数评估了模型的性能。所有指标都大于 94%,因此我们可以说我们的模型表现良好,并且能够以较好的精度和召回率对两种情感类别进行分类。在本节中,我们了解了如何通过文本分类进行情感分析。文本分类使用了 BoW 和 TF-IDF 特征。在下一节中,我们将学习如何找出两段文本之间的相似性,比如句子或段落。
文本相似度
文本相似度是确定两个最接近文本的过程。文本相似度对于查找相似文档、问题和查询非常有帮助。例如,像 Google 这样的搜索引擎使用相似度来查找文档的相关性,而像 StackOverflow 这样的问答系统或客户服务系统则使用类似问题。文本相似度通常使用两种度量标准,即 Jaccard 相似度和余弦相似度。
我们还可以使用 spaCy 中可用的相似度方法。nlp
对象的 similarity
方法返回两个句子之间的分数。我们来看以下示例:
# Import spacy
import spacy
# Load English model for tokenizer, tagger, parser, and NER
nlp = spacy.load('en')
# Create documents
doc1 = nlp(u'I love pets.')
doc2 = nlp(u'I hate pets')
# Find similarity
print(doc1.similarity(doc2))
这将产生以下输出:
0.724494176985974
<ipython-input-32-f157deaa344d>:12: UserWarning: [W007] The model you're using has no word vectors loaded, so the result of the Doc.similarity method will be based on the tagger, parser and NER, which may not give useful similarity judgements. This may happen if you're using one of the small models, e.g. `en_core_web_sm`, which don't ship with word vectors and only use context-sensitive tensors. You can always add your own word vectors, or use one of the larger models instead if available.
在前面的代码块中,我们使用 spaCy 的 similarity()
函数计算了两个句子之间的相似度。spaCy 的相似度函数在使用较小模型(如 en_core_web_sm
和 en
模型)时效果不佳;因此你会收到一个警告:UserWarning: [W007]。要去除此警告,请使用更大的模型,如 en_core_web_lg
。
Jaccard 相似度
Jaccard 相似度通过两个集合中共同单词(交集)与完全独特单词(并集)的比例来计算相似度。它会列出每个句子或文档中的唯一单词列表。这个方法适用于单词重复不重要的场景。Jaccard 相似度的范围是 0-100%;百分比越高,两个集合的相似度越大:
让我们看一个 Jaccard 相似度的示例:
def jaccard_similarity(sent1, sent2):
"""Find text similarity using jaccard similarity"""
# Tokenize sentences
token1 = set(sent1.split())
token2 = set(sent2.split())
# intersection between tokens of two sentences
intersection_tokens = token1.intersection(token2)
# Union between tokens of two sentences
union_tokens=token1.union(token2)
# Cosine Similarity
sim_= float(len(intersection_tokens) / len(union_tokens))
return sim_
jaccard_similarity('I love pets.','I hate pets.')
这将产生以下输出:
0.5
在上述示例中,我们创建了一个函数 jaccard_similarity()
,该函数接受两个参数 sent1
和 sent2
。它将计算两个句子中关键词的交集与并集之间的比例。
余弦相似度
余弦相似度计算两个多维投影向量之间的夹角余弦值。它表示两个文档之间的关系。两个向量可以由词袋模型、TF-IDF 或任何等效的文档向量组成。它适用于单词重复重要的情况。余弦相似度可以测量文本相似度,而不考虑文档的大小。
让我们看一个余弦相似度的示例:
# Let's import text feature extraction TfidfVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
docs=['I love pets.','I hate pets.']
# Initialize TfidfVectorizer object
tfidf= TfidfVectorizer()
# Fit and transform the given data
tfidf_vector = tfidf.fit_transform(docs)
# Import cosine_similarity metrics
from sklearn.metrics.pairwise import cosine_similarity
# compute similarity using cosine similarity
cos_sim=cosine_similarity(tfidf_vector, tfidf_vector)
print(cos_sim)
这将产生以下输出:
[[1\. 0.33609693]
[0.33609693 1\. ]]
在上述示例中,首先我们导入 TfidfVectorizer
并生成给定文档的 TF-IDF 向量。之后,我们应用 cosine_similarity()
度量标准对文档列表进行相似度计算。
总结
本章探讨了使用 NLTK 和 spaCy 进行文本分析。主要内容包括文本预处理、情感分析和文本相似度。章节从文本预处理任务入手,如文本标准化、分词、去除停用词、词干提取和词形还原。我们还重点讲解了如何创建词云、识别给定文本中的实体并找到词元之间的依赖关系。在后续部分,我们将重点讨论 BoW、TFIDF、情感分析和文本分类。
下一章,第十三章,图像数据分析,主要讲解图像处理、基本图像处理操作以及使用 OpenCV 进行人脸检测。该章节从图像颜色模型开始,并介绍了图像操作,如在图像上绘制、调整图像大小、翻转和模糊图像。在后续部分,将重点讨论如何在人脸检测输入图像中进行分析。
第十四章:分析图像数据
我们正处在信息时代,每一个动作都会生成各种格式的数据,如文本、图像、地理空间数据和视频。智能手机已经进入世界各地的乡村,人们正在捕捉活动,特别是图像和视频,并将它们分享到社交媒体平台。这就是大量数据产生的方式,而且大部分数据以图像和视频的形式存在。工业和研究机构希望分析图像和视频数据集,以创造价值并制定自动化解决方案以降低成本。图像处理和计算机视觉是探索和开发基于图像和视频的解决方案的领域。在计算机视觉领域,有许多研究、创新和初创企业的机会。在本章中,我们将重点介绍图像处理的基础知识,帮助你建立计算机视觉领域的基本知识。
图像处理是计算机视觉的一个子集。计算机视觉是机器学习和人工智能领域内的一个高级且更强大的领域。计算机视觉提供了大量的应用,例如物体检测、图像和物体分类、图像字幕生成和图像分割。图像可以在信号处理中定义为二维信号,在几何学中定义为二维或三维的点集,在 Python 中则是二维或三维的 NumPy 数组。图像处理是指处理图像数据,并执行绘制、书写、调整大小、翻转、模糊、改变亮度和检测人脸等操作。在本章中,我们将详细探讨所有这些图像处理操作。
本章将涵盖以下主题:
-
安装 OpenCV
-
理解图像数据
-
色彩模型
-
在图像上绘制
-
在图像上写字
-
调整图像大小
-
翻转图像
-
改变亮度
-
模糊图像
-
人脸检测
技术要求
本章具有以下技术要求:
-
你可以通过以下 Github 链接找到代码、人脸分类器文件以及数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter13
. -
所有代码块均可在
ch13.ipynb
文件中找到。 -
本章使用
.jpg
/.jpeg
文件(google.jpg
、image.jpg
、messi.png
、nature.jpeg
、barcelona.jpeg
和tajmahal.jpg
)进行练习。 -
本章使用一个人脸分类器 XML 文件(
haarcascade_frontalface_default.xml
)。 -
在本章中,我们将使用 OpenCV、NumPy 和 matplotlib Python 库。
安装 OpenCV
OpenCV 是一个开源库,提供计算机视觉操作,如图像和视频分析。OpenCV 主要由 Intel 使用 C++开发,并提供 Python、Java 和 Matlab 的接口。OpenCV 具有以下特点:
-
它是一个开源的图像处理 Python 库。
-
OpenCV 是图像处理和计算机视觉的核心 Python 库。
-
OpenCV 易于学习和在网页和移动应用程序中部署。
-
OpenCV 在 Python 中是一个 API 和它 C++核心实现的包装器。
-
它因为后台 C++代码而运行速度很快。
我们可以使用以下命令安装 OpenCV:
pip install opencv-python
使用前面提到的 pip 命令,我们可以轻松安装 OpenCV。OpenCV 是最受欢迎的图像处理和计算机视觉库。它提供了多种与图像分析操作相关的用例,例如提高图像质量、过滤和转换图像、在图像上绘制、改变颜色、检测人脸和物体、识别人类动作、跟踪物体、分析运动以及查找相似图像。安装完 OpenCV 库后,接下来就是了解图像处理的基础。
理解图像数据
图像数据是一个二维数组或函数f(x,y)
,带有空间坐标。coordinate(x,y)
的幅度称为强度。在 Python 中,图像是一个二维或三维的 NumPy 数组,包含像素值。像素是最小的图像单元,决定图像的质量。像素数量越多,分辨率越高。此外,还有多种图像格式可用,例如.jpeg
、.png
、.gif
和.tiff
。这些文件格式有助于组织和管理数字图像文件。在分析图像数据之前,我们需要了解图像的类型。图像数据有三种类型:
-
二值图像
-
灰度
-
颜色
二值图像
二值图像像素只有两种颜色,一般是黑色和白色。二值图像像素仅使用二进制值 0 或 1。
上面的图像是二值图像的示例。它只有两种颜色,黑色和白色,且不使用黑白的不同阴影。
灰度图像
灰度图像看起来像黑白图像。它由每个像素 8 位表示,也就是说有 256 个强度值或色调,范围从 0 到 255。这 256 种色调从纯黑到纯白过渡;0 代表纯黑,而 255 代表白色。
上面的图像是灰度图像。它是一个黑白图像,其中的色调从纯黑过渡到纯白。
彩色图像
彩色图像是由红色、蓝色和绿色三种基本颜色混合而成。这些基本颜色具有按一定比例混合形成新颜色的能力。每种颜色使用 8 位(强度值在 0 到 255 之间),即每个像素 24 位。让我们看一个彩色图像的例子:
在前面的图像文件中,我们可以看到不同强度的各种颜色阴影。了解了图像类型之后,接下来就是了解 RGB、CMYK、HSV、HSL 和灰度等颜色模型。让我们来看看颜色模型。
颜色模型
颜色模型是一种用于处理和测量原色组合的结构。它们帮助我们解释颜色在计算机屏幕或纸张上的显示方式。颜色模型可以分为两种类型:加法模型和减法模型。加法模型用于计算机屏幕,例如 RGB(红色、绿色和蓝色)模型,减法模型用于图像打印,例如 CMYK(青色、品红色、黄色和黑色)模型:
除了 RGB 和 CMYK,还有许多其他模型,如 HSV、HSL 和灰度模型。HSV 是色调、饱和度和明度的缩写。它是一个三维颜色模型,是 RGB 模型的改进版本。在 HSV 模型中,中心轴的顶部是白色,底部是黑色,剩余的颜色位于其间。在这里,色调是角度,饱和度是离中心轴的距离,明度是离轴底部的距离。
HSL 是色调、饱和度和亮度的缩写。HSV 和 HSL 的主要区别在于亮度的量和颜色在中心轴上的值。
让我们学习如何读取和显示图像文件:
# Import cv2 latest version of OpenCV library
import cv2
# Import numeric python (NumPy) library
import numpy as np
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# Read image using imread() function
image = cv2.imread('google.jpg')
# Let's check image data type
print('Image Type:',type(image))
# Let's check dimension of image
print('Image Dimension:',image.shape)
# Let's show the image
plt.imshow(image)
plt.show()
这将产生以下输出:
在前面的示例中,我们导入了cv2
、NumPy 和matplotlib.pyplot
。cv2
用于图像处理,NumPy 用于数组,matplotlib.pyplot
用于显示图像。我们使用imread()
函数读取图像,并返回一个图像数组。我们可以使用type()
函数检查其类型,并通过 NumPy 数组的shape
属性查看其形状。我们可以使用matpltlib.pyplot
模块的show()
函数显示图像。前面的图像未显示 Google 徽标图像的正确颜色,这是因为imread()
函数以 BGR 颜色模型读取图像。让我们使用cvtColor()
函数将 BGR 转换为 RGB 颜色模型,并传递标志cv2.COLOR_BGR2RGB
:
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
plt.show()
这将产生以下输出:
在这里,您可以看到 RGB 格式下正确的图像。
让我们使用imwrite()
函数将图像文件写入本地磁盘:
# Write image using imwrite()
cv2.imwrite('image.jpg',image)
Output: True
在前面的代码块中,我们已经将图像文件写入本地磁盘,文件名为image.jpg
。理解了颜色模型之后,接下来就是学习如何在图像上绘制元素。
在图像上绘制
让我们学习如何在图像上使用 OpenCV 绘制不同的图形形状,如线条、正方形或三角形。当我们在图像上绘制任何形状时,我们需要注意形状的坐标、颜色和粗细。首先,我们创建一个背景为白色或黑色的空白图像:
# Import cv2 latest version of OpenCV library
import cv2
# Import numeric python (NumPy) library
import numpy as np
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# Magic function to render the figure in a notebook
%matplotlib inline
# Let's create a black image
image_shape=(600,600,3)
black_image = np.zeros(shape=image_shape,dtype=np.int16)
# Show the image
plt.imshow(black_image)
这将产生以下输出:
在前面的示例中,我们使用 NumPy 模块的zeros()
函数创建了一个背景为黑色的空白图像。zeros()
函数创建一个给定大小的数组,并用零填充矩阵。
让我们创建一个背景为白色的空白图像:
# Create a white image
image_shape=(600,600,3)
white_image = np.zeros(shape=image_shape,dtype=np.int16)
# Set every pixel of the image to 255
white_image.fill(255)
# Show the image
plt.imshow(white_image)
这将产生以下输出:
在前面的示例中,我们使用 NumPy 模块的zeros()
函数创建了一个背景为白色的空白图像,并将每个像素的值填充为 255。zeros()
函数创建一个给定大小的数组,并用零填充矩阵。fill()
函数将给定的值赋给矩阵中的所有元素。接下来,我们将在一张黑色图像上使用 OpenCV 绘制一条线:
# Draw a line on black image
line = cv2.line(black_image,(599,0),(0,599),(0,255,0),4)
# Show image
plt.imshow(line)
这将产生以下输出:
在前面的示例中,我们使用line()
函数在黑色图像上绘制了绿色线条。line()
函数接受以下参数:图像文件,start_point
,end_point
,颜色和粗细。在我们的示例中,起点和终点分别是 (599,0) 和 (0,599),颜色元组是 (0,255,0),粗细为 4。同样,我们可以在白色图像上创建一条线。让我们看看以下示例:
# Let's draw a blue line on white image
line = cv2.line(white_image,(599,0),(0,599),(0,0,255),4)
# Show the image
plt.imshow(line)
这将产生以下输出:
让我们看一个在白色图像上绘制圆形的示例:
# Let's create a white image
img_shape=(600,600,3)
white_image = np.zeros(shape=image_shape,dtype=np.int16)
# Set every pixel of the image to 255
white_image.fill(255)
# Draw a red circle on white image
circle=cv2.circle(white_image,(300, 300), 100, (255,0,0),6)
# Show the image
plt.imshow(circle)
这将产生以下输出:
在前面的示例中,我们创建了一个白色图像并使用circle()
函数绘制了一个圆。circle()
函数接受以下参数:图像,center_coordinates
,半径,颜色和粗细。在我们的示例中,圆心是 (300, 300),半径是 100,颜色元组是 (255,0,0),粗细是 6。
让我们看一个在黑色图像上绘制矩形的示例:
# Let's create a black image
img_shape=(600,600,3)
black_image = np.zeros(shape=image_shape,dtype=np.int16)
# Draw a green rectangle on black image
rectangle= cv2.rectangle(black_image,(200,200),(400,500),(0,255,0),5)
# Show the image
plt.imshow(rectangle)
这将产生以下输出:
在前面的示例中,我们创建了一个黑色图像,并使用rectangle()
函数绘制了一个矩形。rectangle()
函数接受以下参数:图像,start_point
,end_point
,颜色和粗细。这里,粗细还可以接受一个参数-1
,-1
像素值将填充矩形形状并指定颜色。让我们看一个填充矩形的示例:
# Let's create a black image
img_shape=(600,600,3)
black_image = np.zeros(shape=image_shape,dtype=np.int16)
# Draw a green filled rectangle on black image
rectangle= cv2.rectangle(black_image,(200,200),(400,500),(0,255,0),-1)
# Show the image
plt.imshow(rectangle)
这将产生以下输出:
在前面的例子中,我们通过传递厚度值为-1 px 来填充矩形。简而言之,我们可以说,线条主要以起始点和终点为输入,矩形以左上角和右下角坐标为输入,圆形则以中心坐标和半径值为输入。
在图像上写字
在前一节中,我们在图像上创建了各种形状。现在,我们将学习如何在图像上写文字。在图像上写文字类似于绘制形状。让我们看看在图像上写字的例子:
# Let's create a black image
img_shape=(600,800,3)
black_image = np.zeros(shape=image_shape,dtype=np.int16)
# Write on black image
text = cv2.putText(black_image,'Thanksgiving',(10,500),
cv2.FONT_HERSHEY_SIMPLEX, 3,(255,0,0),2,cv2.LINE_AA)
# Display the image
plt.imshow(text)
这将得到以下输出:
在前面的例子中,我们创建了一个颜色为黑色的空白图像。我们使用putText()
函数在图像上写了文字。putText()
函数将接受以下参数:图像、文字、左下角坐标、字体、fontScale
、颜色、厚度和linetype
。
调整图像大小
调整图像大小是指改变给定图像的尺寸或缩放。缩放或调整大小可以通过宽度、高度或两者进行。调整图像大小的应用之一是训练深度学习模型,在这种情况下,缩小的图像尺寸可以加速训练。本书不涉及训练深度学习模型。如果你感兴趣,可以参考 Packt 出版的任何深度学习书籍。让我们看看调整图像大小的例子:
# Import cv2 module
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# read image
image = cv2.imread('tajmahal.jpg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
这将得到以下输出:
在前面的代码中,我们读取了图像并将其从 BGR 空间转换为 RGB 空间。现在让我们使用resize()
函数来调整图像大小:
# Resize the image
image_resized = cv2.resize(rgb_image, (200, 200))
interpolation = cv2.INTER_NEAREST
# Display the image
plt.imshow(image_resized)
这将得到以下输出:
在前面的例子中,我们读取了图像,使用cvtColor()
函数将 BGR 转换为 RGB 颜色,并使用resize()
函数调整了图像大小。resize()
函数将接受以下参数:图像、尺寸和插值。插值用于缩放无摩尔纹的图像。插值接受以下标志之一:INTER_NEAREST
(最近邻插值)、INTER_LINEAR
(双线性插值)和INTER_AREA
(使用像素区域关系进行重采样)。
翻转图像
翻转图像相当于镜像效果。让我们学习如何沿* x 轴(垂直翻转)、 y *轴(水平翻转)或两个轴翻转图像。OpenCV 提供了flip()
函数来翻转图像。flip()
函数将接受两个参数:图像和 flipcode。图像是像素值的 NumPy 数组,flipcode 定义了翻转类型,如水平翻转、垂直翻转或两者。以下 flipcode 值表示不同类型的翻转:
-
Flipcode > 0 表示水平翻转。
-
Flipcode = 0 表示垂直翻转。
-
Flipcode < 0 表示水平和垂直翻转。
让我们看看翻转图像的例子:
# Import OpenCV module
import cv2
# Import NumPy
import numpy as np
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# Read image
image = cv2.imread('messi.png')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
这将得到以下输出:
这是原始图像,展示了莱昂内尔·梅西的照片。让我们使用flip()
函数通过将 1 作为 flipcode 传递给flip()
函数,将图像水平翻转:
# Flipping image (Horizontal flipping)
image_flip = cv2.flip(rgb_image, 1)
# Display the image
plt.imshow(image_flip)
这将产生以下输出:
这是水平翻转后的图像。让我们将原始图像垂直翻转:
# Flipping image (Vertical flipping)
image_flip = cv2.flip(rgb_image,0)
# Display the image
plt.imshow(image_flip)
这将产生以下输出:
你可以看到垂直翻转后的图像。让我们在两个轴上翻转原始图像:
# Flipping image (Horizontal and vertical flipping)
image_flip = cv2.flip(rgb_image, -1)
# Display the image
plt.imshow(image_flip)
这将产生以下输出:
你可以看到垂直和水平翻转后的图像。在翻转图像后,接下来让我们学习如何更改图像的亮度。
改变亮度
亮度是一个由视觉感知决定的相对概念。有时候很难感知亮度。像素强度的值可以帮助我们找到更亮的图像。例如,如果两个像素的强度值分别是 110 和 230,那么后者更亮。
在 OpenCV 中,调整图像亮度是一个非常基础的操作。亮度可以通过改变图像中每个像素的强度来控制:
# Import cv2 latest version of OpenCV library
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# Magic function to render the figure in a notebook
%matplotlib inline
# Read image
image = cv2.imread('nature.jpeg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
这将产生以下输出:
在前面的代码示例中,我们读取了图像并将基于 BGR 色彩模型的图像转换为基于 RGB 色彩模型的图像。接下来,让我们在以下代码块中更改图像的亮度:
# set weightage for alpha and betaboth the matrix
alpha_=1
beta_=50
# Add weight to the original image to change the brightness
image_change=cv2.addWeighted(rgb_image, alpha_,
np.zeros(image.shape,image.dtype),0, beta_)
# Display the image
plt.imshow(image_change)
这将产生以下输出:
在前面的示例中,我们使用addWeighted()
函数将两个矩阵按给定的权重(alpha 和 beta)相加。addWeighted()
函数需要以下参数:first_image
、alpha
、second_image
、gamma
和beta
。在我们的示例中,first_image
是输入图像,而second_image
是空矩阵。alpha
和beta
的值是两个矩阵的权重,gamma
为 0。
图像模糊
模糊是图像预处理中的一个关键步骤。在预处理过程中,去除噪声对算法的表现有很大影响。模糊是减少图像数据噪声的过程,以实现更好的准确性。模糊也帮助我们处理像素强度。
让我们看一个模糊图像的示例:
# Import OpenCV module
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# Magic function to render the figure in a notebook
%matplotlib inline
# Read image
image = cv2.imread('tajmahal.jpg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
这将产生以下输出:
在前面的代码示例中,我们读取了图像并将其从 BGR 转换为 RGB 图像。现在,让我们使用blur()
函数对其进行模糊处理。blur()
函数需要两个参数:图像和内核大小。blur()
函数使用平均模糊方法:
# Blur the image using blur() function
image_blur = cv2.blur(rgb_image,(15,15))
# Display the image
plt.imshow(image_blur)
这将产生以下输出:
在前面的示例中,我们读取了图像,使用cvtColor()
函数将 BGR 转换为 RGB 颜色,并显示了图像。在这里,我们使用blur()
函数对图像进行了模糊处理。blur()
函数应用了均值模糊,使用了归一化的盒式滤波器。blur()
函数接受以下参数:图像和内核大小。
我们已经看到了使用均值模糊处理的图像。让我们探索使用高斯模糊进行模糊处理。在这种模糊处理中,使用了高斯核而不是盒式滤波器。GaussianBlur()
将接受图像和内核大小。内核大小将是一个由宽度和高度组成的元组。宽度和高度必须是正数且为奇数:
# Import cv2 module
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# read image
image = cv2.imread('tajmahal.jpg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Blurring the image using Gaussian Blur
image_blur = cv2.GaussianBlur(rgb_image, (7,7), 0)
# Display the image
plt.imshow(image_blur)
这将产生以下输出:
让我们探索给定图像的中值模糊。中值模糊将内核区域的像素取出,并将中央元素替换为中位值。medianBlur()
将接受图像和内核大小作为参数。建议内核大小应该是一个大于 1 的奇数,例如 3、5、7、9、11 等:
# Import cv2 module
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# Convert image color space BGR to RGB
%matplotlib inline
# read image
image = cv2.imread('tajmahal.jpg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Blurring the image using Median blurring
image_blur = cv2.medianBlur(rgb_image,11)
# Display the image
plt.imshow(image_blur)
这将产生以下输出:
在前面的代码块中,我们使用中值模糊对图像进行了模糊处理。在这里,我们使用了medianBlur()
方法进行中值模糊处理,并且可以在输出中观察到模糊后的图像。在本节中,我们讨论了均值模糊、高斯模糊和中值模糊技术。在下一节中,我们将学习如何在图像中检测人脸。
人脸检测
现在,大家都在使用 Facebook,你们一定都见过 Facebook 上的图像人脸识别。人脸识别能够识别出一张脸属于谁,而人脸检测仅仅是找出图像中的人脸,也就是说,人脸检测并不确定被检测到的脸属于谁。人脸检测在许多应用中都很常见;例如,统计图像中的人数。在人脸检测中,算法试图在数字图像中找到人脸。
人脸检测是一个分类问题。我们可以将图像分为两类,脸或非脸。我们需要大量的图像来训练这样的分类模型。幸运的是,OpenCV 提供了预训练的模型,如 Haar 特征级联分类器和局部二值模式(LBP)分类器,这些分类器在数千张图像上进行了训练。在我们的示例中,我们将使用 Haar 特征提取来检测人脸。让我们看看如何使用 OpenCV 在图像中捕捉人脸:
- 读取图像并将其转换为灰度图:
# Import cv2 latest version of OpenCV library
import cv2
# Import numeric python (NumPy) library
import numpy as np
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# Read image
image= cv2.imread('messi.png')
# Convert image color space BGR to grayscale
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Displaying the grayscale image
plt.imshow(image_gray, cmap='gray')
这将产生以下输出:
在前面的代码示例中,我们读取了莱昂内尔·梅西的图像,并使用cvtColor()
函数将其转换为灰度图像。
让我们在生成的灰度图像中找到人脸:
- 加载 Haar 级联人脸分类器文件:
# Load the haar cascade face classifier file
haar_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
- 获取图像中所有人脸的坐标:
# Get the faces coordinates for all the faces in the image
faces_cordinates = haar_cascade.detectMultiScale(image_gray, scaleFactor = 1.3, minNeighbors = 7);
- 在检测到的面部上绘制矩形:
# Draw rectangle on detected faces
for (p,q,r,s) in faces_cordinates:
cv2.rectangle(image, (p, q), (p+r, q+s), (255,255,0), 2)
- 将图像颜色空间从 BGR 转换为 RGB 并显示图像:
# Convert image color space BGR to RGB
image_rgb=cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Display face detected image
plt.imshow(image_rgb)
这将得到以下输出:
在前面的示例中,我们将 BGR 图像转换为灰度图像。OpenCV 已经预先训练了面部、眼睛和微笑检测的分类器。我们可以使用预训练的面部级联分类器 XML 文件(haarcascade_frontalface_default.xml
)。你可以从官方 Git 仓库获取分类器文件(haarcascade_frontalface_default.xml
):github.com/opencv/opencv/tree/master/data/haarcascades
,或者你也可以从我们的 GitHub 仓库获取: github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter13
。
完成后,我们可以将图像传递给级联分类器,并获取图像中的面部坐标。我们已经使用rectangle()
函数在这些面部坐标上绘制了矩形。显示输出之前,我们需要将 RGB 图像转换为 BGR,以便正确显示它。让我们在一张包含多个面部的图像上尝试这个示例:
# Read the image
image= cv2.imread('barcelona.jpeg')
# Convert image BGR to grayscale
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Load the haar cascade face classifier file
haar_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
# Get the faces coordinates for all the faces in the image
faces_cordinates = haar_cascade.detectMultiScale(image_gray, scaleFactor = 1.3, minNeighbors = 5);
# Draw rectangle on detected faces
for (x1,y1,x2,y2) in faces_cordinates:
cv2.rectangle(image, (x1, y1), (x1+x2, y1+y2), (255,255,0), 2)
# Convert image color space BGR to RGB
image_rgb=cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Display face detected the image
plt.imshow(image_rgb)
这将得到以下输出:
在前面的示例中,我们可以看到程序已经检测到图像中的所有面部。
总结
本章讨论了使用 OpenCV 进行图像处理。本章的主要内容是基础的图像处理操作和面部检测。章节开始时介绍了图像类型和图像颜色模型。在后续部分,重点讨论了图像操作,例如绘制、调整大小、翻转和模糊图像。在最后部分,我们讨论了在给定输入图像中的面部检测。
下一章,第十四章,使用 Dask 进行并行计算,将重点讨论使用 Dask 对基本的数据科学 Python 库(如 Pandas、NumPy 和 scikit-learn)进行并行计算。章节将从 Dask 的数据类型(如数据框、数组和袋子)开始。在后续部分,我们将把重点从数据框和数组转移到使用 Dask 进行并行延迟、预处理和机器学习算法。
第十五章:使用 Dask 进行并行计算
Dask 是处理数据并行方式的最简单方法之一。这个平台适合那些在大数据集上挣扎的 pandas 爱好者。Dask 提供类似 Hadoop 和 Spark 的可扩展性,同时具备 Airflow 和 Luigi 提供的灵活性。Dask 可以用来处理无法放入 RAM 中的 pandas 数据框和 NumPy 数组。它将这些数据结构拆分并进行并行处理,同时对代码的修改最小化。它利用笔记本电脑的计算能力,并可以本地运行。我们也可以像部署 Python 应用一样在大规模分布式系统上部署 Dask。Dask 可以并行执行数据,并减少处理时间。它还可以在不迁移到更大或分布式环境的情况下,扩展工作站的计算能力。
本章的主要目标是学习如何使用 Dask 在大规模数据集上进行灵活的并行计算。该平台提供三种并行执行的数据类型:Dask 数组、Dask 数据框和 Dask 包。Dask 数组类似于 NumPy 数组,而 Dask 数据框则类似于 pandas 数据框。两者都可以并行执行数据。Dask 包是 Python 对象的封装器,使它们能够同时执行操作。本章还将介绍 Dask Delayed,这是一个将代码并行化的概念。Dask 还提供了数据预处理和机器学习模型开发的并行模式。
在本章中,我们将涵盖以下主题:
-
使用 Dask 进行并行计算
-
Dask 数据类型
-
Dask Delayed
-
大规模数据预处理
-
大规模机器学习
让我们开始吧!
使用 Dask 进行并行计算
Python 是数据专业人员中最流行的编程语言之一。Python 的数据科学库,如 Numpy、Pandas、Scipy 和 Scikit-learn,可以顺序地执行数据科学任务。然而,对于大数据集,这些库在处理时会变得非常缓慢,因为它们无法在单台机器上进行扩展。这时,Dask 就派上了用场。Dask 帮助数据专业人员处理超过单台机器 RAM 大小的数据集。Dask 利用处理器的多个核心,或者将其作为分布式计算环境使用。Dask 具有以下特点:
-
它熟悉现有的 Python 库
-
它提供灵活的任务调度
-
它为并行计算提供了单机和分布式环境
-
它执行快速操作,具有较低的延迟和开销
-
它可以实现横向扩展和缩减
Dask 提供了与 pandas、NumPy 和 Scikit-learn 相似的概念,这使得它更容易学习。它是一个开源的并行计算 Python 库,运行在 pandas、NumPy 和 Scikit-learn 之上,可以跨多个 CPU 核心或多个系统运行。例如,如果一台笔记本电脑有四核处理器,Dask 就会使用 4 个核心来处理数据。如果数据无法全部装入内存,它会在处理之前将数据分割成多个块。Dask 扩展了 pandas 和 NumPy 的能力,用来处理适中的大数据集。让我们通过以下图表了解 Dask 如何并行执行操作:
Dask 创建一个任务图来并行执行程序。在任务图中,节点表示任务,节点之间的边表示一个任务对另一个任务的依赖关系。
让我们在本地系统上安装 Dask 库。默认情况下,Anaconda 已经安装了 Dask,但如果你想重新安装或更新 Dask,可以使用以下命令:
conda install dask
我们也可以使用pip
命令来安装它,如下所示:
pip install dask
有了这些,我们已经学会了如何安装 dask
库以实现并行和快速执行。现在,让我们来看看 Dask 库的核心数据类型。
Dask 数据类型
在计算机编程中,数据类型是编写任何功能的基本构建块。它们帮助我们处理不同类型的变量。数据类型是存储在变量中的值的种类。数据类型可以是基本数据类型和次级数据类型。
基本数据类型是像 int、float 和 char 这样的基本数据类型,而次级数据类型是通过基本数据类型开发的,如列表、数组、字符串和数据框。Dask 提供了三种用于并行操作的数据结构:数据框、袋和数组。这些数据结构将数据分割成多个分区,并将它们分配到集群中的多个节点。Dask 数据框是多个小的 pandas 数据框的组合,它的操作方式类似。Dask 数组类似于 NumPy 数组,并支持 NumPy 的所有操作。最后,Dask 袋用于处理大型 Python 对象。
现在,到了探索这些数据类型的时候了。我们从 Dask 数组开始。
Dask 数组
Dask 数组是 NumPy n 维数组的抽象,通过并行处理并分割成多个子数组。这些小数组可以存储在本地或分布式远程机器上。Dask 数组通过利用系统中所有可用的核心来计算大型数组。它们可以应用于统计、优化、生物信息学、商业领域、环境科学等多个领域。它们还支持大量的 NumPy 操作,如算术和标量运算、聚合操作、矩阵和线性代数运算。然而,它们不支持未知形状。此外,tolist
和sort
操作在并行执行时较为困难。让我们通过以下图示来了解 Dask 数组如何将数据分解成 NumPy 数组并并行执行:
如我们所见,有多个形状不同的块,这些块都表示 NumPy 数组。这些数组形成了一个 Dask 数组,并可以在多个机器上执行。让我们用 Dask 创建一个数组:
# import Dask Array
import dask.array as da
# Create Dask Array using arange() function and generate values from 0 to 17
a = da.arange(18, chunks=4)
# Compute the array
a.compute()
这将产生以下输出:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,17])
在前面的例子中,我们使用了compute()
函数来获取最终输出。da.arange()
函数只会创建计算图,而compute()
函数用于执行该图。我们使用da.arange()
函数生成了 18 个值,块大小为 4。我们也来检查一下每个分区中的块:
# Check the chunk size
a.chunks
这将产生以下输出:
((4, 4, 4, 4, 2),)
在前面的例子中,一个包含 18 个值的数组被分成了五个部分,每个部分的大小为 4,其中前四个部分各包含 4 个值,最后一个部分包含 2 个值。
Dask DataFrame
Dask DataFrame 是 pandas DataFrame 的抽象。它们是并行处理的,并分割成多个较小的 pandas DataFrame,如下图所示:
这些小的 DataFrame 可以存储在本地或分布式远程机器上。Dask DataFrame 通过利用系统中所有可用的核心来计算大型 DataFrame。它们通过索引来协调 DataFrame,并支持标准的 pandas 操作,如groupby
、join
和时间序列
。与在索引操作中使用set_index()
和join()
相比,Dask DataFrame 在元素级、行级、isin()
和日期操作上执行速度更快。现在,让我们来测试一下 Dask 的性能或执行速度:
# Read csv file using pandas
import pandas as pd
%time temp = pd.read_csv("HR_comma_sep.csv")
这将产生以下输出:
CPU times: user 17.1 ms, sys: 8.34 ms, total: 25.4 ms
Wall time: 36.3 ms
在前面的代码中,我们使用 pandas 的read_csv()
函数测试了文件的读取时间。现在,让我们测试 Dask 的read_csv()
函数的读取时间:
# Read csv file using Dask
import dask.dataframe as dd
%time df = dd.read_csv("HR_comma_sep.csv")
这将产生以下输出:
CPU times: user 18.8 ms, sys: 5.08 ms, total: 23.9 ms
Wall time: 25.8 ms
在这两个例子中,我们可以看到,使用 Dask 的read_csv()
函数时,数据读取的执行时间有所减少。
DataFrame 索引
Dask DataFrame 支持两种类型的索引:基于标签的索引和基于位置的索引。Dask 索引的主要问题是它不维护分区信息。这意味着执行行索引很困难;只可能进行列索引。DataFrame.iloc
仅支持基于整数的索引,而 DataFrame.loc
支持基于标签的索引。DataFrame.iloc
只能选择列。
让我们对 Dask DataFrame 执行这些索引操作:
- 首先,我们必须创建一个 DataFrame 并执行列索引:
# Import Dask and Pandas DataFrame
import dask.dataframe as dd
import pandas as pd
# Create Pandas DataFrame
df = pd.DataFrame({"P": [10, 20, 30], "Q": [40, 50, 60]},
index=['p', 'q', 'r'])
# Create Dask DataFrame
ddf = dd.from_pandas(df, npartitions=2)
# Check top records
ddf.head()
这将产生以下输出:
P Q
p 10 40
q 20 50
r 30 60
在上面的示例中,我们创建了一个 pandas DataFrame(具有 p
、q
和 r
索引,以及 P
和 Q
列),并将其转换为 Dask DataFrame。
- 在 Dask 中,列选择过程与我们在 pandas 中的操作类似。让我们在 Dask DataFrame 中选择单列:
# Single Column Selection
ddf['P']
这将产生以下输出:
Dask Series Structure:
npartitions=1
p int64
r ...
Name: P, dtype: int64
Dask Name: getitem, 2 tasks
在上面的代码中,我们通过传递列名选择了单个列。要选择多个列,我们需要传递列名的列表。
- 让我们在 Dask DataFrame 中选择多列:
# Multiple Column Selection
ddf[['Q', 'P']]
这将产生以下输出:
Dask DataFrame Structure:
Q P
npartitions=1
p int64 int64
r ... ...
Dask Name: getitem, 2 tasks
在这里,我们从可用的列列表中选择了两列。
- 现在,让我们创建一个具有整数索引的 DataFrame:
# Import Dask and Pandas DataFrame
import dask.dataframe as dd
import pandas as pd
# Create Pandas DataFrame
df = pd.DataFrame({"X": [11, 12, 13], "Y": [41, 51, 61]})
# Create Dask DataFrame
ddf = dd.from_pandas(df, npartitions=2)
# Check top records
ddf.head()
这将产生以下输出:
X Y
0 11 41
1 12 51
2 13 61
在上面的代码中,我们创建了一个 pandas DataFrame,并使用 from_pandas()
函数将其转换为 Dask DataFrame。
- 让我们使用基于位置的整数索引选择所需的列:
ddf.iloc[:, [1, 0]].compute()
这将产生以下输出:
Y X
0 41 11
1 51 12
2 61 13
在上面的代码中,我们使用 iloc
交换了列的位置,同时使用了基于位置的整数索引。
- 如果我们尝试选择所有行,我们会遇到
NotImplementedError
错误,如下所示:
ddf.iloc[0:4, [1, 0]].compute()
这将产生以下输出:
NotImplementedError: 'DataFrame.iloc' only supports selecting columns. It must be used like 'df.iloc[:, column_indexer]'.
在上面的代码块中,我们可以看到 DataFrame.iloc
仅支持选择列。
过滤数据
我们可以像对待 pandas DataFrame 一样过滤 Dask DataFrame 中的数据。让我们看一个例子:
# Import Dask DataFrame
import dask.dataframe as dd
# Read CSV file
ddf = dd.read_csv('HR_comma_sep.csv')
# See top 5 records
ddf.head(5)
这将产生以下输出:
在上面的代码中,我们使用 read_csv()
函数将人力资源 CSV 文件读入 Dask DataFrame。此输出只显示了一些列。然而,当你自己运行笔记本时,你将能够看到所有可用的列。让我们过滤数据集中低薪的员工:
# Filter employee with low salary
ddf2 = ddf[ddf.salary == 'low']
ddf2.compute().head()
这将产生以下输出:
在上面的代码中,我们通过条件将低薪员工筛选到括号内。
Groupby
groupby
操作用于聚合相似的项目。首先,它根据值拆分数据,找到相似值的聚合,然后将聚合结果合并。这可以在以下代码中看到:
# Find the average values of all the columns for employee left or stayed
ddf.groupby('left').mean().compute()
这将产生以下输出:
在前面的示例中,我们根据左侧列(显示了员工是留下还是离开公司)对数据进行了分组,并按均值进行了聚合。
将 pandas DataFrame 转换为 Dask DataFrame
Dask DataFrames 是基于 pandas DataFrames 实现的。对于数据分析师来说,学习如何将 Dask DataFrame 转换为 pandas DataFrame 是必要的。请查看以下代码:
# Import Dask DataFrame
from dask import dataframe as dd
# Convert pandas dataframe to dask dataframe
ddf = dd.from_pandas(pd_df,chunksize=4)
type(ddf)
这将导致以下输出:
dask.dataframe.core.DataFrame
在这里,我们使用from_pandas()
方法将一个 pandas DataFrame 转换为 Dask DataFrame。
将 Dask DataFrame 转换为 pandas DataFrame
在前一小节中,我们将一个 pandas DataFrame 转换为 Dask DataFrame。同样,我们也可以使用compute()
方法将 Dask DataFrame 转换为 pandas DataFrame,如下所示:
# Convert dask DataFrame to pandas DataFrame
pd_df = df.compute()
type(pd_df)
这将导致以下输出:
pandas.core.frame.DataFrame
现在,让我们学习另一个重要的主题:Dask Bags。
Dask Bags
Dask Bag 是对通用 Python 对象的抽象。它通过 Python 迭代器在较小的 Python 对象的并行接口中执行map
、filter
、fold
和groupby
操作。其执行方式类似于 PyToolz 或 PySpark RDD。Dask Bags 更适合用于非结构化和半结构化数据集,如文本、JSON 和日志文件。它们通过多进程计算来加速处理,但在工作节点间通信方面表现不佳。Bags 是不可变的数据结构,不能更改,并且与 Dask Arrays 和 DataFrames 相比速度较慢。Bags 在执行groupby
操作时也较慢,因此推荐使用foldby
代替groupby
。
现在,让我们创建各种 Dask Bag 对象并对它们执行操作。
使用 Python 可迭代项创建 Dask Bag
让我们使用 Python 可迭代项创建一些 Dask Bag 对象:
# Import dask bag
import dask.bag as db
# Create a bag of list items
items_bag = db.from_sequence([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], npartitions=3)
# Take initial two items
items_bag.take(2)
这将导致以下输出:
(1, 2)
在前面的代码中,我们使用from_sequence()
方法创建了一个列表项的 bag。from_Sequence()
方法接受一个列表并将其放入npartitions
(分区数)。让我们从列表中过滤出奇数:
# Filter the bag of list items
items_square=items_bag.filter(lambda x: x if x % 2 != 0 else None)
# Compute the results
items_square.compute()
这将导致以下输出:
[1, 3, 5, 7, 9]
在前面的代码中,我们使用filter()
方法从列表的 bag 中过滤出了奇数。现在,让我们使用map
函数将 bag 中的每个项平方:
# Square the bag of list items
items_square=items_b.map(lambda x: x**2)
# Compute the results
items_square.compute()
这将导致以下输出:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
在前面的代码中,我们使用map()
函数对 bag 中的项进行了映射。我们将这些项映射到它们的平方值。
使用文本文件创建 Dask Bag
我们可以使用read_text()
方法,通过文本文件创建一个 Dask Bag,代码如下:
# Import dask bag
import dask.bag as db
# Create a bag of text file
text = db.read_text('sample.txt')
# Show initial 2 items from text
text.take(2)
这将导致以下输出:
('Hi! how are you? \n', '\n')
在前面的代码中,我们使用read_text()
方法将文本文件读取到一个dask bag
对象中。这让我们能够展示 Dask Bag 中的前两个项目。
将 Dask Bag 存储到文本文件中
让我们将 Dask Bag 存储到文本文件中:
# Convert dask bag object into text file
text.to_textfiles('/path/to/data/*.text.gz')
这将导致以下输出:
['/path/to/data/0.text.gz']
在前面的代码中,to_textfiles()
将bag
对象转换为一个文本文件。
将 Dask Bag 存储到 DataFrame 中
让我们将 Dask Bag 存储到 DataFrame 中:
# Import dask bag
import dask.bag as db
# Create a bag of dictionary items
dict_bag = db.from_sequence([{'item_name': 'Egg', 'price': 5},
{'item_name': 'Bread', 'price': 20},
{'item_name': 'Milk', 'price': 54}],
npartitions=2)
# Convert bag object into dataframe
df = dict_bag.to_dataframe()
# Execute the graph results
df.compute()
这将产生以下输出:
在前面的示例中,我们创建了一个包含字典项的 Dask Bag,并使用 to_dataframe()
方法将其转换为 Dask DataFrame。在下一节中,我们将讨论 Dask 延迟。
Dask 延迟
Dask 延迟是一种用于并行化代码的方法。它可以延迟任务图中的依赖函数调用,并在提高性能的同时为用户提供对并行进程的完全控制。它的惰性计算帮助我们控制函数的执行。然而,这与并行执行中函数的执行时机有所不同。
让我们通过一个示例来理解 Dask 延迟的概念:
# Import dask delayed and compute
from dask import delayed, compute
# Create delayed function
@delayed
def cube(item):
return item ** 3
# Create delayed function
@delayed
def average(items):
return sum(items)/len(items)
# create a list
item_list = [2, 3, 4]
# Compute cube of given item list
cube_list= [cube(i) for i in item_list]
# Compute average of cube_list
computation_graph = average(cube_list)
# Compute the results
computation_graph.compute()
这将产生以下输出:
33.0
在前面的示例中,cube
和 average
两个方法都被 @dask.delayed
注解。创建了一个包含三个数字的列表,并计算了每个值的立方。在计算完列表值的立方后,我们计算了所有值的平均值。所有这些操作都是惰性计算,只有当程序员需要输出时,计算才会执行,执行流会被存储在计算图中。我们通过 compute()
方法来执行这一操作。在这里,所有的立方操作都将并行执行。
现在,我们将可视化计算图。然而,在此之前,我们需要安装 Graphviz 编辑器。
在 Windows 上,我们可以使用 pip
安装 Graphviz。我们还必须在环境变量中设置路径:
pip install graphviz
在 Mac 上,我们可以使用 brew
安装,如下所示:
brew install graphviz
在 Ubuntu 上,我们需要通过 sudo apt-get
命令在终端中安装:
sudo apt-get install graphviz
现在,让我们可视化计算图:
# Compute the results
computation_graph.visualize()
这将产生以下输出:
在前面的示例中,我们使用 visualize()
方法打印了一个计算图。在该图中,所有的立方体操作都是并行执行的,并且它们的结果被 average()
函数消费。
大规模数据预处理
Dask 预处理提供了 scikit-learn 功能,如缩放器、编码器和训练/测试拆分。这些预处理功能与 Dask DataFrame 和数组配合良好,因为它们可以并行地拟合和转换数据。在本节中,我们将讨论特征缩放和特征编码。
Dask 中的特征缩放
正如我们在第七章 清理混乱数据 中讨论的,特征缩放,也叫做特征归一化,用于将特征缩放到相同的水平。它可以处理不同列范围和单位的问题。Dask 还提供了具有并行执行能力的缩放方法。它使用了 scikit-learn 提供的大多数方法:
Scaler | 描述 |
---|---|
MinMaxScaler | 通过将每个特征缩放到给定范围来转换特征 |
RobustScaler | 使用对异常值稳健的统计方法进行特征缩放 |
StandardScaler | 通过去除均值并将特征缩放到单位方差来标准化特征 |
让我们对人力资源数据集中的last_evaluation
(员工表现得分)列进行缩放:
# Import Dask DataFrame
import dask.dataframe as dd
# Read CSV file
ddf = dd.read_csv('HR_comma_sep.csv')
# See top 5 records
ddf.head(5)
这将产生以下输出:
在前面的代码中,我们使用read_csv()
函数将人力资源 CSV 文件读取到 Dask DataFrame 中。前面的输出仅显示了部分可用列。然而,当你自己运行笔记本时,你将能够看到数据集中的所有列。现在,让我们对last_evalaution
列(最后评估的表现得分)进行缩放:
# Import MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
# Instantiate the MinMaxScaler Object
scaler = MinMaxScaler(feature_range=(0, 100))
# Fit the data on Scaler
scaler.fit(ddf[['last_evaluation']])
# Transform the data
performance_score=scaler.transform(ddf[['last_evaluation']])
# Let's see the scaled performance score
performance_score
这将产生以下输出:
array([[26.5625],
[78.125 ],
[81.25 ],
...,
[26.5625],
[93.75 ],
[25\. ]])
在前面的示例中,我们对last_evaluation
(最后评估的表现得分)列进行了缩放。我们将其从 0-1 的范围缩放到 0-100 的范围。接下来,我们将看一下 Dask 中的特征编码。
Dask 中的特征编码
正如我们在第七章清理杂乱数据中讨论的那样,特征编码是处理类别特征的一个非常有用的技术。Dask 还提供了具有并行执行能力的编码方法。它使用了 scikit-learn 提供的大多数方法:
编码器 | 描述 |
---|---|
LabelEncoder | 将标签编码为介于 0 和 1 之间的值,且该值小于可用类别的数量。 |
OneHotEncoder | 将类别整数特征编码为独热编码。 |
OrdinalEncoder | 将类别列编码为序数变量。 |
让我们尝试使用这些方法:
# Import Dask DataFrame
import dask.dataframe as dd
# Read CSV file
ddf = dd.read_csv('HR_comma_sep.csv')
# See top 5 records
ddf.head(5)
这将产生以下输出:
在前面的代码中,我们使用read_csv()
函数将人力资源 CSV 文件读取到 Dask DataFrame 中。前面的输出仅显示了部分可用列。然而,当你自己运行笔记本时,你将能够看到数据集中的所有列。现在,让我们对last_evalaution
列(最后评估的表现得分)进行缩放:
# Import Onehot Encoder
from dask_ml.preprocessing import Categorizer
from dask_ml.preprocessing import OneHotEncoder
from sklearn.pipeline import make_pipeline
# Create pipeline with Categorizer and OneHotEncoder
pipe = make_pipeline(Categorizer(), OneHotEncoder())
# Fit and transform the Categorizer and OneHotEncoder
pipe.fit(ddf[['salary',]])
result=pipe.transform(ddf[['salary',]])
# See top 5 records
result.head()
这将产生以下输出:
salary_low | salary_medium | salary_high | |
---|---|---|---|
0 | 1.0 | 0.0 | 0.0 |
1 | 0.0 | 1.0 | 0.0 |
2 | 0.0 | 1.0 | 0.0 |
3 | 1.0 | 0.0 | 0.0 |
4 | 1.0 | 0.0 | 0.0 |
在前面的示例中,scikit-learn
管道是通过Categorizer()
和OneHotEncoder()
创建的。然后,使用fit()
和transform()
方法对人力资源数据的薪资列进行了编码。请注意,分类器将把 DataFrame 的列转换为类别数据类型。
类似地,我们也可以使用序数编码器对薪资列进行编码。让我们来看一个例子:
# Import Onehot Encoder
from dask_ml.preprocessing import Categorizer
from dask_ml.preprocessing import OrdinalEncoder
from sklearn.pipeline import make_pipeline
# Create pipeline with Categorizer and OrdinalEncoder
pipe = make_pipeline(Categorizer(), OrdinalEncoder())
# Fit and transform the Categorizer and OneHotEncoder
pipe.fit(ddf[['salary',]])
result=pipe.transform(ddf[['salary',]])
# Let's see encoded results
result.head()
这将产生以下输出:
salary
0 0
1 1
2 1
3 0
4 0
在前面的示例中,scikit-learn 管道是通过Categorizer()
和OrdinalEncoder()
创建的。然后,使用fit()
和transform()
方法对人力资源数据的薪资列进行了编码。请注意,分类器将把 DataFrame 的列转换为类别数据类型。
大规模机器学习
Dask 提供了 Dask-ML 服务,用于使用 Python 进行大规模机器学习操作。Dask-ML 能够缩短中型数据集的模型训练时间,并进行超参数调优实验。它为机器学习操作提供了类似于 scikit-learn 的算法。
我们可以通过三种不同方式扩展 scikit-learn:使用 joblib
并通过随机森林和支持向量机(SVC)并行化 scikit-learn;使用 Dask Arrays 重新实现算法,采用广义线性模型、预处理和聚类;并将其与分布式库如 XGBoost 和 Tensorflow 配对使用。
让我们首先来看一下使用 scikit-learn 进行并行计算。
使用 scikit-learn 进行并行计算
要在单个 CPU 上使用 scikit-learn 进行并行计算,我们需要使用 joblib
。这使得 scikit-learn 操作可以并行计算。joblib
库在 Python 作业上执行并行化。Dask 可以帮助我们在多个 scikit-learn 估计器上执行并行操作。让我们来看看:
- 首先,我们需要读取数据集。我们可以像这样使用 pandas DataFrame 加载数据集:
# Import Dask DataFrame
import pandas as pd
# Read CSV file
df = pd.read_csv('HR_comma_sep.csv')
# See top 5 records
df.head(5)
这会生成以下输出:
在前面的代码中,我们通过 read_csv()
函数将人力资源 CSV 文件读入 Dask DataFrame。前面的输出仅显示了部分可用的列。然而,当你自己运行该笔记本时,你将能看到数据集中的所有列。现在,让我们对 last_evalaution
列(最后评估的表现分数)进行缩放。
- 接下来,我们需要选择因变量和自变量。为此,选择列并将数据分为因变量和自变量,如下所示:
# select the feature and target columns
data=df[['satisfaction_level', 'last_evaluation']]
label=df['left']
- 创建一个调度程序,并并行生成模型。导入
dask.distributed
客户端以在本地计算机上创建调度程序和工作者:
# Import client
from dask.distributed import Client
# Instantiate the Client
client = Client()
- 下一步是使用
sklearn.externals.joblib
创建并行后端,并编写正常的scikit-learn
代码:
# import dask_ml.joblib
from sklearn.externals.joblib import parallel_backend
with parallel_backend('dask'):
# Write normal scikit-learn code here
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
# Divide the data into two parts: training and testing set
X_train, X_test, y_train, y_test = train_test_split(data,label,
test_size=0.2,
random_state=0)
# Instantiate RandomForest Model
model = RandomForestClassifier()
# Fit the model
model.fit(X_train,y_train)
# Predict the classes
y_pred = model.predict(X_test)
# Find model accuracy
print("Accuracy:",accuracy_score(y_test, y_pred))
这会生成以下输出:
Accuracy: 0.92
前面并行生成的随机森林模型给出了 92% 的准确率,非常好。
为 Dask 重新实现机器学习算法
一些机器学习算法已由 Dask 开发团队使用 Dask Arrays 和 DataFrames 重新实现。以下是已重新实现的算法:
-
线性机器学习模型,如线性回归和逻辑回归
-
使用缩放器和编码器进行预处理
-
无监督算法,如 k-means 聚类和谱聚类
在以下小节中,我们将构建一个逻辑回归模型,并对数据集进行聚类。
逻辑回归
让我们使用逻辑回归构建一个分类器:
- 将数据集加载到 Dask DataFrame 中,如下所示:
# Read CSV file using Dask
import dask.dataframe as dd
# Read Human Resource Data
ddf = dd.read_csv("HR_comma_sep.csv")
# Let's see top 5 records
ddf.head()
这会生成以下输出:
在之前的代码中,我们使用read_csv()
函数将人力资源 CSV 文件读取到 Dask DataFrame 中。之前的输出只显示了可用的部分列。然而,当你自己运行笔记本时,你将能够看到数据集中所有的列。现在,让我们对last_evalaution
列(最后评估的性能得分)进行缩放。
- 接下来,选择所需的列进行分类,并将其划分为依赖变量和独立变量:
data=ddf[['satisfaction_level','last_evaluation']].to_dask_array(lengths=True)
label=ddf['left'].to_dask_array(lengths=True)
- 现在,让我们创建一个
LogisticRegression
模型。首先,导入LogisticRegression
和train_test_split
。导入所需的库后,将数据集分成两部分,即训练数据集和测试数据集:
# Import Dask based LogisticRegression
from dask_ml.linear_model import LogisticRegression
# Import Dask based train_test_split
from dask_ml.model_selection import train_test_split
# Split data into training and testing set
X_train, X_test, y_train, y_test = train_test_split(data, label)
- 实例化模型并将其拟合到训练数据集上。现在,你可以预测测试数据并计算模型的准确性,如下所示:
# Create logistic regression model
model = LogisticRegression()
# Fit the model
model.fit(X_train,y_train)
# Predict the classes
y_pred = model.predict(X_test)
# Find model accuracy
print("Accuracy:",accuracy_score(y_test, y_pred))
这将产生以下输出:
Accuracy: 0.7753333333333333
如我们所见,模型提供的准确率为 77.5%,这被认为是一个不错的结果。
聚类
Dask 的开发人员还重新实现了各种 k-means 聚类算法。让我们使用 Dask 进行聚类:
- 将人力资源数据读取到 Dask DataFrame 中,如下所示:
# Read CSV file using Dask
import dask.dataframe as dd
# Read Human Resource Data
ddf = dd.read_csv("HR_comma_sep.csv")
# Let's see top 5 records
ddf.head()
这将产生以下输出:
在之前的代码中,我们使用read_csv()
函数将人力资源 CSV 文件读取到 Dask DataFrame 中。之前的输出只显示了可用的部分列。然而,当你自己运行笔记本时,你将能够看到数据集中所有的列。现在,让我们对last_evalaution
列(最后评估的性能得分)进行缩放。
- 接下来,选择所需的列进行 k-means 聚类。我们在这里选择了
satisfaction_level
和last_evaluation
列:
data=ddf[['satisfaction_level', 'last_evaluation']].to_dask_array(lengths=True)
- 现在,让我们创建一个 k-means 聚类模型。首先,导入 k-means。导入所需的库后,将其拟合到数据集上并获取必要的标签。我们可以使用
compute()
方法找到这些标签:
# Import Dask based Kmeans
from dask_ml.cluster import KMeans
# Create the Kmeans model
model=KMeans(n_clusters=3)
# Fit the model
model.fit(data)
# Predict the classes
label=model.labels_
# Compute the results
label.compute()
这将产生以下输出:
array([0, 1, 2, ..., 0, 2, 0], dtype=int32)
在之前的代码中,我们创建了一个包含三个聚类的 k-means 模型,拟合了模型,并预测了聚类标签。
- 现在,我们将使用
matplotlib
库来可视化 k-means 的结果:
# Import matplotlib.pyplot
import matplotlib.pyplot as plt
# Prepare x,y and cluster_labels
x=data[:,0].compute()
y=data[:,1].compute()
cluster_labels=label.compute()
# Draw scatter plot
plt.scatter(x,y, c=cluster_labels)
# Add label on X-axis
plt.xlabel('Satisfaction Level')
# Add label on X-axis
plt.ylabel('Performance Level')
# Add a title to the graph
plt.title('Groups of employees who left the Company')
# Show the plot
plt.show()
这将产生以下输出:
在之前的代码中,我们使用matplotlib.pyplot
对聚类进行了可视化。在这里,我们将满意度得分绘制在 X 轴上,性能得分绘制在 Y 轴上,并通过使用不同的颜色来区分聚类。
总结
在本章中,我们重点讲解了如何在基础数据科学 Python 库(如 pandas、Numpy 和 scikit-learn)上执行并行计算。Dask 为 DataFrame 和数组提供了完整的抽象,可以在单核/多核机器或集群中的多个节点上处理中等规模的数据集。
我们从本章开始时,先了解了 Dask 的数据类型,如 DataFrame、数组和 Bag。接着,我们重点介绍了 Dask Delayed、预处理以及在并行环境下的机器学习算法。
这是本书的最后一章,意味着我们的学习旅程到此为止。我们重点介绍了用于数据分析和机器学习的核心 Python 库,如 pandas、Numpy、Scipy 和 scikit-learn。我们还关注了可以用于文本分析、图像分析和并行计算的 Python 库,如 NLTK、spaCy、OpenCV 和 Dask。当然,你的学习过程不需要止步于此;继续学习新知识并关注最新的变化。尝试根据你的业务或客户需求探索和修改代码。你也可以开始私人或个人项目来进行学习。如果你无法决定要开始什么样的项目,可以参加 Kaggle 竞赛,访问www.kaggle.com/
等平台!