一、EDA概述

EDA(Exploratory Data Analysis),全名为数据探索性分析,是通过了解数据集,了解变量间的相互关系以及变量与预测值之间的关系,从而帮助我们后期更好地进行特征工程和建立模型,是数据分析,数据挖掘、机器学习中十分重要的一步。

在这个大数据和物联网时代,我们获取数据的途径非常丰富,各种仪器的测量值、事件、文本、图像和视频等都属于可获取的数据来源,整个物联网无时无刻不在涌出大量的信息流。如何将这些大量的原始数据转化为可操作的信息,这才是当今数据科学所面对的主要挑战。首先,就需要将非结构化的原始数据结构化,或是处于研究目的采集有效数据集。

结构化数据有两种基本类型:数值型数据(numeric data)和分类数据(categorical data)。其中,数值型数据还分为连续型和离散型两种形式,连续型数据又称区间数据和浮点型数据,即表示该数据可在一个区间内取任何值;离散型数据通常只能取整数,例如计数,所以一般又称计数型数据。分类数据(因子数据)只能从特定集合中取值,这些值表示这种数据一系列可能的分类,例如:计算机编程语言主要包括汇编语言、机器语言以及高级语言三种(类别);中国的直辖市有北京市,上海市,天津市和重庆市。二元数据是一种特殊的分类数据,数据值只能从两个之中取其一(例如0或1,True或False),也就是一般所称的布尔型数据和逻辑性数据。有序数据(有序因子 数据)是具有明确排序的分类数据,例如数值排序(1,2,3,4或5)。

首先来看看一个常规的EDA都需要做哪些事情:

一是对我们要分析的数据进行一个整体的了解即数据的整体概览,这一环节我们可以了解数据的特征、类型、量纲等基本信息,并对缺失值、异常值等进行处理。

二是查看数据的分布情况,尽量将数据的分布调整成利于机器学习的输入形式,对于偏斜分布、重尾分布进行规整,一般调整为正太分布。

三是对类别型数据进行处理,通常采用不同的编码方式对其进行编码,以便转化成数值型数据。

四是重点对数值型数据进一步进行探索分析,查看数据之间的相关性、独立性、分布情况等,以便选取更为有效的特征。

二、EDA主要内容

1、载入各种数据科学以及可视化库:

数据科学库 pandas、numpy、scipy;(数据分析三剑客)

可视化库 matplotlib、seabon;

#导入常用库 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 使用 jupyter notebook 自身显示图像 %matplotlib inline

2、载入数据:

从各种文件格式比如 CSV、JSON、SQL、Microsoft excel 导入数据到我们程序中。以便对各种数据进行运算操作,比如归并、再成形、选择,还有数据清洗和数据加工特征等行为。

过程:

首先确定数据来源是一次性的到数据,还是实时数据(一般从数据库中得到的数据常为实时数据实时获取)。

对于一次性获取到的数据,需要查看数据包目录结构记录下来。查看数据文件的格记录下来,如果是excel需要进一步查看工作表结构。

根据记录结果,写脚本批量文件读操作,利用pandas等工具读入数据进行进一步操作。

样例:

andas.read_csv 可以读取CSV(逗号分割)文件、文本类型的文件text、log类型到DataFrame test_data = pd.read_csv(path , sep=' ') train_data = pd.read_csv(patn , sep=' ')  pandas.read_excel 可以读取表格文件 pd.read_excel('文件名.xlsx',sheet_name='表名')  Pandas读取数据库 import pymysql import pandas as pd #连接数据库为test conn=pymysql.connect(host="127.0.1",user="root",passwd="1234",db="test") #查询的表为students sql="select * from students" data=pd.read_sql(sql,conn)  Pandas读取html文件 htl=pd.read_html('E:\test.html')

简略观察数据(head() shape);

3、数据总览:

对最终合并好的数据有一个前瞻性,大体性的认识。索引和列的数据类型和占用内存大小。对数值型数据生成描述性统计汇总,包括数据的计数和百分位数,分类型数据中每个类的数量,了解数据的大致分布。

