Python 探索性数据分析实用指南(三)
十、模型开发和评估
到目前为止,我们已经讨论了几种探索性数据分析 ( EDA )技术。我们执行 EDA 的原因是准备数据集并理解它,以便它可以用于预测和分析目的。我们所说的预测性和分析性是指创建和评估机器学习 ( ML )模型。在本章中,我们将为数据科学奠定基础,了解可以构建的不同类型的模型,以及如何对它们进行评估。
在本章中,我们将涵盖以下主题:
- 机器学习的类型
- 理解监督学习
- 理解无监督学习
- 理解强化学习
- 统一机器学习工作流
技术要求
在本章中,我们将使用一些聚类算法。我们将在本章中使用的数据集可以在https://github . com/sureshHARDIYA/PhD-resources/blob/master/Data/Review % 20 paper/ACM/preferred . xlsx?原始=真。
我们还将使用 scikit-learn 库中的一些模块,包括MiniBatchKMeans、TfidfVectorizer、PCA和TSNE。
机器学习的类型
机器学习 ( ML )是计算机科学的一个领域,它处理的是无需明确编程就能自己发现模式的算法的创建。ML 算法有不同的类型,这些算法分为三个不同的类别,如下图所示:
如上图所示,ML 算法有三种不同的类别:
- 监督学习
- 无监督学习
- 强化学习
我们将在接下来的章节中简要讨论这些算法。
理解监督学习
监督学习的主要目标是从标记的训练数据中归纳出一个模型。一旦一个模型被训练好,它就允许用户对看不见的未来数据做出预测。这里,我们所说的标注训练数据是指训练示例知道相关的输出标签。因此,它被称为监督学习。学习过程可以被认为是老师监督整个过程。在这样的学习过程中,我们最初知道正确的答案,学生们随着时间的推移迭代学习足够多,并试图回答看不见的问题。答案中的错误被老师纠正了。当我们能够确保学生的表现达到可接受的水平时,学习的过程就停止了。
*在监督学习中,我们有输入变量(x i )和输出变量(Y i )。有了这个,我们可以学习一个函数,f,如下式所示:
目标是学习一个通用的映射函数f,以便该函数可以为任何新的输入数据x预测输出变量Y。监督学习算法可以分为两组,如下所示:
- 回归
- 分类
让我们简单看一下这些。
回归
回归问题有一个输出变量或因变量。这是一个真实值,例如体重、年龄或任何其他真实数字。我们在第 9 章、假设检验和 R 回归、T7】中详细讨论了回归,包括不同类型的回归(简单线性回归、多元线性回归和非线性回归),并使用波士顿住房数据集进行回归分析。
既然我们在第 9 章、假设检验和 R 回归中讨论了回归问题,我们就继续学习分类问题。
分类
分类问题具有类别值形式的输出变量;例如红或白葡萄酒;年轻人、成年人或老年人。对于分类问题,有不同类型的分类算法。
一些最受欢迎的如下:
-
线性分类器:朴素贝叶斯分类器,逻辑回归,线性 SVM
-
最近邻
-
决策树分类器
-
支持向量机
-
随机森林分类器
-
神经网络分类器
-
增强树分类器
列出了最流行的分类算法后,我们必须指出,浏览每一种分类算法都超出了本书的范围。然而,我们在这里的主要意图是给你指出正确的方向。我们建议您查看本书的进一步阅读部分,了解关于各自主题的更多细节。
关于如何在 Python 中用红葡萄酒和白葡萄酒数据集实现这些分类器的概念证明可以在第 11 章、关于葡萄酒质量数据分析的 EDA中找到。我们也将在那一章讨论不同的评估技术,这些技术可以用于分类目的。
理解无监督学习
无监督机器学习处理未标记的数据。这种类型的学习可以发现数据中的各种未知模式,并可以促进有用的分类。考虑一个场景,患者使用在线网络应用程序来了解疾病,了解他们的症状,并管理他们的疾病。这种提供关于某些疾病的心理教育的网络应用程序被称为“互联网提供的治疗”(【IDT】)。想象一下,几千名患者在一天中不同的时间访问网站,了解他们的病情,他们的所有活动都被记录到我们的数据库中。当我们分析这些日志文件并使用散点图绘制它们时,我们发现一大群患者在下午访问网站,一大块在晚上访问网站。其他一些患者也遵循随机登录模式。这个场景说明了两个不同的病人群:一个在下午活跃,一个在晚上活跃。这个典型的场景是一个集群任务的例子。
我们可以使用几种类型的无监督学习算法。然而,两个主要的无监督学习任务是聚类和降维。在下一节中,我们将更多地讨论无监督学习算法的不同应用。
无监督学习的应用
无监督学习算法有几种应用。下面我们来看几个:
- 聚类:这些类型的算法允许我们将数据集分类成几个相似的组,称为一个聚类。每个簇代表一组相似的点。
- 关联挖掘:这些类型的无监督学习算法允许我们在数据集中找到频繁出现的项目。
- 异常检测:这些类型的无监督学习算法帮助我们确定任何现有数据集中的异常数据点。
- 降维:这些技术常用于数据处理,目的是减少数据集中的特征数量。这是无监督学习中最重要的任务之一。
基于小批量 K 均值聚类的聚类
在本节中,我们将使用无监督学习算法之一,即聚类。具体来说,我们将基于一个名为 MiniBatch K-means 聚类算法的算法对文本进行聚类。让我们了解一下这方面的背景。
每当研究人员开始在任何特定领域工作时,他们都会进行各种文献综述,以了解任何特定领域的技术水平。这样的研究被称为综述论文。撰写此类综述论文时,您需要设置一组搜索关键词,并在许多研究论文索引数据库中执行搜索,例如 scholar.google.com(scholar.google.com/)。在几个数据库中执行搜索后,您将有一个想要研究的相关文章的列表。在这种情况下,我们已经执行了搜索,相关文章的列表已经以 Excel 表格的形式提供。请注意,Excel 文件中的每一行都包含一些关于相关论文的元数据。
You can find out more about the MiniBatch K-means clustering algorithm by looking at the official documentation of the sklearn library: scikit-learn.org/stable/modu….
了解了上下文之后,让我们将数据集加载到笔记本中。这对我们来说应该不是什么谜了:
- 让我们加载 Excel 文件:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import seaborn as sns
sns.set()
plt.rcParams['figure.figsize'] = (14, 7)
df = pd.read_excel("https://github.com/sureshHARDIYA/phd-resources/blob/master/Data/Review%20Paper/acm/preprocessed.xlsx?raw=true")
- 接下来,让我们检查前 10 个条目,以了解数据是什么样子的:
df.head(10)
前面代码的输出如下:
如我们所见,有几列。我们只对研究论文的标题感兴趣。因此,我们将只关注标题列。
提取关键词
下一步是从标题中提取关键词。我们有几种方法可以提取关键词。这里,我们将使用sklearn.feature_extraction模块提供的TfidfVectorizer实用方法。让我们开始吧:
- 要使用该库,我们需要导入基本库:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import MiniBatchKMeans
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
- 接下来,让我们学习如何提取关键词:
tfidf = TfidfVectorizer(
min_df = 5,
max_df = 0.95,
max_features = 8000,
stop_words = 'english'
)
tfidf.fit(df.Title)
text = tfidf.transform(df.Title)
You can find out more about TfidfVectorizer by reading the official documentation: scikit-learn.org/stable/modu….
在前面的代码中,我们将标题转换为 TF-IDF 特性。我们正在从标题中删除停止词。
You can read more about stop words at nlp.stanford.edu/IR-book/htm….
如果你理解了集群的概念,你可能已经理解了围绕集群的最大挑战之一;也就是说,确定有多少集群是最佳的。有一些算法可以帮助我们确定最佳的聚类数。其中一个算法是肘关节法(www.scikit-yb.org/en/latest/a…)。
让我们创建一个函数,获取文本和最大数量的聚类,并将它们绘制在图表上。这样做的代码如下:
def generate_optimal_clusters(data, max_k):
iters = range(2, max_k+1, 2)
sse = []
for k in iters:
sse.append(MiniBatchKMeans(n_clusters=k, init_size=1024, batch_size=2048, random_state=20).fit(data).inertia_)
print('Fitting {} clusters'.format(k))
f, ax = plt.subplots(1, 1)
ax.plot(iters, sse, marker='o')
ax.set_xlabel('Cluster Centers')
ax.set_xticks(iters)
ax.set_xticklabels(iters)
ax.set_ylabel('SSE')
ax.set_title('SSE by Cluster Center Plot')
generate_optimal_clusters(text, 20)
关于前面的功能,请注意以下几点:
- 它需要两个参数,文本和最大聚类数。在这种情况下,我们假设集群的最大数量是 20。
- 接下来,在函数内部,我们调用
MiniBatchKMeans簇上的fit()方法,范围从 2 到允许的最大簇数(2 到 20)。 - 对于每个聚类,我们计算图中的平方误差之和 ( SSE )。
前面代码的输出如下:
如上图所示,弯头在 4 处发生变化。根据弯头理论,该图在最佳簇号处创建弯头。因此,在这种情况下,最佳集群是 4。
绘制集群
现在,让我们在图上画出这些簇。我们将开始使用主成分分析 ( 主成分分析)进行绘图,因为它擅长捕捉数据的全局结构。然后,我们将使用t-分布式随机邻居嵌入 ( TSNE )来绘制图表,因为它擅长捕捉与邻居的关系。让我们开始吧:
- 让我们从再次创建模型开始:
clusters = MiniBatchKMeans(n_clusters=4, init_size=1024, batch_size=2048, random_state=20).fit_predict(text)
- 让我们绘制两张图表。首先,我们将使用主成分分析技术绘图,然后使用 TSNE 技术。使用以下代码来完成此操作:
max_label = max(clusters)
max_items = np.random.choice(range(text.shape[0]), size=3000, replace=True)
pca = PCA(n_components=2).fit_transform(text[max_items,:].todense())
tsne = TSNE().fit_transform(PCA(n_components=50).fit_transform(text[max_items,:].todense()))
idx = np.random.choice(range(pca.shape[0]), size=300, replace=True)
label_subset = clusters[max_items]
label_subset = [cm.hsv(i/max_label) for i in label_subset[idx]]
f, ax = plt.subplots(1, 2, figsize=(14, 6))
ax[0].scatter(pca[idx, 0], pca[idx, 1], c=label_subset)
ax[0].set_title('Generated PCA Cluster Plot')
ax[1].scatter(tsne[idx, 0], tsne[idx, 1], c=label_subset)
ax[1].set_title('Generated TSNE Cluster Plot')
前面代码的输出如下:
每种颜色代表一种集群。在前面的代码中,我们对特征进行了采样,只捕获了 3,000 个文档以加快处理速度,并使用散点图绘制了它们。对于主成分分析,我们将维度减少到 50。
You can learn more about TSNE from the official website: scikit-learn.org/stable/modu….
请注意,很难找出在每种类型的集群中找到了哪些关键字。为了更好地可视化这一点,我们需要绘制每个集群的单词 cloud。
单词云
为了看到属于每个聚类的前几个关键词,我们需要创建一个函数,为我们提供每个聚类的前 50 个单词,并绘制单词云。
检查功能,如下所示:
from wordcloud import WordCloud
fig, ax = plt.subplots(4, sharex=True, figsize=(15,10*4))
plt.rcParams["axes.grid"] = False
def high_frequency_keywords(data, clusters, labels, n_terms):
df = pd.DataFrame(data.todense()).groupby(clusters).mean()
for i,r in df.iterrows():
words = ','.join([labels[t] for t in np.argsort(r)[-n_terms:]])
print('Cluster {} \n'.format(i))
print(words)
wordcloud = WordCloud(max_font_size=40, collocations=False, colormap = 'Reds', background_color = 'white').generate(words)
ax[i].imshow(wordcloud, interpolation='bilinear')
ax[i].set_title('Cluster {} '.format(i), fontsize = 20)
ax[i].axis('off')
high_frequency_keywords(text, clusters, tfidf.get_feature_names(), 50)
前面代码的输出被分成两部分。让我们看看文本输出:
Cluster 0
bipolar,patient,framework,evaluation,risk,older,internet,healthcare,activity,approach,online,anxiety,research,digital,children,assessment,clinical,dementia,adaptive,cognitive,intervention,disorders,technology,learning,psychiatric,community,interventions,management,therapy,review,adults,use,support,designing,schizophrenia,stress,data,people,analysis,care,self,mobile,disorder,using,patients,design,study,treatment,based,depression
Cluster 1
cessation,brief,comparing,single,disorder,people,adults,symptoms,risk,clinical,women,prevention,reduce,improve,training,use,results,online,personalized,internet,cluster,alcohol,anxiety,feedback,efficacy,patients,health,mental,therapy,primary,help,self,program,care,effects,cognitive,pilot,treatment,depression,tailored,effectiveness,web,based,randomised,study,intervention,protocol,randomized,controlled,trial
Cluster 2
qualitative,physical,digital,implementation,self,medical,management,patient,adults,designing,life,quality,work,development,systems,data,related,children,persons,support,online,analysis,assessment,information,intervention,veterans,service,design,patients,problems,behavioral,using,research,systematic,disorders,use,interventions,primary,treatment,based,study,services,review,severe,people,community,illness,care,mental,health
Cluster 3
modeling,implications,ethical,emotion,behavioral,dementia,based,young,designing,homeless,dynamics,group,experiences,robot,predicting,mobile,game,depression,understanding,physical,people,challenges,therapy,study,patients,management,technology,impact,technologies,self,anxiety,use,skills,interaction,networking,personal,disclosure,sites,data,networks,disclosures,using,design,online,network,support,mental,health,media,social
请注意,它打印了四个不同的簇,每个簇中有 50 个经常出现的单词。很容易看到属于每个聚类的关键词,并判断聚类是否正确。为了正确呈现这些单词,我们生成了一个单词云。
字云如下:
我们可以看到,有四个集群。每个聚类显示最相关的单词。例如,聚类 0 显示了许多与医疗保健、干预、框架、数字健康等相关的单词。这样做,更容易看出关键词之间的关系。
在下一节中,我们将讨论强化学习。
理解强化学习
在强化学习中,代理改变其状态以最大化其目标。这里有四个截然不同的概念:代理人、国家、行动和奖励。让我们更详细地看看这些:
- **特工:**这是我们训练的节目。它从环境中的动作空间中为指定的任务选择动作。
- **状态:**这是代理从其环境接收到的观察,代表代理的当前情况。
- **动作:**这是代理从其动作空间做出的选择。该操作会更改代理的状态。
- 奖励:这是关于代理人行为的结果反馈,描述了代理人应该如何表现。
下图说明了这些概念中的每一个:
如上图所示,强化学习涉及一个主体、一个环境、一组动作、一组状态和一个奖励系统。代理与环境交互并修改其状态。基于这种修改,它的输入得到奖励或惩罚。代理的目标是随着时间的推移使回报最大化。
监督学习和强化学习的区别
当我们有标记的训练数据集时,使用监督学习算法。强化学习用于代理与环境交互以观察基本行为并改变其状态以最大化其回报或目标的场景。
下表给出了它们之间的其他差异:
| 标准 | 监督学习 | 强化学习 | | 例子 | 数字识别。 | 象棋比赛。 | | 对…有效 | 给定标记数据集。 | 与给定的环境互动。 | | 决定 | 根据开始时给出的输入做出决定。 | 在这里,算法帮助线性地做出决定。 |
RL 的一个基本特征是,代理的操作可能不会影响它正在工作的环境的当前状态,但会影响后续状态。因此,算法可能不会在初始状态下学习,但可以在一些状态改变后学习。
强化学习的应用
RL 算法有几个用例。
一些最重要的用例如下:
- **文本挖掘:**一些研究人员和公司已经开始使用基于 RL 的文本生成模型,从长文本中生成可读性很强的文本摘要。
- Robotics :在机器人工程领域使用了多种基于深度 RL 的算法,来提升基于奖励系统的机器人性能。
- 医疗保健:多项研究表明,RL 可用于医疗保健,优化用药剂量和治疗政策。
- 交易:交易业务中使用了几种基于 RL 的模型来优化交易结果。
统一机器学习工作流
选择使用什么机器学习算法总是取决于您拥有的数据类型。如果你有一个带标签的数据集,那么你最明显的选择就是选择一种有监督的机器学习技术。此外,如果您的标记数据集包含目标变量的真实值,那么您将选择回归算法。最后,如果您的标记数据集在目标变量中包含一个分类变量,那么您将选择分类算法。无论如何,您选择的算法始终取决于您拥有的数据集类型。
类似地,如果数据集不包含任何目标变量,那么显而易见的选择是无监督算法。在本节中,我们将研究机器学习的统一方法。
机器学习工作流程可以分为几个阶段:
- 数据预处理
- 数据准备
- 训练集和语料库创建
- 模型创建和培训
- 模型评估
- 最佳模型选择和评估
- 模型部署
机器学习算法的整个工作流程如下图所示:
如上图所示,任何机器学习工作流的第一步都是数据预处理。我们将在以下几节中简要解释每个阶段。
数据预处理
数据预处理涉及几个步骤,包括数据收集、数据分析、数据清理、数据规范化和数据转换。数据预处理的第一步是数据收集。让我们来看看。
数据收集
在数据科学中,最重要的是数据。这些数据掌握着我们周围发生的任何事件、现象或实验的基本事实。一旦我们处理了数据,我们就会得到信息。一旦我们处理了这些信息,我们就可以从中获得知识。因此,知识提取中最突出的阶段是被捕获的数据的相关性。有不同类型的数据,包括结构化数据、非结构化数据、半结构化数据。结构化数据在所有观察中保持统一的结构,类似于关系数据库表。非结构化数据不维护任何特定的结构。半结构化数据在观察中保持一定的结构。 JavaScript 对象标注 ( JSON )是存储半结构化数据最流行的方式之一。
任何公司收集数据的过程都取决于需要研究的项目种类和信息类型。不同类型的数据集包括文本数据、文件、数据库、传感器数据和许多其他物联网 ( 物联网)数据。然而,当学习机器学习工作流程时,大多数学生更喜欢避开数据收集阶段,使用来自卡格尔和 UCI 机器学习资源库等地方的开源数据。
数据分析
这是我们执行探索性数据分析以了解数据集的初步分析阶段之一。我们在第 3 章、 EDA 和个人电子邮件分析中讨论了可以执行的几种技术。这一步告诉我们手头的数据类型、目标变量、数据中有多少行和列、每列的数据类型、缺少多少行、数据分布是什么样子等等。
数据清理、标准化和转换
我们在第 4 章、数据转换中详细讨论了数据清理、规范化和数据转换。我们讨论了如何重新缩放数据集,如何将数据集转换为标准数据集,如何对数据进行二值化,以及如何执行一次性编码和标签编码。
经过这三个步骤后,我们丢失的数据将得到处理,噪声数据将被过滤,不一致的数据将被删除。
数据准备
有时,我们拥有的数据集并不总是处于适合机器学习算法使用的状态。在这种情况下,数据准备是我们能做的最基本的事情之一。我们需要集成来自多个来源的数据,执行切片和分组,并将它们聚合成正确的格式和结构。这一步被称为数据准备。
我们在第 6 章、分组数据集中详细讨论了这个过程。需要注意的是,有些书认为数据预处理和数据准备是同一个步骤,因为有几个重叠的操作。
训练集和语料库创建
在数据准备步骤之后,得到的数据集被用作训练语料库。通常,训练语料库被分成三大块:训练集、验证集和测试集。
训练集是您用来训练一个或多个机器学习算法的数据块。验证集是用于验证训练模型的数据块。最后,测试集是你用来评估一个完全训练好的分类器性能的数据块。
模型创建和培训
一旦我们将数据集分成三大块,我们就可以开始训练过程了。我们使用训练集来构建机器学习模型。然后,我们使用验证集来验证模型。一旦模型已经被训练,我们使用测试集来找到模型的最终性能。
模型评估
基于测试数据的表现,我们可以创建一个混淆矩阵。该矩阵包含四个不同的参数:真阳性、真阴性、假阳性和假阴性。考虑以下混淆矩阵:
| | 预测:阳性 | 预测:阴性 | | 实际:正 | 真阳性 | 假阴性 | | 实际:负 | 假阳性 | 真负值 |
该矩阵显示了四个不同的参数:
- 真阳性:当实际值为真时,模型预测为真。
- 真否定:当实际值为假时,模型预测为假。
- 误报:当实际值为假时,模型预测为真。这也被称为第一类错误。
- 假阴性:当实际值为真时,模型预测为假。这也被称为第二类错误。
一旦我们知道了混淆矩阵,我们就可以计算模型的几个精度,包括精度、负谓词值、灵敏度、特异性和准确性。让我们一个接一个地看看它们,并了解它们是如何计算的。
精度是真阳性与真阳性和假阳性之和的比值。公式如下:
阴性预测值 ( 净现值)的公式如下:
相似度,灵敏度的公式如下:
特异性公式如下:
最后,模型的精度由以下公式给出:
我们来看一个例子。假设我们构建了一个监督分类算法,该算法查看窗口的图片,并将其分类为脏或不脏。最终的混淆矩阵如下:
| | 预测:肮脏 | 预测:不脏 | | 实际:脏 | TP = 90 | FN = 40 | | 实际:不脏 | FP = 10 | TN = 60 |
现在,让我们计算一下这种情况下的精度度量:
- 精度= TP / (TP + FP) = 90 /(90 + 10) = 90%。这意味着 90%被归类为脏的图片实际上是脏的。
- 灵敏度= TP / (TP + FN) = 90/(90 + 40) = 69.23%。这意味着 69.23%的脏窗户被正确分类,并从所有非脏窗户中排除。
- 特异性= TN / (TN + FP) = 60 / (10 + 60) = 85.71%。这意味着 85.71%的非脏窗户被准确分类并排除在脏窗户之外。
- 准确率= (TP + TN)/(TP + TN + FP + FN) = 75%。这意味着 75%的样本被正确分类。
你会遇到的另一个常用的准确度模型是 F1 评分。它由以下等式给出:
我们可以看到,F1 的分数是召回率和准确率的加权平均值。准确度测量太多了,对吧?这在一开始可能会令人生畏,但随着时间的推移,你会习惯的。
最佳模型选择和评估
模型选择是机器学习算法工作流程中必不可少的一步。但是,模型选择在不同的上下文中有不同的含义:
- 上下文 1 :在机器学习工作流上下文中,模型选择是选择最佳机器学习算法的过程,如 logistic 回归、SVM、决策树、Random Forest 分类器等等。
- 上下文 2 :类似的,模型选择阶段也是指针对任意选择的机器学习算法,在不同的超参数之间进行选择的过程。
通常,模型选择是从给定训练数据集的可能候选算法列表中选择一个最佳机器学习算法的方法。有不同的模型选择技术。在正常情况下,我们将训练语料库分成训练集、验证集和测试集。然后,我们在训练集上拟合几个候选模型,使用验证集评估模型,并在测试集上报告模型的性能。然而,只有当我们有足够大的训练语料库时,这种模型选择的场景才起作用。
然而,在许多情况下,用于培训和测试的数据量是有限的。在这种情况下,模型选择变得困难。在这种情况下,我们可以使用两种不同的技术:概率测量和重采样方法。如果你想了解这些模型选择技巧,我们建议你继续阅读本章的章节。
模型部署
一旦你得到了基于你的数据集的最佳模型,并且这个模型已经被完全训练好了,是时候部署它了。展示如何将模型完全部署到工作环境中超出了本书的范围。您可以在进一步阅读部分找到足够的资源,为您指明正确的方向。
关于模型部署的主要思想是在真实的工作环境中使用训练好的模型。一旦部署,它应该通过 A/B 用户测试,以便您知道它在真实场景中如何工作。一旦经过全面测试,该应用编程接口就可以向公众开放。
摘要
在这一章中,我们为数据科学奠定了一些基础,了解了可以构建的不同类型的模型,以及如何对它们进行评估。首先,我们讨论了几种监督学习算法,包括回归和分类。然后,我们讨论了无监督学习算法,包括聚类和使用文本数据聚类成不同的聚类使用迷你批处理知识的算法。最后,我们简要讨论了强化学习。
在下一章中,我们将使用到目前为止所学的所有技术对葡萄酒质量数据集执行 EDA。此外,我们将使用监督学习算法对葡萄酒质量进行分类。
进一步阅读
- 使用 Python 的监督式机器学习、泰勒、史密斯、帕克特出版
- 使用 Python 进行大规模机器学习、巴斯蒂安·萨贾丁、卢卡·马萨龙、等人。,派克特出版
- Python 高级机器学习约翰·哈迪帕克特出版
- 用 Python 进行无监督学习的实践、朱塞佩·博纳科尔索、帕克特出版
- 掌握渗透测试的机器学习奇赫布切比派克特出版
- 动手数据科学与 Python 机器学习弗兰克·凯恩帕克特出版
- 用 Python 构建机器学习系统-第三版、路易斯·佩德罗·科埃略、威利·里歇特、等人。,派克特出版*
十一、葡萄酒质量数据 EDA
到目前为止,我们已经讨论了大量关于探索性数据分析 ( EDA )的工具和技术,包括我们如何从不同来源导入数据集,以及如何从数据集中移除异常值,对数据集执行数据分析,并从这样的数据集生成说明性可视化。除此之外,我们还讨论了如何应用高级数据分析,如变量之间的相关性、回归分析和时间序列分析,并基于此类数据集构建高级模型。在本章中,我们将把所有这些技术应用于葡萄酒质量数据集。
本章讨论的主要主题包括以下内容:
- 公开葡萄酒质量数据集
- 分析红酒
- 分析白酒
- 模型开发和评估
- 进一步阅读
技术要求
本章的整个代码库可以在CH012文件夹内与本书共享的 GitHub 资源库中找到。本章使用的数据集可以从 UCI 网站(archive.ics.uci.edu/ml/datasets…)下载,该网站是面向最终用户的开源网站。
我们假设您已经阅读了前面的章节,并且对所需的 Python 库有足够的了解。
公开葡萄酒质量数据集
葡萄酒质量数据集包含葡萄酒各种物理化学特性的信息。整个数据集分为两类:红酒和白酒。每种葡萄酒都有相关的质量标签。标签在 0 到 10 的范围内。在下一节中,我们将下载数据集并将其加载到 Python 中,并执行初步分析以揭示其中的内容。
正在加载数据集
如技术要求部分所述,数据集可以直接从 UCI 网站下载。现在,让我们使用 pandas pd.read_csv()方法将数据集加载到 Python 环境中。到目前为止,这个操作应该是相对容易和直观的:
- 我们从加载 pandas 库开始,创建两个不同的数据框架,即
df_red用于保存红酒数据集,df_white用于保存白酒数据集:
import pandas as pd
df_red = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv", delimiter=";")
df_white = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv", delimiter=";")
- 我们创建了两个数据帧。让我们检查可用列的名称:
df_red.columns
此外,这里给出了前面代码的输出:
Index(['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar',
'chlorides', 'free sulfur dioxide', 'total sulfur dioxide', 'density',
'pH', 'sulphates', 'alcohol', 'quality'],
dtype='object')
如该输出所示,数据集包含以下列:
Fixed acidity:表示葡萄酒中酒石酸的含量,单位为克/分 3 。Volatile acidity:表示酒中醋酸的量。以 g/dm 3 计量。Citric acid:表示酒中柠檬酸的量。也以 g/dm 3 计量。Residual sugar:表示发酵过程完成后,葡萄酒中剩余的糖量。也以 g/dm 3 计量。Free sulfur dioxide:测量游离形式的二氧化硫(SO 2 )的量。也以 g/dm 3 计量。Total sulfur dioxide:测量酒中 SO 2 的总量。这种化学物质作为抗氧化剂和抗菌剂。Density:表示酒的密度,单位为克/分 3 。pH:表示酒的 pH 值。值的范围在 0 到 14.0 之间,表示酸度非常高,14 表示碱性酸度。Sulphates:表示酒中硫酸钾的量。也以 g/dm 3 计量。Alcohol:表示酒中的酒精含量。Quality:表示酒的质量,范围从 1 到 10。在这里,价值越高,酒越好。
讨论了数据集中的不同列后,现在让我们在下一节中查看数据的一些基本统计信息。
描述性统计学
让我们看看红酒数据框中的一些样本数据。请记住,我们可以使用不同的方法来查看数据框中的数据,包括pd.head()、pd.tail()和pd.iloc():
- 在这里,我将检查第 100 行和第 110 行之间的条目:
df_red.iloc[100:110]
这里给出了前面代码的输出:
Figure 12.1 - Display the entries from the 100th to 110th rows from the red wine dataframe
- 除此之外,我们还可以看到每一列的数据类型。让我们使用这里给出的片段:
df_red.dtypes
前面代码的输出如下:
fixed acidity float64
volatile acidity float64
citric acid float64
residual sugar float64
chlorides float64
free sulfur dioxide float64
total sulfur dioxide float64
density float64
pH float64
sulphates float64
alcohol float64
quality int64
dtype: object
如前面的输出所示,除了quality列是int64之外,大部分列都是float64格式。
- 我们还可以描述数据帧以获得更多描述性信息。你还记得这样做的方法的名字吗?当然,我们使用
pd.describe()方法。查看代码片段:
df_red.describe()
这里给出了前面代码的输出:
Figure 12.2 - Output of the described method
注意图 12.2 是pd.describe()方法的输出,表示每一列都有相同的条目数,1,599,显示在行计数中。现在,每一行和每一列的值都应该有意义了。如果你还不明白,我们强烈建议修改第五章 、描述性统计。
数据争论
嗯,图 12.2 显示每一列都有相同的项目数,说明没有遗漏值。
我们可以使用这里显示的pd.info()方法来验证:
df_red.info()
给出了前面代码的输出:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1599 entries, 0 to 1598
Data columns (total 12 columns):
fixed acidity 1599 non-null float64
volatile acidity 1599 non-null float64
citric acid 1599 non-null float64
residual sugar 1599 non-null float64
chlorides 1599 non-null float64
free sulfur dioxide 1599 non-null float64
total sulfur dioxide 1599 non-null float64
density 1599 non-null float64
pH 1599 non-null float64
sulphates 1599 non-null float64
alcohol 1599 non-null float64
quality 1599 non-null int64
dtypes: float64(11), int64(1)
memory usage: 150.0 KB
如前面的输出所示,所有列都没有空值。由于没有空条目,我们不需要处理丢失的值。假设有一些,那么我们将使用我们在第 4 章、数据转换中概述的技术来处理它们。
We can also access the data quality and missing values using the ways shown in Chapter 4, Data Transformation. We can use the pandas method, df_red.isnull().sum().
既然知道不需要进一步的数据转换步骤,我们就在下一节回顾一下红酒的数据分析。
分析红酒
在本节中,我们将继续分析红酒数据集。首先,我们将从探索最相关的列开始。其次,我们将比较两个不同的列并观察它们的列。
我们先从quality栏开始:
import seaborn as sns
sns.set(rc={'figure.figsize': (14, 8)})
sns.countplot(df_red['quality'])
这里给出了前面代码的输出:
Figure 12.3 - The output indicates that the majority of wine is of medium quality
这并不难,不是吗?正如我一直认为的,当你有一个图表时,最重要的一个方面是能够解释结果。如果勾选图 12.3 ,可以看到大部分红酒属于质量标签 3 和 4 的那一组,其次是标签 5 和 6,部分红酒属于标签 7 的那一组,以此类推。
查找相关列
接下来让我们找出红酒数据库中哪些列是高度相关的。如果你还记得,我们在第 7 章*中讨论了不同类型的相关性。*为了让你把握关联背后的意图,我强烈推荐你去翻看第七章、关联,只是为了重整一下记忆。说到这里,让我们继续寻找高度相关的列:
- 我们可以继续使用
seaborn.pairplot()方法,如下图所示:
sns.pairplot(df_red)
您应该会得到一个非常全面的图表,如屏幕截图所示:
Figure 12.4 - Correlation between different columns of the red wine dataframe
前面的屏幕截图显示了每对可能的组合列的分散图。该图说明了固定酸度和密度之间的一些正相关关系。酸度与 pH 呈负相关,同样,酒精百分比与密度也呈负相关。此外,您可以准确地看到哪些列与其他列具有正相关或负相关。然而,由于pairplot图没有数字,解释结果可能会有点偏差。例如,检查固定酸度和挥发性酸度的色谱柱之间的相关性。这个图可能是对称的。然而,你可能会说在图的右边有一些稀疏的点,所以有轻微的负相关。在这里,我的观点是,没有任何具体的可量化的数字,很难讲。这就是为什么我们可以用sns.heatmap()方法来量化相关性的原因。
- 我们可以生成
heatmap图,如下图所示:
sns.heatmap(df_red.corr(), annot=True, fmt='.2f', linewidths=2)
它产生的输出如下:
Figure 12.5 - Heatmap showing the correlation between different columns
图 12.5 描绘了不同列之间的相关性。由于我们关注的是质量柱,因此质量柱与酒精、硫酸盐、残糖、柠檬酸和固定酸度呈正相关。因为有数字,所以很容易看出哪些列是正相关的,哪些列是负相关的。
看图 12.5 是否可以得出以下结论:
- 酒精与红酒的质量呈正相关。
- 酒精与 pH 值呈弱正相关。
- 柠檬酸和密度与固定酸度呈强正相关。
- 酸碱度与密度、固定酸度、柠檬酸和硫酸盐呈负相关。
从图 12.5 的热图中我们可以得出几个结论。此外,我们必须认识到相关性的重要性,以及在数据科学模型开发过程中,相关性如何帮助我们确定特征集。
A column has a perfect positive correlation with itself. For example, the quality of wine has a positive correlation with itself. This is the reason why all of the diagonal elements have a positive correlation of 1.
我们可以进一步深入到单个列并检查它们的分布。比方说,我们想看看酒精浓度相对于红酒质量的分布情况。首先,让我们绘制分布图,如下所示:
sns.distplot(df_red['alcohol'])
前面代码的输出如下:
Figure 12.6 - Alcohol distribution graph
从图 12.6 可以看出,酒精分布与红酒品质呈正相关。我们可以使用 scipy.stats 中的skew方法来验证这一点。
from scipy.stats import skew
skew(df_red['alcohol'])
前面代码的输出如下:
0.8600210646566755
输出证实酒精是正向倾斜的。这让我们对酒精一栏有了更深入的了解。
请注意,我们可以验证每一列,并尝试查看它们相对于另一列的偏斜度、分布和相关性。这通常是必要的,因为我们正在经历特征工程的过程。
酒精与质量
让我们看看葡萄酒的质量是如何随着酒精浓度而变化的。这可以使用方框图来完成。检查这里给出的代码片段:
sns.boxplot(x='quality', y='alcohol', data = df_red)
前面代码的输出如下:
Figure 12.7 - A box plot showing the variation of the quality of wine with respect to alcohol concentration
注意图 12.7 中的方框,显示了图外的一些点。这些都是异常值。图 12.7 所示的大部分异常值都在品质 5 和 6 的葡萄酒附近。我们可以通过传递参数showoutliers=False来移除异常值,如以下代码所示:
sns.boxplot(x='quality', y='alcohol', data = df_red, showfliers=False)
代码的输出更加清晰,如下所示:
Figure 12.8 -A box plot showing the variation of the quality of wine with respect to alcohol concentration without outliers
注意,从图 12.8 来看,似乎随着酒质的提升,酒精浓度也随之提升。有道理,对吧?酒精浓度越高,酒的质量就越高。
酒精对酸碱度
接下来,我们也来看看酒精column与 pH 值的相关性。从图 12.5 我们已经知道它们是弱正相关的。让我们验证本节中的结果:
- 首先,我们来看看联合剧情:
sns.jointplot(x='alcohol',y='pH',data=df_red, kind='reg')
前面的代码现在对您来说应该不是新的。我们已经在第 2 章、【EDA 中的 T2】视觉辅助、第 7 章、关联中讨论过这些情节的意义。由前面的代码生成的图表显示在屏幕截图中:
Figure 12.9 - Joint plot illustrating the correlation between alcohol concentration and the pH values
这张截图显示,酒精与酸碱度呈弱正相关。此外,在截图中描绘了回归线,说明了它们之间的相关性。
- 我们可以从
scipy.stats开始使用皮尔逊回归来量化相关性,如下所示:
from scipy.stats import pearsonr
def get_correlation(column1, column2, df):
pearson_corr, p_value = pearsonr(df[column1], df[column2])
print("Correlation between {} and {} is {}".format(column1, column2, pearson_corr))
print("P-value of this correlation is {}".format(p_value))
- 我们可以使用前面的方法来查看任意两列之间的相关性。让我们看看
alcohol和pH的相关性:
get_correlation('alcohol','pH', df_red)
前面代码的输出如下所示:
Correlation between alcohol and pH is 0.20563250850549825
P-value of this correlation is 9.96449774146556e-17
注意,这与图 12.5 中所示的数值大致相同。现在,您知道了不同的方法,可以检查两个或多个列的关联有多强或多弱。
在下一节中,我们将分析白葡萄酒数据框架,并将其与红葡萄酒进行比较。
分析白酒
在这一节中,我们将分析白酒,并将其与前一节中的红酒分析进行比较。让我们从加载白葡萄酒数据框开始:
df_white = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv", delimiter=";")
该代码将白葡萄酒数据集加载到df_white数据框中。
红酒对白酒
我们的输出类是quality列。基于该栏,我们可以尝试找到每种葡萄酒的平均质量,如下所示:
print("white mean = ",df_white["quality"].mean())
print("red mean =",df_red["quality"].mean())
代码的输出如下:
white mean = 5.87790935075541
red mean = 5.6360225140712945
正如产量所说,白葡萄酒的平均质量为 5.877,红葡萄酒的平均质量为 5.63。两个数据框中的列相同。
添加新属性
让我们向两个数据帧添加一个新属性wine_category。你记得我们是怎么做到的吗?
当然,请检查下面给出的示例代码:
df_white['wine_category'] = 'white'
df_red['wine_category'] = 'red'
很简单,对吧?接下来,让我们看看这两种葡萄酒的柱质量有何独特价值:
print('RED WINE: List of "quality"', sorted(df_red['quality'].unique()))
print('WHITE WINE: List of "quality"', sorted(df_white['quality'].unique()))
前面代码的输出如下所示:
RED WINE: List of "quality" [3, 4, 5, 6, 7, 8]
WHITE WINE: List of "quality" [3, 4, 5, 6, 7, 8, 9]
请注意,红葡萄酒和白葡萄酒在质量栏中具有相同的唯一值。
转换成分类列
虽然质量一栏是数字的,但是在这里,我们感兴趣的是把质量作为类。为了清楚起见,让我们在本小节中将数值转换为分类值。
为此,我们需要一套规则。让我们定义一组规则:
听起来可行,对吧?当然是。让我们检查代码,如下所示:
df_red['quality_label'] = df_red['quality'].apply(lambda value: ('low' if value <= 5 else 'medium') if value <= 7 else 'high')
df_red['quality_label'] = pd.Categorical(df_red['quality_label'], categories=['low', 'medium', 'high'])
df_white['quality_label'] = df_white['quality'].apply(lambda value: ('low' if value <= 5 else 'medium') if value <= 7 else 'high')
df_white['quality_label'] = pd.Categorical(df_white['quality_label'], categories=['low', 'medium', 'high'])
到目前为止,前面的代码应该是不言自明的。我们只是使用pandas.apply()方法来检查quality列中的值。根据它们的价值,如果它们小于或等于 5,我们将其归类为低质量葡萄酒。同样,如果quality列的值大于 5 且小于或等于 7,我们将其归类为中等品质葡萄酒。最后,任何列的quality值大于 7 的行都被归类为优质葡萄酒。
让我们来数一数每一类葡萄酒的价值:
print(df_white['quality_label'].value_counts())
df_red['quality_label'].value_counts()
前面代码的输出如下所示:
medium 3078
low 1640
high 180
Name: quality_label, dtype: int64
medium 837
low 744
high 18
Name: quality_label, dtype: int64
上面的是白酒,下面的是红酒。从前面的输出可以非常明显地看出,在这两种情况下,大多数葡萄酒都是中等质量的。
连接数据帧
让我们对这两种类型的数据帧进行联合探索。你还记得我们如何合并数据帧吗?如果没有,我强烈建议在这里暂停一下,快速浏览一下第 6 章分组数据集:
*1. 让我们看看如何连接这两个数据帧:
df_wines = pd.concat([df_red, df_white])
- 让我们重新洗牌,使数据点随机化:
df_wines = df_wines.sample(frac=1.0, random_state=42).reset_index(drop=True)
请注意,drop=True参数将索引重置为默认的整数索引。
- 接下来,我们要检查前几列,看看是否所有的行都正确合并了:
df_wines.head()
前面代码的输出如下所示:
Figure 12.10 - Output of the df.head(10) code snippet shown earlier
注意在图 12.10 中,我们已经正确填充了列wine_category和quality_label。
分组列
我们已经在第 6 章分组数据集中讨论了使用 Pandas 数据框对列和行进行分组的几种方法。在本节中,我们将使用相同的技术将不同的列组合在一起:
- 让我们使用组合数据框,并使用列
alcohol、density、pH和quality对它们进行分组。 - 接下来,我们可以应用
pd.describe()方法获得最常用的描述性统计:
subset_attr = ['alcohol', 'density', 'pH', 'quality']
low = round(df_wines[df_wines['quality_label'] == 'low'][subset_attr].describe(), 2)
medium = round(df_wines[df_wines['quality_label'] == 'medium'][subset_attr].describe(), 2)
high = round(df_wines[df_wines['quality_label'] == 'high'][subset_attr].describe(), 2)
pd.concat([low, medium, high], axis=1,
keys=[' Low Quality Wine',
' Medium Quality Wine',
' High Quality Wine'])
在前面的代码片段中,首先,我们创建了我们感兴趣的属性子集。然后,我们为低品质葡萄酒、中品质葡萄酒和高品质葡萄酒创建了三个不同的数据框。最后,我们将它们串联起来。这里给出了前面代码的输出:
Figure 12.11 - Output of grouping the columns and performing the describe operation
如前面的截图所示,我们将数据集分为三个不同的组:低品质葡萄酒、中品质葡萄酒和高品质葡萄酒。每组显示三种不同的属性:酒精、密度和酸碱度。在数据分析阶段,使用串联方法根据特定条件对列进行分组非常方便。
在下一节中,我们将讨论葡萄酒质量数据集的单变量分析。
单变量分析
我们已经在第 7 章、相关性中讨论了单变量、双变量和多变量分析。让我们复习一下,看看你还记得多少。
可视化数字数据及其分布的最简单方法是使用直方图。让我们在这里绘制直方图;我们从导入所需的matplotlib.pyplot库开始:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline
接下来,我们绘制直方图,如图所示:
fig = df_wines.hist(bins=15, color='fuchsia', edgecolor='darkmagenta', linewidth=1.0, xlabelsize=10, ylabelsize=10, xrot=45, yrot=0, figsize=(10,9), grid=False)
plt.tight_layout(rect=(0, 0, 1.5, 1.5))
请注意,我们使用了tight_layout()方法来保持图形的组合。
You can get the list of all matplotlib color codes from the official website, at matplotlib.org/examples/co….
前面代码的输出如下所示:
Figure 12.12 - Output of the univariate analysis
前面的截图显示了每个变量/列及其在组合数据框中的分布。到目前为止,结果图应该是不言自明的。
组合数据帧的多元分析
让我们使用组合数据集执行多元分析。我们将使用相同的热图图来执行多元分析:
- 让我们从创建图形开始。首先,我们创建一个子场景:
fig, (ax) = plt.subplots(1, 1, figsize=(14,8))
- 接下来,我们创建热图,如下所示:
hm = sns.heatmap(df_wines.corr(),
ax=ax,
cmap="bwr",
annot=True,
fmt='.2f',
linewidths=.05)
- 最后,让我们绘制子情节,并用合适的标题填充它:
fig.subplots_adjust(top=0.93)
fig.suptitle('Combined Wine Attributes and their Correlation Heatmap', fontsize=14, fontweight='bold')
前面代码的输出如下所示:
Figure 12.13 - A heatmap illustrating correlation between several columns
注意前面的截图类似于图 12.5*,应该用同样的方式解读。在这种情况下,唯一的区别是我们对组合数据帧进行了多元分析。*
*# 离散分类属性
我们的数据框中有一个离散的分类列wine_category。
让我们使用seaborn库使用计数图来可视化它:
fig = plt.figure(figsize=(16, 8))
sns.countplot(data=df_wines, x="quality", hue="wine_category")
前面代码的输出如下所示:
Figure 12.14 - Visualizing the discrete categorical dataset
图 12.14 显示了葡萄酒的不同类别[3、4、5、6、7、8、9]以及它们在一个漂亮的计数图上的频率分布。对于最终利益相关者来说,这是一个更清晰的说明。
三维可视化
一般来说,我们从一维可视化开始,然后进入更深的维度。之前看过二维可视化,让我们再增加一个维度,绘制三维图表。我们将使用matplotlib.pyplot方法来实现:
- 让我们首先创建轴:
fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111, projection='3d')
- 然后,将列添加到轴上:
xscale = df_wines['residual sugar']
yscale = df_wines['free sulfur dioxide']
zscale = df_wines['total sulfur dioxide']
ax.scatter(xscale, yscale, zscale, s=50, alpha=0.6, edgecolors='w')
在这里,我们有兴趣研究残余糖、游离二氧化硫和总二氧化硫柱。
- 最后,让我们将标签添加到所有轴上:
ax.set_xlabel('Residual Sugar')
ax.set_ylabel('free sulfur dioxide')
ax.set_zlabel('Total sulfur dioxide')
plt.show()
前面代码的输出如下所示:
Figure 12.14 - 3-D plot illustrating the correlation between three different columns
图 12.14 显示三个变量相互之间呈正相关。在前面的例子中,我们使用了 Matplotlib 库。我们也可以使用seaborn库绘制三个不同的变量。检查这里给出的代码片段:
fig = plt.figure(figsize=(16, 12))
plt.scatter(x = df_wines['fixed acidity'],
y = df_wines['free sulfur dioxide'],
s = df_wines['total sulfur dioxide'] * 2,
alpha=0.4,
edgecolors='w')
plt.xlabel('Fixed Acidity')
plt.ylabel('free sulfur dioxide')
plt.title('Wine free sulfur dioxide Content - Fixed Acidity - total sulfur dioxide', y=1.05)
请注意,我们使用了s参数来表示第三个变量(二氧化硫总量)。代码的输出如下:
Figure 12.16 - Plot illustrating three different variables as shown in the preceding code
注意在图 12.16 中,圆圈的大小表示第三个变量。在这种情况下,圆的半径越大,残糖的价值越高。因此,如果你仔细观察,你会发现大部分较高的圆位于值为 4 到 10 的 x 轴和值为 25 到 150 的 y 轴之间。
在下一节中,我们将开发不同类型的模型,并应用一些经典的机器学习 ( ML )算法,并评估它们的性能。
模型开发和评估
在本节中,我们将开发不同类型的经典 ML 模型,并评估它们的性能。我们已经在 第 9 章、假设检验和回归和第 10 章、模型开发和评估中详细讨论了模型的开发和评估。在这里,我们将直接进入实现。
我们将使用以下不同类型的算法并评估它们的性能:
- 逻辑回归
- 支持向量机
- k 近邻分类器
- 随机森林分类器
- 决策树分类器
- 梯度提升分类器
- 高斯朴素贝叶斯分类器
虽然深入研究每个分类器超出了本章和本书的范围,但我们在这里的目的是介绍如何在对某些数据库执行 EDA 操作后继续开发 ML 算法:
- 让我们首先导入所需的库:
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC,SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier,GradientBoostingClassifier,AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split,cross_validate
from sklearn.preprocessing import MinMaxScaler,StandardScaler,LabelEncoder
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score
请注意,我们将使用组合的数据帧。接下来,我们将对quality_label列的分类值进行编码。我们将对这些值进行编码,以便所有低值都变为 0,中值变为 1,高值变为 2。
- 让我们执行编码:
label_quality = LabelEncoder()
df_wines['quality_label'] = label_quality.fit_transform(df_wines['quality_label'])
这并不难,对吧?我们刚刚利用了sklearn预处理功能提供的LabelEncoder实用程序功能。
- 现在,让我们将数据集分成训练集和测试集。我们将使用 70%的数据集作为训练集,剩余的 30%作为测试集:
x_train,x_test,y_train,y_test=train_test_split(df_wines.drop(['quality','wine_category'],axis=1),df_wines['quality_label'],test_size=0.30,random_state=42)
我们使用了sklearn库提供的train_test_split()方法。请注意前面类别中的以下内容:
- 在前面的代码中,我们不再需要
quality和wine_category列,所以我们删除了它们。 - 接下来,我们以 30%的数据作为测试集。我们可以通过简单地传递
test_size = 0.30参数来做到这一点。
- 接下来,我们创建模型。请注意,我们可以为上面列出的每个算法单独构建模型。取而代之的是,我们将列出它们,并对每一个进行循环,计算精度。检查下面给出的代码片段:
models=[LogisticRegression(),
LinearSVC(),
SVC(kernel='rbf'),
KNeighborsClassifier(),
RandomForestClassifier(),
DecisionTreeClassifier(),
GradientBoostingClassifier(),
GaussianNB()]
model_names=['LogisticRegression','LinearSVM','rbfSVM', 'KNearestNeighbors', 'RandomForestClassifier', 'DecisionTree', 'GradientBoostingClassifier', 'GaussianNB']
- 接下来,我们将循环每个模型,创建一个模型,然后评估准确性。检查下面给出的代码片段:
acc=[]
eval_acc={}
for model in range(len(models)):
classification_model=models[model]
classification_model.fit(x_train,y_train)
pred=classification_model.predict(x_test)
acc.append(accuracy_score(pred,y_test))
eval_acc={'Modelling Algorithm':model_names,'Accuracy':acc}
eval_acc
这里给出了前面代码的输出:
{'Accuracy': [0.9687179487179487,
0.9733333333333334,
0.6051282051282051,
0.6912820512820513,
1.0,
1.0,
1.0,
1.0],
'Modelling Algorithm': ['LogisticRegression',
'LinearSVM',
'rbfSVM',
'KNearestNeighbors',
'RandomForestClassifier',
'DecisionTree',
'GradientBoostingClassifier',
'GaussianNB']}
- 让我们创建一个精确度的数据框架,并将其显示在条形图中:
acc_table=pd.DataFrame(eval_acc)
acc_table = acc_table.sort_values(by='Accuracy', ascending=[False])
acc_table
前面代码的输出如下所示:
Figure 12.17 - Accuracy dataframe of different algorithms
请注意,将质量转换为分类数据集给了我们更高的准确性。大多数算法给出了 100%的准确性,如前面的截图所示。
- 让我们创建一个条形图:
sns.barplot(y='Modelling Algorithm',x='Accuracy',data=acc_table)
前面代码的输出如下所示:
Figure 12.18 - Different types of algorithms and their accuracies
注意,如截图所示,随机森林、决策树、梯度提升分类器和高斯朴素贝叶斯分类器都给出了 100%的准确率。
太好了。恭喜你,你已经成功完成了主要项目。请注意,本书中所有的代码、片段和方法都是为了提供解决特定问题的最少方法。总有一种方法可以让你进行更深入的分析。我们鼓励您阅读每章的进一步阅读部分,以获得特定领域的高级知识。
摘要
在本章中,我们使用了 UCI 提供的葡萄酒质量数据集来执行 EDA。我们讨论了如何执行 EDA 技术,例如数据加载、数据争论、数据转换、变量之间的相关性、回归分析以及基于数据集构建经典的 ML 模型。
这是这本书的最后一章。如前所述,本书提供的理论、代码和插图的解释是为了给你提供一个基础知识集。我们假设读完这本书后,你将获得足够的洞察力、技巧和技能,使它更上一层楼。
进一步阅读
- Python 有监督机器学习,泰勒·史密斯,帕克特出版,2019 年 5 月 26 日
- 使用 Python 进行大规模机器学习、Bastian Sjardin、 Luca Massaron 、e t al。、packkt**发布,2016 年 8 月 2 日
- Python 高级机器学习,约翰·哈迪,帕克特 出版,2016 年 7 月 27 日
- Python 的动手无监督学习Giuseppe BonaccorsoPackt**出版,2019 年 2 月 28 日
- 掌握渗透测试的机器学习奇赫布切比帕克特 出版,2018 年 6 月 26 日
- 动手数据科学与 Python 机器学习弗兰克·凯恩帕克特 出版,2017 年 7 月 30 日
- 用 Python 构建机器学习系统–第三版、路易斯·佩德罗·科埃略、威利·里歇特、e t al。、packkt**发布,2018 年 7 月 30 日
- 葡萄酒质量数据集归属于 P. Cortez 、 A. Cerdeira 、 F. Almeida 、 T. Matos 和 J. Reis 。建模 w 通过从理化性质中进行数据挖掘来确定偏好。在决策支持系统,爱思唯尔,47(4):547-553,2009。**
十二、附录
如前所述,数据预处理和数据转换是数据挖掘和其他数据科学方法中最重要的两个过程。在数据处理阶段,我们的数据通常是字符串形式的。互联网上的大多数数据集都是基于字符串的。因此,管柱操纵技术是探索性数据分析 ( EDA )的重要组成部分。
在本附录章节中,我们将了解以下主题:
- 字处理
- 使用 Pandas 向量化字符串函数
- 使用正则表达式
字处理
对于字符串操作,我们指的是如何创建字符串、访问这些字符串中的字符、分割字符串、删除或更新字符串中的字符以及其他字符串操作符。在接下来的部分中,我们将一个接一个地看到所有这些步骤。
创建字符串
我们可以用三种不同的方式在 Python 中创建字符串:
- 使用单引号
- 使用双引号
- 使用三重引号。
请看下面的例子:
String1 = 'Creating a String with single Quotes.'
String2 = "Creating a String with double Quotes."
String3 = '''Creating a String with triple Quotes.'''
print(String1)
print(String2)
print(String3)
所有三个print语句的输出都是相同的。他们按照预期创建了一个字符串。
在 Python 中访问字符
Python 提供了一种索引机制来访问任何字符串中的任何字符。任何字符串的索引都以 0 开头。我们还可以使用负索引值从任何字符串的后面访问任何字符。例如,-1 表示字符串的最后一个字符,-2 表示倒数第二个字符,依此类推。请注意,如果我们尝试访问任何不在字符串限制内的索引,Python 会以TypeError提醒我们。
下面是用于访问字符串字符的 Python 代码:
# characters of String
String = "Exploratory Data Analysis"
# Printing First character
print("\nFirst character of String is: ")
print(String[0])
# Printing Last character
print("\nLast character of String is: ")
print(String[-1])
每个代码块的输出都显示有一个内联注释,这样更容易理解。
字符串切片
要访问字符串中的一系列字符,可以使用切片方法。使用切片操作符(:)对字符串进行切片。这里有一个演示字符串切片的程序:
# Creating a String
String = "Exploratory Data Analysis"
# Outputs: Slicing characters from 3-12:loratory
print("\nSlicing characters from 3-12: ")
print(String[3:12])
# Outputs:Slicing characters between 3rd and 2nd last character: loratory Data Analys
print("\nSlicing characters between " + "3rd and 2nd last character: ")
print(String[3:-2])
每个代码块的输出都显示有一个内联注释,这样更容易理解。
从字符串中删除/更新
Python 不支持字符串变异。也就是说,它不允许删除或更新字符串中的任何字符。如果我们试图这样做,就会产生一个错误。但是,使用内置的del关键字可以删除整个字符串。在字符串更新的情况下出现错误的主要原因是字符串是不可变的。在下面的代码块中,让我们创建一个字符串,并尝试更新其中的一些字符,如下所示:
# Updation of a character
String = "Exploratory Data Analysis"
String[2] = 'p'
print("\nUpdating character at 2nd Index: ")
print(String)
前面的代码应该会给你一个错误。
Python 中的转义序列
一个字符串的语法中已经包含了单引号(')和双引号('')。因此,如果我们需要在一个字符串中打印一个单引号或一个双引号,就会产生一个SyntaxError。为了避免这种错误,必须对引号(无论是单引号还是双引号)进行转义。这种现象被称为逃逸序列。转义序列以反斜杠(\)开头,可以有不同的理解。如果我们打算使用单引号或双引号作为字符串,那么它必须通过在前面附加一个反斜杠来转义。让我们看看它在行动。
我们将针对所有三种情况(单引号、双引号和三引号)显示以下示例:
String = '''I'm a "Data Scientist"'''
# Initial String
print("Initial String with use of Triple Quotes: ")
print(String)
# Escaping Single Quote
String = 'I\'m a "Data Scientist"'
print("\nEscaping Single Quote: ")
print(String)
# Escaping Double Quotes
String = "I'm a \"Data Scientist\""
print("\nEscaping Double Quotes: ")
print(String)
在计算机科学中,我们经常需要在不同的场合提供一些文件或数据集的路径。处理数据科学时也是如此。第一步是加载数据集。为此,我们必须使用双斜线提供文件的链接或路径,以便转义双斜线。然后,我们用转义序列打印路径,如下例所示:
String = "C:\\Python\\DataScience\\"
print("\nEscaping Backslashes: ")
print(String)
上述代码生成以下输出:
Escaping Backslashes:
C:\Python\DataScience\
注意代码中双斜线的使用——这提供了单斜线作为输出。这就是为什么逃跑是一种非常有用的机制。
格式化字符串
我们可以使用format()方法在 Python 中格式化字符串。当以任何特定格式显示输出时,这种方法非常灵活和强大。format()方法将大括号{}作为占位符,可以根据特定的顺序被任何特定的参数替换。看看下面这些例子。
让我们首先看一个默认订单的例子:
# Default order
String1 = "{} {} {}".format('Exploratory ', 'Data ', 'Analysis')
print("Print String in default order: ")
print(String1)
前面代码的输出如下:
Print String in default order:
Exploratory Data Analysis
除了这个默认顺序,我们还可以使用位置格式。比如你有一个('Exploratory', 'Data', 'Analysis')这样的字符串,我们要显示一个('Data', 'Exploratory', 'Analysis')字符串。我们可以通过使用位置格式来实现这一点,如下例所示:
# Positional Formatting
String1 = "{1} {0} {2}".format('Exploratory', 'Data', 'Analysis')
print("\nPrint String in Positional order: ")
print(String1)
我们也可以通过使用关键字来格式化任何字符串。例如,看看下面的代码:
# Keyword Formatting
String1 = "{l} {f} {g}".format(g = 'Exploratory', f = 'Data', l = 'Analysis')
print("\nPrint String in order of Keywords: ")
print(String1)
前面代码的输出如下:
Print String in order of Keywords:
Analysis Data Exploratory
接下来,我们将了解如何加载文本数据集并执行预处理操作。
使用 Pandas 向量化字符串函数
对于字符串格式,最好使用稍微混乱一点的数据集。我们将在撰写综述论文时使用我在博士研究期间收集的数据集。可以在这里找到:https://raw . githubusercontent . com/sureshHARDIYA/PhD-resources/master/Data/Review % 20 paper/preferred . CSV。
- 让我们加载这篇文本文章,然后显示前八个条目。让我们从加载数据并检查其结构和一些注释开始,如下所示:
import numpy as np
import pandas as pd
import os
- 接下来,让我们阅读文本文件并显示最后的
10项,如下所示:
text = pd.read_csv("https://raw.githubusercontent.com/sureshHARDIYA/phd-resources/master/Data/Review%20Paper/preprocessed.csv")
text = text["TITLE"]
print (text.shape)
print( text.tail(10))
- 前面代码的输出可以在下面的截图中看到:
Figure 1: This is the output of the preceding code
Pandas 扩展了操作整个字符串系列的内置功能。在下一节中,我们将对 pandas 字符串函数使用相同的数据集。
对 Pandas 数据框使用字符串函数
让我们使用 Pandas 数据框的内置函数。我们将继续使用上一节中导入的相同数据集。Python 中的大多数字符串操作函数都使用 Pandas 向量化字符串方法。
以下是 Pandas 字符串函数的列表,这些函数反映了 Python 字符串方法:
Figure 2 - List of vectorized string functions in pandas
让我们练习以下用例:
- 从数据框的
text列中提取第一句,并将其转换为小写字符,如下所示:
text[0].lower()
- 将
text列中的所有注释转换为小写,并显示前八个条目,如下所示:
text.str.lower().head(8)
- 提取第一句并将其转换为大写字符,如下所示:
text[0].upper()
- 获取文本字段中每个注释的长度,并显示前八个条目,如下所示:
text.str.len().head(8)
- 将所有注释组合成一个字符串,并显示前 500 个字符,如下所示:
text.str.cat()[0:500]
明智的做法是验证所有的注释都连接在一起。你能想到我们可能需要将所有注释组合成一个字符串的用例吗?好吧,不如——比如说——我们希望看到所有用户在评论时最常选择的词语。
- 将每个字符串分割成一个系列,并使用
series.str.slice()以元素方式返回结果,如以下代码片段所示:
text.str.slice(0, 10).head(8)
- 使用
str.replace()将给定子串的出现替换为不同的子串,如以下代码片段所示:
text.str.replace("Wolves", "Fox").head(8)
在前面的例子中,Wolves的所有情况将被替换为Fox。这充当了一个搜索和替换的功能,你可以在许多内容管理系统和编辑器中找到。
在处理文本数据时,我们经常测试字符串是否包含特定的子字符串或字符模式。让我们只搜索那些提到安德鲁·威金斯的评论。我们需要匹配所有提到他的帖子,避免匹配没有提到他的帖子。
使用series.str.contains()获取一系列真/假值,指示每个字符串是否包含给定的子字符串,如下所示:
# Get first 10 comments about Andrew Wiggins
selected_comments = text.str.lower().str.contains("wigg|drew")
text[selected_comments].head(10)
仅供参考,让我们计算一下提到安德鲁·威金斯的评论比例,如下所示:
len(text[selected_comments])/len(text)
输出是 0.06649063850216035。可以看到,6.6%的评论都提到了安德鲁·威金斯。这是我们提供给str.contains()的字符串模式参数的输出。
关于安德鲁·威金斯的帖子可以用任何不同的名字来指代他——维金斯、安德鲁、维格、德鲁——所以我们需要比单个子字符串更灵活的东西来匹配我们感兴趣的所有帖子。我们提供的模式是正则表达式的一个简单示例。
使用正则表达式
正则表达式(regex)是一系列字符和特殊元字符,用于匹配一组字符串。正则表达式允许您在字符串匹配操作中比仅仅提供简单的子字符串更具表现力。你可以把它想象成一个模式,你想用不同长度的字符串进行匹配,由不同的字符组成。
在str.contains()方法中,我们提供了正则表达式wigg|drew。在这种情况下,竖线|是充当OR运算符的元字符,因此该正则表达式匹配包含子字符串wigg或drew的任何字符串。
元字符允许您更改匹配方式。当您提供不包含元字符的正则表达式时,它只匹配精确的子字符串。例如,Wiggins将只匹配包含精确子串Wiggins的字符串。
下面是基本元字符的列表,以及它们的作用:
".":句点是匹配除换行符以外的任何字符的元字符,如下面的代码块所示:
# Match any substring ending in ill
my_words = pd.Series(["abaa","cabb","Abaa","sabb","dcbb"])
my_words.str.contains(".abb")
"[ ]":方括号指定一组要匹配的字符。查看以下示例片段,并将您的输出与本章中给出的笔记本进行比较:
my_words.str.contains("[Aa]abb")
"^":在方括号外,插入符号在字符串的开头搜索匹配项,如下面的代码块所示:
Sentence_series= pd.Series(["Where did he go", "He went to the shop", "he is good"])
Sentence_series.str.contains("^(He|he)")
"( )":正则表达式中的括号用于分组和强制执行正确的操作顺序,就像它们用于数学和逻辑表达式一样。在前面的例子中,括号让我们对 OR 表达式进行分组,以便"^"和"$"符号对整个OR语句进行操作。"*":星号匹配前面字符的0个或多个副本。"?":问号匹配前面字符的0或1副本。"+":加号匹配前面字符的 1 个或多个副本。"{ }":花括号匹配指定重复次数的前一个字符:"{m}":前面的元素匹配m次。"{m,}":前面的元素匹配m次以上。"{m,n}":前一个元素在m和n时间之间匹配。
正则表达式包括几个特殊的字符集,允许我们快速指定某些常见的字符类型。它们包括以下内容:
- 在方括号内添加
"^"符号匹配集合中的任何字符而不是: - Python 正则表达式还包括一个指定常见序列的简写:
- \d:匹配任意数字。
- \D:匹配任何非数字。
- \w:匹配一个单词字符。
- \W:匹配一个非单词字符。
- \s:匹配空白(空格、制表符、换行符等。).
- \S:匹配非空白。
请记住,我们确实在字符串格式化时逃脱了排序。同样,当您想要匹配元字符符号本身时,您必须在元字符中用""转义。
例如,如果你想匹配句点,你不能使用".",因为它是一个匹配任何东西的元字符。相反,您可以使用.来转义句点的元字符行为,并匹配句点本身。下面的代码块说明了这一点:
# Match a single period and then a space
Word_series3 = pd.Series(["Mr. SK","Dr. Deepak","Miss\Mrs Gaire."])
Word_series3.str.contains("\. ")
如果你想匹配转义字符\本身,你要么必须使用四个反斜杠"\"要么以r"mystring"的形式将字符串编码为原始字符串,然后使用双反斜杠。原始字符串是 Python 中的一种替代字符串表示形式,它简化了在普通字符串上执行正则表达式的一些奇怪之处,如下面的代码片段所示:
# Match strings containing a backslash
Word_series3.str.contains(r"\\")
在处理正则表达式中的特殊字符串时,通常使用原始字符串,因为它避免了这些特殊字符可能出现的问题。
正则表达式通常用于匹配文本之间的电话号码、电子邮件地址和网址的模式。Pandas 有几个接受正则表达式模式并执行操作的字符串函数。我们现在熟悉这些功能:series.str.contains()和series.str.replace()。
现在,让我们在注释数据集中使用更多的函数。
使用series.str.count()统计每个字符串中模式的出现次数,如下所示:
text.str.count(r"[Ww]olves").head(8)
使用series.str.findall()获取每个匹配的子串,并将结果作为列表返回,如下所示:
text.str.findall(r"[Ww]olves").head(8)
操纵一根弦的方法有很多。我们选择了最基本的方法来说明,以便让您简单地理解。
进一步阅读
- 用 NLTK 2.0 食谱处理 Python 文本,雅各布·帕金斯,Packt 出版,11 月 9 日, 2010 。
- NLTK 精粹尼廷哈登亚·帕克特出版T52015 年 7 月 26 日 。
- Python 自然语言处理实操, Rajesh Arumugam,Rajalingappaa Shanmugamani,Packt Publishing ,7 月 17 日, 2018 。
- 用 Python 进行数据分析David Taieb,Packt PublishingT512 月 31 日2018。
第一部分:EDA 的基础
本节的主要目的是介绍探索性数据分析 ( EDA )的基础知识,并了解 EDA 过程的不同阶段。我们还将研究概要分析、质量评估的关键概念、EDA 的主要方面以及 EDA 中的挑战和机遇。除此之外,我们将发现不同的有用的可视化技术。最后,我们将讨论基本的数据转换技术,包括数据库风格的数据帧合并、转换技术以及数据转换的好处。
本节包含以下章节:
第二部分:描述性统计
描述性统计有助于总结所提供的数据集,并确定所考虑的数据的最重要特征。本节的主要目的是让您熟悉描述性统计及其主要技术,包括中心趋势的度量和可变性的度量。此外,我们将学习不同的数据集分组方法、相关性,更重要的是,时间序列分析。
本节包含以下章节:
第三部分:模型开发和评估
EDA 的主要目标之一是准备您的数据集,以开发能够表征感测数据的有用模型。要创建这样的模型,我们首先需要了解数据集。如果我们的数据集被标记,我们将执行有监督的学习任务,如果我们的数据没有被标记,那么我们将执行无监督的学习任务。此外,一旦我们创建了这些模型,我们就需要量化我们的模型有多有效。我们可以通过对这些模型进行几次评估来做到这一点。在本节中,我们将深入讨论如何使用 EDA 进行模型开发和评估。本节的主要目标是允许您在真实数据集上使用 EDA 技术,准备不同类型的模型,并对它们进行评估。
本节包含以下章节: