Part1 案例背景
影片根据真实事件改编,片中的泰坦尼克号是英国白星航运公司下辖的一艘奥林匹克级邮轮,于1909年动工建造,1912年首次航行。1912年4月15日,在首次航行期间,泰坦尼克号撞上冰山后沉没,2224名乘客和机组人员中有1502人遇难。这场轰动的旷世悲剧震撼了国际社会,并警醒人们制定了更完善的船舶安全条例。
本研究中,我们希望分析具有哪种特征的人可能在这样的灾难中存活。具体而言,就是运用机器学习算法来预测哪些乘客将幸免于难,从而帮助人们做好灾前预警,提高在灾难中的存活率。
本项目使用随机森林进行预测。
Part 2 数据来源及变量说明
本研究数据来源于Kaggle中的项目:Titanic: Machine Learning from Disaster,网页链接:https://www.kaggle.com/c/titanic。Kaggle提供的数据集包括Training Set (train.csv)、Test Set (test.csv),其中训练集有891个观察值和12个变量,测试集有418个观察值和11个变量(缺失Survived变量)。变量说明如下:
变量类型 | 变量 | 类型 | 定义 | 取值范围/示例 |
---|---|---|---|---|
因变量 | Survived | int | 存活与否 | 0=No, 1=Yes |
自变量 | PassengerId | int | 乘客序号 | 1-1309 |
Pclass | int | 乘客等级 | 1- 3 | |
Name | chr | 姓名 | Braund, Mr. Owen Harris; Cumings, Mrs. John Bradley (Florence Briggs Thayer); Nicola-Yarred, Miss. Jamila等 | |
Sex | chr | 性别 | male, female | |
Age | num | 年龄 | 0.17-80 (如果年龄小于1,则年龄是分数。如果为估计年龄,为xx.5) | |
SibSp | int | 共同登船的兄弟姐妹&配偶的总数 | 0-8 | |
Parch | int | 共同登船的父母&子女的总数 | 0-9 | |
Ticket | chr | 票号 | ||
Fare | num | 票价 | 0-512.3292 | |
Cabin | chr | 舱号 | A/B/C/D/E/F/G+数字 | |
Embarked | chr | 登船港口 | C = Cherbourg, Q = Queenstown, S = Southampton |
Part 3 加载数据及初步探索
第一,加载和安装必要的R包,并将测试集和训练集导入R。
第二,定义测试集的Survived变量为NA,合并测试集和训练集。
第三,查看数据完整性,是否有缺失值。
#查看数据完整性
sapply(all, function(x) {sum(is.na(x))})
结果显示:
从输出结果可以看出,变量Survived缺失的刚好是418个值,对应测试集的418个观察值,这是我们需要预测的目标。变量Age、Fare、Cabin、Embarked存在缺省值,后续需要填补缺省值。
第四,发现重要变量Sex和Pclass。
首先将Survived、Sex和Pclass转化为因子变量。接着我们调用ggplot()函数绘制了以下图形,总览了训练集中死亡和幸存的人数。
Figure 2 训练集中死亡和幸存的人数
经计算观察,在泰坦尼克号上的1309人(训练集891人+测试集418人)中,64.4%是男性。这一比例与训练集中的男性比例几乎相同(64.7%)。在训练集中,81.1%的男性死亡,25.8%的女性死亡。性别不同导致的存活率差异巨大,因此我们认为Sex似乎是一个非常重要的预测指标。
另一方面,经绘图观察,存活与否和乘客阶层密切相关。大部分头等乘客幸存下来,大多数三等乘客死亡。同样值得注意的是,几乎所有一、二等的女性都幸存下来了。而对于男性来说,二等乘客的存活率几乎和三等乘客一样糟糕(见图3)。
Figure 3 阶层、性别和存活的关系
由此可见,Pclass和Sex同时影响存活与否,所以我们将两个变量结合,创造出PclassSex变量。PclassSex变量为因子变量,包括6个水平——“P1Male”、“P2Male”、“P3Male”、“P1Female”、“P2Female”、“P3Female”。
Part 4 数据清洗
我们对原始的变量进行一定的处理,主要是对缺省值进行处理。
4.1
>>> 挖掘Name中的称谓,建立“Title”变量 <<<
在数据中,“名字”(name)变量是完整的(即非空的),但是实际上这个变量不仅仅包含了姓氏和名字,还包括了对每个人的称呼(Title),我们必须将其分开,以便整理数据。同理,将姓氏分解出来(Surname,在称呼之前),我们想在后续分析家庭是否一起旅行时使用这个变量。
从上边的表格中我们可以看到,部分Title的数量很少,我们希望能够减少Title的种类,以便更好的用于预测。Ms.经常用于较年轻的已婚女士,因此我们希望将Ms.加入到Miss中,在法语中,Mlle同样也可以用Miss来表示。再者,我们假设Mme代表已婚女士,我们将其加入Mrs。对于其他低频的Title,我们将用“Rare Title”来代替。
4.2
>>> 处理Age缺失值 <<<
我们绘制Age和Survived变量的密度图,如下:
从密度图中可以看出,18岁以下的存活几率相对更大。
为了处理年龄缺失值,我们试图探究Age变量和Title变量、Pclass变量之间的关系。从箱线图可以看到,不同的年龄段对应不同的称谓(Title)。值得注意的是,Master这个称谓都是儿童。通过搜索得知,Master这个称谓一般指的是最大的儿子,因此有Master称谓的都是男孩。
我们进行线性回归,结果如下:
可以看出,Pclass变量和Title变量与Age之间的关系更为显著,与前面箱线图预期一致。
之后我们利用回归出来的方程预测年龄的缺省值,具体如下:
4.3
>>> 处理Fare和Embarked缺失值 <<<
登船地点变量(Embarked)存在2个缺失值,票价(Fare)存在着1个缺省值。登船地点对票价来说是重要的,因为不同的登船地点表示的路程不同。
没有家人陪同而出来旅行的两个女性(Fsize =1),彼此之间肯定是朋友,她们是结伴出行的,因为他们的船票号码都相同,为113572(没有其他人的船票号码和她们一样)。
我们尝试找出登船地点信息与票价信息都存在的乘客的信息后,对它们按照登船地点与船舱等级分组,算出每组中人均票价的中位数。
因为这两个平均票价都是40,所以她们可能在Cherbourgh登船的,因此我们补全票价数据。
同理,我们运用同样的方法预测Mr Story的票价。
由于Mr.Story是阶级三且在S港登船,我们预测Mr Story的票价为7.8。
尽管现在票价这个变量已经没有空缺值了,但我们留意到有17个人的票价为0,且这些人都不是儿童。虽然我们觉得信息可能正确的(可能有些人的船票是靠赢来的呢?),但我们认为这些票价会扰乱算法。比如,在一等船舱,有些乘客的票价也为0。避免这些问题,我们用每个人的等级所对应票价的中位数去代替那些为0的票价值。
处理数据后,fare的数据如下:
4.4
>>> 处理Cabin缺失值 <<<
Cabin变量的值的第一个字母代表的是甲板的层级,甲板A-E是顶级的,往往是第一阶层的人住在里面。
用甲板U来代替Cabin的缺失值,并且只保留Cabin变量的第一个字母。
各阶层各舱位的乘客存活情况直方图如下:
可以看到,甲板A的存活率不高,甲板F和甲板E的存活率较高。
我们具体看下缺省值的数量:
可以看到Cabin的缺失值很多,尽管Cabin可以较好地预测乘客的存活与否,但是我们没有一个合适的方法进行筛选,考虑之下,我们在后续还是选择放弃这个变量。
4.5
>>> 寻找一起旅行的团体 <<<
逻辑上来讲,如果是以团体出行的,出事时一起生存的概率会提高(可能是更好的对灾难反应)。我们希望能够找到在泰坦尼克号上一起旅行的团体,这些团体可能包括直系家庭(如父母、孩子),也可能包括旁系家庭(如表、堂),也有可能包括一起订票的朋友,在本部分中我们将尝试区分出这些团体。
4.5.1
>> 家庭规模:兄弟姐妹、配偶、父母和孩子 <<
为了能够建立每个人在船上的家庭规模,我们将个体在船上的父母/孩子数量(Parch)和兄弟姐妹/配偶(SibSp)相加起来,当然,再加上自己。
从下面的图中可以很明显的看到,单独旅行的人有更大的可能遇险。此外,我们可以看到,家庭成员在2-4个人之间的有更大的可能性生存下来。然而,5及5个人以上的大家庭生存的几率明显降低。
现在我们可以按照家庭规模将数据分为3组——包括单独(solo),小规模家庭(small family)和大规模家庭(large family),但是在这之前,我们想先检验下数据中家庭规模是否与其他变量(如Surname)存在矛盾。
4.5.2
>> 检验家庭规模是否存在矛盾 <<
为了检验家庭规模数据的不一致性,我们建立一个变量(FsizeName),这个变量连接Surname和Fsize,之后,我们对不一致的数据进行定位,分别处理。
数据中显示,有93个样本数据的“NumObs”和“Fsize”不一致,即同家庭规模同姓氏的人数与同一个家庭的人数不相等。通过网络搜索我们发现,泰坦尼克号上确实有大约1300名乘客(其余的为船员),所以我们的数据集中应该没有缺失的乘客。
4.5.3
>> 尝试纠正退票带来的影响 <<
对于家庭规模中存在的矛盾,一个合理的解释是,部分乘客订票后又将票取消(这种情况下,SibSp和Parch不会实时更新)。我们以“Davies”为例,从表格中我们可以看到,这里应该有2个Davies家庭(Fsize=3,NumObs=5)。在我们上一步的检查过程中,由于Surname一致,这两个家庭的姓氏被加总了,导致了不一致。
票号为A/4的三位“Davies”应该是完整的一个家庭。因此错误应该出现在编号【550】和【1222】两位乘客上。通过SibSp和Parch的数据可以推测,No.550和No.1222是母子。一种可能是,No.1222号乘客原来打算携带2位孩子登船,但最终只带了一位。
严格意义上来说,数据集中肯定还有类似这样的错误没有被修正,但是考虑到最终可能对模型结果影响很小,也考虑到可行性,我们决定暂时忽略这类错误,寻找并修正其他错误。
4.5.4
>> 家庭规模:叔叔阿姨、表兄弟姐妹等 <<
在这些数据里面,其实有一些隐藏的家庭关系比退票情况更为有趣。例如, Hockings和Richards这两个姓氏很可能是相关的。我们将这两个姓氏的异常值输出。
从表格上来看,我们可以猜测,438号乘客带了2个孩子(408和832),一位母亲(775),一个弟弟(530)和一个妹妹(944),对于438号乘客来说,这些都是直系亲属。但是,对于其他人来说,这些可能是间接相关的。比如408和832两个孩子,对于他们来说,只会计算SibSp为1和Parch为1,不会将530和944这类间接相关的亲属计算进去。这样导致了Fsize将会虚高,这些大规模的家庭实际上可以细分成更小的群组。438这位乘客作为母亲更可能跟530和832两位孩子在一起,而她的弟弟(530)和妹妹(944)更可能与她的母亲(775)待在一起。
为了处理这种情况,将大的家庭规模分解成更小的家庭,我们需要先将归属于女方(maiden name)的家庭找出来
如图我们可以看到,以女方为线索,一共有7个大规模家庭被找出,这些家庭成员没有一致的Fsize, 这意味着他们存在着间接的亲属关系(如祖父母等)。同理,我们将未处理的男方的大规模家庭寻找出来。
我们以Mr Julius Vander Planke(1037)为例子,他与他的妻子(19)和2个兄弟姐妹(39和334)一起旅行。而他的妻子和他的兄弟姐妹实际上是间接相关的。
经过上面的分析,我们可以看到一共有9个家庭(涉及37个乘客)包含间接家庭成员。我们希望能够给这些家庭一样的Fsize(这将给每个在这样的家庭里面的成员拥有一致的生存机会)。为了降低家庭规模,我们将各个姓氏家庭的不同Fsize进行平均。
这确实是我们想要的效果,我们想要赋予这些属于同个家庭的人一个一致的Fsize(给予这些成员共同的生存机会),但并不想用最大Fsize去替代,因为比起直系家庭,这些大规模家庭在灾难发生时,不太可能一直呆在一起。
4.5.5
>> 寻找更多的二级家庭 <<
我们在考虑是否忽略了某些二级家庭,我们对数据进行再次的浏览,发现有些具有相同Surname的人的票号(Ticket)往往具有一致性。
这些人没有兄弟姐妹、配偶、父母或者孩子在船上,他们之间票号的关联更可能意味着他们是表兄弟姐妹或者叔伯关系。在本项目中,我们并不是想将所有的人的家庭关系都找出来,而是想知道灾难发生时这些人是否有可能待在一起。一个合理的假设是,一般来说直系家庭(比如父母和孩子)经常会待在一起,而叔伯/堂表兄弟姐妹更可能相互照顾。我们在数据集中,寻找家庭规模(Fsize)只有1个人,但是Surname有重复且Ticket只差两位尾数的乘客,一共有56个人。下表我们只展示前12个人。
4.5.6
>> 一起旅行的朋友 <<
除了我们上面所提及的家庭群组之外,朋友之间也有可能一起旅行。以Ticket号为1601的乘客为例:
上述这几个乘客,年龄基本相仿,所预定的票号也是一致的,我们有理由相信这是一个团队来出游(或许是旅行团呢)。因此,我们将拥有一样Ticket号码的人进行统计,并且建立Tsize变量。
与家庭规模非常类似,从下面的柱状图可以看到,2-4人的小规模团体往往有更高的几率生存下来。
经过上述的分组后,家庭规模和朋友(用Ticket分组)规模可能有一部分重叠,我们将Fsize和Tsize结合起来,利用这些数据创建一个分类变量。
由于1个人和2个人旅行生存机会显然不同,我们将其分为两个组(solo和group),3个人和4个人似乎有最好的生存机会(group),5个及5个人以上显然有更加糟糕的生存机会。
4.6
>>> 根据票号(Ticket)分组 <<<
票号可以检查是否有集体出游的人存活下来,考虑到,处在同个位置的组有人存活下来的话,组内其他人存活的概率更大。我们由此建立AnySurvivors变量。
4.7
>>> 考虑儿童对模型的影响 <<<
从之前绘制的年龄与存活的密度图看到,大概在14.5岁以下的儿童比其他年龄段的人存活率高。但是年龄在14.5岁以下的儿童大部分来自第三阶层,并且绝大多数没有存活下来,因此我们认为第三阶层是一个噪音,需要考虑从模型中去除。
同时考虑到将第三阶层儿童分离出来这种分类是否合理,所以我们也生成一个新的变量:是否儿童-IsChild。
Part 5 建立模型
随机森林模型拟合度较高,且过度拟合问题不严重,首先我们选择了随机森林模型来进行分析。
首先我们利用原始变量作出第一步模型预测,基于前文的分析,我们选取了5个变量来进行随机森林模拟(分别是Pclass,Sex,Age,Cabin,Embarked)。
#情况一:'Pclass', 'Sex', 'Age', 'Cabin', 'Embarked'
该随机模型用了891个样本。5个自变量,要预测的变量的结果是0或1。Accuracy的值最大时,模型最合适。所以,我们选择了mtry = 2,此时对应的Accuracy是0.804。
初步预测的准确率似乎已经较高,我们将基于前文对各变量的分析,进一步调整模型变量以期继续调整模型预测准确度。即使前面我们分析了那么多变量,为了避免产生拟合过度的问题,我们模型预测时,变量个数均不超过5个。我们依次考虑了是否加入港口(Embarked)、是否是一二层级小孩(IsChildP12)、是否是小孩(IsChild)变量。具体结果分析见附录一。最终当我们加入AnySurvivors变量时,我们发现选择5个变量(PclassSex,GroupSize,FarePP,AnySurvivors,IsChildP12)预测时,此时准确率显著提高。
#情况二:'PclassSex', 'GroupSize', 'FarePP', 'AnySurvivors', 'IsChildP12'
Accuracy的值最大时, mtry = 3,此时对应的Accuracy是0.857。
MeanDecreaseGini通过基尼(Gini)指数计算每个变量对分类树每个节点上观测值的异质性的影响,从而比较变量的重要性。该值越大表示该变量的重要性越大。所以,我们用MeanDecreaseGini来对5个变量的重要性进行评分:
考虑到IschildP12变量重要性最低,我们进一步考虑采用Ischild来代替IschildP12。
#情况三:'PclassSex', 'GroupSize', 'FarePP', 'AnySurvivors', 'IsChild'
#显示预测质量
最后,我们可以用训练出来的模型去预测测试集里的存活变量(Survived):
solution_rf <- predict(caret_matrix, testClean)
##随机森林模型的Accuracy是0.868。
-- the end --
文案、代码、审核 / 张元新、郑少淳、李大伟、郭丽丽、朱小青(皆为研二)
排版 / 陈志(大四)
指导老师 / 朱文斌教授
如对文中内容有疑问,欢迎交流。PS:部分资料来自网络。
朱文斌(华南理工大学工商管理学院教授、i@zhuwb.com)
张元新(华南理工大学工商管理学院研究生二年级、jerry.yx@foxmail.com)
郑少淳(华南理工大学工商管理学院研究生二年级、sczheng_zoe@163.com)
李大伟(华南理工大学工商管理学院研究生二年级、397406076@qq.com)
郭丽丽(华南理工大学工商管理学院研究生二年级、201720130156@mail.scut.edu.cn)
朱小青(华南理工大学工商管理学院研究生二年级、1740384223@qq.con)