数据可视化Kaggle大数据,机器学习,人工智能

精通数据探索-第一课

2019-06-25  本文已影响7人  readilen

数据探索是进行数据分析和建模的关键步骤,笔者推荐一本书


image.png

.目前大数据已经成为热词,但是如果真的获取了很多数据,是不是一头雾水,有一种淹死在数据海洋里面的感觉,本系列精通数据挖掘,将介绍一系列在数据游泳的方法,让你畅游在数据的海洋里,体会大数据的乐趣.书归正传,本文将介绍如下内容:

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
df_train = pd.read_csv(r'/home/webdev/下载/train.csv')
#check the decoration
df_train.columns
Index(['Id', 'MSSubClass', 'MSZoning', 'LotFrontage', 'LotArea', 'Street',
       'Alley', 'LotShape', 'LandContour', 'Utilities', 'LotConfig',
       'LandSlope', 'Neighborhood', 'Condition1', 'Condition2', 'BldgType',
       'HouseStyle', 'OverallQual', 'OverallCond', 'YearBuilt', 'YearRemodAdd',
       'RoofStyle', 'RoofMatl', 'Exterior1st', 'Exterior2nd', 'MasVnrType',
       'MasVnrArea', 'ExterQual', 'ExterCond', 'Foundation', 'BsmtQual',
       'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinSF1',
       'BsmtFinType2', 'BsmtFinSF2', 'BsmtUnfSF', 'TotalBsmtSF', 'Heating',
       'HeatingQC', 'CentralAir', 'Electrical', '1stFlrSF', '2ndFlrSF',
       'LowQualFinSF', 'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath',
       'HalfBath', 'BedroomAbvGr', 'KitchenAbvGr', 'KitchenQual',
       'TotRmsAbvGrd', 'Functional', 'Fireplaces', 'FireplaceQu', 'GarageType',
       'GarageYrBlt', 'GarageFinish', 'GarageCars', 'GarageArea', 'GarageQual',
       'GarageCond', 'PavedDrive', 'WoodDeckSF', 'OpenPorchSF',
       'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'PoolQC',
       'Fence', 'MiscFeature', 'MiscVal', 'MoSold', 'YrSold', 'SaleType',
       'SaleCondition', 'SalePrice'],
      dtype='object')

1 这个是我要的吗?

为了理解我们的数据,我们可以查看每个变量并尝试理解它们的含义以及与此问题的相关性。我知道这很费时间,但它会给我们带来数据集的味道。
为了寻找规律,我们可以创建一个包含以下列的电子表格:

虽然“Type”和“Segment”仅供将来参考,但“Expectation”一栏很重要,因为它将帮助我们发展“第六感”。 要填写此专栏,我们应该阅读所有变量的描述,并不断的问自己以下几个问题:

通过上面的练习,我们针对所有变量进行了初期评估,接下来可以开始衡量以下变量可以在这个问题中发挥重要作用:

这个练习的主要目的是想一想我们的数据和期望,所以我认为我们实现了目标。 现在是时候“少谈一点,请多一点动作”。

2 首先要做的事情是:分析'SalePrice'

'SalePrice'首先要分析的。 就像我们去参加派对一样,我们是去看美女。来举一个形象一点例子,这是一个kaggle舞会,我们搜索了一遍舞会后,发现一个穿着舞鞋的美女,这个美女是否能答应和我一起跳舞呢?我的去问一下才知道,先算一下我们的相关性,初步分析一下可能性会更大.

df_train#descriptive statistics summary
df_train['SalePrice'].describe()

count      1460.000000
mean     180921.195890
std       79442.502883
min       34900.000000
25%      129975.000000
50%      163000.000000
75%      214000.000000
max      755000.000000
Name: SalePrice, dtype: float64

'很好......似乎你的最偏亮。 优秀! 你有其他的照片吗?沙滩照健身房拍照都可以'*.美女,我知道一个特别棒的化妆,叫seaborn,如果你外出使用,非常优雅.

