# Importsimport pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import re
import numpy as np
from sklearn import tree
from sklearn.model_selection import GridSearchCV
# Figures inline and set visualization style%matplotlib inline
sns.set()# Import data
df_train = pd.read_csv('data/train.csv')
df_test = pd.read_csv('data/test.csv')# Store target variable of training data in a safe place
survived_train = df_train.Survived
# Concatenate training and test sets
data = pd.concat([df_train.drop(['Survived'], axis=1), df_test])# View head
data.info()技术支持
<class'pandas.core.frame.DataFrame'>Int64Index:1309 entries,0 to 417
Data columns(total 11 columns):
PassengerId 1309 non-null int64
Pclass 1309 non-null int64
Name 1309 non-null object
Sex 1309 non-null object
Age 1046 non-null float64
SibSp 1309 non-null int64
Parch 1309 non-null int64
Ticket 1309 non-null object
Fare 1308 non-null float64
Cabin 295 non-null object
Embarked 1307 non-null object
dtypes:float64(2),int64(4),object(5)
memory usage:122.7+KB技术支持
413 Spector, Mr. Woolf
414 Oliva y Ocana, Dona. Fermina
415 Saether, Mr. Simon Sivertsen
416 Ware, Mr. Frederick
417 Peter, Master. Michael JName: Name,dtype: object
技术支持
# Extract Title from Name, store in column and plot barplot
data['Title']= data.Name.apply(lambda x: re.search(' ([A-Z][a-z]+)\.', x).group(1))
sns.countplot(x='Title', data=data);
plt.xticks(rotation=45);技术支持
# Transform into binary variables
data_dum = pd.get_dummies(data, drop_first=True)
data_dum.head()技术支持
Pclass
Has_Cabin
CatAge
CatFare
Sex_male
Embarked_Q
Embarked_S
Title_Miss
Title_Mr
Title_Mrs
Title_Special
0
3
False
0
0
1
0
1
0
1
0
0
1
1
True
3
3
0
0
0
0
0
1
0
2
3
False
1
1
0
0
1
1
0
0
0
3
1
True
2
3
0
0
1
0
0
1
0
4
3
False
2
1
1
0
1
0
1
0
0
一切就绪,可以开始构建最终模型了!
用新数据集构建模型!
与之前一样,先将 data 划分回训练集和测试集。然后将它们转换为数组:
# Split into test.train
data_train = data_dum.iloc[:891]
data_test = data_dum.iloc[891:]# Transform into arrays for scikit-learn
X = data_train.values
test = data_test.values
y = survived_train.values
技术支持
# Setup the hyperparameter grid
dep = np.arange(1,9)
param_grid ={'max_depth': dep}# Instantiate a decision tree classifier: clf
clf = tree.DecisionTreeClassifier()# Instantiate the GridSearchCV object: clf_cv
clf_cv = GridSearchCV(clf, param_grid=param_grid, cv=5)# Fit it to the data
clf_cv.fit(X, y)# Print the tuned parameter and scoreprint("Tuned Decision Tree Parameters: {}".format(clf_cv.best_params_))print("Best score is {}".format(clf_cv.best_score_))技术支持
Tuned Decision Tree Parameters:{'max_depth':3}
Best score is 0.8103254769921436技术支持
在前两个 Kaggle 教程中,您已经学习了如何通过探索性数据分析和基线机器学习模型将数据整理为可用于构建第一个机器学习模型的形式。接着,您成功构建了第一个机器学习模型——决策树分类器。您将这些模型都提交到了 Kaggle,并对其准确率进行了分析。
在第三个教程中,您将进一步学习特征工程。它是一个利用您对数据的领域知识,创建额外且相关的特征,从而提升算法预测能力并让您的机器学习模型表现更佳的过程!
更具体地说,
开始上手!
在正式开始之前,像上一期教程那样,先完成所有导入,使用 IPython 魔法命令确保图形在 Jupyter Notebook 内联显示,并设置可视化样式。接下来导入数据,并确保将训练数据中的目标变量安全地单独存放。之后,将训练集和测试集合并(但不包含
df_train的'Survived'列),并将结果存入data。请记住,这样做是为了确保您对数据进行的任意预处理,都能同时体现在训练集和测试集中!
最后,使用
.info()方法查看数据概况:为什么要做特征工程?
进行特征工程是为了从数据中提取更多信息,以便在构建模型时更胜一筹。
泰坦尼克号乘客的称谓
我们通过一个示例来看看这意味着什么。使用
.tail()方法查看'Name'列,看看数据最后五行是什么样的:您会突然注意到不同的称谓!换句话说,这一列包含了包含称谓的字符串或文本,例如“Mr”“Master”和“Dona”。
这些称谓当然能反映社会地位、职业等信息,而这些最终可能与生存有关。
乍一看,将姓名与称谓分开似乎很难,但别慌!记住,您可以轻松用正则表达式提取称谓,并将其存入新列
'Title':注意,这个新的
'Title'列实际上就是您数据集的新特征!小贴士:想进一步了解正则表达式,请查看我对上一次FB Live 实时编程活动的记录,或参阅 DataCamp 的Python 正则表达式教程。
从上面的图可以看到,有不少不同的称谓,而且很多出现频次并不高。因此,将它们合并为更少的类别是有意义的。
例如,您或许希望将
'Mlle'和'Ms'替换为'Miss',将'Mme'替换为'Mrs',因为这些是法语称谓,而理想情况下,您希望所有数据使用同一种语言。接下来,再把一些暂时无法立即分类的称谓合到一个叫'Special'的桶中。小贴士:可以多尝试不同分组方式,看看算法表现如何随之变化!
然后,使用
.countplot()方法绘制结果的柱状图:这就是新构造的特征
'Title'的样子!现在,确认已经有了
'Title'列,并再次使用.tail()方法查看数据:乘客舱位
当您载入并检查数据时,会看到
'Cabin'列中有不少NaN或缺失值。我们有理由假设,这些
NaN表示乘客没有舱位,这可能与'Survival'有关。因此,创建一个新的'Has_Cabin'列来编码此信息,指示乘客是否有舱位。注意,下面的代码中使用了
.isnull()方法,如果乘客没有舱位则返回True,否则为False。但由于要将结果存入'Has_Cabin'列,您需要将结果取反:当乘客有舱位时返回True。这就是使用波浪号~的原因。接下来,您需要删除一些不再包含有效信息(或尚不清楚如何处理)的列。在本例中,可以考虑
['Cabin', 'Name', 'PassengerId', 'Ticket'],原因如下:'Has_Cabin'列中提取;'Name'列中提取了称谓;'PassengerId'和'Ticket'列大概率无法进一步解释乘客的生存情况,因此也可以删除。小贴士:
'Cabin'列中可能还蕴含更多信息,但在本教程中,我们假设没有!要在实际的
dataDataFrame 中删除这些列,请在.drop()方法中使用inplace参数并设为True:恭喜!您已经成功构造了
'Title'、'Has_Cabin'等新特征,并且移除了对机器学习模型不再提供有用信息的特征!接下来,您将处理缺失值、对数值数据分箱,并再次使用
.get_dummies()将所有特征转换为数值变量。最后,您将构建本教程的最终模型。请继续查看下一节了解具体步骤!处理缺失值
在对原始
dataDataFrame 做了以上修改后,最好用.info()检查是否还有缺失值:从结果可知,
'Age'、'Fare'和'Embarked'存在缺失值。请记住,可以先看总条目数(1309),再核对
.info()列出的各列非空值数量是否相等。此处'Age'只有 1046 个非空值,意味着有 263 个缺失;类似地,'Fare'只有 1 个缺失,'Embarked'有 2 个缺失。与上一期教程一样,您将使用
.fillna()来填补缺失值:注意,这里依然用中位数填充
'Age'和'Fare'列,因为中位数对异常值更稳健。其他填补方式包括使用均值(将所有数据点相加再除以数量)或众数(出现频率最高的值)。对于
'Embarked'列的两个缺失值,用'S'(南安普敦)填充,因为该值是该列中最常见的值。小贴士:您可以通过进一步的探索性数据分析来复核这一点!
数值数据分箱
接下来,您需要对数值数据进行分箱,因为年龄和票价都有一定范围。然而,这些数值中可能存在不反映数据模式的波动,即噪声。因此,您会将落在某个年龄或票价范围内的人分到同一箱。可以使用
pandas的qcut()来对数值数据分箱:注意,您传入的是 Series(
data.Age和data.Fare),然后指定分位数个数q=4。最后将labels设为False,以数值编码各个箱。现在有了分箱信息,您可以安全地删除
'Age'和'Fare'列。别忘了查看数据前五行!同船家属人数
接下来可以创建一个新列,表示在泰坦尼克号上的同船家属人数。在本教程中,我们不将其纳入,并观察模型在没有该特征时的表现。若您想看看加入该列后模型表现如何,请运行以下代码:
目前,我们将直接从 DataFrame 中删除
'SibSp'和'Parch'列:将变量转换为数值变量
现在,您已经构造了更多特征(如
'Title'和'Has_Cabin'),并处理了缺失值、完成了数值数据分箱,是时候将所有变量转换为数值型了。这是因为机器学习模型通常接收数值输入。如之前所做,使用
.get_dummies()完成转换:一切就绪,可以开始构建最终模型了!
用新数据集构建模型!
与之前一样,先将
data划分回训练集和测试集。然后将它们转换为数组:现在,您将在全新特征工程后的数据集上构建决策树。为了选择超参数
max_depth,您将使用一种基于训练/测试划分的变体——“交叉验证”。首先把数据集分成 5 组或称为折(fold)。然后将第一折作为测试集,使用其余四折拟合模型,在测试集上预测并计算指标。接着将第二折作为测试集,用其余数据训练,在测试集上预测并计算指标。第三、第四和第五折以此类推。
最终您会得到五个准确率值,并可据此计算感兴趣的统计量,如中位数和/或均值以及 95% 置信区间。
对每个待调优的超参数的每个取值都这样做,并选择表现最佳的一组超参数。这就叫做网格搜索。
原理就说到这里,开始动手吧!
下面将使用交叉验证和网格搜索为新特征工程数据集选择最佳的
max_depth:现在,您可以在测试集上进行预测,创建新的
'Survived'列并将预测结果存入其中。别忘了将df_test的'PassengerId'与'Survived'两列保存为 .csv 文件并提交到 Kaggle!您本次提交的准确率为 78.9。
后续步骤
看看能否进行更多特征工程,并尝试新的模型来进一步提升成绩。本笔记本与前两个一并发布在 GitHub,期待大家改进这些模型。
还有许多预处理内容值得学习,例如数据缩放。您也会发现 scikit-learn 的流水线非常有用。请查看我们的scikit-learn 监督学习课程和 scikit-learn 文档以获取更多信息。