一. 数据来源
泰坦尼克生还预测是kaggle的入门级经典案例之一, 目前网上利用机器学习方法对泰坦尼克数据集的案例已数不胜数. 本章为作者数据分析日常练习, 利用ID3决策树进行泰坦尼克乘客生存预测.
二. 数据分析
(一) 数据加载与探索
使用python中的pandas工具读取数据训练集(train.csv)与测试集(test.csv)文件, 并利用相关方法对数据进行探索, 结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/1d48f478b927ed1cf4105bee28568be5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1b82a0cc6d7ff7d8a2b9d80e045af231.png)
![](https://i-blog.csdnimg.cn/blog_migrate/92e3af325c93584509034b4065763a1c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/36f63f8366b09028724a335626e83216.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f87dc5d756da731dff3162037fcac318.png)
![](https://i-blog.csdnimg.cn/blog_migrate/aee14dee3a2562363c229d14c6f976fc.png)
代码如下:
# 数据加载
train_data = pd.read_csv('./Data/train.csv')
test_data = pd.read_csv('./Data/test.csv')
# 数据探索
print(train_data.info())
print(train_data.describe(include=['O']))
print(train_data.head())
print(test_data.info())
print(test_data.describe(include=['O']))
print(test_data.head())
在describe方法中使用了include=[‘O’]的参数值来查看数据中非数字类型的整体情况, 输出结果包含count(非空值总数量), unique(非空不重复的值数量), top(统计次数最多的值的名称), freq(统计次数最多的值的出现次数).
在describe方法中可以看到有5个字段为非数字类型数据, 在info方法中可以看到, 训练集的'Age', 'Carbin', 'Embarked'字段和测试集的'Age', 'Fare', 'Carbin'字段含有缺失值.
通过分析可以发现: 'PassengerId'为乘客编号, 对分类没有作用,可以放弃; 'Name'为乘客姓名, 对分类没有作用, 可以放弃; 'Cabin'字段缺失值太多, 可以放弃; 'Ticket'字段为船票号码, 杂乱无章且无规律, 可以放弃.
另外, 测试集相对训练集没有'Survived'字段, 也就是说测试集没有真是预测结果test_y, 所以我们不能用常规方法对模型进行评分.
(二) 数据处理
在经过数据探索之后, 决定保留数据中'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked'这些相关性相对较大的特征, 并用平均值填补训练集与测试集中'Age'与'Fare'字段的缺失值, 同时由于'Embarked'为非数字类型且缺失值较少(2个), 我们使用出现频率最高的值'S'来填补缺失值.
代码如下:
# 缺失值处理
# 使用平均年龄来填充年龄中的 nan 值
train_data['Age'].fillna(train_data['Age'].mean(), inplace=True)
test_data['Age'].fillna(test_data['Age'].mean(), inplace=True)
# 使用票价的均值填充票价中的 nan 值
train_data['Fare'].fillna(train_data['Fare'].mean(), inplace=True)
test_data['Fare'].fillna(test_data['Fare'].mean(), inplace=True)
# 使用登录最多的港口来填充登录港口的 nan 值
train_data['Embarked'].fillna('S', inplace=True)
test_data['Embarked'].fillna('S', inplace=True)
# 特征选择
features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
train_features = train_data[features]
train_labels = train_data['Survived']
test_features = test_data[features]
之后需要对数据中非数字类型的内容进行处理, 我尝试使用三种方法对相关数据进行了处理:
1. 使用字典特征提取器处理符号化对象
使用 sklearn 特征选择中的 DictVectorizer 类, 用它将可以处理符号化的对象, 将符号转成数字 0/1 进行表示. 具体方法如下:
# 1. 使用字典特征提取器处理符号化对象
dvec = DictVectorizer(sparse=False)
train_features = dvec.fit_transform(train_features.to_dict(orient='record'))
test_features = dvec.fit_transform(test_features.to_dict(orient='record'))
print(dvec.feature_names_)
最后输出特征名称列表为:
2. 使用pandas中的get_dummies() 方法进行独热编码
由于需要的数据为 pandas 的 DataFrame 类型, 可直接使用get_dummies() 方法进行独热编码, 代码及输出如下:
# 2. 使用pd.get_dummies() 进行独热编码
train_features = pd.get_dummies(train_features)
test_features = pd.get_dummies(test_features)
print(train_features.columns)
print(test_features.head())
![](https://i-blog.csdnimg.cn/blog_migrate/c75dcf8b529e75b0b0a897b680bbf674.png)
3. 使用pd.factorize()因数分解
pandas中factorize函数可以将Series中的标称型数据映射称为一组数字, 相同的标称型映射为相同的数字.
提取DataFrame中相应列进行因数分解, 返回值为一个元组, 包含两个元素, 第一个元素为数据转换后对应的数字类型数组, 第二个为index索引列表, 对应数字类型数组中相应的数字.
在对训练集与测试集的'Embarked'特征做因数分解时发现, 两边的index列表元素顺序并不相同, 训练集为Index(['S', 'C', 'Q'], 而测试集为Index(['Q', 'S', 'C'], 所以训练集和测试集处理之后的对应数组中相同数字代表的含义不同. 在此认为pd.factorize()因数分解的方法不适用于本案例. 但是在其他需要手动划分训练测试集的案例中, 可先对整体数据的相应内容进行因数分解后再划分训练测试集, 不会影响后续的模型预测结果.
代码如下:
# 3. 使用pd.factorize() 转换
sex_res1 = pd.factorize(train_features['Sex'])
sex_res2 = pd.factorize(test_features['Sex'])
embarked_res1 = pd.factorize(train_features['Embarked'])
embarked_res2 = pd.factorize(test_features['Embarked'])
print(sex_res1)
print(sex_res2)
print(embarked_res1) # Index(['S', 'C', 'Q']
print(embarked_res2) # Index(['Q', 'S', 'C']
# 如果index 对应, 可继续执行以下操作
train_features['Sex'] = sex_res1[0]
train_features['Embarked'] = embarked_res1[0]
test_features['Sex'] = sex_res2[0]
test_features['Embarked'] = embarked_res2[0]
print(train_features.head(10))
(三) 模型构建与评估
本案例使用ID3决策树进行模型预测, 首先在DecisionTreeClassifier() 中传入 criterion='entropy', 使用基于信息增益的ID3决策树.
相应数据选取使用字典特征提取器处理之后的数据, 通过模型进行训练与预测.
由于之前提到, 测试集没有实际的结果(test_lables), 因此无法用测试集的预测结果与实际结果做对比. 如果我们使用 score 函数对训练集的准确率进行统计, 正确率会接近于100%, 无法对分类器的在实际环境下做准确率的评估.
在此我们使用K折交叉验证对训练集进行多次划分交叉检测, 最后取平均值作为模型准确率.
# 构造 ID3 决策树
clf = DecisionTreeClassifier(criterion='entropy')
# 决策树训练和预测
clf.fit(train_features, train_labels)
pre = clf.predict(test_features)
# 得到决策树训练集准确率
acc_decision_tree = round(clf.score(train_features, train_labels), 6)
print(u'score 准确率为 %.4lf' % acc_decision_tree)
# 使用 K 折交叉验证 统计决策树准确率
print(u'cross_val_score 准确率为 %.4lf' % np.mean(cross_val_score(clf, train_features, train_labels, cv=10)))
输出结果score 准确率为 0.9820, cross_val_score 准确率为 0.7790.
(四) 查看预测结果
拼接原始测试集与预测结果, 储存为excel表格, 在表格中可更加直观地看到预测结果.
相关代码:
# 查看结果并储存
test_data['Survived'] = pre
print(test_data.head(10))
test_data.to_excel('./result/titanic_res.xls')
完整代码及数据请点击:GitHub