通过describe()来熟悉数据的相关统计量

eda工程数据分析(机器学习EDA探索性数据分析)(1)

不加include 参数,默认只统计数值型的变量。

data.describe(include=['O']) #只统计非数值变量

eda工程数据分析(机器学习EDA探索性数据分析)(2)

通过info()来熟悉数据类型

通过info来了解数据每列的type,有助于了解是否存在除了nan以外的特殊符号异常(注意object类型)

获取每一列中数据为空的数量

df.isnull().sum()

eda工程数据分析(机器学习EDA探索性数据分析)(3)

统计某一列不同的取值的数量

Product_Category 为列名

df.Product_Category.value_counts()

返回值第一列为取值情况,第二列为该值的数量。

以下为一些常见的查看数据的操作以及相应的代码:

查看数据前5行:dataframe.head() dataframe头尾一起查看 data.head().append(data.tail()) 查看数据的信息,包括每个字段的名称、非空数量、字段的数据类型:data.info() 查看数据的统计概要(count/mean/std/min/25%/50%/75%max):data.describe() 查看dataframe的大小:dataframe.shape 查看某一列取值的唯一值,Age、City_category为列名: df.Age.unique() array(['0-17', '55 ', '26-35', '46-50', '51-55', '36-45', '18-25'], dtype=object) df.City_Category.unique() array(['A', 'C', 'B'], dtype=object) 对某列取值进行映射: #例如将Gender列原取值F映射为0,M映射为1 df['Gender']=df['Gender'].map({'F':0, 'M':1}) #将Age取值映射到1-7。 df['Age']=df['Age'].map({'0-17':1, '18-25':2, '26-35':3, '36-45':4, '46-50':5, '51-55':6, '55 ':7 }) 改变某一列的数据类型 df['Stay_In_Current_City_Years']=df['Stay_In_Current_City_Years'].astype(int) 使用某一列或者某几列为唯一标识,删除重复数据,保留唯一值: #inplace=True表示直接在原数据上进行操作,否则返回一个新DataFrame data.drop_duplicates(['Age'],'first',inplace=True) 类别特征和数字特征的筛选方法 numerical_features = Train_data.select_dtypes(include=[np.number]) categorical_features = Train_data.select_dtypes(include=[np.object]) 对某一列的内容进行字符替换 Train_data['notRepairedDamage'].replace('-', np.nan, inplace=True) 按列/数组排序 按某列排序:正序(倒序)df.groupby(['列名']).cumcount() 对该列或该行进行值排序:sort_values(by="列名/行名") 对数组进行升序排序,返回索引值。降序的话可以给a加负号。 numpy.argsort(a) 或者 a.argsort() 数据相加 a.sum(axis=1) :a为数组,sum(axis=1)表示每行的数相加,平时不加axis则默认为0,为0表示每列的数相加。 字典操作 sorted对字典或者列表的后面一个值排序 sorted(dic.items() , key=lambda x:x[1] , reverse=True ) sorted (dic.items(),key=operator.itemgetter(1) ,reverse=True) 字典的get函数: dic.get(key,0)相当于if ……else ,若key在字典dic中则返回dic[key]的值,若不在则返回0

4、判断数据缺失值和异常值情况

缺失值分析

完全随机缺失(missing completely at random,MCAR):指的是数据的缺失是完全随机的,不依赖于任何不完全变量或完全变量,不影响样本的无偏性。

完全随机缺失(missing completely at random,MCAR):指的是数据的缺失是完全随机的,不依赖于任何不完全变量或完全变量,不影响样本的无偏性。

非随机缺失(missing not at random,MNAR):指的是数据的缺失与不完全变量自身的取值有关。

对于随机缺失和非随机缺失,直接删除记录是不合适的,原因上面已经给出。随机缺失可以通过已知变量对缺失值进行估计,而非随机缺失的非随机性还没有很好的解决办法。