#histogram
sns.distplot(df_train['SalePrice']);
单变量分布

这很有意思! 'SalePrice',我的给这个美女偷偷测评一下,打一下分?

#skewness and kurtosis
print("Skewness: %f" % df_train['SalePrice'].skew())
print("Kurtosis: %f" % df_train['SalePrice'].kurt())
Skewness: 1.882876
Kurtosis: 6.536282

太好了! 如果我俩星象符合,我们的成功概率是97.834657%。 我们还会见面的! 如果你下周五有空,请保留我的电话号码给我打个电话。真希望下次再见到你.

'SalePrice',她的朋友和她的兴趣

选择你将要战斗的环境,这是军事智慧。 一旦'SalePrice'离开,我们就去了Facebook。。 请注意,这不是跟踪。 如果你知道我的意思,那只是对个人的深入研究。

根据她的个人资料,我们有一些共同的朋友。 除了查克诺里斯,我们都道'GrLivArea'和'TotalBsmtSF'。 此外,我们也有共同的兴趣,如'OverallQual'和'YearBuilt'。 看起来很有希望!*

为了充分利用我们的研究,我们将首先仔细研究我们共同朋友的概况,然后我们将关注我们的共同兴趣。

与数值变量的关系

#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));

sns.scatterplot(y='SalePrice', x='GrLivArea',data=df_train)
image.png

*嗯......似乎'SalePrice'和'GrLivArea'真的是老朋友,有线性关系 *
那么'TotalBsmtSF'呢?

#scatter plot grlivarea/saleprice
var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));

sns.scatterplot(y='SalePrice', x='TotalBsmtSF',data=df_train)
image.png

*'TotalBsmtSF'也是'SalePrice'的好朋友,但这似乎是一种更加情感化的关系! 一切都很好,突然间,在强烈的线性(指数?)反应中,一切都在变化。 此外,很明显,有时候“TotalBsmtSF”本身会自闭,并且不搭理“SalePrice”

与分类功能的关系

plt.subplots(figsize=(8, 6))
fig = sns.boxplot(x='OverallQual', y="SalePrice", data=df_train)
fig.axis(ymin=0, ymax=800000);
image.png

*像所有漂亮的女孩一样,'SalePrice'喜欢'OverallQual'。 自我注意:考虑麦当劳是否适合第一次约会。

fig = sns.boxplot(x='YearBuilt', y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000);
plt.xticks(rotation=90);
image.png

虽然这不是一个强烈的倾向,但我会说'SalePrice'更倾向于花更多钱买新东西而不是旧文物。*

注意:我们不知道'SalePrice'是否价格不变。 不变价格试图消除通胀的影响。 如果'SalePrice'价格不是固定的,那么它应该是多年来价格相当的。

综上所述

抛开故事,我们可以得出结论:

*'GrLivArea'和'TotalBsmtSF'似乎与'SalePrice'线性相关。 两种关系都是正的,这意味着当一个变量增加时,另一个变量增加。 在'TotalBsmtSF'的情况下,我们可以看到线性关系的斜率特别高。
*'OverallQual'和'YearBuilt'似乎也与'SalePrice'有关。 在'OverallQual'的情况下,这种关系似乎更强,其中箱形图显示销售价格如何随着整体质量而增加。

我们刚刚分析了四个变量,但还有许多其他我们应该分析的变量。 这里的诀窍似乎是选择正确的特征(特征选择)而不是它们之间复杂关系的定义(特征工程)。

那就是说,让我们将小麦与谷壳分开。

3 保持冷静,聪明地工作

到现在为止,我们只是按照我们的直觉来分析我们认为重要的变量。 尽管我们努力为我们的分析提供客观的特征,但我们必须说我们的出发点是主观的。

作为一名工程师,应该使用更精确的方法,所以,让我们克服惯性,做一个更客观的分析。

'等离子汤'

