用随机森林填补缺失值——以泰坦尼克号数据为案例,对比不同类型的填补缺失值对于模型拟合效果的影响
# 导入相关的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_val_score # 交叉验证
from sklearn.ensemble import RandomForestClassifier
path = r'E:\菜菜机器学习\data&code\01 决策树数据及代码\data.csv'
# 前面加上r表示转义字符,否则无法读取数据
data = pd.read_csv(path)
type(data) # 查看数据是数组还是表格,以此方便粗略查看数据
>>pandas.core.frame.DataFrame
data.info() # 初步发现,可以对年龄进行填补缺失值处理;一种方式为均值填补,一种方式为回归填补
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Name 891 non-null object
4 Sex 891 non-null object
5 Age 714 non-null float64
6 SibSp 891 non-null int64
7 Parch 891 non-null int64
8 Ticket 891 non-null object
9 Fare 891 non-null float64
10 Cabin 204 non-null object
11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
data.head() # 查看数据前几行的数据
# 数据预处理部分
# 首先删除无关变量和缺失值过多的变量
data.drop(labels = ['PassengerId','Name','Ticket', 'Cabin'], axis = 1, inplace = True)
首先运用均值对年龄这一行的缺失值进行填补,并用决策树分类,用交叉验证检查模型精度
# 对年龄这个特征变量进行填补
# 因为要进行对比,所以在填补过程中,创建出一个新的矩阵,避免影响到原矩阵
data_copy1 = data.copy()
data_copy1.info() # 查看数据信息
# 首先用均值填补年龄列;年龄适合用均值填补
data_copy1.loc[:,'Age'] = data_copy1.loc[:,'Age'].fillna(data_copy1.loc[:,'Age'].mean())
data_copy1.info() # 查看数据检查是否填补完成
data_copy1.dropna(how = 'any', axis = 0, inplace = True) # 对Embarked列的缺失值进行处理
data_copy1.info() # 查看数据检查是否处理完成
# 对object类型数据进行转化分类
data_copy1.loc[:,'Sex'].unique(),data_copy1.loc[:,'Embarked'].unique()
# 采用replace的方式进行变化,unique()能够去重处理
>>(array([1, 0], dtype=int64), array([1, 2, 3], dtype=int64))
sex_dic = {'male':1, 'female':0} # 建立replace的字典
embarked_dic = {'S':1,'C':2,'Q':3}
data_copy1.loc[:,'Sex'].replace(sex_dic, inplace = True) # 对object对象编码
data_copy1.loc[:,'Embarked'].replace(embarked_dic, inplace = True)
data_copy1.head() # 查看数据是否编码完成
# 数据预处理完成,划分数据集
target_bymean = data_copy1.loc[:, data_copy1.columns == 'Survived']
feature_bymean = data_copy1.loc[:, data_copy1.columns != 'Survived']
'''
交叉验证查看结果
'''
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(random_state = 0, max_depth = 5)
score = cross_val_score(clf,feature_bymean, target_bymean, cv = 10).mean()
score
>>0.8031920326864148
下面使用随机森林填补缺失值,然后再用分类树进行预测对比,看精确度是否比0.8389高
data_copy2 = data.copy() # 复制新的数据,避免影响到原数据
data_copy2.info()
# 运用随机森林填补缺失值时,需要将填补的那一列中,没有缺失值的和有缺失值的分别抽取出来
'''
遇到一个问题:对于Embarked这个特征,有两个缺失值应该如何处理?——做法上为:对原数据复制出一份新的出来,在新的表格数据中用0或者其他数值填补缺失值,再对目标缺失值进行预测,最后将预测得到的结果返回到原数据中
'''
# 本题中,Embarked类型的数据是一个定性类型的数据,可以用众数进行填补
from sklearn.impute import SimpleImputer # 导入填补的包
embarked_ = data_copy2.loc[:, 'Embarked'].values.reshape(-1,1) # 提取出embarked的值,形成数组,并升维
type(embarked_) # 是数组类型
embarked_[:10], embarked_mode.shape # 查看数据
'''
(array([['S'],
['C'],
['S'],
['S'],
['S'],
['Q'],
['S'],
['S'],
['S'],
['C']], dtype=object),
(891, 1))
'''
embarked_mode = SimpleImputer(strategy = 'most_frequent').fit_transform(embarked_) # 采用众数填补
'''
这儿经常有一个理解误区:进行填补的时候,是否需要将缺失值先提取出来,,等计算得到众数或者均值之后,再对缺失值进行填补
——其实是不用的,SimpleImputer能够对整体的数据进行处理
——在embarked_ 这个数据中,缺失值为:[nan],而其他的数据则为['C'],二者是有着区别的
'''
type(embarked_mode) # 查看数据类型
>>numpy.ndarray
data_copy2.loc[:, 'Embarked'] = embarked_mode # 将填补好以后的值赋值回数据
data_copy2.info() # 检查是否填补完成
sex_dic = {'male':1, 'female':0} # 建立replace时使用的编码字典
embarked_dic = {'S':1,'C':2,'Q':3}
data_copy2.loc[:, 'Sex'].replace(sex_dic, inplace = True)
data_copy2.loc[:, 'Embarked'].replace(embarked_dic, inplace = True)
# 相比于特征编码而言,这种编码方式的缺点体现在,效率比较低,只能一个一个变化,而且需要先创建字典,然后再replace
data_copy2.head()
data_copy2.loc[:, 'Age'].isnull() # 筛选缺失值的方法,可以作为条件,此时返回结果为bool型数据
cond_train = data_copy2.loc[:, 'Age'].notnull() # age列为非空值
cond_test = data_copy2.loc[:, 'Age'].isnull() # age列为空值
# 行筛选条件为:Age列非空,列筛选为:除Age列之外的特征
xtrain = data_copy2.loc[cond_train, data_copy2.columns != 'Age']
# 行筛选条件为:Age列非空,列筛选为:Age列
ytrain = data_copy2.loc[cond_train, data_copy2.columns == 'Age']
# 行筛选条件为:Age列空值,列筛选为:除Age列之外的特征
xtest = data_copy2.loc[cond_test, data_copy2.columns != 'Age']
type(xtrain),xtrain.shape # 714个数据,说明行筛选是正确的
>>(pandas.core.frame.DataFrame, (714, 7))
xtrain.head() # 没有age列
Survived Pclass Sex SibSp Parch Fare Embarked
0 0 3 1 1 0 7.2500 1
1 1 1 0 1 0 71.2833 2
2 1 3 0 0 0 7.9250 1
3 1 1 0 1 0 53.1000 1
4 0 3 1 0 0 8.0500 1
xtest.head()
Survived Pclass Sex SibSp Parch Fare Embarked
5 0 3 1 0 0 8.4583 3
17 1 2 1 0 0 13.0000 1
19 1 3 0 0 0 7.2250 2
26 0 3 1 0 0 7.2250 2
28 1 3 0 0 0 7.8792 3
type(ytrain), ytrain.shape # 检查数据是否有问题
>>(pandas.core.frame.DataFrame, (714, 1))
# 准备工作结束,运用随机森林填补缺失值
from sklearn.ensemble import RandomForestRegressor
rfc = RandomForestRegressor(random_state = 0, n_estimators = 50)
rfc = rfc.fit(xtrain, ytrain)
age_pre = rfc.predict(xtest) # 得到年龄的预测值
type(age_pre), age_pre.shape # 查看数据类型和 维度;177个数据是无误的
>>(numpy.ndarray, (177,))
# 重新赋值回原数据中进行填补
data_copy2.loc[cond_test, data_copy2.columns == 'Age'] = age_pre
data_copy2.info() # 检查数据是否填补完毕
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Survived 891 non-null int64
1 Pclass 891 non-null int64
2 Sex 891 non-null int64
3 Age 891 non-null float64
4 SibSp 891 non-null int64
5 Parch 891 non-null int64
6 Fare 891 non-null float64
7 Embarked 891 non-null int64
dtypes: float64(2), int64(6)
# 用决策树进行分类预测,查看拟合效果如何 用交叉验证检验结果
feature_rfc = data_copy2.iloc[:, 1:] # 选择rfc填补的特征
target_rfc = data_copy2.iloc[:, 0] # 选择rfc填补的Survived标签
type(feature_rfc), feature_rfc.shape
>>(pandas.core.frame.DataFrame, (891, 7))
clf = DecisionTreeClassifier(random_state = 0, max_depth = 5)
score = cross_val_score(clf,feature_rfc, target_rfc, cv = 10).mean()
score # 相比之前提高了 2 %
>>0.8227715355805245