查看缺失情况

元素级别的判断,把对应的所有元素的位置都列出来,元素为空或者NA就显示True,否则就是False

dataframe.isnull()

列级别的判断,只要该列有为空或者NA的元素,就为True,否则False

dataframe.isnull().any()

将为空或者NA的列找出来:

missing = dataframe.columns[ dataframe.isnull().any() ].tolist()

将列中为空或者NA的个数统计出来:

dataframe [ missing ].isnull().sum()

缺失值比例:

len(data["feature"] [ pd.isnull(data["feature"]) ]) / len(data))

使用missingno模块可视化缺失值

import missingno as msno missing = data.isnull().sum() missing = missing[missing>0] missing.sort_values(inplace=True) missing.plot.bar() # 可视化看下缺省值 msno.matrix(Train_data.sample(250)) msno.bar(Train_data.sample(1000))

缺失值处理方式

# 直接删除含有缺失值的行/列: new_drop = dataframe.dropna ( axis=0,subset=["Age","Sex"] ) # 【在子集中有缺失值,按行删除】 new_drop = dataframe.dropna ( axis=1) # 【将dataframe中含有缺失值的所有列删除】 # 插补: a, 固定值插补 dataframe.loc [ dataframe [ column ] .isnull(),column ] = value # 将某一列column中缺失元素的值,用value值进行填充。 b, 均值插补 data.Age.fillna(data.Age.mean(),inplace=True) # 将age列缺失值填充均值。(偏正态分布,用均值填充,可以保持数据的均值) c, 中值插补 df['price'].fillna(df['price'].median()) # 偏长尾分布,使用中值填充,避免受异常值的影响。 d, 最近数据插补 dataframe ['age'].fillna(method='pad') # 使用前一个数值替代空值或者NA,就是NA前面最近的非空数值替换 dataframe ['age'].fillna(method='bfill',limit=1) # 使用后一个数值替代空值或者NA,limit=1就是限制如果几个连续的空值,只能最近的一个空值可以被填充。 e, 回归插补 from scipy.interpolate import interp1d # 线性关系插值 f, 拉格朗日插值 from scipy.interpolate import lagrange # 拉格朗日 【非线性插值】 # g, 牛顿插值法 # h, 分段插值 # i, K-means 通过K均值的聚类方法将所有样本进行聚类划分,然后再通过划分的种类的均值对各自类中的缺失值进行填 补。归其本质还是通过找相似来填补缺失值。缺失值填补的准确性就要看聚类结果的好坏了,而聚类结果的可变 性很大,通常与初始选择点有关,因此使用时要慎重。 # g, KNN填补空值 h, 时间类型 df.interpolate():对于时间序列的缺失,可以使用这种方法。

异常值

导致异常值的几种原因

人为原因(Artificial (Error) / Non-natural) 和自然原因(Natural ):

输入错误 Data Entry Errors: 可能人工输入时手误多打了一个0,使得客户的年收入从10万,变成了100万。

测量误差 Measurement Error: 这是最常见的异常值来源。比如有十个称重器,其中一个是坏的,那么用这个坏的称重器测出来的值就会明显异于其他正常的称重器。错误/坏的测量工具会导致异常值

实验误差Experimental Error: 例如:在一次有7名运动员参加的100米短跑中,有一位运动员没有集中精力完成“出发”,这导致他开始晚了。因此,这导致跑步者的跑步时间比其他跑步者多。他的总运行时间可能是一个例外。

有意离群值Intentional Outlier: 这通常在涉及敏感数据的自我报告方法中发现。例如:青少年通常会少报他们的饮酒量。其中只有一小部分会报告实际价值。在这里,实际值可能看起来像离群值,因为其余的孩子有意少报饮酒量。

处理错误 Data Processing Error: 当我们执行数据挖掘时,我们从多个来源提取数据。一些操作或提取错误可能会导致数据集中出现异常值。