“一开始除了等离子汤外什么都没有。 在我们对宇宙学的研究开始时,对这些短暂时刻的了解在很大程度上是推测的。 然而,科学已经根据今天对宇宙的了解,设计了一些可能发生的概述。--大爆炸

为了探索宇宙,我们将从一些实用的食谱开始,以了解我们的'等离子汤':
*相关矩阵(热图样式)。
*'SalePrice'相关矩阵(缩放热图样式)。
*最相关变量之间的散点图。

相关矩阵(热图样式)

#correlation matrix
corrmat = df_train.corr()
f, ax = plt.subplots(figsize=(12, 9))
sns.heatmap(corrmat, vmax=.8, square=True);
image.png

在我看来,这个热图是快速了解我们的'血浆汤'及其关系的最佳方式。 (谢谢@seaborn!)

乍一看,有两个红色方块引起了我的注意。第一个引用'TotalBsmtSF'和'1stFlrSF'变量,第二个引用'Garage X'变量。两种情况都表明这些变量之间的相关性有多大。实际上,这种相关性非常强,可以表明多重共线性的情况。如果我们考虑这些变量,我们可以得出结论,它们给出了几乎相同的信息,因此实际上发生了多重共线性。热图非常适合检测这种情况,并且在功能选择主导的问题中,它们是必不可少的工具。

引起我注意的另一件事是'SalePrice'相关性。我们可以看到我们众所周知的'GrLivArea','TotalBsmtSF'和'OverallQual'说一个大'嗨!',但我们也可以看到许多其他应该考虑的变量。这就是我们接下来要做的事情。

'SalePrice'相关矩阵(缩放热图样式)

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()
image.png

根据我们的放大分析,这些是与'SalePrice'最相关的变量。我们有如下结论:
*'OverallQual','GrLivArea'和'TotalBsmtSF'与'SalePrice'密切相关。已经验证!
*'GarageCars'和'GarageArea'也是一些最强相关的变量。然而,正如我们在上一个子点中所讨论的那样,车库中的车辆数量是车库区域的结果。 'GarageCars'和'GarageArea'就像孪生兄弟。你永远无法区分它们。因此,我们在分析中只需要其中一个变量(我们可以保留'GarageCars',因为它与'SalePrice'的相关性更高)。
*'TotalBsmtSF'和'1stFloor'似乎也是孪生兄弟。我们可以保持'TotalBsmtSF'只是说我们的第一个猜测是正确的(重新阅读'那么......我们能期待什么?')。
*'FullBath'??真?
*'TotRmsAbvGrd'和'GrLivArea',孪生兄弟。这个数据集来自切尔诺贝利吗?
*啊......'YearBuilt'......似乎'YearBuilt'与'SalePrice'略有关联。老实说,让我想起'YearBuilt'让我感到害怕,因为我开始觉得我们应该做一些时间序列分析才能做到这一点。

我们来看散点图。

'SalePrice'和相关变量之间的散点图

为即将看到的内容做好准备。 我必须承认,我第一次看到这些散点图时,被惊呆了! 这么小的空间里有如此多的信息......这真是太神奇了。 再一次,谢谢@seaborn!

#scatterplot
sns.set()
cols = ['SalePrice', 'OverallQual', 'GrLivArea', 'GarageCars', 'TotalBsmtSF', 'FullBath', 'YearBuilt']
sns.pairplot(df_train[cols], size = 2.5)
plt.show();
image.png

虽然我们已经知道了一些主要数据,但这个巨大的散点图给出了关于变量关系的合理概念。

我们可能感兴趣的一个数字是'TotalBsmtSF'和'GrLiveArea'之间的数字。在这个图中,我们可以看到绘制一条直线的点,这几乎就是一个边界。大多数点都低于该线,这是完全有道理的。地下室区域可以与地上生活区域相等,但预计地下室区域不会超过地上生活区域(除非您尝试购买地堡)。

