文章目录
前言
EDA(探索性分析)指的是在已有数据的基础上,通过作图等各种方法探索数据的结构和内在信息的一种研究手段,由美国统计学家J.K.Tukey在上世纪70年代提出。在一个机器学习项目中,EDA起着非常重要的作用,他在项目初进行,决定了数据预处理和特征工程的方向,也决定着算法的拟合度和泛化性能。
本文主要翻译参考自kaggle社区 COMPREHENSIVE DATA EXPLORATION WITH PYTHON。 介绍了EDA的一般思路和一些比较基础的方法。原文作者笔触非常幽默,把项目比作了一个party 🍺,还把特征想象成一个“辣妹”,与其进行对话。不得不感叹数据工作者每天面对成千上万的数据是用怎样的方式自娱自乐的
没有做好的EDA模板,只有最适合自己的EDA,不同类型的机器学习问题EDA思路也是不一样的,本文只提供了一个一般性思路,介绍了一个回归问题如何思考EDA
数据集: House Prices: Advanced Regression Techniques
我们首先需要明确做一个EDA的核心有什么
- 明白我们的任务:我们要观察每一个变量,并对这些变量的含义 以及对这个问题的重要性程度做一些感性的认识。
- 单变量学习:我们关注目标变量(‘SalePrice’)并试着去发现一些关于它的特点
- 多变量学习:我们试着去了解目标变量与其他变量之间的联系。
- 基础的数据清洗:我们对数据集进行简单的清洗,并处理一些缺失值,异常值以及类别变量
- 假设检查:我们检查一下我们是数据是否符合大多数多元模型所要求的假设
导入party必要元素
#invite people for the Kaggle party
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.stats import norm
from sklearn.preprocessing import StandardScaler
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
来一扎数据
#bring in the six packs
df_train = pd.read_csv('../input/train.csv')
#check the decoration
print('特征个数:',len(df_train.columns))
df_train.columns
1.So… 我们期待从中知道些什么
为了了解我们的数据,我们可以观察所有变量尝试着理解他们的含义以及与这个机器学习问题的关联。虽然这个工作费时费力,但最终会让我们初步尝出手中数据独特的风味 (原文表述,觉得还挺有意思的)
为了使我们的分析更有条理,我们可以针对每个变量使用以下几列创建一个Excel:
- Variable - 变量名
- Type - 变量的类型,设为两个值,‘numerical’ 或者 ‘categorical’,也就是数值型或者类别型,数值型就是例如身高、体重等数值型变量;类别型就是例如性别、星座等变量
- Segment - 变量段。 我们可以定义三个可能的分段:建筑物,空间或位置。 当我们说“建筑物”时,是指与建筑物的物理特性相关的变量(例如“ OverallQual”)。 当我们说“空间”时,是指一个描述房屋空间属性的变量(例如“ TotalBsmtSF”)。 最后,当我们说“位置”时,是指一个变量,该变量提供有关房屋所在位置的信息(例如“Neighborhood”)。
- Expectation - 我们对该变量对目标变量“SalePrice”影响程度的期望值,我们可以用类别型值来描述:比如“High”,“Medium”,“Low”。
- Conclusion - 我们对这个变量重要性的结论,在我们快速地了解了数据之后(可能是用相应的图表,数值分布图了解),我们可以用在Expectation中一样的数据,比如“High”,“Medium”,“Low”来填写这一列。
- Comments - 一些我们对特征产生的评论。
‘Type’ 和 'Segment’列可以让之后的工作变得方便一些,当我们遇到一个特征时可以看看它的种类;但Expectation列很重要因为它可以培养我们对于数据的第六感,为了填写这一列,我们拿到每一个特征,详细地了解了它之后,我们都应当问自己
- 我们买房时会考虑这个特征吗(比如,我们买房时会考虑 ‘Masonry veneer type’ 砖石装饰种类吗?)
- 如果考虑的话,这个特征会有多重要(意思就是,如果我们用“Excellent”的砖头,比起用“Poor”的砖头,会带来怎么的影响?用“Excellent”的砖头比起“Good”的砖头呢?)
译者理解:当我们这样想的时候,就能理解这个特征是否会影响最后的“SalePrice”,是怎样影响的。因为我们就是那个买房者 - 这个特征所承担的信息是否在别的特征里已经表达过了
经历这样令人生厌的工作之后,我们可以小心地扫一遍我们自己填写的这个excel文件,找到那些我们填了“High”Expectation的特征,然后直接画出这个特征与"SalePrice"之间的散点图,描绘其关系,然后根据描绘的散点图,判断这个特征的重要性,填入“Conclusion”中,来修正我们的Expectation
我这样做了之后,总结出了以下几个特征会对结果产生重要影响
- OverallQual
- YearBuilt
- TotalBsmtSF
- GrLivArea
结果显示这里面有2个“building”变量(OverallQual和YearBuilt),还有两个“space”变量(“TotalBsmtSF”和“GrLivArea”)这也许会有点不尽如人意,因为这样的方法会对分类变量有点不友好,我期待Neighborhood变量会有很大作用,但实际上不是如此,可能是因为散点的数量太少。
不过,这个过程最关键的点在于粗略地了解特征与结果的关系,以及培养自己对数据的第六感,所以我想我们还是达到了目的,现在是时候“少点言语,多点实际行动”了,让我们摇摆起来!
2.第一件事,分析“SalePrice”
“SalePrice”是我们出现在这里的原因,就像我们奔赴一场party,往往会有很多原因,但女人经常是第一因素(免责声明:也有可能是男人,跳舞,喝酒,根据你的喜好而定)
根据“Women”定律,让我们发展出一些小故事,就叫做“我是怎么遇上SalePrice”的吧
一切发生在那场Kaggle party上,我们在人群中寻找着一位舞伴,当目光短暂地扫过舞池,你看见了一个女孩,倚靠在吧台旁,穿着你喜欢的舞鞋,表明她刚刚也是跳动着的人群的一员。我们花了大部分时间在建立模型和练习参加数据竞赛上,所以和女孩子讲话并不是我们的强项,但尽管如此,英勇的程序员从不说“不”,于是你上前:
“Hi,我是Kaggly!你的名字是?‘SalePrice’?好美的名字!你知道关于房子售价的一切信息,你可以给我一些关于你的数据吗?我刚刚训练了一个模型,可以计算两个人发展出一段关系的可能性。我想我可以将它用在我们上!”
#descriptive statistics summary
df_train['SalePrice'].describe()
“呃.很好…看起来你的Minimum Price 大于0。太棒了!你不会有一些能够毁坏我的模型的性格,你有一些关于你的照片可以发给我吗…比如…就像 你在沙滩的照片?… 或者你在健身房的自拍?”
#histogram
sns.distplot(df_train['SalePrice']);
“噢!看来你出门前是用Seaborn化的妆…这让你看起来亭亭玉立,我再看看,看来你:”
- 偏离正态分布
- 处于正偏
- 有明显的峰值
“Wow,多么令人着迷!“SalePrice”,你可以给我一些关于你身材的信息吗” (偏度与峰度)
#skewness and kurtosis
print("Skewness: %f" % df_train['SalePrice'].skew())
print("Kurtosis: %f" % df_train['SalePrice'].kurt())
“太不可思议了,如过我的模型没有出错的话,我们牵线的成功率是97.834567%。我认为我们应该多见面!这是我的号码,请你留下,如果你下一个周五有空,记得给我打电话,一会儿见,crocodile!(翻译为鳄鱼?可能是什么语气词吧)”
3.SalePrice,她的身体和她的兴趣爱好
有一句俗语说“选择好的环境是战斗中的智慧”,当SalePrice离开后,我们立马登上了FaceBook,没错,你现在需要变得严肃起来。意识到这不是在开玩笑,这是对一个人个人信息的深入调查,如果你懂我的意思的话
根据她上传的照片可以看出,我们有一些共同的朋友。除了 Chuck Norris(一个武打明星,演过《猛龙过江》),我们还都认识 ‘GrLivArea’ 和 ‘TotalBsmtSF’(这两个是数据中的特征,别忘了这是一篇数据分析文章)。除此之外,我们都还有共同的兴趣,比如’OverallQual’ 和 ‘YearBuilt’.这看起来很有希望!
SalePrice与数值型特征 ‘GrLivArea’ 和 'TotalBsmtSF’的关系
#scatter plot grlivarea/saleprice
var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
emmmm…看起来GrLivArea和SalePrice是老朋友了,他们是线性关系,那和’TotalBsmtSF’呢?
#scatter plot totalbsmtsf/saleprice
var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
'TotalBsmtSF’看起来也和SalePrice是个要好的朋友,但是他们之间的关系看起来很情绪化!一切都很正常但是直到有一天,在一场剧烈的线性反应中,一切都变了。除此之外,可以很明显地看出有时’TotalBsmtSF’会自闭,对于SalePrice基本没有反应
4.SalePrice与类别特征的关系
#box plot overallqual/saleprice
var = 'OverallQual'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(8, 6))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000);
就像所有美丽的女孩一样,SalePrice和OverallQual相处地很愉快。时常告诉自己:麦当劳作为第一次约会见面的场所是否合适
var = 'YearBuilt'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(16, 8))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000);
plt.xticks(rotation=90);
虽然看起来并不是一个很强的趋势,但可以看出SalePrice非常愿意在新的东西上面花钱
小贴士:我们不知道SalePrice是否实在一个稳定变动的幅度上,Constant prices try to remove the effect of inflation. If ‘SalePrice’ is not in constant prices, it should be, so than prices are comparable over the years.(翻译不通,能力有限)
总结
将故事抛在一旁,我们可以得到结论:
- ‘GrLivArea’ 和’TotalBsmtSF’ 看起来和SalePrice线性相关.且都是正相关,说明其中任意一个变量增长,SalePrice都会跟着增长,对于TotalBsmtSF’来说,我们可以看到线性相关的斜率非常之高
- ‘OverallQual’ 和 ‘YearBuilt’ 看起来也和SalePrice有很大瓜西,"OverallQual’ 的关系看起来更强,箱型图表明了SalePrice是如何随着他增长的
我们刚刚仅分析了四个变量,但是还有很多变量是我们应该分析的。这里的小技巧就是选择正确的特征(特征选择),而不是它们之间复杂关系的定义(特征工程)。
就是说,让我们从谷壳中分离出小麦。
5.保持镇定且明智
我们刚刚根据我们的直觉分析了那些我们认为重要的变量。尽管我们的分析结果是客观的,但必须得承认,我们的出发点是偏向主观的。我所受的所有教育都在教会我尽量变得客观。这是有原因的,当你将主观代入结构化工程,你会看到工程的结果或多或少都会变差。
The ‘plasma soup’ (翻译为等离子汤,应该说的是宇宙爆炸初的一团混沌的意思)
“在一切的开始,除了一片混沌一切都没有。在我们的宇宙学研究之初,这些短暂的时刻在很大程度上是推测的。但是,科学根据当今对宇宙的了解,为当时可能发生的事情做了一些大胆的猜测” (摘自: http://umich.edu/~gs265/bigbang.htm)
为了探索宇宙,我们将用一些特别的配方来感知这一片虚无下到底有什么隐藏的秘密:
- Correlation matrix (heatmap style)热度图形式的相关矩阵
- ’SalePrice’ correlation matrix (zoomed heatmap style)缩放热度图形式的 SalePrice相关矩阵
- Scatter plots between the most correlated variables (move like Jagger style) 最相关变量之间的散点图
-(注:moves like Jagger 是魔力红乐队的一首很好听的作品,Jagger是滚石乐队的主唱Mick Jagger,作者这里表达的意思应该是用这个方法做出来的图很炫酷)
Correlation matrix (heatmap style)
#correlation matrix
corrmat = df_train.corr()
f, ax = plt.subplots(figsize=(12, 9))
sns.heatmap(corrmat, vmax=.8, square=True);
对于我来说,热度图是在一无所知的情况下快速了解特征以及他们之间关系的最好方式(万分感谢!@Seaborn)
第一眼看过去,那里有两块红色的区域立刻映入了我的眼帘,第一块是 ‘TotalBsmtSF’ 和 ‘1stFlrSF’ 变量的关系区域,第二块是 the ‘GarageX’ 变量(注:就是带有Garage的那紧挨着的三个变量)。两块区域都表明了这些变量之间的关系是多么重要。实际上,这种相关性是如此之强,以至于可以指示出他们的多重共线性。如果我们仔细考虑,会发现他们几乎给出的是相同的信息,因为多重共线性
-(注:多重共线性就是自变量之间高度相关,导致模型失真的现象,可以理解为这些变量中取其一二就够了)
热图非常适合检测这种情况,在特征选择过程中给也能起到很大作用,所以对我们来说,热图是必不可少的工具。
另一方面,我们可以发现,对SalePrice变量,我们熟悉的 ‘GrLivArea’, ‘TotalBsmtSF’, ‘OverallQual’ 正对她挥着手兴奋地说“Hi”,但是我们还可以发现其他许多变量也应该被考虑进和她的关系中。这指明了我们接下来该做的事情。
’SalePrice’ correlation matrix (zoomed heatmap style)
#saleprice correlation matrix
k = 10 #number of variables for heatmap
cols = corrmat.nlargest(k, 'SalePrice')['SalePrice'].index
cm = np.corrcoef(df_train[cols].values.T)
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f', annot_kws={'size': 10}, yticklabels=cols.values, xticklabels=cols.values)
plt.show()
根据我们的魔法水晶球,可以得出这些是和SalePice最相关的变量,我对此的理解是:
- ‘OverallQual’, ‘GrLivArea’ and 'TotalBsmtSF’和SalePrice有着强大的关联!如我所料!
- “ GarageCars”和“ GarageArea”也是最相关的变量中的一部分。 但是,正如我们在上一个段中讨论的那样,一个车库中能容纳的车辆数就是由车库面积所决定的。 “ GarageCars”和“ GarageArea”就像孪生兄弟。 您将永远无法区分它们。 因此,我们在分析中只需要保留其中的一个变量(我们可以保留“ GarageCars”,因为它与“ SalePrice”的相关性更高)。
- “ TotalBsmtSF”和“ 1stFloor”似乎也是孪生兄弟。 我们可以保留’TotalBsmtSF’说明我们的第一个猜测是正确的(重读上面章节’So… 我们期待从中知道些什么’)。
- 竟然有’FullBath’?? 认真的吗?
- “ TotRmsAbvGrd”和“ GrLivArea”,又是双胞胎兄弟。 这个数据集来自切尔诺贝利吗?(注:不懂这是什么梗)
- 啊…‘YearBuilt’… 看来’YearBuilt’与’SalePrice’略有相关。 老实说,“ YearBuilt”这个变量让我感到害怕,因为我开始觉得我们应该做一些时间序列分析来分析它。 我将把它作为作业留给您。
让我们继续快乐地画散点图吧
Scatter plots between ‘SalePrice’ and correlated variables (move like Jagger style)
请做好准备面对接下来你要看到的东西。 我承认,当我第一次看到这些散点图时,我完全被震撼了! 在如此短的页面里描绘了如此多的信息……真是太神奇了。 再次感谢@seaborn! 你让我‘move like Jagger ’
#scatterplot
sns.set()
cols = ['SalePrice', 'OverallQual', 'GrLivArea', 'GarageCars', 'TotalBsmtSF', 'FullBath', 'YearBuilt']
sns.pairplot(df_train[cols], size = 2.5)
plt.show();
我们已经了解了其中一些主要关系,但这个巨型散点图为我们提供了研究相关变量关系的合理思路。
其中一个你也许会感到兴趣的关系图是’TotalBsmtSF’ 和 'GrLiveArea’的,在这个图中你可以看到一部分点汇成了一条直线,几乎像一个边界一样。从特征的角度来看这完全合理,大多数点都位于这条线以下,地下室的面积可以等于地面上的居住面积,但是地下室的面积不能大于地面上的居住面积(除非您要购买掩体)。
关于“ SalePrice”和“ YearBuilt”的图也可以带给我们思考,在云状的散点之下,我们可以看到一个害羞的指数函数(发挥你的想象力),我们还可以在“点云”的上限看到这种趋势(甚至变得更具有想象力)。另外,请注意过去几年的点集如何趋于保持在此限制之上(我只是想说现在价格上涨得更快)。
好的,到目前为止,我们已经做了很多罗夏(Rorschach)测试。 让我们前进到缺失的地方:缺失的数据!
-(注:Rorschach test 罗夏墨迹检测,是一种用于心理分析的方法,作者这里应该指的是我们看着散点图进行了很多发散性联想,就像在做罗夏墨迹检测一样)
6. Missing data
当我们思考缺失值时我们应该想到的重要问题:
- 缺失值有多普遍
- 缺失值是随机的还是有规律的
出于实际原因,这些问题的答案很重要,因为缺少数据可能意味着样本量减少。 可能会阻止我们继续进行分析。 此外,从现实的角度来看,我们需要确保这些数据丢失的原因中不存在偏见或者是为了掩盖不便的事实。(译者理解为确保这些缺失值不是在收集过程中人为造成缺失的)
#missing data
total = df_train.isnull().sum().sort_values(ascending=False)
percent = (df_train.isnull().sum()/df_train.isnull().count()).sort_values(ascending=False)
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing_data.head(20)
我们就分析它来理解应当怎么处理缺失值吧
我们应当考虑到,当丢失超过15%的数据时,我们应该删除相应的变量并假装它不存在。 不考虑填充其丢失的数据。 据此,我们应删除一组变量(例如“ PoolQC”,“ MiscFeature”,“ Alley”等)。 但是关键是:我们会错过这些数据吗? 我不这么认为。 这些变量似乎都不是很重要,因为其中大多数都不是我们在购买房屋时要考虑的方面(也许这就是数据丢失的原因?)。 此外,仔细观察变量,我们可以说像’PoolQC’,'MiscFeature’和’FireplaceQu’这样的变量是异常值的强力候选者,因此我们很乐意将其删除。
在剩下的情况里,我们可以看到几个含有’Garage’的变量具有相同数量的丢失数据。 我敢打赌,缺失的数据是来自相同的样本的(尽管我不会对其进行检查;因为他们仅占5%)。 由于有关车库的最重要信息是由“ GarageCars”表示的,并且考虑到我们只是在谈论丢失的数据的5%,因此我将删除这些“ Garage”变量。 相同的逻辑适用于含“ Bsmt”的这些变量。
关于“ MasVnrArea”和“ MasVnrType”,我们可以认为这些变量不是必需的。 此外,它们与已经考虑的“ YearBuilt”和“ OverallQual”有很强的相关性。 因此,如果删除“ MasVnrArea”和“ MasVnrType”,我们将不会丢失信息。
最后,我们在“Electrical”中有一个缺失值。考虑到只有一个,所以我们将删除该样本呢并保留变量。
总之,我们删除以上除了“Electrical”外所有的变量,而对于“Electrical”,仅删除那个缺失的样本
#dealing with missing data
df_train = df_train.drop((missing_data[missing_data['Total'] > 1]).index,1)
df_train = df_train.drop(df_train.loc[df_train['Electrical'].isnull()].index)
df_train.isnull().sum().max() #just checking that there's no missing data missing...
Out[ ] : 0
7.Out liars! (离群值)
-(注:作者称离群值为Out liars ,还有很多叫法为out liers 应该无所谓,都是同一个意思,还有异常值与离群值的区别,感觉大部分的说法是异常值范围较大,包含离群值、缺失值、重复值。也有说法认为异常值和离群值是一个概念)
离群值也是我们应注意的事情。 为什么? 因为离群值会明显影响我们的模型,同时,他们也是有价值的信息来源,可以为我们提供了有关特定行为的见解。
离群值是一个复杂的主题,应引起更多关注。 在这里,我们将通过“ SalePrice”的标准偏差和一组散点图进行快速分析。
Univariate analysis单变量分析
这里我们要建立一个将观察值定义为异常值的阈值。 为此,我们将对数据进行标准化。数据标准化意味着将数据值转换为均值0和标准差1的标准正态分布
#standardizing data
saleprice_scaled = StandardScaler().fit_transform(df_train['SalePrice'][:,np.newaxis]);
low_range = saleprice_scaled[saleprice_scaled[:,0].argsort()][:10]
high_range= saleprice_scaled[saleprice_scaled[:,0].argsort()][-10:]
print('outer range (low) of the distribution:')
print(low_range)
print('\nouter range (high) of the distribution:')
print(high_range)
让我们看看SalePrice穿上了新衣服后看上去如何:
- 低于正常水平的值之间相似,并离0值不远
- 高于正常水平的值距离0较远,并且超过7的值确实差得很远了
但是目前,我们不会将这些值中的任何一个视为异常值,但应谨慎使用这两个7.something值。
Bivariate analysis双变量分析
我们已经绘制过下面这个散点图。 但是,当我们从新的角度看待事物时,总会发现新的地方。 正如艾伦·凯(Alan Kay)所说,“改变一次观点需要80智商点”。
#bivariate analysis saleprice/grlivarea
var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
我们可以发现
- 那两个“ GrLivArea”很大的值似乎很奇怪,并且它们并未在点群中。 我们可以推测为什么会这样。 也许他们指的是农业领域,这可以解释价格低廉的原因。 我不确定这一点,但我很有把握,这两点并不能代表典型案例。 因此,我们将它们定义为离群值并将其删除。
- 图顶部的两个观测值是我们应注意的那超出7的观测值。 它们看起来像两种特殊情况,但是它们似乎在点群的运动趋势中。 因此,我们将保留它们。
删除那两个“ GrLivArea”很大的值
#deleting points
df_train.sort_values(by = 'GrLivArea', ascending = False)[:2]
df_train = df_train.drop(df_train[df_train['Id'] == 1299].index)
df_train = df_train.drop(df_train[df_train['Id'] == 524].index)
接着分析’TotalBsmtSF’和SalePrice
var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
我们可能会想删掉某些值(例如TotalBsmtSF> 3000的那三个点),但我认为这样做是不值得的。 我们可以忍受,所以我们什么也不会做。
8. Getting hard core
在艾恩·兰德(Ayn Rand)的小说《Atlas Shrugged》中,有一个经常重复出现的问题:约翰·加尔特是谁? 这本书的很大一部分是关于寻找这个问题答案的探索。
我现在有个疑问:谁是SalePrice
这个问题的答案在于测试多变量统计所基于的基础假设。 我们已经进行了一些数据清理,并发现了很多有关“ SalePrice”的信息。 现在是时候深入了解“ SalePrice”如何符合统计假设,这使我们能够应用多元技术。
根据 Hair et al. (2013), 我们应检验四个假设
- Normality - 当我们谈论正态性时,意思是数据应该看起来像正态分布。 这很重要,因为几个统计检验都依赖于此(例如t统计)。 在本练习中,我们将仅检查“ SalePrice”的单变量正态性(这是一种有限的方法)。 请记住,单变量正态性不能确保多元正态性(这是我们希望拥有的),但是它会有所帮助。 要考虑的另一个细节是,在大样本(> 200个观察值)中,正态性不是问题。 但是,如果我们解决正态性,就可以避免很多其他问题(例如,异方差性),这就是我们进行此分析的主要原因。
- Homoscedasticity - 我希望我写对了。 同质性是指“假设因变量在预测变量范围内表现出相等的方差水平”(Hair等人,2013提出)。 同质性是可取的,因为我们希望误差项在自变量的所有值上都相同。
- Linearity - 评估数据的线性最常用的方法是检查散点图并找出线性模式。 如果模式不是线性的,则可以尝试数据转换等方法。 但是,由于我们所看到的大多数散点图似乎都具有线性关系,因此我们暂时不会对数据转换进行讨论。
- Absence of correlated errors - 就像字面指出的那样,相关错误是在一个错误与另一个错误相关时发生的。 例如,如果一个正误差系统地产生一个负误差,则意味着这些变量之间存在关系。 这通常发生在时间序列中,其中某些模式与时间相关。 我们暂时也不会涉及到这一点。 但是,如果您已经发现了某些异常,请尝试添加一个变量,该变量可以解释您所获得的效果。 这是相关性错误的最常见解决方案。
您认为猫王会对这个冗长的解释怎么说? “少点言语,多点实际行动”? 也许吧…顺便问一下,您知道猫王的最后一场演出是在哪里吗
(…)
The bathroom floor.
-(注:猫王去世时是在住处的厕所地板上被发现的)
In the search for normality
这里的重点是我们会用到非常简便的方法检测SalePrice的正态性:
- 直方图:峰度和偏度
- 正态概率图:数据分布应紧跟代表正态分布的对角线。
OK,SalePrice显然不是正态分布,它显示了“峰值”,正偏,并且没有贴着对角线。
但是,这并不影响什么。简单的数据转换可以解决该问题。 这是您可以在统计书籍中学到的很棒的事情之一:如果出现正偏斜,对数转换通常效果很好。 当我发现这一点时,我感觉我就像是霍格沃茨的学生,发现了一个新的炫酷的法术。
Avada kedavra! 阿瓦达索命咒!
Log转换:
#applying log transformation
df_train['SalePrice'] = np.log(df_train['SalePrice'])
重新绘图
#transformed histogram and normal probability plot
sns.distplot(df_train['SalePrice'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)
做到了!接下来让我们再看看’GrLivArea’吧
#histogram and normal probability plot
sns.distplot(df_train['GrLivArea'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)
看起来它也是正偏的…阿瓦达索命!
#data transformation
df_train['GrLivArea'] = np.log(df_train['GrLivArea'])
#transformed histogram and normal probability plot
sns.distplot(df_train['GrLivArea'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)
很好,下一个,‘TotalBsmtSF’
#histogram and normal probability plot
sns.distplot(df_train['TotalBsmtSF'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['TotalBsmtSF'], plot=plt)
OK,现在我们遇到Boss了,这该怎么办
- 总的来说,有点偏度
- 大量值为0的观测值(无地下室的房屋)。
- 比较大的问题是0值不允许我们进行对数转换。
要在此处应用对数转换,我们将创建一个变量,该变量可以代表具有或不具有地下室的效果(二进制变量)。 然后,我们将对所有非零观测值进行对数转换,而忽略那些值为零的观测值。 这样,我们可以转换数据,而不会失去具有或不具有地下室的影响。
我不确定这种方法是否正确。 对我来说这似乎是正确的。 这就是我所说的“高风险工程”。
#create column for new variable (one is enough because it's a binary categorical feature)
#if area>0 it gets 1, for area==0 it gets 0
df_train['HasBsmt'] = pd.Series(len(df_train['TotalBsmtSF']), index=df_train.index)
df_train['HasBsmt'] = 0
df_train.loc[df_train['TotalBsmtSF']>0,'HasBsmt'] = 1
#transform data
df_train.loc[df_train['HasBsmt']==1,'TotalBsmtSF'] = np.log(df_train['TotalBsmtSF'])
#histogram and normal probability plot
sns.distplot(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], plot=plt)
应该是完成了
寻找同质性的第一次尝试
测试两个度量变量的均方差的最佳方法是画图。 通过圆锥(图形的一侧较小的色散,相对的一侧较大的色散)或菱形(分布中心的大量点)等形状来表示偏离均等色散的情况。
从“ SalePrice”和“ GrLivArea”开始…
#scatter plot
plt.scatter(df_train['GrLivArea'], df_train['SalePrice']);
之前这个散点图散点图(在对数转换之前)具有圆锥形状(上文“'SalePrice’和相关变量之间的散点图(moves like Jagger style)”)。 如您所见,当前散点图不再具有圆锥形状。 那就是正常的力量! 仅通过确保某些变量的正态性,我们就解决了均方差问题。
现在让我们来看看SalePrice和’TotalBsmtSF’之间的散点图
#scatter plot
plt.scatter(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], df_train[df_train['TotalBsmtSF']>0]['SalePrice']);
我们可以说, SalePrice在“ TotalBsmtSF”的整个范围内均表现出相同的方差水平。 太棒了!
9.Conclusion
很好!现在我们已经到了这场探索性分析的尾声了
在整个过程中中,我们将Hair等人提出的许多策略付诸实践。 (2013)。 我们对变量进行了一些感性的思考,仅对“ SalePrice”进行了分析,并且使用了相关性最高的变量,处理了缺失的数据和异常值,测试了一些基本的统计假设。 Python帮助我们简化了很多工作。
但是追求还没有结束。 请记住,我们的故事在Facebook研究中停止了。 现在该打电话给“ SalePrice”并邀请她共进晚餐。 尝试预测她的行为。 您认为她是一个喜欢正规线性回归方法的女孩吗? 或是她更喜欢集成方法? 也许还有其他东西?
这一切就交给你来继续探索了
写在最后
原文到这里就结束了,感谢原文作者Pedro Marcelino的分享
kaggle作者主页 https://www.kaggle.com/pmarcelino
这篇文章讲述了一个数据科学家是如何看待EDA的,包含的技术性内容很少,但带来的启发是最难能可贵的,包括对自变量与因变量关系的比喻,对散点图的大胆的想象,都很精彩。虽然译者还没接触到实际工作,但可以预想在全民机器学习的环境下,现在的实际工作场景中不缺构建模型,调参的工程师,但是懂数据、能将业务与数据还有模型结合的人才还是比较可贵的。包括之后的特征工程,模型训练和优化,都和对数据的掌握有着密切相关,因此我相信在现在学习机器学习的大浪潮下,能够沉淀下来专心做好这些看起来没有立竿见影效果的工作,还是有长久性意义的,与其看着人们一拥而上干着急,不如静下来好好和数据对话一下。