抽样误差 Sampling error: 例如,我们要测量运动员的身高。我们错误地将一些篮球运动员包括在样本中。这种包含可能会导致数据集中出现异常值。

自然离群值Natural Outlier: 当离群值不是人为错误导致的,它就是自然离群值。例如: 我们在看人均收入时,富豪榜单上的人群收入水平远远高于普通人,这些数据没有任何错误,是自然离群点

异常值的影响

异常值检测

数据质量差的根源一方面来自于缺失值,一方面也来自于存在很多的异常数据值。异常值的存在会降低训练模型的鲁棒性,异常值分析和处理之后模型的拟合性会提高。

# 画数据的散点图。观察偏差过大的数据,是否为异常值; plt.scatter(x1,x2) # 画箱型图,箱型图识别异常值比较客观,因为它是根据3σ原则,如果数据服从正态分布,若超过平均值的3倍标准差的值被视为异常值。 Percentile = np.percentile(df['length'],[0,25,50,75,100]) IQR = Percentile[3] - Percentile[1] UpLimit = Percentile[3] ageIQR*1.5 DownLimit = Percentile[1]-ageIQR*1.5 Ql为下四分位数:表示全部观察值中有四分之一的数据取值比它小; Qu为上四分位数:表示全部观察值中有四分之一的数据取值比它大; IQR称为四分位数间距:是上四分位数Qu和下四分卫数Ql之差,之间包含了全部观察值的一半。 # seaborn画boxplot f,ax=plt.subplots(figsize=(10,8)) sns.boxplot(y='length',data=df,ax=ax) plt.show() # 基于模型预测 构建概率分布: 离群点在该分布下概率低就视为异常点 #基于近邻度的离群点检测: KNN #基于密度的离群点检测: 对象到k个最近邻的平均距离的倒数,如果该距离小,则密度高; DBSCAN:一个对象周围的密度等于该对象指定距离d内对象的个数 #基于聚类的方法来做异常点检测: K-means #专门的离群点检测: One class SVM和Isolation Forest

异常值处理方式

1.视为缺失值:修补(平均数、中位数等)

2.直接删除:是否要删除异常值可根据实际情况考虑。因为一些模型对异常值不很敏感,即使有异常值也不影响模型效果,但是一些模型比如逻辑回归LR对异常值很敏感,如果不进行处理,可能会出现过拟合等非常差的效果。

3.不处理:直接在具有异常值的数据集上进行数据挖掘

4. 平均值修正:可用前后两个观测值的平均值修正该异常值

5、特征分为类别特征和数字特征,并对类别特征查看unique分布

# 特征nunique分布 for cat_fea in categorical_features: print(cat_fea "的特征分布如下:") print("{}特征有{}个不同的值".format(cat_fea, Train_data[cat_fea].nunique())) print(Train_data[cat_fea].value_counts())

输出的结果:

name特征有99662个不同的值 model特征有248个不同的值 brand特征有40个不同的值 bodyType特征有8个不同的值 fuelType特征有7个不同的值 gearbox特征有2个不同的值 notRepairedDamage特征有2个不同的值 regionCode特征有7905个不同的值

如果 类别特征严重倾斜,一般不会对预测有什么帮助(类别分布严重偏斜), 要删除特征。

(以下部分将在后续章节详细讲解)

6、数字特征分析

相关性分析

查看几个特征的偏度和峰值

每个数字特征的分布可视化

数字特征相互之间的关系可视化

多变量互相回归关系可视化

7、类型特征分析

unique分布

类别特征箱形图可视化

类别特征的小提琴图可视化

类别特征的柱形图可视化类别

特征的每个类别频数可视化(count_plot)

8、了解预测值的分布

总体分布概况(无界约翰逊分布等)

查看skewness and kurtosis

查看预测值的具体频数

9、用pandas_profiling生成数据报告

用pandas_profiling生成一个较为全面的可视化和数据报告

import pandas_profiling pfr = pandas_profiling.ProfileReport(Train_data) pfr.to_file("./example.html")

,