关于'SalePrice'和'YearBuilt'的情节也可以让我们思考。在“点云”的底部,我们看到几乎看起来像一个害羞的指数函数(具有创造性)。我们也可以在“点云”的上限看到同样的趋势(更具创造性)。此外,请注意关于过去几年的点集如何保持高于此限制(我只是想说价格现在增长得更快)。

好的,现在Rorschach测试已经足够了。让我们继续前进到缺少的东西:缺少数据!

4 缺失数据

在考虑缺少数据时的重要问题:
*缺失数据有多普遍?
*随机丢失数据还是有模式?
出于实际原因,这些问题的答案很重要,因为缺少数据可能意味着样本量的减少。 这可以阻止我们继续进行分析。 此外,从实质的角度来看,我们需要确保缺失的数据流程没有偏见并隐藏一个不方便的事实。

#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)
    Total   Percent
PoolQC  1453    0.995205
MiscFeature 1406    0.963014
Alley   1369    0.937671
Fence   1179    0.807534
FireplaceQu 690 0.472603
LotFrontage 259 0.177397
GarageCond  81  0.055479
GarageType  81  0.055479
GarageYrBlt 81  0.055479
GarageFinish    81  0.055479
GarageQual  81  0.055479
BsmtExposure    38  0.026027
BsmtFinType2    38  0.026027
BsmtFinType1    37  0.025342
BsmtCond    37  0.025342
BsmtQual    37  0.025342
MasVnrArea  8   0.005479
MasVnrType  8   0.005479
Electrical  1   0.000685
Utilities   0   0.000000

让我们分析一下,了解如何处理丢失的数据。

我们会考虑当超过15%的数据丢失时,我们应该删除相应的变量并假装它从未存在过。这意味着在这些情况下我们不会尝试任何技巧来填充缺失的数据。据此,我们应该删除一组变量(例如'PoolQC','MiscFeature','Alley'等)。关键是:我们会错过这些数据吗?我不这么认为。这些变量似乎都不是非常重要,因为它们中的大多数都不是我们在购买房屋时考虑的方面(也许这就是数据缺失的原因?)。此外,仔细观察变量,我们可以说像'PoolQC','MiscFeature'和'FireplaceQu'这样的变量是异常值,所以我们很乐意删除它们。

在剩下的情况下,我们可以看到'Garage * X *'变量具有相同数量的缺失数据。我打赌缺少的数据是指同一组观察结果(虽然我不会检查它;它只是5%而我们不应该在5美元的问题上花费20美元)。由于关于车库的最重要的信息是由'GarageCars'表示的,并且考虑到我们只讨论了5%的缺失数据,我将删除提到的'Garage * X *'变量。相同的逻辑适用于'Bsmt * X *'变量。

关于'MasVnrArea'和'MasVnrType',我们可以认为这些变量不是必需的。此外,它们与已经考虑过的'YearBuilt'和'OverallQual'有很强的相关性。因此,如果我们删除'MasVnrArea'和'MasVnrType',我们不会丢失信息。

最后,我们在'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...

#异常值!
异常值也是我们应该注意的事情。 为什么? 因为异常值可以显着影响我们的模型,并且可以成为有价值的信息来源,为我们提供有关特定行为的见解。

异常值是一个复杂的主题,值得更多关注。 在这里,我们将通过'SalePrice'的标准偏差和一组散点图进行快速分析。

单变量分析

这里主要关注的是建立一个阈值,将观察定义为异常值。 为此,我们将标准化数据。 在这种情况下,数据标准化意味着将数据值转换为平均值为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”如何看待她的新衣服:

目前,我们不会将这些值中的任何一个视为异常值,但我们应该小心这两个值。

双变量分析

我们已经了解了散点图。 然而,当我们从新的角度看待事物时,总会有新发现。 正如艾伦凯所说,'改变观点值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));
image.png

揭晓的内容:
*具有更大“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)
#bivariate analysis saleprice/grlivarea
var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
image.png

我们可以尝试消除一些观察结果(例如TotalBsmtSF> 3000),但我认为这不值得。 我们可以忍受,所以我们什么都不做。

