【回顾&引言】前面一章的内容大家可以感觉到我们主要是对基础知识做一个梳理,让大家了解数据分析的一些操作,主要做了数据的各个角度的观察。那么在这里,我们主要是做数据分析的流程性学习,主要是包括了数据清洗以及数据的特征处理,数据重构以及数据可视化。这些内容是为数据分析最后的建模和模型评价做一个铺垫。
开始之前,导入numpy、pandas包和数据
import numpy as np
import pandas as pd
data=pd.read_csv('train.csv')
data.head(20)
2 第二章:数据清洗及特征处理
我们拿到的数据通常是不干净的,所谓的不干净,就是数据中有缺失值,有一些异常点等,需要经过一定的处理才能继续做后面的分析或建模,所以拿到数据的第一步是进行数据清洗,本章我们将学习缺失值、重复值、字符串和数据转换等操作,将数据清洗成可以分析或建模的样子。
2.1 缺失值观察与处理
我们拿到的数据经常会有很多缺失值,比如我们可以看到Cabin列存在NaN,那其他列还有没有缺失值,这些缺失值要怎么处理呢
2.1.1 任务一:缺失值观察
- 请查看每个特征缺失值个数
- 请查看Age, Cabin, Embarked列的数据
#写入代码
data.isnull().sum()
# temp = data.isnull().any() #列中是否存在空值
# print(temp)
#写入代码
data.info()
#写入代码
data[['Age','Cabin','Embarked']]
2.1.2 任务二:对缺失值进行处理
(1)处理缺失值一般有几种思路
(2) 请尝试对Age列的数据的缺失值进行处理
(3) 请尝试使用不同的方法直接对整张表的缺失值进行处理
#写入代码
# data[data['Age']==None]=0 #不可行
data[data['Age'].isnull()] = 0 # 可行
# data[ddataf['Age'] == np.nan] = 0 #不可行
# data[data['Age'].isna()] = 0 #可行
data.head(20)
# data.isnull().sum()
#写入代码
data1=data.fillna(0)
data1.isnull().sum()
#写入代码
# 使用均值填充,计算均值时,分母默认不会加上NaN的值
'''
pandas (sum、mean、max、min、idxmax、idxmin、cumsum、mad、std、var、diff、pct_change、corr)这些函数会默认排除NaN值
通过指定 skipna=False ,可以使其包含 Nan
'''
# for column in list(data.columns[data.isnull().sum() > 0]):
# print(column)
# mean_val = data[column].mean()
# data[column].fillna(mean_val, inplace=True)
data1=data.fillna(data.mean())
data1.isnull().sum()
**【思考1】dropna和fillna有哪些参数,分别如何使用呢? **
处理缺失值的一般思路: 提醒:可使用的函数有—>dropna函数与fillna函数
在遇到某列数据(标签或指标等)大量丢失时,我们常用的方法,是将该含有过多空值的行/列删除,通常这也是一种行之有效的做法。而一般在pandas里,我们用dropna函数实现这一操作。
参考资料:https://zhuanlan.zhihu.com/p/109366433
具体函数:
DataFrame.dropna(axis=0, how=‘any’, thresh=None, subset=None, inplace=False)
函数作用:
删除含有空值的行或列
函数具体参数:
- axis:维度,axis=0表示index行,axis=1表示columns列,默认为0
- how:"all"表示这一行或列中的元素全部缺失(为nan)才删除这一行或列,"any"表示这一行或列中只要有元素缺失,就删除这一行或列
- thresh: axis中至少有thresh个非缺失值,否则删除。
- subset:在某些列的子集中选择出现了缺失值的列删除,不在子集中的含有缺失值得列或行不会删除(有axis决定是行还是列)
- inplace:刷选过缺失值得新数据是存为副本还是直接在原数据上进行修改。默认是False,即创建新的对象进行修改,原对象不变,和深复制和浅复制有些类似。
也可以使用fillna()函数填充缺失值。
参考资料:https://zhuanlan.zhihu.com/p/109434512
具体函数:
DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
函数作用:填充缺失值
函数具体参数:
- value:需要用什么值去填充缺失值
- axis:确定填充维度,从行开始或是从列开始
- method:填充缺失值所用的方法。
- limit:确定填充的个数,如果limit=2,则只填充两个缺失值。
【思考】检索空缺值用np.nan ,None 以及.isnull() 哪个更好,这是为什么?如果其中某个方式无法找到缺失值,原因又是为什么?
# np.nan()的一些奇妙性质:
np.nan == np.nan
>>> False
np.isnan(np.nan)
>>> True
np.nan is None
>>> False
type(np.nan)
>>> float
总结一下:
- np.nan不是一个“空”对象,用 i is None判断是False;
- 对某个值是否为空值进行判断,只能用np.isnan(i)函数,万万不可用 i == np.nan()来做,因为空值并不能用判断相等的“==”正确识别(上例前两条);
- np.nan非空对象,其类型为基本数据类型float
由上可见,其实np.isnan()和pd.isnull()都可以对不论是DataFrame、Python list还是仅仅一个数值进行空值检测。但一般在实际应用中,np.isnan()多用于单个值的检验,pd.isnull()用于对一个DataFrame或Series(整体)的检验。
# 最终处理
# data.fillna(0,inplace=True)
# data.dropna(inplace=True)
# data.head(20)
# data.isnull().sum()
【参考】https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.dropna.html
【参考】https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.fillna.html
2.2 重复值观察与处理
由于这样那样的原因,数据中会不会存在重复值呢,如果存在要怎样处理呢
2.2.1 任务一:请查看数据中的重复值
data[data.duplicated()]
2.2.2 任务二:对重复值进行处理
(1)重复值有哪些处理方式呢?
(2)处理我们数据的重复值
data.drop_duplicates(inplace=True)
data.count()
2.2.3 任务三:将前面清洗的数据保存为csv格式
#写入代码
data.to_csv('clear_train.csv')
2.3 特征观察与处理
我们对特征进行一下观察,可以把特征大概分为两大类:
数值型特征:Survived ,Pclass, Age ,SibSp, Parch, Fare,其中Survived, Pclass为离散型数值特征,Age,SibSp, Parch, Fare为连续型数值特征
文本型特征:Name, Sex, Cabin,Embarked, Ticket,其中Sex, Cabin, Embarked, Ticket为类别型文本特征,数值型特征一般可以直接用于模型的训练,但有时候为了模型的稳定性及鲁棒性会对连续变量进行离散化。文本型特征往往需要转换成数值型特征才能用于建模分析。
2.3.1 任务一:对年龄进行分箱(离散化)处理
(1) 分箱操作是什么?
(2) 将连续变量Age平均分箱成5个年龄段,并分别用类别变量12345表示
(3) 将连续变量Age划分为[0,5) [5,15) [15,30) [30,50) [50,80)五个年龄段,并分别用类别变量12345表示
(4) 将连续变量Age按10% 30% 50% 70% 90%五个年龄段,并用分类变量12345表示
(5) 将上面的获得的数据分别进行保存,保存为csv格式
分箱操作是什么: 分箱操作就是将连续数据转换为分类对应物的过程。?如将连续的身?数据划分为:矮中?。 举个常见的例子: 最常见的就是对年龄进行分箱操作。假设人的年龄从0-120岁不等,我们将0-5认为是婴幼儿,6-15岁认为是少年,16-30岁认为是青年,31-50认为是中年,50-60认为是中老年,60岁以上认为是老年。在这个过程中,就将连续的年龄分为了婴幼儿、少年、青年、中年、中老年、老年这六个类别,或者说分成了六个“箱子”,每个"箱子"代表的就是一个类别。
分箱操作分为等距分箱和等频分箱。 pandas.cut和pandas.qcut,pandas.cut根据指定分界点对连续数据进行分箱处理,pandas.qcut可以指定箱子的数量对连续数据进行等宽分箱处理(注意:所谓等宽指的是每个箱子中的数据量是相同的)
原型 pandas.cut(x, bins, right=True, labels=None, retbins=False, precision=3, include_lowest=False, duplicates=‘raise’)
参数含义
- x:被切分的类数组(array-like)数据,必须是1维的(不能用DataFrame);
- bins:bins是被切割后的区间(或者叫“桶”、“箱”、“面元”),有3中形式:一个int型的标量、标量序列(数组)或者pandas.IntervalIndex 。一个int型的标量,当bins为一个int型的标量时,代表将x平分成bins份。x的范围在每侧扩展0.1%,以包括x的最大值和最小值。标量序列,标量序列定义了被分割后每一个bin的区间边缘,此时x没有扩展。pandas.IntervalIndex,定义要使用的精确区间。
- right:bool型参数,默认为True,表示是否包含区间右部。比如如果bins=[1,2,3],right=True,则区间为(1,2],(2,3];right=False,则区间为(1,2),(2,3)。
- labels:给分割后的bins打标签,比如把年龄x分割成年龄段bins后,可以给年龄段打上诸如青年、中年的标签。labels的长度必须和划分后的区间长度相等,比如bins=[1,2,3],划分后有2个区间(1,2],(2,3],则labels的长度必须为2。如果指定labels=False,则返回x中的数据在第几个bin中(从0开始)。
- retbins:bool型的参数,表示是否将分割后的bins返回,当bins为一个int型的标量时比较有用,这样可以得到划分后的区间,默认为False。
- precision:保留区间小数点的位数,默认为3.
- include_lowest:bool型的参数,表示区间的左边是开还是闭的,默认为false,也就是不包含区间左部(闭)。
- duplicates:是否允许重复区间。有两种选择:raise:不允许,drop:允许。
返回值
- out:一个pandas.Categorical, Series或者ndarray类型的值,代表分区后x中的每个值在哪个bin(区间)中,如果指定了labels,则返回对应的label。
- bins:分隔后的区间,当指定retbins为True时返回
#写入代码
# 将连续变量Age平均分箱成5个年龄段,并分别用类别变量12345表示
data['AgeBand']=pd.cut(data['Age'],5,labels=[1,2,3,4,5])
data.head(20)
# data.to_csv('train_ave.csv')
#写入代码
# 将连续变量Age划分为[0,5) [5,15) [15,30) [30,50) [50,80)五个年龄段,并分别用类别变量12345表示
data['AgeBand']=pd.cut(data['Age'],[0,5,15,30,50,80],labels=[1,2,3,4,5])
data.head(20)
# data.to_csv('train_cut.csv')
#写入代码
#将连续变量Age按10% 30% 50% 70% 90%五个年龄段,并用分类变量12345表示
data['AgeBand']=pd.qcut(data['Age'],[0,0.1,0.3,0.5,0.7,0.9],labels=[1,2,3,4,5])
data.head(20)
# data.to_csv('train_pre.csv')
【参考】https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.cut.html
【参考】https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.qcut.html
2.3.2 任务二:对文本变量进行转换
(1) 查看文本变量名及种类 (2) 将文本变量Sex, Cabin ,Embarked用数值变量12345表示 (3) 将文本变量Sex, Cabin, Embarked用one-hot编码表示
# 写入代码
# 查看文本变量名及种类
# 方法1
# print(data['Sex'].value_counts())
# 方法2
print(data['Sex'].unique())
print(data['Sex'].nunique()) #有几种
# data['Cabin'].value_counts()
# data['Embarked'].value_counts()
#写入代码
# 将文本变量Sex, Cabin ,Embarked用数值变量12345表示
#将类别文本转换为12345
#方法一: replace
data['Sex_num'] = data['Sex'].replace(['male','female'],[1,2])
data['Embarked_num']=data['Embarked'].replace(['S','C','Q'],[1,2,3])
# #方法二: map
# data['Sex_num'] = data['Sex'].map({'male': 1, 'female': 2})
# #方法三: 使用sklearn.preprocessing的LabelEncoder
# from sklearn.preprocessing import LabelEncoder
# for feat in ['Cabin', 'Ticket']:
# lbl = LabelEncoder()
# label_dict = dict(zip(data[feat].unique(), range(data[feat].nunique())))
# data[feat + "_labelEncode"] = data[feat].map(label_dict)
# data[feat + "_labelEncode"] = lbl.fit_transform(data[feat].astype(str))
data.head()
为了解决上述问题,其中一种可能的解决方法是采用独热编码(One-Hot Encoding)。独热编码即 One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。
例如:
自然状态码为:000,001,010,011,100,101
独热编码为:000001,000010,000100,001000,010000,100000
可以这样理解,对于每一个特征,如果它有m个可能值,那么经过独热编码后,就变成了m个二元特征(如成绩这个特征有好,中,差变成one-hot就是100, 010, 001)。并且,这些特征互斥,每次只有一个激活。因此,数据会变成稀疏的。
这样做的好处主要有:
- 解决了分类器不好处理属性数据的问题
- 在一定程度上也起到了扩充特征的作用
独热编码优缺点
- 优点:独热编码解决了分类器不好处理属性数据的问题,在一定程度上也起到了扩充特征的作用。它的值只有0和1,不同的类型存储在垂直的空间。
- 缺点:当类别的数量很多时,特征空间会变得非常大。在这种情况下,一般可以用PCA来减少维度。而且one hot encoding+PCA这种组合在实际中也非常有用。
什么情况下(不)用独热编码?
- 用:独热编码用来解决类别型数据的离散值问题,
- 不用:将离散型特征进行one-hot编码的作用,是为了让距离计算更合理,但如果特征是离散的,并且不用one-hot编码就可以很合理的计算出距离,那么就没必要进行one-hot编码。 有些基于树的算法在处理变量时,并不是基于向量空间度量,数值只是个类别符号,即没有偏序关系,所以不用进行独热编码。 Tree Model不太需要one-hot编码: 对于决策树来说,one-hot的本质是增加树的深度。
总的来说,要是one hot encoding的类别数目不太多,建议优先考虑。
#写入代码
# 将文本变量Sex, Cabin, Embarked用one-hot编码表示
#将类别文本转换为one-hot编码
'''
pandas.get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, columns=None, sparse=False, drop_first=False)
该方法可以讲类别变量转换成新增的虚拟变量/指示变量。
常用参数
data : array-like, Series, or DataFrame. 输入的数据
prefix : string, list of strings, or dict of strings, default None. get_dummies转换后,列名的前缀
*columns : list-like, default None .指定需要实现类别转换的列名
dummy_na : bool, default False .增加一列表示空缺值,如果False就忽略空缺值
drop_first : bool, default False .获得k中的k-1个类别值,去除第一个
'''
#方法一: OneHotEncoder
for feat in ["Age", "Embarked"]:
# 先根据某一列的数据生成一个独热编码的DataFrame
x = pd.get_dummies(data[feat], prefix=feat)
# 使用concat函数将原来的data和x按column方向合并(横向拼接)
data = pd.concat([data, x], axis=1)
data.head()
2.3.3 任务三:从纯文本Name特征里提取出Titles的特征(所谓的Titles就是Mr,Miss,Mrs等)
建议看一下正则表达式
#写入代码
data['Title'] = data.Name.str.extract('([A-Za-z]+)\.', expand=False)
data.head()
#保存最终你完成的已经清理好的数据
data.to_csv('train_fin.csv')
|