原文:
annas-archive.org/md5/5058e6970bd2a8d818ecc1f7f8fef74a
译者:飞龙
第六章:第五章
处理缺失值和相关性分析
学习目标
到本章结束时,你将能够:
-
使用 PySpark 检测和处理数据中的缺失值
-
描述变量之间的相关性
-
计算 PySpark 中两个或多个变量之间的相关性
-
使用 PySpark 创建相关矩阵
在本章中,我们将使用 Iris 数据集处理缺失数据并找到数据值之间的相关性。
介绍
在上一章中,我们学习了 Spark DataFrame 的基本概念,并了解了如何利用它们进行大数据分析。
在本章中,我们将进一步学习如何处理数据中的缺失值和进行相关性分析,这些概念将帮助我们为机器学习和探索性数据分析准备数据。
我们将简要介绍这些概念,以便为读者提供一些背景,但我们将重点介绍如何在 Spark DataFrame 中实现它们。我们将使用上一章中使用的相同 Iris 数据集进行本章的练习。但由于 Iris 数据集没有缺失值,我们随机从原始数据集中删除了Sepallength
列中的两个条目和Petallength
列中的一个条目。因此,现在我们有了一个包含缺失值的数据集,我们将学习如何使用 PySpark 处理这些缺失值。
我们还将通过计算相关系数和相关矩阵来查看 Iris 数据集中变量之间的相关性。
设置 Jupyter Notebook
开始练习之前,需要执行以下步骤:
-
在 Jupyter Notebook 中导入所有必要的模块和包:
import findspark findspark.init() import pyspark import random
-
现在,使用以下命令设置
SparkContext
:from pyspark import SparkContext sc = SparkContext()
-
同样,使用以下命令在 Jupyter Notebook 中设置
SQLContext
:from pyspark.sql import SQLContext sqlc = SQLContext(sc)
注意
在执行下一个命令之前,确保已经从 Databricks 网站(
databricks.com/
)安装并准备好 PySpark CSV 读取器包。如果没有,请使用以下命令下载:pyspark –packages com.databricks:spark-csv_2.10:1.4.0
-
将 Iris 数据集从 CSV 文件读取到 Spark DataFrame 中:
df = sqlc.read.format('com.databricks.spark.csv').options(header = 'true', inferschema = 'true').load('/Users/iris.csv')
上述命令的输出如下:
df.show(5)
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_01.jpg
图 5.1:Iris DataFrame
缺失值
没有分配值的数据项称为缺失值。在实际应用中,数据中遇到缺失值是很常见的。缺失值可能由多种原因造成,例如系统/响应者无响应、数据损坏或部分删除。
某些字段比其他字段更容易包含缺失值。例如,来自调查的收入数据可能包含缺失值,因为人们不愿透露自己的收入。
然而,这仍然是困扰数据分析领域的主要问题之一。根据缺失数据的比例,缺失值可能会成为数据准备和探索性分析中的重大挑战。因此,在开始数据分析之前,计算缺失数据的百分比是非常重要的。
在接下来的练习中,我们将学习如何检测并计算 PySpark DataFrame 中缺失值的数量。
练习 38:统计 DataFrame 中的缺失值
在这个练习中,我们将学习如何统计 PySpark DataFrame 列中的缺失值:
-
使用以下命令检查 Spark DataFrame 是否存在缺失值:
from pyspark.sql.functions import isnan, when, count, col df.select([count(when(isnan(c) | col(c).isNull(), c)).alias(c) for c in df.columns]).show()
-
现在,我们将统计加载到 PySpark DataFrame
df
对象中的鸢尾花数据集Sepallength
列中的缺失值:df.filter(col('Sepallength').isNull()).count()
输出结果如下:
2
练习 39:统计 DataFrame 所有列中的缺失值
在这个练习中,我们将统计 PySpark DataFrame 所有列中的缺失值:
-
首先,导入所有必需的模块,如下所示:
from pyspark.sql.functions import isnan, when, count, col
-
现在,使用以下命令显示数据:
df.select([count(when(isnan(i) | col(i).isNull(), i)).alias(i) for i in df.columns]).show()
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_02.jpg
图 5.2:鸢尾花 DataFrame,统计缺失值
输出结果显示,
Seapllength
列中有2
个缺失值,而Petallength
列中有1
个缺失值,出现在 PySpark DataFrame 中。 -
一种简单的方法是使用
describe()
函数,它提供每一列非缺失值的数量,并给出一些其他汇总统计数据。我们在笔记本中执行以下命令:df.describe().show(1)
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_03.jpg
图 5.3:鸢尾花 DataFrame,使用不同方法统计缺失值
如我们所见,Sepallength
列中有 148
个非缺失值,表示有 2
个缺失值,而 Petallength
列中有 149
个非缺失值,表示有 1
个缺失值。
在接下来的部分,我们将探讨如何从 DataFrame 中查找缺失值。
从 DataFrame 中获取缺失值记录
我们还可以使用以下代码过滤掉 PySpark DataFrame 中包含缺失值记录的行:
df.where(col('Sepallength').isNull()).show()
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_04.jpg
图 5.4:鸢尾花 DataFrame,获取缺失值
show
函数显示 PySpark DataFrame 中的前 20 条记录。由于 Sepallength
列只有两条缺失值记录,所以我们这里只看到两条。
处理 Spark DataFrame 中的缺失值
处理缺失值是数据科学中一个复杂的领域。根据缺失数据的类型和具体业务场景,存在多种技术用于处理缺失值。
这些方法包括从基于简单逻辑的方法到先进的统计方法,如回归和 KNN。然而,无论采用何种方法来处理缺失值,最终我们都会对缺失值数据执行以下两种操作之一:
-
从数据中删除包含缺失值的记录
-
用某个常数值填充缺失的条目
在本节中,我们将探索如何使用 PySpark DataFrame 执行这两个操作。
练习 40:从 DataFrame 中删除包含缺失值的记录
在本练习中,我们将删除包含缺失值条目的 PySpark DataFrame 记录。请执行以下步骤:
-
要从特定列中删除缺失值,可以使用以下命令:
df.select('Sepallength').dropna().count()
上述代码将返回
148
作为输出,因为包含缺失值条目的两条Sepallength
列记录已经从 PySpark DataFrame 中删除。 -
要删除 PySpark DataFrame 中包含任何列的缺失值条目的所有记录,请使用以下命令:
df.dropna().count()
DataFrame 中有 3
条记录缺失了值,正如我们在 练习 2:计算所有 DataFrame 列中缺失的值 中看到的那样——其中有两条记录在 Sepallength
列中缺失值,另外一条记录在 Petallength
列中缺失值。
上述代码删除了所有三条记录,从而在 PySpark DataFrame 中返回了 147 条完整的记录。
练习 41:用常数填充 DataFrame 列中的缺失值
在本练习中,我们将把 PySpark DataFrame 列的缺失值条目替换为常数数值。
我们的 DataFrame 中有两个列包含缺失值——Sepallength
和 Petallength
:
-
现在,让我们将这两列中的缺失值条目都替换为常数数值
1
:y = df.select('Sepallength','Petallength').fillna(1)
-
现在,让我们统计刚刚创建的新 DataFrame
y
中的缺失值。这个新 DataFrame 应该没有缺失值:y.select([count(when(isnan(i) | col(i).isNull(), i)).alias(i) for i in y.columns]).show()
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image43351.jpg
图 5.5:Iris DataFrame,查找缺失值
有时,我们希望用单一的常数值替换 DataFrame 中所有的缺失值。
-
使用以下命令将 PySpark DataFrame 中所有缺失的值替换为常数数值 1:
z = df.fillna(1)
-
现在,让我们统计刚刚创建的新 DataFrame
z
中的缺失值。这个新 DataFrame 应该没有缺失值:z.select([count(when(isnan(k) | col(k).isNull(), k)).alias(k) for k in z.columns]).show()
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image43362.jpg
图 5.6:Iris DataFrame,打印缺失值
相关性
相关性是衡量两个数值变量之间关联程度的统计量。它给我们一个关于两个变量彼此关系密切程度的概念。例如,年龄和收入是非常相关的变量。观察发现,在一定的阈值范围内,平均收入会随着年龄的增长而增加。因此,我们可以假设年龄和收入之间是正相关的。
注意
然而,相关性并不建立因果关系。因果关系意味着一个变量正在引起另一个变量的变化。
用来计算这种关联的最常见指标是皮尔逊积矩相关系数,通常称为皮尔逊相关系数或简写为相关系数。它以其发明者卡尔·皮尔逊的名字命名。
皮尔逊相关系数通过将两个变量的协方差除以它们标准差的乘积来计算。相关值介于*-1和+1之间,接近1或-1的值表示强关联,接近0*的值表示弱关联。系数的符号(+
,-
)告诉我们关联是正相关(两个变量一起增加/减少)还是负相关(反之亦然)。
注意
相关性仅捕捉变量之间的线性关联。因此,如果关联是非线性的,相关系数将无法捕捉到它。两个不相关的变量将具有较低或零的相关系数,但零/低相关值的变量不一定是完全不相关的。
相关性在统计分析中具有重要意义,因为它帮助解释数据,有时还突出变量之间的预测关系。在这一部分,我们将学习如何计算变量之间的相关性,并在 PySpark 中计算相关矩阵。
练习 42:计算相关性
在这个练习中,我们将计算两个数值变量之间的皮尔逊相关系数以及我们 PySpark DataFrame 中所有数值列的相关矩阵。相关矩阵帮助我们可视化所有数值列之间的相关性:
-
按照以下步骤计算两个变量之间的相关性:
df.corr('Sepallength', 'Sepalwidth')
前面的代码输出了这两个变量之间的皮尔逊相关系数
-0.1122503554120474
。 -
如下所示,导入相关模块:
from pyspark.mllib.stat import Statistics import pandas as pd
-
使用以下命令删除数据中的任何缺失值:
z = df.fillna(1)
-
在计算相关矩阵之前,使用以下命令去除任何非数值列:
a = z.drop('Species')
-
现在,让我们使用以下命令计算相关矩阵:
features = a.rdd.map(lambda row: row[0:]) correlation_matrix = Statistics.corr(features, method="pearson")
-
要将矩阵转换为 pandas DataFrame 以便于可视化,执行以下命令:
correlation_df = pd.DataFrame(correlation_matrix)
-
使用原始 PySpark DataFrame 中列的名称重命名 pandas DataFrame 的索引:
correlation_df.index, correlation_df.columns = a.columns, a.columns
-
现在,使用以下命令可视化 pandas DataFrame:
correlation_df
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_07.jpg
图 5.7:鸢尾花数据框,计算相关性
活动 12:缺失值处理和使用 PySpark 数据框的相关性分析
在本活动中,我们将检测和处理鸢尾花数据集中的缺失值。我们还将计算相关矩阵,并通过将变量绘制在一起并在图表上拟合一条线性线来验证显示强相关性的变量:
-
在 Jupyter 笔记本中执行导入包和库的初始程序。
-
设置
SparkContext
和SQLContext
。 -
从 CSV 文件读取数据到 Spark 对象中:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_08.jpg
图 5.8:鸢尾花数据框,读取数据
-
用
Sepallength
列的列均值填充缺失值。 -
计算数据集的相关矩阵。确保导入所需的模块。
-
从 PySpark 数据框中移除
String
列,并计算 Spark 数据框中的相关矩阵。 -
将相关矩阵转换为 pandas 数据框:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_09.jpg
图 5.9:鸢尾花数据框,将相关矩阵转换为 pandas 数据框
-
加载所需的模块并绘制数据,绘制显示强正相关的变量对,并在其上拟合一条线性线。
这是
x = "Sepallength", y = "Petalwidth"
的图表:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_10.jpg
图 5.10:鸢尾花数据框,绘制图表,x = “Sepallength”,y = “Petalwidth”
这是x = "Sepallength", y = "Petalwidth"
的图表:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_11.jpg
图 5.11:鸢尾花数据框,绘制图表,x = “Sepallength”,y = “Petalwidth”
这是x = "Petallength", y = "Petalwidth"
的图表:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_12.jpg
图 5.12:鸢尾花数据框,绘制图表,x = “Petallength”,y = “Petalwidth”
注意
或者,你可以使用任何数据集进行此活动。
此活动的解决方案可以在第 229 页找到。
总结
本章中,我们学习了如何在 PySpark 数据框中检测和处理缺失值。我们研究了如何进行相关性分析,并量化皮尔逊相关系数的指标。随后,我们计算了不同数值变量对的皮尔逊相关系数,并学习了如何计算 PySpark 数据框中所有变量的相关矩阵。
在下一章中,我们将学习什么是问题定义,并理解如何进行 KPI 生成。我们还将使用数据聚合和数据合并操作(在前面的章节中学习过)并使用图表分析数据。
第七章:第六章
探索性数据分析
学习目标
本章结束时,您将能够:
-
使用 Jupyter 笔记本实现可重复性概念
-
以可重复的方式进行数据收集
-
实施适当的代码实践和标准,以保持分析的可重复性
-
通过使用 IPython 脚本避免工作重复
本章将学习什么是问题定义,以及如何使用 KPI 分析技术来实现数据的连贯和全面分析。
介绍
数据科学项目中最重要的阶段之一,也是最初的一步,是理解和定义商业问题。然而,这不能仅仅是对现有问题的简单重复陈述或书面报告。为了详细调查商业问题并定义其范围,我们可以使用现有的商业指标来解释与之相关的模式,或者量化并分析历史数据并生成新指标。这些识别出的指标就是关键绩效指标(KPIs),用于衡量当前的问题,并向业务利益相关者传达问题的影响。
本章的重点是理解和定义商业问题,识别与之相关的关键指标,并通过 pandas 及类似库使用这些已识别和生成的 KPI 进行描述性分析。本章还涵盖了如何通过结构化的方法和方法论规划数据科学项目,并最终展示如何使用图形和可视化技术呈现问题。
定义商业问题
数据科学中的商业问题是企业在长期或短期内面临的挑战,这些问题可能会阻碍商业目标的实现,成为增长和可持续性的制约因素,而这些问题本可以通过高效的数据驱动决策系统来避免。一些典型的数据科学商业问题包括预测下周消费品需求、优化第三方物流(3PL)的物流操作、以及识别保险索赔中的欺诈交易。
数据科学和机器学习并不是可以通过将数据输入到预构建算法中来解决这些商业问题的神奇技术。它们在方法和设计方面复杂,需要创建端到端的分析项目。
当企业需要此类解决方案时,如果没有明确理解最终目标,可能会陷入形成需求差距的境地。构建这一强大基础的第一步是定量定义商业问题,然后根据需求进行范围界定和解决方案实施。
以下是一些常见数据科学应用场景的例子,它们能直观地展示当前行业面临的典型商业问题,这些问题通过数据科学和分析得以解决:
-
不准确的需求/收入/销售预测
-
低效的客户转化、流失和保持
-
借贷行业和保险中的欺诈和定价
-
无效的客户和供应商/分销商评分
-
无效的交叉销售/追加销售推荐系统
-
不可预测的机器故障和维护
-
通过文本数据进行客户情感/情绪分析
-
未自动化的重复任务,这些任务需要非结构化数据分析
正如我们所知,近年来,行业在技术和创新的推动下发生了巨大的变化。随着技术不断发展,成功的企业能够适应这些变化,从而产生高度发展和复杂的商业挑战和问题。在如此动态的环境中理解新的业务问题并不是一个简单的过程。尽管每个案例的业务问题和应对方法可能会变化,但这种方法在很大程度上是可以概括的。
以下要点是定义和解决业务问题的广泛步骤,接下来的部分将详细描述每个步骤:
-
问题识别
-
需求收集
-
数据管道和工作流
-
确定可衡量的指标
-
文档和展示
注意
目标变量,或研究变量,在数据集中用作分析业务问题的属性/变量/列,也被称为因变量(DV),所有其他被考虑用于分析的属性被称为自变量(IVs)。
问题识别
让我们从一个例子开始:一家在其共同基金领域拥有强大客户获取能力的资产管理公司(AMC),即能够针对正确的客户并将其引入,正在寻求通过基于数据科学的解决方案提高客户保持率,以改善其高端客户的平均客户收入和钱包份额。
在这里,业务问题是如何从现有客户那里增加收入并提高他们的钱包份额。
问题陈述是“我们如何通过客户保持分析提高平均客户收入并增加高端客户的钱包份额?” 总结问题的陈述将是定义业务问题的第一步。
需求收集
一旦问题被识别,与你的客户进行逐条讨论,客户可以是主题专家(SME)或在该问题领域有深厚知识的人。
力求从客户的角度理解问题,并从不同的角度询问问题,理解需求,并总结如何从现有的历史数据中定义问题。
有时,你会发现客户自己并不能很好地理解问题。在这种情况下,你应该与客户合作,制定出一个双方都能接受的问题定义。
数据管道和工作流
在你详细理解了问题后,接下来的阶段是定义并商定用于衡量问题的可量化指标,即与客户达成一致,确定用于进一步分析的指标。长期来看,这将为你节省很多麻烦。
这些指标可以与现有的业务绩效追踪系统相关,或者可以从历史数据中导出新的指标。
当你研究跟踪问题的指标时,识别和量化问题的数据可能来自多个数据源、数据库、遗留系统、实时数据等。参与此工作的数据科学家需要与客户的数据管理团队密切合作,提取并收集所需数据,并将其推送到分析工具中进行进一步分析。因此,必须有一个强大的数据获取管道。获取的数据进一步分析,以识别其重要属性及其随时间变化的情况,从而生成 KPI。这是客户参与的关键阶段,与他们团队的密切合作有助于使工作更加顺利。
确定可衡量的指标
一旦通过数据管道收集了所需的数据,我们就可以开发描述性模型来分析历史数据,并生成业务问题的洞察。描述性模型/分析主要是通过时间趋势分析、数据分布密度分析等方法,了解过去发生了什么。为此,必须研究历史数据中的多个属性,以洞察哪些数据属性与当前问题相关。
如前述案例中所解释的例子,某资产管理公司(AMC)正在寻找解决客户留存问题的方案。我们将研究如何生成 KPI,以便理解留存问题。
为此,需要挖掘历史数据,分析以前投资的客户交易模式,并从中导出 KPI。数据科学家必须根据 KPI 在解释问题变动性方面的相关性和效率来开发这些 KPI,或者在此案例中,即客户留存。
文档编制与展示
最后的步骤是记录已识别的关键绩效指标(KPI)、它们的重要趋势,以及它们如何在长期内影响业务。在前述的客户留存案例中,所有这些指标——关系长度、平均交易频率、流失率——都可以作为 KPI,并用于定量解释问题。
如果我们观察到流失率的趋势,并假设在过去几个月中呈上升趋势,如果我们用图表表示这一点,客户就可以轻松理解,建立预测流失分析来识别即将流失的客户,以及采取更强有力的留存措施的重要性。
需要向客户展示是否有可能建立一个客户留存系统,为此需要完成 KPIs 的文档化和图形表示。在前面的案例中,已识别的 KPIs 及其变化模式需要进行文档化并呈现给客户。
将业务问题转化为可衡量的指标和探索性数据分析(EDA)
如果有一个具体的业务问题出现,我们需要确定定义该业务问题的关键绩效指标(KPIs),并研究与之相关的数据。在生成与问题相关的 KPIs 之后,下一步将是通过探索性数据分析(EDA)方法,分析趋势并量化问题。
探索 KPIs 的方法如下:
-
数据收集
-
数据生成分析
-
KPI 可视化
-
特征重要性
数据收集
分析问题所需的数据是定义业务问题的一部分。然而,从数据中选择的特征会根据业务问题的不同而有所变化。以下是几个例子:
-
如果是推荐引擎或客户流失分析,我们需要查看历史购买和了解你的客户(KYC)数据等其他数据。
-
如果与需求预测相关,我们需要查看每日销售数据。
需要得出结论,所需的数据会根据问题的不同而变化。
数据生成分析
从可用的数据源中,下一步是识别与已定义问题相关的度量指标。除了数据预处理(有关数据处理的详细信息,请参阅第一章,Python 数据科学栈),有时我们需要对数据进行处理,以生成这些度量指标,或者它们可以直接从给定数据中获得。
例如,假设我们正在进行监督分析,如预测性维护问题(使用预测分析来预测在设备或机器故障之前的服务状态问题),其中使用的是传感器或计算机生成的日志数据。尽管日志数据是非结构化的,我们仍然可以识别哪些日志文件解释了机器故障,哪些没有。非结构化数据没有列或行。例如,它可能是 XML 格式或类似格式。计算机生成的日志数据就是一个例子。这样的数据需要转换为列和行,或使其结构化,或者对其进行标签化,即通过将数据转换为行和列来为数据提供列名。
另一个例子是识别客户流失并预测未来可能流失的客户,我们拥有与每次购买相关的交易数据及其特征。在这种情况下,我们需要处理数据并转化当前数据,以识别哪些客户已流失,哪些客户没有,从所有与购买相关的数据中进行筛选。
为了更好地解释这一点,在原始数据中,可能会有每个客户的多个购买记录,包括购买日期、购买数量、价格等。所有与某个客户相关的购买记录需要合并为一行,不论该客户是否已经流失(流失指的是停止使用产品或服务的客户,也称为客户流失),并且包含所有相关信息。
在这里,我们将为客户生成一个 KPI:流失或未流失属性,其他所有客户也同样如此。定义业务问题的目标变量是已识别的变量。目标变量也称为响应变量或因变量。在本章的练习 XX中,通过流失属性进行捕捉和定义。
KPI 可视化
为了理解 KPI 的趋势和模式,我们需要通过交互式可视化技术来表示它们。我们可以使用不同的方法,如箱线图、时间趋势图、密度图、散点图、饼图和热图。在本章的练习 XX中,我们将进一步学习如何生成目标变量的特征重要性并进行 EDA。
特征重要性
一旦确定了目标变量,就需要研究数据中其他属性及其在解释目标变量的变异性方面的重要性。为此,我们使用关联、方差和相关方法来建立其他变量与目标变量之间的关系(解释性或独立变量)。
根据研究中变量的类型,可以使用多种特征重要性方法和算法,如皮尔逊相关、卡方检验、基于基尼变量重要性、决策树和 Boruta 等算法。
注意
目标变量或研究变量,即用于作为数据集中研究业务问题的属性/变量/列,也被称为因变量(DV),而所有其他被考虑用于分析的属性则称为自变量(IVs)。
在接下来的练习中,我们将涵盖数据收集和分析——数据**(通过合并或结合多个数据源生成的分析数据集)**的生成与 KPI 可视化,随后我们将介绍特征重要性是什么。
练习 43:从给定数据中识别目标变量及与业务问题相关的 KPI
让我们以银行领域中的订阅问题为例。我们将使用来自葡萄牙某银行机构的直接营销活动的数据,其中客户在活动后要么开设定期存款(参考:www.canstar.com.au/term-deposits/what-is-a-term-deposit/
),要么不开设。每个组织对订阅问题的定义都不同。在大多数情况下,愿意订阅某项服务(在这里是定期存款)的客户具有更高的转化潜力(即,从潜在客户到实际客户的转化)。因此,在这个问题中,订阅指标,也就是历史数据的结果,被视为目标变量或 KPI。
我们将使用描述性分析来探索数据趋势。我们将首先识别并定义目标变量(此处为订阅与未订阅)及相关的 KPI:
-
从以下在线资源下载 bank.csv 数据:
-
为练习创建一个文件夹(
packt_exercises
),并将下载的数据保存在其中。 -
启动 Jupyter notebook 并按示例导入所有所需的库。然后,使用
os.chdir()
函数设置工作目录:import numpy as np import pandas as pd import seaborn as sns import time import re import os import matplotlib.pyplot as plt sns.set(style="ticks") os.chdir("/Users/svk/Desktop/packt_exercises")
-
使用以下代码读取 CSV 并探索数据集:
df = pd.read_csv('bank.csv', sep=';') df.head(5) print(df.shape) df.head(5) df.info() df.describe()
-
执行前述命令后,你将看到类似以下的输出:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_01.jpg
图 6.1: 银行数据框
在研究目标变量(订阅与未订阅——
y
)时,重要的是要查看其分布情况。此数据集中的目标变量类型是分类的,或者说是多类的。在这种情况下,它是二元的(是/否)。当分布偏向某一类别时,问题被称为变量不平衡。我们可以通过条形图研究目标变量的比例。这可以让我们了解每个类别的数量(在此案例中,了解“no”和“yes”各自的数量)。其中,no 的比例远高于 yes,这就解释了数据中的不平衡。
-
让我们执行以下命令,以绘制给定数据的条形图:
count_number_susbc = df["y"].value_counts() sns.barplot(count_number_susbc.index, count_number_susbc.values) df['y'].value_counts()
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image38462.jpg
图 6.2: 条形图
-
现在,我们将查看每个变量并观察它们的分布趋势。下面的直方图是数据集中’
age'
列(属性)的示例。直方图/密度图是探索数值型/浮动变量的好方法,类似于条形图。它们也可以用于分类数据变量。在这里,我们将使用直方图展示两个数值型变量(age
和balance
)的示例,并用条形图展示两个分类变量(education
和month
):# histogram for age (using matplotlib) plt.hist(df['age'], color = 'grey', edgecolor = 'black', bins = int(180/5)) # histogram for age (using seaborn) sns.distplot(df['age'], hist=True, kde=False, bins=int(180/5), color = 'blue', hist_kws={'edgecolor':'black'})
直方图如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_03.jpg
图 6.3:年龄的直方图
-
要绘制数据集中
balance
属性的直方图,请使用以下命令:# histogram for balance (using matplotlib) plt.hist(df['balance'], color = 'grey', edgecolor = 'black', bins = int(180/5)) # histogram for balance (using seaborn) sns.distplot(df['balance'], hist=True, kde=False, bins=int(180/5), color = 'blue', hist_kws={'edgecolor':'black'})
平衡的直方图如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_04.jpg
图 6.4:平衡的直方图
-
现在,使用以下代码,绘制数据集中
education
属性的条形图:# barplot for the variable 'education' count_number_susbc = df["education"].value_counts() sns.barplot(count_number_susbc.index, count_number_susbc.values) df['education'].value_counts()
education
属性的条形图如下所示:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_05.jpg
图 6.5:教育的条形图
-
使用以下命令绘制数据集的
month
属性的条形图:# barplot for the variable 'month' count_number_susbc = df["month"].value_counts() sns.barplot(count_number_susbc.index, count_number_susbc.values) df['education'].value_counts()
绘制的图形如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_06.jpg
图 6.6:月度的条形图
-
下一个任务是为目标变量的每个类别生成分布并比较分布。绘制目标变量的
age
属性的直方图(yes
/no
):# generate separate list for each subscription type for age x1 = list(df[df['y'] == 'yes']['age']) x2 = list(df[df['y'] == 'no']['age']) # assign colors for each subscription type colors = ['#E69F00', '#56B4E9'] names = ['yes', 'no'] # plot the histogram plt.hist([x1, x2], bins = int(180/15), density=True, color = colors, label=names) # plot formatting plt.legend() plt.xlabel('IV') plt.ylabel('prob distr (IV) for yes and no') plt.title('Histogram for Yes and No Events w.r.t. IV')
目标变量的
month
属性条形图如下所示:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_07.jpg
图 6.7:目标变量的月份属性条形图
-
现在,使用以下命令,绘制按月分组的目标变量的条形图:
df.groupby(["month", "y"]).size().unstack().plot(kind='bar', stacked=True, figsize=(20,10))
绘制的图形如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_08.jpg
图 6.8:按月分组的直方图
在本练习中,我们研究了建立 KPI 和目标变量—数据收集和分析数据(通过合并或结合多个数据源生成的数据集,用于分析)生成。KPI 和目标变量已经确定—KPI 可视化。现在,在下一个练习中,我们将识别哪些变量在解释目标变量的方差方面是重要的—特征重要性。
练习 44:生成目标变量的特征重要性并进行 EDA
在前面的练习中,我们研究了属性的趋势,识别它们的分布,以及如何使用各种图表和可视化方法来进行这些分析。在处理建模问题之前,无论是预测问题还是分类问题(例如,从先前的营销活动数据中,如何预测未来最有可能转化的客户),我们必须对数据进行预处理,并选择那些对订阅活动输出模型有影响的重要特征。为此,我们需要查看属性与结果(目标变量)之间的关联,即每个变量解释目标变量的变异性程度。
变量之间的关联可以通过多种方法绘制;然而,在选择方法/算法时,我们必须考虑数据类型。例如,如果我们研究的是数值型变量(有序的整数、浮动数值等),我们可以使用相关分析;如果我们研究的是具有多个类别的分类变量,则可以使用卡方方法。然而,也有许多算法可以同时处理这两者,并提供可衡量的结果来比较变量的重要性。
在本练习中,我们将研究如何使用各种方法来识别特征的重要性:
-
下载
bank.csv
文件,并使用以下命令读取数据:import numpy as np import pandas as pd import seaborn as sns import time import re import os import matplotlib.pyplot as plt sns.set(style="ticks") # set the working directory # in the example, the folder # 'packt_exercises' is in the desktop os.chdir("/Users/svk/Desktop/packt_exercises") # read the downloaded input data (marketing data) df = pd.read_csv('bank.csv', sep=';')
-
使用以下命令开发相关矩阵,以识别变量之间的相关性:
df['y'].replace(['yes','no'],[1,0],inplace=True) df['default'].replace(['yes','no'],[1,0],inplace=True) df['housing'].replace(['yes','no'],[1,0],inplace=True) df['loan'].replace(['yes','no'],[1,0],inplace=True) corr_df = df.corr() sns.heatmap(corr_df, xticklabels=corr_df.columns.values, yticklabels=corr_df.columns.values, annot = True, annot_kws={'size':12}) heat_map=plt.gcf(); heat_map.set_size_inches(10,5) plt.xticks(fontsize=10); plt.yticks(fontsize=10); plt.show()
绘制的相关矩阵热图如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_09.jpg
图 6.9:相关矩阵
注意
-1
到+1
,其中接近0
的值表示无关系,-1 表示一种变量增加时,另一变量减少(反向关系),+1 表示一种变量增加时,另一变量也增加(正向关系)。独立变量之间的高相关性(即所有除目标变量之外的变量)可能会导致变量之间的多重共线性,这可能影响预测模型的准确性。
注意
如果尚未安装 Boruta,请使用以下命令确保安装:
pip install boruta --upgrade
-
基于 Boruta(一个随机森林包装算法)构建特征重要性输出:
# import DecisionTreeClassifier from sklearn and # BorutaPy from boruta import numpy as np from sklearn.ensemble import RandomForestClassifier from boruta import BorutaPy # transform all categorical data types to integers (hot-encoding) for col_name in df.columns: if(df[col_name].dtype == 'object'): df[col_name]= df[col_name].astype('category') df[col_name] = df[col_name].cat.codes # generate separate dataframes for IVs and DV (target variable) X = df.drop(['y'], axis=1).values Y = df['y'].values # build RandomForestClassifier, Boruta models and # related parameter rfc = RandomForestClassifier(n_estimators=200, n_jobs=4, class_weight='balanced', max_depth=6) boruta_selector = BorutaPy(rfc, n_estimators='auto', verbose=2) n_train = len(X) # fit Boruta algorithm boruta_selector.fit(X, Y)
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_10.jpg
图 6.10:拟合 Boruta 算法
-
按照以下方式检查特征的排名:
feature_df = pd.DataFrame(df.drop(['y'], axis=1).columns.tolist(), columns=['features']) feature_df['rank']=boruta_selector.ranking_ feature_df = feature_df.sort_values('rank', ascending=True).reset_index(drop=True) sns.barplot(x='rank',y='features',data=feature_df) feature_df
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_11.jpg
图 6.11:Boruta 输出
数据科学项目生命周期的结构化方法
开始数据科学项目时,需要有一个稳健的方法来规划项目,考虑到潜在的扩展性、维护性和团队结构。我们已经学习了如何定义一个业务问题并通过可量化的参数加以量化,接下来的阶段是项目计划,涵盖了解决方案的开发到部署一个可用的商业应用程序。
本主题结合了业界一些最佳实践,以结构化方式提供数据科学项目生命周期管理的示例。这种方法是一个理想化的阶段顺序;然而,在实际应用中,顺序可以根据所需解决方案的类型而变化。
通常,一个数据科学项目单个模型的部署需要大约三个月的时间,但这可能增加到六个月,甚至长达一年。定义从数据到部署的过程是缩短部署时间的关键。
数据科学项目生命周期阶段
数据科学项目生命周期的各个阶段如下:
-
理解和定义业务问题
-
数据访问与发现
-
数据工程与预处理
-
建模开发与评估
阶段 1:理解并定义业务问题
每个数据科学项目都始于了解业务领域和框定业务问题。在大多数组织中,高级分析和数据科学技术的应用还是一个新兴学科,参与其中的大多数数据科学家对业务领域的理解有限。
为了理解业务问题和领域,需要识别关键利益相关者和主题专家(SMEs)。然后,主题专家和数据科学家相互合作,提出初步假设,并确定开发解决方案所需的数据源。这是理解数据科学项目的第一阶段。
一旦我们有了结构清晰的业务问题,并确定了所需的数据和数据源,就可以开始数据发现的下一阶段。第一阶段对建立强大的基础来确定范围和解决方案方法至关重要。
阶段 2:数据访问与发现
这一阶段包括识别数据源,并构建数据管道和数据工作流以获取数据。解决方案的性质和依赖的数据在结构、速度和体积方面可能因问题而异。
在这个阶段,重要的是要确定如何从数据源获取数据。获取数据的方式可以通过直接连接器(使用 Python 中的数据库访问库)、使用提供数据访问的 API、直接从网络来源抓取数据,或者甚至是初步原型开发所提供的 CSV 格式的数据转储。一旦建立了强大的数据管道和数据获取工作流,数据科学家就可以开始探索数据,准备分析数据**(通过合并或组合多个数据源以生成一个分析用的数据集)。**
阶段 3:数据工程与预处理
数据预处理是指将原始数据转化为机器学习算法可以使用的形式。实际上,这是将数据处理成适合进一步分析的结构,或者将数据转化为可以输入到建模过程中的形式。通常,所需的分析数据可能存储在多个表格、数据库,甚至是外部数据源中。
数据科学家需要从这些数据源中识别出所需的属性,并将现有的数据表合并,以获取分析模型所需的数据。这是一个繁琐且耗时的阶段,数据科学家通常会在开发周期中花费大量时间。
数据预处理包括处理异常值、缺失值填充、特征缩放、将数据映射到高斯(或正态)分布、编码类别数据、离散化等活动。
为了开发强大的机器学习模型,数据必须高效地进行预处理。
注意
Python 有一些库用于数据预处理。scikit-learn 有许多高效的内置方法用于数据预处理。scikit-learn 的文档可以在scikit-learn.org/stable/modules/preprocessing.html
找到。
让我们通过以下活动,了解如何使用其中一种预处理技术(例如高斯归一化)来进行数据工程和预处理。
活动 13:对给定数据的数值特征进行高斯分布映射
在将数据输入算法之前,需要进行各种预处理技术来准备数据。
注意
访问此网站,了解各种预处理方法:scikit-learn.org/stable/modules/preprocessing.html
。
在本次练习中,我们将进行数据归一化,这对线性回归、逻辑回归等许多参数化模型非常重要:
-
使用
bank.csv
文件,并将所有所需的包和库导入到 Jupyter 笔记本中。 -
现在,确定 DataFrame 中的数值数据。根据数据类型(如类别、数值(浮动或整数)、日期等)对数据进行分类。让我们对数值数据进行归一化处理。
-
进行正态性检验,识别出具有非正态分布的特征。
-
绘制特征的概率密度图,以直观分析特征的分布。
-
准备功率转换模型,并根据
box-cox
或yeo-johnson
方法对识别出的特征进行转换,将它们转换为正态分布。 -
对新数据(评估数据)应用相同的生成参数进行转换,应用到训练数据中识别出的特征。
注意
在转换完成后,前面的图中显示了多个变量的密度图。图中的特征分布更接近高斯分布。
本活动的解决方案可以在第 236 页找到。
一旦转换完成,我们再次分析特征的正态性,以查看其效果。你会发现,经过转换后,一些特征的原假设未被拒绝(即分布仍然不是高斯分布),并且几乎所有特征的p
值都较高(参考本活动的第 2 点)。一旦生成了转换后的数据,我们将其与数值数据绑定。
第 4 阶段:模型开发
一旦我们获得了清理和预处理过的数据,就可以将其输入到机器学习算法中进行探索性分析或预测分析,或用于其他应用。尽管问题的处理方法可以设计出来,比如分类、关联或回归问题,但需要为数据确定的特定算法也必须被识别出来。例如,如果是分类问题,它可能是决策树、支持向量机,或者是一个具有多层的神经网络。
在建模过程中,数据需要分为测试数据和训练数据。模型在训练数据上进行开发,并在测试数据上评估其性能(准确性/误差)。在选择算法后,需要通过数据科学家调整与之相关的参数,以开发一个稳健的模型。
总结
在本章中,我们学习了如何通过一个明确结构化的方法,从数据科学的角度定义业务问题。我们首先理解了如何处理业务问题,如何从利益相关者和业务专家处收集需求,以及如何通过开发初步假设来定义业务问题。
一旦业务问题通过数据管道和工作流定义完成,我们就开始了解如何对收集到的数据进行分析,以生成 KPI 并进行描述性分析,通过各种可视化技术识别历史数据中的关键趋势和模式。
我们还了解了数据科学项目生命周期的结构,从定义业务问题到各种预处理技术和模型开发。在下一章中,我们将学习如何在 Jupyter notebook 上实现高可复现性的概念,以及它在开发中的重要性。
第八章:第七章
大数据分析中的可重复性
学习目标
本章结束时,您将能够:
-
使用 Jupyter 笔记本实现可重复性的概念
-
以可重复的方式进行数据收集
-
实施适当的编码实践和标准,以保持分析的可重复性
-
避免 IPython 脚本的重复工作
本章中,我们将探讨可重复性在大数据分析中的重要作用。
介绍
在上一章中,我们通过非常结构化的方法学习了如何从数据科学的角度定义商业问题,其中包括如何识别和理解业务需求,解决方案的方法以及如何构建数据管道并进行分析。
本章将讨论计算工作和研究实践的可重复性,这也是今天业界和学术界面临的主要挑战,尤其是在数据科学工作中,其中大部分数据、完整的数据集和相关的工作流程无法完全访问。
今天,大多数研究和技术论文的结尾都会提到样本数据的使用方法、简要描述采用的研究方法以及解决方案的理论思路。这些作品大多缺乏详细的计算过程和逐步方法。这对读者来说,几乎没有提供足够的知识来重复进行相同的工作。这就是可重复编码的基本目标——代码的可复现性至关重要。
在笔记本的整体发展中,已出现包括文本元素来详细注释的功能,这有助于改进复制过程。正因如此,Jupyter 作为一种笔记本在数据科学和研究界日益受到关注。
Jupyter 的开发旨在成为开源软件,采用开放标准和服务,支持多种编程语言的交互式计算,包括 Python、Spark 和 R。
使用 Jupyter 笔记本进行可重复性分析
让我们首先学习什么是计算可重复性。如果提供了开发解决方案所用的原始源代码,并且构建相关软件所用的数据能够产生相同的结果,那么研究、解决方案、原型甚至一个简单的算法都可以被称为可重复的。然而,今天科学界在重现同行以前开发的工作方面遇到了一些挑战,主要是由于缺乏文档和理解过程工作流的困难。
缺乏文档的影响可以在各个层面上看到,从理解方法到代码层面。Jupyter 是一种非常适合改善这一过程的工具,有助于更好的可重现性,以及开发代码的重用。这不仅包括理解每一行或代码片段的作用,还包括理解和可视化数据。
注意
Jon Claerbout,被认为是可重现计算研究的奠基人,早在 1990 年代初期,他要求学生们开发可以一键重新生成的研究工作和结果。他认为,完成的工作是需要时间和精力的,应当保持原样,以便后续的工作能够在不遇到困难的情况下重用之前的工作。从宏观角度看,一个经济体的增长在很大程度上由创新的数量决定。早期工作或研究的可重现性促进了整体创新的增长。
现在让我们看看如何使用 Jupyter 笔记本维护有效的计算可重现性。
以下是通过 Jupyter 笔记本在 Python 中实现可重现性的一些广泛方法:
-
提供商业问题的详细介绍
-
记录方法和工作流程
-
解释数据管道
-
解释依赖关系
-
使用源代码版本控制
-
模块化过程
在接下来的章节中,让我们简要地探索并讨论前面提到的主题。
商业问题介绍
使用 Jupyter 笔记本的一个关键优势是,它将文本内容与代码结合,形成一个工作流程。
我们必须从一个对已识别的商业问题的良好介绍开始,并将其总结文档化在 Jupyter 笔记本中,以提供问题的要点。它必须包含一个问题陈述,从数据科学的角度描述已识别的商业问题,最后得出为什么我们需要进行此分析或过程目标是什么。
记录方法和工作流程
在数据科学中,计算工作可能会有很多反复,如例如进行的探索、使用的算法类型以及调整的参数。
一旦方法的变化已最终确定,就需要对这些变化进行文档记录,以避免工作重复。记录方法和工作流程有助于建立流程。在开发过程中,给代码添加注释是必须的,这应当是一个持续的实践,而不是等到结束或结果出来后再添加注释。到过程结束时,你可能已经忘记了细节,这可能导致对所需努力的错误估算。维护 Jupyter 笔记本并进行良好的文档记录有以下几个优点:
-
跟踪开发工作
-
具有自解释性的代码,并为每个过程添加注释
-
更好地理解代码工作流程以及每一步的结果
-
通过使特定任务的先前代码片段容易找到,避免反复工作
-
通过理解代码的重复使用来避免重复工作
-
知识转移的便利性
解释数据管道
用于识别和量化问题的数据可以来自多个数据源、数据库、遗留系统、实时数据源等。参与此过程的数据科学家将与客户的数据管理团队密切合作,提取和收集所需数据,并将其导入分析工具以进行进一步分析,并创建强大的数据管道来获取这些数据。
详细记录数据来源非常重要(在上一章中已讨论),以维护一个数据字典,解释所考虑的变量、为什么要考虑这些变量、我们拥有的数据类型(结构化或非结构化),以及我们所拥有的数据类型;即,我们是否拥有时间序列数据、多变量数据,或是需要从原始数据源(如图像、文本、语音等)进行预处理和生成的数据。
解释依赖关系
依赖关系是工具中可用的包和库。例如,你可以使用 OpenCV(docs.opencv.org/3.0-beta/doc/py_tutorials/py_tutorials.html
),这是一个用于图像相关建模的 Python 库,或者你可以使用像 TensorFlow 这样的 API 进行深度学习建模。另一个例子是:如果你在 Python 中使用 Matplotlib(matplotlib.org/
)进行可视化,Matplotlib 可以成为依赖关系的一部分。另一方面,依赖关系还可以包括分析所需的硬件和软件规格。你可以通过使用像 Conda 环境这样的工具,从一开始就显式地管理你的依赖关系,列出所有相关的依赖项(包括它们的包/库版本),这些在之前关于 pandas、NumPy 等依赖关系的章节中已经覆盖。
使用源代码版本控制
版本控制是任何涉及代码的计算活动中非常重要的方面。在开发代码时,错误或缺陷是难以避免的。如果有以前版本的代码可用,我们就可以明确定位到缺陷何时被发现、何时解决,以及解决的过程和付出的努力。版本控制使得这一切成为可能。有时,由于可扩展性、性能或其他原因,你可能需要回退到旧版本。通过使用源代码版本控制工具,你可以随时轻松访问以前版本的代码。
过程模块化
避免重复代码是管理重复任务、维护代码和调试的有效做法。为了高效地实现这一目标,你必须对过程进行模块化。
让我们详细理解这一点。假设你执行了一组数据处理过程,在这些过程中,你开发了代码来完成任务。现在,假设你需要在后续部分再次使用相同的代码;你需要再次添加、复制或运行相同的步骤,这就变成了重复的任务。输入数据和变量名称可能会发生变化。为了处理这个问题,你可以将之前的步骤编写成一个函数,应用于数据集或变量,并将所有这些函数保存为一个独立的模块。你可以称它为函数文件(例如,functions.py
,一个 Python 文件)。
在接下来的部分中,我们将更详细地讨论这一点,特别是在以可重现的方式收集和构建高效的数据管道方面。
以可重现的方式收集数据
一旦问题被定义,分析任务的第一步是收集数据。数据可以从多个来源提取:数据库、遗留系统、实时数据、外部数据等。数据来源及其如何输入模型需要被记录下来。
让我们理解如何在 Jupyter 笔记本中使用 markdown 和代码块功能。可以通过 Markdown 单元向 Jupyter 笔记本中添加文本。与任何文本编辑器一样,这些文本可以修改为粗体或斜体。要将单元类型更改为 Markdown,可以使用Cell菜单。接下来,我们将探讨如何在 Jupyter 中使用 Markdown 和代码单元中的各种功能。
Markdown 和代码单元中的功能
- Jupyter 中的 Markdown:要在Jupyter中选择 Markdown 选项,请点击下拉菜单中的Widgets和Markdown:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_01.jpg
图 7.1:Jupyter 笔记本中的 Markdown 选项
<h1>
和<h2>
标签:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_02.jpg
图 7.2:Jupyter 笔记本中的标题级别
- Jupyter 中的文本:要添加文本,保持其原样,我们不需要为其添加任何标签:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image36337.jpg
图 7.3:在 Jupyter 笔记本中使用普通文本
**
) 在文本的开始和结束处,例如,粗体:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_04.jpg
图 7.4:在 Jupyter 笔记本中使用粗体文本
*
) 在文本的开始和结束处:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_05.jpg
图 7.5:在 Jupyter 笔记本中使用斜体文本
- Jupyter 中的代码:要让文本以代码形式出现,从下拉菜单中选择Code选项:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_06.jpg
图 7.6:Jupyter 笔记本中的代码
在 Markdown 中解释业务问题
简要介绍业务问题,以便理解项目的目标。业务问题定义是对问题陈述的总结,并包括如何通过数据科学算法解决问题的方法:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_07.jpg
图 7.7:问题定义的片段
提供数据源的详细介绍
需要正确记录数据源,以了解数据许可的可重复性及进一步工作的要求。数据源添加的示例如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_08.jpg
图 7.8:Jupyter notebook 中的数据源
在 Markdown 中解释数据属性
需要维护一个数据字典,以便理解属性层面上的数据。这可以包括定义属性及其数据类型:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_09.jpg
图 7.9:Markdown 中的详细属性
为了在属性层面理解数据,我们可以使用诸如info
和describe
之类的函数;然而,pandas_profiling
是一个提供大量描述性信息的库,通过一个函数,我们可以提取以下信息:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_10.jpg
图 7.10:数据概况报告
在 DataFrame 层面,即针对整体数据,这包括所有被考虑的列和行:
-
变量数量
-
观察数量
-
总缺失值 (%)
-
内存中的总大小
-
内存中的平均记录大小
-
相关矩阵
-
示例数据
在属性层面,即针对特定列,规格如下:
-
唯一计数
-
唯一值 (%)
-
缺失值 (%)
-
缺失值 (n)
-
无限值 (%)
-
无限值 (n)
-
分布的直方图
-
极值
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_11.jpg
图 7.11:属性层面的数据概况报告
练习 45:执行数据可重复性
本练习的目的是学习如何开发具有高可重复性的数据理解代码。我们将使用来自此链接的 UCI 银行和营销数据集:raw.githubusercontent.com/TrainingByPackt/Big-Data-Analysis-with-Python/master/Lesson07/Dataset/bank/bank.csv
。
让我们执行以下步骤以实现数据的可重复性:
-
在 notebook 中添加标题并提到业务问题,使用标记法:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_12.jpg
图 7.12:介绍和业务问题
-
将所需的库导入到 Jupyter notebook 中:
import numpy as np import pandas as pd import time import re import os import pandas_profiling
-
现在设置工作目录,如以下命令所示:
os.chdir("/Users/svk/Desktop/packt_exercises")
-
使用 pandas 的
read_csv
函数导入并读取输入数据集为df
:df = pd.read_csv('bank.csv', sep=';')
-
现在使用
head
函数查看数据集的前五行:df.head(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_13.jpg
图 7.13:CSV 文件中的数据
-
在 Jupyter notebook 中添加数据字典和数据理解部分:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_14.jpg
图 7.14:数据字典
数据理解部分如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_15.jpg
图 7.15:数据理解
-
要理解数据规范,可以使用 pandas profiling 生成描述性信息:
pandas_profiling.ProfileReport(df)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_16.jpg
图 7.16:与数据相关的规范摘要
教学人员备注:
本练习展示了如何创建 Jupyter notebook,并包括如何为银行营销问题开发一个可重复的 Jupyter notebook。必须包括对商业问题、数据、数据类型、数据来源等的良好介绍。
代码实践和标准
编写符合一套实践和标准的代码对代码的可重复性非常重要,就像在逐步描述中解释流程一样重要。
这适用于任何你可能使用的编码工具,不仅仅是 Jupyter。某些编码实践和标准应严格遵守,接下来将讨论其中的一些内容。
环境文档
在安装过程中,你应该保留一段代码,用于安装必要的软件包和库。以下实践有助于代码的可重复性:
-
包括所使用的库/软件包的版本。
-
下载所使用的库/软件包的原始版本,并在新环境中调用软件包进行安装。
-
通过在脚本中有效实施,自动安装依赖项。
编写带注释的可读代码
代码注释是一个重要的方面。除了 Jupyter 上的 markdown 选项外,我们还必须为每个代码片段添加注释。有时我们对代码进行的修改可能不会立即使用,但稍后的步骤可能需要。比如,我们可以创建一个对象,虽然下一步不需要,但稍后会用到。如果没有注释,可能会让新用户难以理解代码的流程。注释这些细节非常重要。
当我们使用特定的方法时,必须提供使用该方法的理由。例如,假设在进行数据正态分布转换时,你可以使用 box-cox
或 yeo-johnson
。如果数据中有负值,你可能更倾向于使用 yeo-johnson
,因为它能够处理负值。此时需要进行如下注释:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_17.jpg
图 7.17:带理由的注释
我们还应遵循良好的命名实践来命名我们创建的对象。例如,您可以将原始数据命名为raw_data
,模型数据、预处理数据、分析数据等也可以使用相同的命名方式。当创建像模型和方法这样的对象时,也可以采用类似的命名方式,例如,我们可以将幂变换命名为pt
。
有效的工作流分割
在开发代码时,您需要设计一系列步骤以实现最终结果。每个步骤都可以是一个过程的一部分。例如,读取数据、理解数据、进行各种转换或构建模型。每个步骤都需要清晰地分开,原因有多个;首先是为了提高代码的可读性,便于理解每个阶段是如何进行的,其次是为了明确每个阶段是如何生成结果的。
例如,在这里,我们正在查看两组活动。一组是生成循环以识别需要归一化的列,另一组是使用前一阶段的输出生成不需要归一化的列:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_18.jpg
图 7.18:有效的工作流分割
工作流文档
当产品和解决方案被开发时,它们通常是在沙箱环境中开发、监控、部署和测试的。为了确保在新环境中顺利部署,我们必须为技术和非技术用户提供足够的支持文档。工作流文档包括需求和设计文档、产品文档、方法论文档、安装指南、软件用户手册、硬件和软件要求、故障排除管理以及测试文档。这些通常是产品或解决方案开发中所必需的。我们不能仅仅把一堆代码交给客户或用户,让他们自行运行。工作流文档在客户或用户环境中的部署和集成阶段非常重要,尤其是在确保代码可重复性方面。
从高层次来看,数据科学项目文档可以分为两个部分:
-
产品文档
-
方法论文档
产品文档提供了关于如何在 UI/UX 中使用每项功能及其应用的信息。产品文档可以进一步细分为:
-
安装指南
-
软件设计与用户手册
-
测试文档
-
故障排除管理
方法论文档提供了关于所使用的算法、方法、解决方案方式等信息。
练习 46:高可重复性的缺失值预处理
本练习的目的是学习如何开发具有高可重复性的代码,处理缺失值预处理问题。我们将使用来自这个链接的 UCI 银行和营销数据集:raw.githubusercontent.com/TrainingByPackt/Big-Data-Analysis-with-Python/master/Lesson07/Dataset/bank/bank.csv
。
执行以下步骤以查找缺失值预处理的可重复性:
-
在 Jupyter notebook 中导入所需的库和包,如下所示:
import numpy as np import pandas as pd import collections import random
-
设置您选择的工作目录,如下所示:
os.chdir("/Users/svk/Desktop/packt_exercises")
-
从
back.csv
导入数据集到 Spark 对象中,使用read_csv
函数,如下所示:df = pd.read_csv('bank.csv', sep=';')
-
现在,使用 head 函数查看数据集的前五行:
df.head(5)
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_19.jpg
图 7.19:银行数据集
由于数据集没有缺失值,我们需要人为地添加一些缺失值。
-
首先,设置循环参数,如下所示:
replaced = collections.defaultdict(set) ix = [(row, col) for row in range(df.shape[0]) for col in range(df.shape[1])] random.shuffle(ix) to_replace = int(round(.1*len(ix)))
-
创建一个
for
循环来生成缺失值:for row, col in ix: if len(replaced[row]) < df.shape[1] - 1: df.iloc[row, col] = np.nan to_replace -= 1 replaced[row].add(col) if to_replace == 0: break
-
使用以下命令,通过查看每一列的缺失值,识别数据中的缺失值:
print(df.isna().sum())
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_20.jpg
图 7.20:查找缺失值
-
定义四分位数范围(IQRs)并将其应用于数据集,以识别离群值:
num = df._get_numeric_data() Q1 = num.quantile(0.25) Q3 = num.quantile(0.75) IQR = Q3 - Q1 print(num < (Q1 - 1.5 * IQR)) print(num > (Q3 + 1.5 * IQR))
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_21.jpg
图 7.21:识别离群值
避免重复
我们都知道,代码的重复或冗余并不是一个好的编程习惯。它会导致调试困难,并且代码长度增加。相同代码的不同版本会在某些时候带来理解上的困难,尤其是在确定哪个版本是正确时。对于调试来说,某个位置的改动需要在代码中各处体现。为了避免不良编程习惯,编写和维护高质量的代码,让我们在接下来的章节中学习一些最佳实践。
使用函数和循环来优化代码
一个函数封装了一个任务,该任务需要一组步骤,将一个或多个输入转换为一个或多个输出,而循环用于对相同代码块的不同样本或子集数据执行重复任务。函数可以为单个变量、多个变量、DataFrame,或者多个参数输入集编写。
例如,假设你只需要对 DataFrame 或矩阵中的数值变量进行某种转换。可以为单个变量编写函数,并将其应用于所有数值列,或者可以为整个 DataFrame 编写函数,该函数会识别数值变量集并将其应用于生成输出。一旦编写了一个函数,它可以应用于接下来代码中的任何类似应用。这将减少重复工作。
编写函数时需要考虑以下挑战:
-
内部参数变化:任务之间的输入参数可能会发生变化,这是一个常见的挑战。为了处理这个问题,你可以在定义函数输入时,提到函数输入中的动态变量或对象。
-
未来任务计算过程中的变化:编写一个包含内部函数的函数,如果需要捕获任何变化,将不需要进行太多修改。这样,为新任务编写函数将变得容易。
-
避免在函数中使用循环:如果需要在数据的多个子集上按行进行处理,可以直接在每个循环中应用函数。这样,函数就不会受到对相同数据的重复代码块的限制。
-
处理数据类型变化:函数中的返回对象对于不同的任务可能是不同的。根据任务的不同,返回对象可以根据需要转换为其他数据类或数据类型。然而,输入数据类或数据类型可能会因任务的不同而变化。为了解决这个问题,你需要清楚地提供注释,以帮助理解函数的输入。
-
编写优化函数:数组在执行诸如循环或函数等重复任务时非常高效。在 Python 中,使用 NumPy 数组可以对大多数算术操作生成非常高效的数据处理。
开发用于代码/算法重用的库/包
包包或库封装了一组模块。它们在代码可复现性和生成的模块方面非常可靠。每天,全球的开发者和研究人员都会生成成千上万个包/库。你可以参考 Python 项目打包指南中的包开发说明来开发一个新的包(packaging.python.org/tutorials/packaging-projects/
)。本教程将为你提供如何上传并公开分发包以及内部使用的相关信息。
活动 14:进行数据归一化
本次活动的目的是应用在之前练习中学到的各种预处理技术,并使用预处理后的数据开发模型。
现在,让我们执行以下步骤:
-
导入所需的库并从
bank.csv
文件读取数据。 -
导入数据集并将 CSV 文件读取到 Spark 对象中。
检查数据的正态性——下一步是确定数据的正态性。
-
将数据按数值和类别进行分段,并对数值数据进行分布转换。
-
创建一个
for
循环,循环遍历每一列,以进行正态性测试,检测数据的正态分布。 -
创建一个幂变换器。幂变换器将把非正态分布的数据转换为正态分布。所开发的模型将用于转换之前识别的非正态列。
-
将创建的幂变换器模型应用于非正态数据。
-
要开发一个模型,首先将数据拆分为训练集和测试集以进行交叉验证,训练模型,然后在测试数据上进行预测以进行交叉验证。最后,生成一个交叉验证的混淆矩阵。
注意事项
本活动的解决方案可以在第 240 页找到。
总结
在本章中,我们从数据科学的角度学习了如何通过结构化的标准和实践来保持代码的可重复性,以避免在使用 Jupyter Notebook 时重复劳动。
我们首先了解了什么是可重复性,以及它如何影响研究和数据科学工作。我们探讨了可以提高代码可重复性的领域,特别是如何在数据可重复性方面保持有效的编码标准。随后,我们研究了重要的编码标准和实践,以通过有效的代码管理来避免重复劳动,方法是通过工作流分段、为所有关键任务开发函数,以及如何从可重用性角度推广编码,创建库和包。
在下一章,我们将学习如何使用目前为止学到的所有功能来生成完整的分析报告。我们还将学习如何使用各种 PySpark 功能进行 SQL 操作,并且如何开发各种可视化图表。
第九章:第八章
创建完整的分析报告
学习目标
本章结束时,您将能够:
-
从不同源读取 Spark 数据
-
对 Spark DataFrame 执行 SQL 操作
-
以一致的方式生成统计度量
-
使用 Plotly 生成图表
-
汇总包含所有先前步骤和数据的分析报告
在本章中,我们将使用 Spark 读取数据、聚合数据并提取统计度量。我们还将使用 Pandas 从聚合数据生成图表,并形成分析报告。
介绍
如果您已经在数据行业工作了一段时间,您会理解与不同数据源打交道、分析它们并以可消费的业务报告呈现它们的挑战。在使用 Python 上的 Spark 时,您可能需要从各种数据源读取数据,如平面文件、REST API 中的 JSON 格式等。
在现实世界中,获取正确格式的数据始终是一个挑战,需要进行多个 SQL 操作来收集数据。因此,任何数据科学家都必须知道如何处理不同的文件格式和数据源,进行基本的 SQL 操作,并将其以可消费的格式呈现。
本章提供了读取不同类型数据、对其进行 SQL 操作、进行描述性统计分析并生成完整分析报告的常用方法。我们将从理解如何将不同类型的数据读取到 PySpark 中开始,然后对其生成各种分析和图表。
从不同数据源读取 Spark 数据
Spark 的一个优势是能够从各种数据源读取数据。然而,这种能力并不一致,并且随着每个 Spark 版本的更新而变化。本章的这一部分将解释如何从 CSV 和 JSON 文件中读取数据。
练习 47:使用 PySpark 对象从 CSV 文件读取数据
要读取 CSV 数据,您必须编写spark.read.csv("带有.csv 的文件名")
函数。这里,我们读取的是前面章节中使用的银行数据。
注意
这里使用了sep
函数。
我们必须确保根据源数据的分隔方式使用正确的sep
函数。
现在,让我们执行以下步骤从bank.csv
文件读取数据:
-
首先,让我们将所需的包导入到 Jupyter 笔记本中:
import os import pandas as pd import numpy as np import collections from sklearn.base import TransformerMixin import random import pandas_profiling
-
接下来,导入所有必需的库,如下所示:
import seaborn as sns import time import re import os import matplotlib.pyplot as plt
现在,使用tick
主题,这将使我们的数据集更加可视化,并提供更高的对比度:
sns.set(style="ticks")
-
现在,使用以下命令更改工作目录:
os.chdir("/Users/svk/Desktop/packt_exercises")
-
让我们导入构建 Spark 会话所需的库:
from pyspark.sql import SparkSession spark = SparkSession.builder.appName('ml-bank').getOrCreate()
-
现在,让我们在创建
df_csv
Spark 对象后,通过以下命令读取 CSV 数据:df_csv = spark.read.csv('bank.csv', sep=';', header = True, inferSchema = True)
-
使用以下命令打印模式:
df_csv.printSchema()
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_01.jpg
图 8.1:银行模式
使用 PySpark 对象读取 JSON 数据
要读取 JSON 数据,必须在设置 SQL 上下文后使用read.json("带有.json 的文件名")
函数:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_02.jpg
图 8.2:在 PySpark 中读取 JSON 文件
Spark DataFrame 上的 SQL 操作
Spark 中的 DataFrame 是一个分布式的行列集合。它与关系型数据库中的表或 Excel 工作表相同。Spark 的 RDD/DataFrame 能够高效处理大量数据,并且可以处理 PB 级别的数据,无论是结构化数据还是非结构化数据。
Spark 通过将 DataFrame 组织成列来优化数据查询,这有助于 Spark 理解数据的模式。最常用的一些 SQL 操作包括对数据进行子集化、合并数据、过滤、选择特定列、删除列、删除所有空值以及添加新列等。
练习 48:在 PySpark 中读取数据并进行 SQL 操作
对于数据的总结统计,我们可以使用 spark_df.describe().show()
函数,它将提供 DataFrame 中所有列的count
、mean
、standard deviation
、max
和min
等信息。
例如,在我们所考虑的数据集——银行营销数据集(raw.githubusercontent.com/TrainingByPackt/Big-Data-Analysis-with-Python/master/Lesson08/bank.csv
)——中,可以通过以下方式获得总结统计数据:
-
创建一个新的 Jupyter 笔记本后,导入所有必要的包,如下所示:
import os import pandas as pd import numpy as np
-
现在,使用以下命令更改工作目录:
os.chdir("/Users/svk/Desktop/packt_exercises")
-
导入所有需要的库以构建 Spark 会话:
from pyspark.sql import SparkSession spark = SparkSession.builder.appName('ml-bank').getOrCreate()
-
使用 Spark 对象创建并读取 CSV 文件中的数据,如下所示:
spark_df = spark.read.csv('bank.csv', sep=';', header = True, inferSchema = True)
-
现在,让我们使用以下命令打印 Spark 对象的前五行:
spark_df.head(5)
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_03.jpg
图 8.3:银行数据的前五行(非结构化)
-
前面的输出是非结构化的。让我们首先识别数据类型,以便获取结构化数据。使用以下命令打印每列的数据类型:
spark_df.printSchema()
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_04.jpg
图 8.4:银行数据类型(结构化)
-
现在,让我们计算行和列的总数,并查看数据的概况:
spark_df.count()
输出结果如下:
4521 len(spark_df.columns), spark_df.columns
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image38437.jpg
图 8.5:行和列名称的总数
-
使用以下命令打印 DataFrame 的总结统计:
spark_df.describe().show()
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_06.jpg
图 8.6:数值列的总结统计
要从 DataFrame 中选择多个列,可以使用
spark_df.select('col1', 'col2', 'col3')
函数。例如,使用以下命令从balance
和y
列中选择前五行:spark_df.select('balance','y').show(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_07.jpg
图 8.7:余额和 y 列的数据
-
为了识别两个变量之间在频率层次上的关系,可以使用
crosstab
。要得出两个列之间的交叉表,可以使用spark_df.crosstab('col1', 'col2')
函数。交叉表是针对两个分类变量进行的,而不是数字变量:spark_df.crosstab('y', 'marital').show()
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_08.jpg
图 8.8:分类列的配对频率
-
现在,让我们向数据集添加一列新列:
# sample sets sample1 = spark_df.sample(False, 0.2, 42) sample2 = spark_df.sample(False, 0.2, 43) # train set train = spark_df.sample(False, 0.8, 44) train.withColumn('balance_new', train.balance /2.0).select('balance','balance_new').show(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_09.jpg
图 8.9:新添加列的数据
-
使用以下命令删除新创建的列:
train.drop('balance_new)
练习 49:创建和合并两个 DataFrame
在本练习中,我们将提取并使用银行营销数据(archive.ics.uci.edu/ml/datasets/bank+marketing
)来自 UCI 机器学习库。目标是使用 PySpark 对 Spark DataFrame 执行合并操作。
数据与葡萄牙银行机构的直接营销活动相关。营销活动主要通过电话进行。通常,需要对同一客户进行多次联系,以判断该产品(银行定期存款)是否会被(是)或不会被(否)订阅。
现在,让我们从当前的银行营销数据创建两个 DataFrame,并基于主键将它们合并:
-
首先,在 Jupyter notebook 中导入所需的头文件:
import os import pandas as pd import numpy as np import pyspark
-
现在,使用以下命令更改工作目录:
os.chdir("/Users/svk/Desktop/packt_exercises")
-
导入构建 Spark 会话所需的所有库:
from pyspark.sql import SparkSession spark = SparkSession.builder.appName('ml-bank').getOrCreate()
-
使用以下命令将数据从 CSV 文件读取到 Spark 对象:
spark_df = spark.read.csv('bank.csv', sep=';', header = True, inferSchema = True)
-
打印 Spark 对象的前五行:
spark_df.head(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image38484.jpg
图 8.10:前五行的银行数据(非结构化)
-
现在,为了使用主键(ID)合并两个 DataFrame,首先需要将其拆分成两个 DataFrame。
-
首先,添加一个包含
ID
列的新 DataFrame:from pyspark.sql.functions import monotonically_increasing_id train_with_id = spark_df.withColumn("ID", monotonically_increasing_id())
-
然后,创建另一个列
ID2
:train_with_id = train_with_id.withColumn('ID2', train_with_id.ID)
-
使用以下命令拆分 DataFrame:
train_with_id1 = train_with_id.drop('balance', "ID2") train_with_id2 = train_with_id.select('balance', "ID2")
-
现在,修改
train_with_id2
的 ID 列名:train_with_id2 = train_with_id2.withColumnRenamed("ID2", "ID")
-
使用以下命令合并
train_with_id1
和train_with_id2
:train_merged = train_with_id1.join(train_with_id2, on=['ID'], how='left_outer')
练习 50:子集化 DataFrame
在本次练习中,我们将从 UCI 机器学习库提取并使用银行营销数据(archive.ics.uci.edu/ml/datasets/bank+marketing
)。目标是使用 PySpark 对 Spark 数据框进行过滤/子集操作。
让我们从银行营销数据中提取出余额大于0
的子集:
-
首先,在 Jupyter 笔记本中导入所需的头文件:
import os import pandas as pd import numpy as np import pyspark
-
现在,使用以下命令更改工作目录:
os.chdir("/Users/svk/Desktop/packt_exercises")
-
导入构建 Spark 会话所需的所有库:
from pyspark.sql import SparkSession spark = SparkSession.builder.appName('ml-bank').getOrCreate()
-
现在,使用以下命令将 CSV 数据作为 Spark 对象读取:
spark_df = spark.read.csv('bank.csv', sep=';', header = True, inferSchema = True)
-
让我们运行 SQL 查询以子集化并过滤数据框:
train_subsetted = spark_df.filter(spark_df.balance > 0.0) pd.DataFrame(train_subsetted.head(5))
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image38496.jpg
图 8.11:过滤后的数据框
生成统计度量
Python 是一种通用语言,具有统计模块。很多统计分析(如描述性分析,包括识别数值变量的数据分布,生成相关矩阵,识别类别变量的各个层次频率及其众数等)可以在 Python 中完成。以下是一个相关性示例:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_12.jpg
图 8.12:分段数值数据与相关矩阵输出
识别数据分布并对其进行标准化对参数模型(如yeo-johnson
方法)进行数据标准化非常重要:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_13.jpg
图 8.13:识别数据分布——正态性检验
然后使用yeo-johnson
或box-cox
方法对识别出的变量进行标准化。
生成特征的重要性在数据科学项目中非常重要,尤其是在使用预测技术时。这大致属于统计分析范畴,因为各种统计技术用于识别重要的变量。这里使用的一种方法是Boruta
,它是一个围绕RandomForest
算法的变量重要性分析方法。为此,我们将使用BorutaPy
包:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_14.jpg
图 8.14:特征重要性
活动 15:使用 Plotly 生成可视化
在本次活动中,我们将从 UCI 机器学习库提取并使用银行营销数据。目标是使用 Python 中的 Plotly 生成可视化图表。
注意
Plotly 的 Python 绘图库可以生成互动式、出版质量的图表。
执行以下步骤以使用 Plotly 生成可视化图表:
-
将所需的库和包导入到 Jupyter 笔记本中。
-
导入用于 Plotly 的数据可视化所需的库:
import plotly.graph_objs as go from plotly.plotly import iplot import plotly as py
-
从
bank.csv
文件中读取数据到 Spark 数据框。 -
检查您系统上运行的 Plotly 版本。确保您正在运行更新版本。使用
pip install plotly --upgrade
命令,然后运行以下代码:from plotly import __version__ from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot print(__version__) # requires version >= 1.9.0
输出如下:
3.7.1
-
现在导入所需的库以使用 Plotly 绘制图表:
import plotly.plotly as py import plotly.graph_objs as go from plotly.plotly import iplot init_notebook_mode(connected=True)
-
在以下命令中设置 Plotly 凭据,如此处所示:
plotly.tools.set_credentials_file(username='Your_Username', api_key='Your_API_Key')
注意
要为 Plotly 生成 API 密钥,请注册帐户并转到
plot.ly/settings#/
。单击API Keys选项,然后单击Regenerate Key选项。 -
现在,使用 Plotly 绘制以下每个图形:
条形图:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_15.jpg
图 8.15: 银行数据的条形图
散点图:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_16.jpg
图 8.16: 银行数据的散点图
箱线图:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_17.jpg
图 8.17: 银行数据的箱线图
注意
此活动的解决方案可在第 248 页找到。
总结
在本章中,我们学习了如何将数据从各种来源导入 Spark 环境作为 Spark DataFrame。此外,我们还学习了如何对该 DataFrame 执行各种 SQL 操作,以及如何生成各种统计措施,如相关性分析,数据分布识别,构建特征重要性模型等。我们还研究了如何使用 Plotly 离线生成有效的图表,您可以生成各种图表以开发分析报告。
本书希望为您提供一次关于大数据的激动人心的旅程。我们从 Python 开始,涵盖了 Python 数据科学堆栈的几个库:NumPy 和 Pandas,我们还看到如何使用 Jupyter 笔记本。然后,我们学习了如何创建信息化的数据可视化图表,介绍了良好图表的一些指导原则,并使用 Matplotlib 和 Seaborn 生成图形。接着,我们开始使用大数据工具 - Hadoop 和 Spark,从而理解了基本原理和操作。
我们已经看到如何在 Spark 中使用 DataFrame 来操作数据,并且已经掌握了利用诸如相关性和维度缩减等概念来更好地理解我们的数据。本书还涵盖了可复现性,以便在需要时能够支持和更好地复制分析,我们以最终报告结束了我们的旅程。希望本书涵盖的主题和实际示例能帮助您在数据旅程的各个领域中取得成功。
第十章:附录
关于
本节内容旨在帮助学生完成书中的活动。它包括学生必须执行的详细步骤,以实现活动目标。
第一章:Python 数据科学工具栈
活动 1:IPython 和 Jupyter
-
在文本编辑器中打开
python_script_student.py
文件,将内容复制到 IPython 中的笔记本,并执行操作。 -
将 Python 脚本中的代码复制并粘贴到 Jupyter 笔记本中:
import numpy as np def square_plus(x, c): return np.power(x, 2) + c
-
现在,更新
x
和c
变量的值。然后,修改函数的定义:x = 10 c = 100 result = square_plus(x, c) print(result)
输出如下:
200
活动 2:处理数据问题
-
导入 pandas 和 NumPy 库:
import pandas as pd import numpy as np
-
从美国环境保护局获取 RadNet 数据集,数据可通过 Socrata 项目下载:
url = "https://opendata.socrata.com/api/views/cf4r-dfwe/rows.csv?accessType=DOWNLOAD" df = pd.read_csv(url)
-
创建一个包含 RadNet 数据集中放射性同位素的数值列的列表:
columns = df.columns id_cols = ['State', 'Location', "Date Posted", 'Date Collected', 'Sample Type', 'Unit'] columns = list(set(columns) - set(id_cols)) columns
-
对一列使用
apply
方法,并使用一个lambda
函数比较Non-detect
字符串:df['Cs-134'] = df['Cs-134'].apply(lambda x: np.nan if x == "Non-detect" else x) df.head()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_19.jpg
图 1.19:应用 lambda 函数后的 DataFrame
-
将一列中的文本值替换为
NaN
,使用np.nan
:df.loc[:, columns] = df.loc[:, columns].applymap(lambda x: np.nan if x == 'Non-detect' else x) df.loc[:, columns] = df.loc[:, columns].applymap(lambda x: np.nan if x == 'ND' else x)
-
使用相同的 lambda 比较,并对多个列同时使用
applymap
方法,使用在第一步中创建的列表:df.loc[:, ['State', 'Location', 'Sample Type', 'Unit']] = df.loc[:, ['State', 'Location', 'Sample Type', 'Unit']].applymap(lambda x: x.strip())
-
创建一个包含所有非数值列的列表:
df.dtypes
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_20.jpg
图 1.20:列及其类型列表
-
使用
to_numeric
函数将 DataFrame 对象转换为浮动数值:df['Date Posted'] = pd.to_datetime(df['Date Posted']) df['Date Collected'] = pd.to_datetime(df['Date Collected']) for col in columns: df[col] = pd.to_numeric(df[col]) df.dtypes
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_21.jpg
图 1.21:列及其类型列表
-
使用选择和过滤方法,确保字符串列的名称没有任何空格:
df['Date Posted'] = pd.to_datetime(df['Date Posted']) df['Date Collected'] = pd.to_datetime(df['Date Collected']) for col in columns: df[col] = pd.to_numeric(df[col]) df.dtypes
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_22.jpg
图 1.22:应用选择和过滤方法后的 DataFrame
活动 3:使用 Pandas 绘制数据
-
使用我们正在处理的 RadNet DataFrame。
-
修复所有数据类型问题,正如我们之前看到的那样。
-
创建一个图表,对
Location
进行筛选,选择San Bernardino
市,并且选择一个放射性同位素,x轴设置为date
,y轴为放射性同位素I-131
:df.loc[df.Location == 'San Bernardino'].plot(x='Date Collected', y='I-131')
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_23.jpg
图 1.23:收集日期与 I-131 的关系图
-
创建一个散点图,显示两个相关放射性同位素
I-131
和I-132
的浓度:fig, ax = plt.subplots() ax.scatter(x=df['I-131'], y=df['I-132']) _ = ax.set( xlabel='I-131', ylabel='I-132', title='Comparison between concentrations of I-131 and I-132' )
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_01_24.jpg
图 1.24:I-131 和 I-132 浓度图
第二章:使用 Matplotlib 和 Seaborn 进行统计可视化
活动 4:使用面向对象 API 和 Pandas DataFrame 绘制线形图
-
在 Jupyter notebook 中导入所需的库,并从 Auto-MPG 数据集仓库读取数据集:
%matplotlib inline import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np import pandas as pd url = "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data" df = pd.read_csv(url)
-
提供列名以简化数据集,如下所示:
column_names = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'year', 'origin', 'name']
-
现在读取包含列名的新数据集并显示:
df = pd.read_csv(url, names= column_names, delim_whitespace=True) df.head()
图形如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_13.jpg
图 2.29:auto-mpg 数据框
-
使用以下命令将
horsepower
和year
数据类型转换为浮动和整数:df.loc[df.horsepower == '?', 'horsepower'] = np.nan df['horsepower'] = pd.to_numeric(df['horsepower']) df['full_date'] = pd.to_datetime(df.year, format='%y') df['year'] = df['full_date'].dt.year
-
让我们显示数据类型:
df.dtypes
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_30.jpg
图 2.30:数据类型
-
现在使用以下命令绘制每年平均的
horsepower
:df.groupby('year')['horsepower'].mean().plot()
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_31.jpg
图 2.31:折线图
活动 5:使用散点图理解变量之间的关系
-
在 Jupyter notebook 中导入所需的库,并从 Auto-MPG 数据集仓库读取数据集:
%matplotlib inline import pandas as pd import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt import seaborn as sns url = "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data" df = pd.read_csv(url)
-
提供列名以简化数据集,如下所示:
column_names = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'year', 'origin', 'name']
-
现在读取包含列名的新数据集并显示:
df = pd.read_csv(url, names= column_names, delim_whitespace=True) df.head()
图形如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_13.jpg
图 2.32:Auto-mpg 数据框
-
现在使用
scatter
方法绘制散点图:fig, ax = plt.subplots() ax.scatter(x = df['horsepower'], y=df['weight'])
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54488.jpg
图 2.33:使用 scatter 方法的散点图
活动 6:将图形导出为磁盘文件
-
在 Jupyter notebook 中导入所需的库,并从 Auto-MPG 数据集仓库读取数据集:
%matplotlib inline import pandas as pd import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt import seaborn as sns url = "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data" df = pd.read_csv(url)
-
提供列名以简化数据集,如下所示:
column_names = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'year', 'origin', 'name']
-
现在读取包含列名的新数据集并显示:
df = pd.read_csv(url, names= column_names, delim_whitespace=True)
-
使用以下命令创建条形图:
fig, ax = plt.subplots() df.weight.plot(kind='hist', ax=ax)
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_02_34.jpg
图 2.34:条形图
-
使用
savefig
函数将其导出为 PNG 文件:fig.savefig('weight_hist.png')
活动 7:完整的图表设计
-
在 Jupyter notebook 中导入所需的库,并从 Auto-MPG 数据集仓库读取数据集:
%matplotlib inline import pandas as pd import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt import seaborn as sns url = "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data" df = pd.read_csv(url)
-
提供列名以简化数据集,如下所示:
column_names = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'year', 'origin', 'name']
-
现在读取包含列名的新数据集并显示:
df = pd.read_csv(url, names= column_names, delim_whitespace=True)
-
对
year
和cylinders
进行分组,并取消将它们用作索引的选项:df_g = df.groupby(['year', 'cylinders'], as_index=False)
-
计算每加仑英里数的平均值,并按分组设置
year
为索引:df_g = df_g.mpg.mean()
-
将
year
设置为数据框的索引:df_g = df_g.set_index(df_g.year)
-
使用面向对象的 API 创建图形和坐标轴:
import matplotlib.pyplot as plt fig, axes = plt.subplots()
-
按
cylinders
对df_g
数据集进行分组,并使用创建的大小为 (10
,8
) 的坐标轴绘制每加仑英里数变量:df = df.convert_objects(convert_numeric=True) df_g = df.groupby(['year', 'cylinders'], as_index=False).horsepower.mean() df_g = df_g.set_index(df_g.year)
-
设置 标题、x 轴标签和 y 轴标签:
fig, axes = plt.subplots() df_g.groupby('cylinders').horsepower.plot(axes=axes, figsize=(12,10)) _ = axes.set( title="Average car power per year", xlabel="Year", ylabel="Power (horsepower)" )
输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54506.jpg
图 2.35:每年平均汽车功率的折线图(无图例)
-
包括图例,如下所示:
axes.legend(title='Cylinders', fancybox=True)
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54515.jpg
图 2.36:每年平均汽车功率的折线图(有图例)
-
将图像保存为 PNG 文件:
fig.savefig('mpg_cylinder_year.png')
第三章:与大数据框架协作
活动 8:解析文本
-
使用
text
方法将文本文件读入 Spark 对象:rdd_df = spark.read.text("/localdata/myfile.txt").rdd
为了解析我们正在读取的文件,我们将使用 lambda 函数和 Spark 操作,例如
map
、flatMap
和reduceByKey
。flatmap
将一个函数应用到 RDD 的所有元素,扁平化结果并返回转换后的 RDD。reduceByKey
会根据给定的键合并值,进行值的合并。借助这些函数,我们可以统计文本中的行数和单词数。 -
使用以下命令从文本中提取
lines
:lines = rdd_df.map(lambda line: line[0])
-
这将把文件中的每一行拆分成列表中的一个条目。要检查结果,可以使用
collect
方法,它会将所有数据收集回驱动程序进程:lines.collect()
-
现在,让我们使用
count
方法统计行数:lines.count()
注意
使用
collect
方法时要小心!如果被收集的 DataFrame 或 RDD 大于本地驱动程序的内存,Spark 会抛出错误。 -
现在,让我们首先将每一行按空格分割成单词,并将所有元素合并,去除大写字母的单词:
splits = lines.flatMap(lambda x: x.split(' ')) lower_splits = splits.map(lambda x: x.lower())
-
我们还要移除 停用词。我们本可以使用 NLTK 提供的更一致的停用词列表,但现在我们将自定义一个:
stop_words = ['of', 'a', 'and', 'to']
-
使用以下命令从我们的 token 列表中移除停用词:
tokens = lower_splits.filter(lambda x: x and x not in stop_words)
现在我们可以处理我们的 token 列表并统计唯一单词的数量。这个思路是生成一个元组列表,第一个元素是
token
,第二个元素是该特定 token 的count
。 -
首先,让我们将我们的 token 映射到一个列表:
token_list = tokens.map(lambda x: [x, 1])
-
使用
reduceByKey
操作,它将对每个列表应用该操作:count = token_list.reduceByKey(add).sortBy(lambda x: x[1], ascending=False) count.collect()
记住,将所有数据收集回驱动节点!始终使用如 top
和 htop
等工具检查是否有足够的内存。
第四章:深入探索 Spark
活动 9:Spark DataFrame 入门
如果你正在使用 Google Collab 运行 Jupyter notebook,请添加以下几行代码以确保你已经设置好环境:
!apt-get install openjdk-8-jdk-headless -qq > /dev/null
!wget -q http://www-us.apache.org/dist/spark/spark-2.4.0/spark-2.4.0-bin-hadoop2.7.tgz
!tar xf spark-2.4.0-bin-hadoop2.7.tgz
!pip install -q findspark
import os
os.environ["JAVA_HOME"] = "/usr/lib/jvm/java-8-openjdk-amd64"
os.environ["SPARK_HOME"] = "/content/spark-2.4.2-bin-hadoop2.7"
如果未安装 findspark,请使用以下命令进行安装:
pip install -q findspark
-
通过手动指定模式来创建一个示例 DataFrame,并导入 findspark 模块以连接 Jupyter 和 Spark:
import findspark findspark.init() import pyspark import os
-
使用以下命令创建
SparkContext
和SQLContext
:sc = pyspark.SparkContext() from pyspark.sql import SQLContext sqlc = SQLContext(sc) from pyspark.sql import * na_schema = Row("Name","Subject","Marks") row1 = na_schema("Ankit", "Science",95) row2 = na_schema("Ankit", "Maths", 86) row3 = na_schema("Preity", "Maths", 92) na_list = [row1, row2, row3] df_na = sqlc.createDataFrame(na_list) type(df_na)
输出如下:
pyspark.sql.dataframe.DataFrame
-
使用以下命令检查 DataFrame:
df_na.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_29.jpg
图 4.29:示例 DataFrame
-
从现有的 RDD 创建一个示例 DataFrame。首先按如下方式创建 RDD:
data = [("Ankit","Science",95),("Preity","Maths",86),("Ankit","Maths",86)] data_rdd = sc.parallelize(data) type(data_rdd)
输出如下:
pyspark.rdd.RDD
-
使用以下命令将 RDD 转换为 DataFrame:
data_df = sqlc.createDataFrame(data_rdd) data_df.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54539.jpg
图 4.30:从 RDD 转换到 DataFrame
-
通过读取 CSV 文件中的数据来创建一个示例 DataFrame:
df = sqlc.read.format('com.databricks.spark.csv').options(header='true', inferschema='true').load('mtcars.csv') type(df)
输出如下:
pyspark.sql.dataframe.DataFrame
-
打印 DataFrame 的前七行:
df.show(7)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_31.jpg
图 4.31:DataFrame 的前七行
-
打印 DataFrame 的模式:
df.printSchema()
-
输出如下:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_32.jpg
图 4.32:DataFrame 的架构
-
打印 DataFrame 中的列数和行数:
print('number of rows:'+ str(df.count())) print('number of columns:'+ str(len(df.columns)))
输出如下:
number of rows:32 number of columns:11
-
打印 DataFrame 和任意两列的汇总统计:
df.describe().show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_33.jpg
图 4.33:DataFrame 的汇总统计
打印任意两列的汇总:
df.describe(['mpg','cyl']).show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_34.jpg
图 4.34:mpg 和 cyl 列的汇总统计
-
将样本 DataFrame 的首几行写入 CSV 文件:
df_p = df.toPandas() df_p.head(7).to_csv("mtcars_head.csv")
活动 10:使用 Spark DataFrame 进行数据操作
-
按照下面所示安装相关包:
!apt-get install openjdk-8-jdk-headless -qq > /dev/null !wget -q http://www-us.apache.org/dist/spark/spark-2.4.0/spark-2.4.0-bin-hadoop2.7.tgz !tar xf spark-2.4.0-bin-hadoop2.7.tgz !pip install -q findspark import os os.environ["JAVA_HOME"] = "/usr/lib/jvm/java-8-openjdk-amd64" os.environ["SPARK_HOME"] = "/content/spark-2.4.0-bin-hadoop2.7"
-
然后,导入
findspark
模块,使用以下命令将 Jupyter 与 Spark 连接:import findspark findspark.init() import pyspark import os
-
现在,创建
SparkContext
和SQLContext
,如下面所示:sc = pyspark.SparkContext() from pyspark.sql import SQLContext sqlc = SQLContext(sc)
-
按照下面所示在 Spark 中创建 DataFrame:
df = sqlc.read.format('com.databricks.spark.csv').options(header='true', inferschema='true').load('mtcars.csv') df.show(4)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_35.jpg
图 4.35:Spark 中的 DataFrame
-
使用以下命令重命名 DataFrame 中的任意五列:
data = df new_names = ['mpg_new', 'cyl_new', 'disp_new', 'hp_new', 'drat_new'] for i,z in zip(data.columns[0:5],new_names): data = data.withColumnRenamed(str(i),str(z)) data.columns
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_36.jpg
图 4.36:DataFrame 的列
-
从 DataFrame 中选择任意两列数值型数据和一列类别型数据:
data = df.select(['cyl','mpg','hp']) data.show(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_37.jpg
图 4.37:DataFrame 中的两列数值型和一列类别型数据
-
统计类别型变量中不同类别的数量:
data.select('cyl').distinct().count() #3
-
通过对两列数值型数据求和和相乘,创建 DataFrame 中的两个新列:
data = data.withColumn('colsum',(df['mpg'] + df['hp'])) data = data.withColumn('colproduct',(df['mpg'] * df['hp'])) data.show(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_38.jpg
图 4.38:DataFrame 中的新列
-
删除原始数值型的两列:
data = data.drop('mpg','hp') data.show(5)
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_39.jpg
图 4.39:删除列后的 DataFrame 中的新列
-
按类别列对数据进行排序:
data = data.orderBy(data.cyl) data.show(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_40.jpg
图 4.40:按类别列排序的数据
-
计算
categorical
变量中每个不同类别的求和列的mean
:data.groupby('cyl').agg({'colsum':'mean'}).show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_41.jpg
图 4.41:求和列的均值
-
过滤出所有大于前一步骤中计算出的
mean
的行:data.count()#15 cyl_avg = data.groupby('cyl').agg({'colsum':'mean'}) avg = cyl_avg.agg({'avg(colsum)':'mean'}).toPandas().iloc[0,0] data = data.filter(data.colsum > avg) data.count() data.show(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_42.jpg
图 4.42:所有求和列均值的平均值
-
对结果 DataFrame 进行去重,以确保它包含所有唯一记录:
data = data.dropDuplicates() data.count()
输出结果是
15
。
活动 11:Spark 中的图形
-
在 Jupyter Notebook 中导入所需的 Python 库:
import pandas as pd import os import matplotlib.pyplot as plt import seaborn as sns %matplotlib inline
-
使用以下命令读取并显示 CSV 文件中的数据:
df = pd.read_csv('mtcars.csv') df.head()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_43.jpg
图 4.43:Auto-mpg DataFrame
-
使用直方图可视化数据集中任何连续数值变量的离散频率分布:
plt.hist(df['mpg'], bins=20) plt.ylabel('Frequency') plt.xlabel('Values') plt.title('Frequency distribution of mpg') plt.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_44.jpg
图 4.44:离散频率分布直方图
-
使用饼图可视化数据集中各类别的百分比份额:
## Calculate count of records for each gear data = pd.DataFrame([[3,4,5],df['gear'].value_counts().tolist()]).T data.columns = ['gear','gear_counts'] ## Visualising percentage contribution of each gear in data using pie chart plt.pie(data.gear_counts, labels=data.gear, startangle=90, autopct='%.1f%%') plt.title('Percentage contribution of each gear') plt.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_45.jpg
图 4.45:使用饼图显示类别的百分比份额
-
使用箱线图绘制连续变量在分类变量各类别下的分布:
sns.boxplot(x = 'gear', y = 'disp', data = df) plt.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_46.jpg
图 4.46: 使用箱线图绘制连续变量的分布
-
使用折线图可视化连续数值变量的值:
data = df[['hp']] data.plot(linestyle='-') plt.title('Line Chart for hp') plt.ylabel('Values') plt.xlabel('Row number') plt.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54696.jpg
图 4.47: 使用折线图绘制连续数值变量
-
在同一折线图上绘制多个连续数值变量的值:
data = df[['hp','disp', 'mpg']] data.plot(linestyle='-') plt.title('Line Chart for hp, disp & mpg') plt.ylabel('Values') plt.xlabel('Row number') plt.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_04_48.jpg
图 4.48: 多个连续数值变量
第五章: Spark 中的缺失值处理和相关性分析
活动 12: 缺失值处理和使用 PySpark DataFrame 进行相关性分析
-
在 Jupyter notebook 中导入所需的库和模块,如下所示:
import findspark findspark.init() import pyspark import random
-
在 Jupyter notebook 中使用以下命令设置
SparkContext
:sc = pyspark.SparkContext(appName = "chapter5")
-
同样,在 notebook 中设置
SQLContext
:from pyspark.sql import SQLContext sqlc = SQLContext(sc)
-
现在,使用以下命令将 CSV 数据读取到 Spark 对象中:
df = sqlc.read.format('com.databricks.spark.csv').options(header = 'true', inferschema = 'true').load('iris.csv') df.show(5)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_14.jpg
图 5.14: Iris 数据框,将 CSV 数据读取到 Spark 对象中
-
用列的均值填充
Sepallength
列中的缺失值。 -
首先,使用以下命令计算
Sepallength
列的均值:from pyspark.sql.functions import mean avg_sl = df.select(mean('Sepallength')).toPandas()['avg(Sepallength)']
-
现在,用列的均值填补
Sepallength
列中的缺失值,如下所示:y = df y = y.na.fill(float(avg_sl),['Sepallength']) y.describe().show(1)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_15.jpg
图 5.15: Iris 数据框
-
计算数据集的相关矩阵。确保导入所需的模块,如下所示:
from pyspark.mllib.stat import Statistics import pandas as pd
-
现在,在计算相关性之前,先填充 DataFrame 中的缺失值:
z = y.fillna(1)
-
接下来,从 PySpark DataFrame 中删除
String
类型的列,如下所示:a = z.drop('Species') features = a.rdd.map(lambda row: row[0:])
-
现在,在 Spark 中计算相关矩阵:
correlation_matrix = Statistics.corr(features, method="pearson")
-
接下来,使用以下命令将相关矩阵转换为 pandas DataFrame:
correlation_df = pd.DataFrame(correlation_matrix) correlation_df.index, correlation_df.columns = a.columns, a.columns correlation_df
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54735.jpg
图 5.16: 将相关矩阵转换为 pandas DataFrame
-
绘制显示强正相关的变量对,并在其上拟合一条线性回归线。
-
首先,将数据从 Spark DataFrame 加载到 pandas DataFrame:
import pandas as pd dat = y.toPandas() type(dat)
输出如下:
pandas.core.frame.DataFrame
-
接下来,使用以下命令加载所需的模块并绘制数据:
import matplotlib.pyplot as plt import seaborn as sns %matplotlib inline sns.lmplot(x = "Sepallength", y = "Petallength", data = dat) plt.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54744.jpg
图 5.17: Seaborn 绘图,x = “Sepallength”,y = “Petallength”
-
绘制图形,使得
x
等于Sepallength
,y
等于Petalwidth
:import seaborn as sns sns.lmplot(x = "Sepallength", y = "Petalwidth", data = dat) plt.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54754.jpg
图 5.18: Seaborn 图,x = “Sepallength”,y = “Petalwidth”
-
绘制图表,使得
x
等于Petalwidth
,y
等于Petalwidth
:sns.lmplot(x = "Petallength", y = "Petalwidth", data = dat) plt.show()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_05_19.jpg
图 5.19: Seaborn 图,x = “Petallength”,y = “Petalwidth”
第六章:业务流程定义与探索性数据分析
活动 13:根据给定数据对数值特征进行高斯分布映射
-
下载
bank.csv
。现在,使用以下命令读取其中的数据:import numpy as np import pandas as pd import seaborn as sns import time import re import os import matplotlib.pyplot as plt sns.set(style="ticks") # import libraries required for preprocessing import sklearn as sk from scipy import stats from sklearn import preprocessing # set the working directory to the following os.chdir("/Users/svk/Desktop/packt_exercises") # read the downloaded input data (marketing data) df = pd.read_csv('bank.csv', sep=';')
-
从数据框中识别数值数据。数据可以根据其类型进行分类,例如类别型、数值型(浮动、整数)、日期等。我们在这里识别数值数据,因为我们只能对数值数据进行归一化:
numeric_df = df._get_numeric_data() numeric_df.head()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_12.jpg
图 6.12: 数据框
-
进行正态性测试,并识别具有非正态分布的特征:
numeric_df_array = np.array(numeric_df) # converting to numpy arrays for more efficient computation loop_c = -1 col_for_normalization = list() for column in numeric_df_array.T: loop_c+=1 x = column k2, p = stats.normaltest(x) alpha = 0.001 print("p = {:g}".format(p)) # rules for printing the normality output if p < alpha: test_result = "non_normal_distr" col_for_normalization.append((loop_c)) # applicable if yeo-johnson is used #if min(x) > 0: # applicable if box-cox is used #col_for_normalization.append((loop_c)) # applicable if box-cox is used print("The null hypothesis can be rejected: non-normal distribution") else: test_result = "normal_distr" print("The null hypothesis cannot be rejected: normal distribution")
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_13.jpg
图 6.13: 正态性测试并识别特征
注意
此处进行的正态性测试基于 D’Agostino 和 Pearson 的测试(
docs.scipy.org/doc/scipy/reference/generated/scipy.stats.normaltest.html
),该测试结合了偏度和峰度来识别特征的分布与高斯分布的接近程度。在此测试中,如果 p 值小于设定的 alpha 值,则拒绝零假设,表明该特征不具有正态分布。我们通过循环函数查看每一列,并识别每个特征的分布。 -
绘制特征的概率密度图,以便直观地分析其分布:
columns_to_normalize = numeric_df[numeric_df.columns[col_for_normalization]] names_col = list(columns_to_normalize) # density plots of the features to check the normality columns_to_normalize.plot.kde(bw_method=3)
检查正态性的特征密度图如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_14.jpg
图 6.14: 特征图
注意
多个变量的密度图在前面的图表中展示。图表中各特征的分布具有较高的正峰度,这不是正态分布。
-
准备幂变换模型,并对已识别的特征进行变换,以便根据
box-cox
或yeo-johnson
方法将其转换为正态分布:pt = preprocessing.PowerTransformer(method='yeo-johnson', standardize=True, copy=True) normalized_columns = pt.fit_transform(columns_to_normalize) normalized_columns = pd.DataFrame(normalized_columns, columns=names_col)
在之前的命令中,我们准备了幂变换模型,并将其应用于所选特征的数据。
-
在变换之后,再次绘制特征的概率密度图,以直观地分析特征的分布:
normalized_columns.plot.kde(bw_method=3)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_06_15.jpg
图 6.15: 特征图
第七章:大数据分析中的可重复性
活动 14:测试数据属性(列)的正态性,并对非正态分布的属性进行高斯归一化
-
在 Jupyter 笔记本中导入所需的库和包:
import numpy as np import pandas as pd import seaborn as sns import time import re import os import matplotlib.pyplot as plt sns.set(style="ticks")
-
现在,导入预处理所需的库:
import sklearn as sk from scipy import stats from sklearn import preprocessing
-
使用以下命令设置工作目录:
os.chdir("/Users/svk/Desktop/packt_exercises")
-
现在,将数据集导入到 Spark 对象中:
df = pd.read_csv('bank.csv', sep=';')
-
识别数据中的目标变量:
DV = 'y' df[DV]= df[DV].astype('category') df[DV] = df[DV].cat.codes
-
使用以下命令生成训练和测试数据:
msk = np.random.rand(len(df)) < 0.8 train = df[msk] test = df[~msk]
-
创建
Y
和X
数据,如下所示:# selecting the target variable (dependent variable) as y y_train = train[DV]
-
使用
drop
命令删除DV
或y
:train = train.drop(columns=[DV]) train.head()
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_22.jpg
图 7.22:银行数据集
-
对数据进行数值和分类分割,并对数值数据执行分布变换:
numeric_df = train._get_numeric_data()
对数据进行预处理:
-
现在,创建一个
loop
,使用以下命令识别具有非正态分布的列(将数据转换为 NumPy 数组,以提高计算效率):numeric_df_array = np.array(numeric_df) loop_c = -1 col_for_normalization = list() for column in numeric_df_array.T: loop_c+=1 x = column k2, p = stats.normaltest(x) alpha = 0.001 print("p = {:g}".format(p)) # rules for printing the normality output if p < alpha: test_result = "non_normal_distr" col_for_normalization.append((loop_c)) # applicable if yeo-johnson is used #if min(x) > 0: # applicable if box-cox is used #col_for_normalization.append((loop_c)) # applicable if box-cox is used print("The null hypothesis can be rejected: non-normal distribution") else: test_result = "normal_distr" print("The null hypothesis cannot be rejected: normal distribution")
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_23.jpg
图 7.23:识别具有非线性分布的列
-
创建基于
PowerTransformer
的变换(box-cox
):pt = preprocessing.PowerTransformer(method='yeo-johnson', standardize=True, copy=True)
注意
box-cox
仅能处理正值。 -
对数据应用幂变换模型。选择需要标准化的列:
columns_to_normalize = numeric_df[numeric_df.columns[col_for_normalization]] names_col = list(columns_to_normalize)
-
创建密度图以检查正态性:
columns_to_normalize.plot.kde(bw_method=3)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54824.jpg
图 7.24:密度图检查正态性
-
现在,使用以下命令将列转换为正态分布:
normalized_columns = pt.fit_transform(columns_to_normalize) normalized_columns = pd.DataFrame(normalized_columns, columns=names_col)
-
再次创建密度图,以检查正态性:
normalized_columns.plot.kde(bw_method=3)
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54838.jpg
图 7.25:另一个密度图以检查正态性
-
使用
loop
在转换后的数据上识别具有非正态分布的列:numeric_df_array = np.array(normalized_columns) loop_c = -1 for column in numeric_df_array.T: loop_c+=1 x = column k2, p = stats.normaltest(x) alpha = 0.001 print("p = {:g}".format(p)) # rules for printing the normality output if p < alpha: test_result = "non_normal_distr" print("The null hypothesis can be rejected: non-normal distribution") else: test_result = "normal_distr" print("The null hypothesis cannot be rejected: normal distribution")
输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/Image54847.jpg
图 7.26:幂变换模型应用于数据
-
绑定标准化和非标准化列。选择不进行标准化的列:
columns_to_notnormalize = numeric_df columns_to_notnormalize.drop(columns_to_notnormalize.columns[col_for_normalization], axis=1, inplace=True)
-
使用以下命令绑定非标准化列和标准化列:
numeric_df_normalized = pd.concat([columns_to_notnormalize.reset_index(drop=True), normalized_columns], axis=1) numeric_df_normalized
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_07_27.jpg
图 7.27:非标准化和标准化列
第八章:创建完整的分析报告
活动 15:使用 Plotly 生成可视化
-
将所有必需的库和包导入到 Jupyter 笔记本中。确保将数据从
bank.csv
读取到 Spark DataFrame 中。 -
导入 Plotly 库,如下所示:
import plotly.graph_objs as go from plotly.plotly import iplot import plotly as py
-
现在,为了在 Plotly 中进行可视化,我们需要启动一个离线会话。使用以下命令(要求版本>= 1.9.0):
from plotly import __version__ from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot print(__version__)
-
现在,Plotly 已启动离线模式。使用以下命令启动 Plotly 笔记本:
import plotly.plotly as py import plotly.graph_objs as go init_notebook_mode(connected=True)
启动 Plotly 笔记本后,我们可以使用 Plotly 生成多种类型的图表,如条形图、箱型图或散点图,并将整个输出转换为一个用户界面或一个支持 Python Flask 框架的应用程序。
-
现在,使用 Plotly 绘制每个图表:
柱状图:
df = pd.read_csv('bank.csv', sep=';') data = [go.Bar(x=df.y, y=df.balance)] py.iplot(data)
柱状图如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_18.jpg
图 8.18:柱状图
散点图:
py.iplot([go.Histogram2dContour(x=df.balance, y=df.age, contours=dict(coloring='heatmap')),
go.Scatter(x=df.balance, y=df.age, mode='markers', marker=dict(color='red', size=8, opacity=0.3))], show_link=False)
散点图如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_19.jpg
图 8.19:散点图
箱形图:
plot1 = go.Box(
y=df.age,
name = 'age of the customers',
marker = dict(
color = 'rgb(12, 12, 140)',
)
)
py.iplot([plot1])
箱形图如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/bgdt-anal-py/img/C12913_08_20.jpg