#5 获得核心
我们已经做了一些数据清理并发现了很多关于'SalePrice'的信息。 现在是时候深入了解'SalePrice'如何符合统计假设,使我们能够应用多变量技术。

根据[Hair et al (2013)](https://amzn.to/2uC3j9p),应测试四个假设:

寻找正态

这里的重点是以非常精益的方式测试'SalePrice'。 我们这样做会注意:

#histogram and normal probability plot
sns.distplot(df_train['SalePrice'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)
image.png
image.png

好的,'SalePrice'不正常。 它显示“峰值”,正偏斜并且不遵循对角线。

但一切都没有丢失。 简单的数据转换可以解决问题。 这是你可以在统计书中学到的很棒的东西之一:如果是正偏斜,日志转换通常很有效。 当我发现这一点时,我感觉自己就像霍格沃茨的学生发现了一个新的酷咒。

#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)
image.png
image.png

完成! 让我们来看看'GrLivArea'发生了什么。

#histogram and normal probability plot
sns.distplot(df_train['GrLivArea'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)
image.png
image.png

有点偏斜......

#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)
image.png
image.png

漂亮!!

#histogram and normal probability plot
sns.distplot(df_train['TotalBsmtSF'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['TotalBsmtSF'], plot=plt)
image.png
image.png

好的,现在我们正在与大老板打交道。 我们有什么在这里?

*通常会出现偏斜的东西。
*大量观测值为零(没有地下室的房屋)。
*一个很大的问题,因为零值不允许我们进行log转换。

要在此处应用日志转换,我们将创建一个可以获得具有或不具有基础(二进制变量)的效果的变量。 然后,我们将对所有非零观测值进行对数转换,忽略值为零的观测值。 这样我们就可以转换数据,而不会失去有或没有地下室的影响。

我不确定这种方法是否正确。 这对我来说似乎是对的。 这就是我所谓的“高风险工程”。

#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)
image.png
image.png

开始常识寻找“同分布”

测试两个度量变量的同方差性的最佳方法是图形。 通过诸如锥体(在图的一侧的小色散,在相对侧的大的色散)或菱形(在分布的中心处的大量点)的形状示出了相等色散的偏离。

从'SalePrice'和'GrLivArea'开始......

#scatter plot
plt.scatter(df_train['GrLivArea'], df_train['SalePrice']);
image.png

此散点图的旧版本(在log转换之前)具有圆锥形状(返回并检查'SalePrice'和相关变量之间的散点图(像Jagger样式一样移动)')。 如您所见,当前的散点图不再具有圆锥形状。 这是正常的力量! 只是通过确保某些变量的正态性,我们解决了同方差性问题。

现在让我们用'TotalBsmtSF'检查'SalePrice'。

#scatter plot
plt.scatter(df_train[df_train['TotalBsmtSF']>0]['TotalBsmtSF'], df_train[df_train['TotalBsmtSF']>0]['SalePrice']);
image.png

我们可以说,一般来说,'SalePrice'在'TotalBsmtSF'范围内表现出相同的差异水平。 cool!
#最后但并非最不重要的虚拟变量

结论

在整个kernel中,我们实践了[Hair等人提出的许多策略。(2013)](https://amzn.to/2uC3j9p)。 我们对变量进行了深入思考,我们单独分析了“SalePrice”,并且使用最相关的变量,我们处理了缺失数据和异常值,我们测试了一些基本的统计假设,我们甚至将分类变量转换为虚拟变量。 这是Python帮助我们轻松完成的大量工作。

但是这个任务还没有结束。 请记住,我们的故事在Facebook研究中停止了。 现在是时候打电话给'SalePrice'并邀请她吃饭。 试着预测她的行为。 你认为她是一个喜欢正则化线性回归方法的女孩吗? 或者你认为她更喜欢合奏方法? 或者别的什么?

上一篇下一篇

猜你喜欢

热点阅读