前言:跟Kaggle上大师们做的第一个项目,记录下目前的心得体会,便于后续查阅。
同步转载至个人公众号:R语言学习
同步转载至个人知乎专栏:R语言可视化进阶
泰坦尼克沉船事故已经过去多年,但是关于它的生存预测问题一直是数据分析与建模的经典案例,今天抽空把Chuck Talbert大师做的预测进行简单翻译和再现,并加入个人理解,原文链接:Titanic: A TidyCaret Approach - (0.8086)
开始数据分析与建模之前,我们一起看看Titanic数据集字段介绍:
#PassengerId:乘客ID编号
#Survived:是否存活,0-未存活,1-存活
#Pclass:船舱号,共1,2,3类舱
#Name:乘客姓名
#Sex:乘客性别,Male,Female
#Age:乘客年龄
#SibSp:兄弟姐妹/配偶数量,0~8
#Parch:父母/子女数量,0~6
#Ticket:船票编号
#Fare:票价
#Cabin:舱位编号
#Embarked:登陆口岸,C、Q、S
原数据共12个字段,分为train集和test集.
1、进行数据读入工作,并简要了解数据结构概况
setwd("E:/R/Kaggle/泰坦尼克")
train <- read.csv("train.csv",stringsAsFactors = FALSE)#读取训练数据
dim(train)#查看数据维度(行数x列数)
#[1] 891 12
test <- read.csv("test.csv",stringsAsFactors = FALSE)#读取测试数据
test$Survived <- NA#向test集新增字段Survived并设置为空值
titanicCombo<- rbind(train,test)#合并训练集与测试集
dim(titanicCombo)#查看数据维度(行数x列数)
#[1] 1309 12
str(titanicCombo)#查看合并数据结构
'data.frame': 1309 obs. of 12 variables:
$ PassengerId: int 1 2 3 4 5 6 7 8 9 10 ...
$ Survived : int 0 1 1 1 0 0 0 0 1 1 ...
$ Pclass : int 3 1 3 1 3 3 1 3 3 2 ...
$ Name : chr "Braund, Mr. Owen Harris""Cumings, Mrs. John Bradley (Florence Briggs Thayer)""Heikkinen, Miss. Laina" "Futrelle, Mrs. Jacques Heath (Lily MayPeel)" ...
$ Sex : chr "male" "female" "female""female" ...
$ Age : num 22 38 26 35 35 NA 54 2 27 14 ...
$ SibSp : int 1 1 0 1 0 0 0 3 0 1 ...
$ Parch : int 0 0 0 0 0 0 0 1 2 0 ...
$ Ticket : chr "A/5 21171" "PC 17599""STON/O2. 3101282" "113803" ...
$ Fare : num 7.25 71.28 7.92 53.1 8.05 ...
$ Cabin : chr "" "C85" """C123" ...
$ Embarked : chr "S" "C" "S""S" ...
2、查看数据缺失情况
colSums(is.na(titanicCombo[1:891,]))#计算每列数据缺失个数
# PassengerId Survived Pclass Name Sex Age
# 0 0 0 0 0 177
# SibSp Parch Ticket Fare Cabin Embarked
# 0 0 0 0 687 2
#计算每列数据缺失率,值保留3位小数
round(colSums(100*(is.na(titanicCombo[1:891,])/nrow(titanicCombo[1:891,]))),3)
# PassengerId Survived Pclass Name Sex Age
# 0.000 0.000 0.000 0.000 0.000 19.865
# SibSp Parch Ticket Fare Cabin Embarked
# 0.000 0.000 0.000 0.000 77.104 0.224
3、数据预处理
将Survived、Pclass、Sex、Embarked 、Cabin等字段转为因子型,且更改Cabin值。
更改原理:如果Cabin为空,即无座乘客,将其Cabin值填充为0,否则填充1。
library(magrittr)#为了使用其中的管道行数%>%
library(plyr)#为了使用mutate()函数
titanicCombo <- titanicCombo %>%
mutate(Survived =as.factor(Survived), Pclass = as.factor(Pclass),
Sex = as.factor(Sex),Embarked = as.factor(Embarked),
Cabin =as.factor(ifelse(nchar(Cabin)>0 ,1,0)))
注:mutate()函数用于对已有列进行数据运算并添加为新列与 base包的transform() 相似, 优势在于可以在同一语句中对刚增加的列进行操作;
nchar(Cabin)>0则为有座乘客,nchar用于计算字符串长度。
4、初级可视化,数据探索
4.1观察船舱等级(Pclass)与存活(Survived)是否有关系
library(ggplot2)
g1 <- titanicCombo[1:891,] %>%
ggplot() +
geom_bar(aes(x = Pclass, fill =Survived))
g2 <- titanicCombo[1:891,] %>%
ggplot() +
geom_bar(aes(x = Pclass, fill =Survived), position = "fill")
library(gridExtra)#为使用grid.arrange()函数
grid.arrange(g1,g2,nrow=2)
g1注:绘制柱状图,横轴为船舱等级,按是否存活进行分类填充颜色;titanicCombo[1:891,] %>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数;gggplot默认为计数,统计各个船舱等级存活、未存活频数。
g2注:titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数;position = "fill"将每个柱状体设置为百分制,显示每个船舱等级下,存活、未存活比例。
其他注:grid.arrange()函数用于多个图形排版,ncol=2表示绘制两列图形,即各个图形按列分布,也可以设置nrow=2,表示图形按行排列。
绘制图形:
结论1:从图形结果可以看出,1等舱存活人数最多且存活比例最高;其次为2等舱,3等舱存活比例最低,随着舱位等级增加,存活率降低。
4.2增加性别字段(Sex),判断性别是否有存活优势
library(stringr)#为使用其中str_c()函数
titanicCombo[1:891,] %>%
ggplot() +
geom_bar(aes(x = Pclass, fill =str_c(Survived,Sex))) +
scale_fill_discrete("Survived| Sex")
注:titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数。
str_c() 把多个字符串拼接起来,str_c(Survived,Sex)运行结果为0female、0male、1female、1male。
0female表示没有存活的女性人数,0male表示没有存活的男性人数。
1female表示存活的女性人数,1male表示存活的男性人数。
fill = str_c(Survived,Sex)表示按照0female、0male、1female、1male四类进行颜色填充。
scale_fill_discrete("Survived| Sex")用于设置分类型(离散型)数据数据图例名称,具体为0female、0male、1female、1male,discrete意为离散的。
绘制图形:
结论2:可以看出,各个船舱等级中,女性存活人数都比男性高。
4.3将性别(Sex)、船舱等级(Pclass)结合起来看看他们与存活率的关系
titanicCombo[1:891,] %>%
ggplot() +
geom_bar(aes(x = Pclass, fill =Survived), position = "fill") +
facet_wrap(~Sex)
注:titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数。
position ="fill"表示绘制百分比图。
facet_wrap(~Sex)表示分面,主要用于分类型(离散型)变量,这里按照性别分面,因为只有male、female两种性别,因此会分成两面,且默认为按照列分面,即两个面之间为并列关系。如果facet_wrap(~colname)中colname有多个取值,且我们需要限定最大分面列数为3,可以设置facet_wrap(~colname,ncol=3)。后面还会用到另一个分面函数:facet_grid(),也是用于分类型(离散型)数据分面。
绘制图形:
结论3:女性在各个船舱等级的存活率均高于男性,可见性别与存活与否有较强关系;另外女性船舱等级与存活率之间存在明显关联,船舱等级越高(1为高),存活率越高。男性群体中也显示船舱等级与存活率之间存在明显关联,船舱等级越高(1为高),存活率越高。
4.4增加年龄(Age)字段,判断性别,船舱等级、年龄等3个字段交互情况下生存率如何
titanicCombo[1:891,] %>%
ggplot(aes(x = Age, y = Survived))+
geom_jitter(aes(color = Sex)) +
facet_wrap(~Pclass)
#Warning message:
#Removed 177 rows containing missing values (geom_point).
#ggplot()在绘图时,会自动将涉及字段中存在缺失值的行删除
注:titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数。
geom_jitter()用于设置抖动点属性,geom_jitter(aes(color= Sex)) 表示抖动点为性别,且不同性别填充不同颜色。设置抖动点可以让原本重叠的点“探出头”来,表示它存在。
facet_wrap(~Pclass)表示按船舱等级分面,共1~3个等级,因此分为3面,并列排列。
绘制图形:
结论4:我们可以看出年龄小于20岁的乘客存活比例明显高于其他年龄段乘客,且女性存活数量、存活比例均明显高于男性,尤其是1、2等舱中,女性存活比例相当高。随着船舱等级降低(1为最高),存活数量、存活比例均在下降。
4.5增加家庭成员数量(FamilySize)因素
绘制散点图,展现不同船舱等级内,不同性别,拥有亲属数量与存活与否的关系
定义:家庭成员数量变量=兄弟姐妹/配偶数量+父母/子女数量
即:FamilySize=SibSp+Parch
titanicCombo[1:891,] %>%
ggplot(aes(x = Age, y = Survived))+
geom_jitter(aes(color =(as.factor(SibSp + Parch)), size = (as.factor(SibSp + Parch)))) +
facet_grid(Pclass~Sex) +scale_color_discrete("FamilySize") +
scale_size_discrete("FamilySize")
注:titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数。
关于geom_jitter(aes(color= (as.factor(SibSp + Parch)), size = (as.factor(SibSp + Parch))))的注释:
# 1)geom_jitter()用于绘制抖动点图,尽量避免数据点重叠。
#2)SibSp + Parch用于计算家属数量,然后as.factor(SibSp + Parch)将计算的数量值转换为因子型。
#3)color =(as.factor(SibSp + Parch))表示按家属数量分类填充颜色。
#4)size =(as.factor(SibSp + Parch))表示按家属数量分类设置点的大小,家属数量越多,数据点越大。
关于facet_grid(Pclass~Sex)的注释:
#facet_grid()函数会严格按照用户指定的方向分面。facet_grid(.x)表示横向分面横向分面,facet_grid(y.~)表示纵向分面,facet_grid(y~x)表示纵横两个维度的方向进行分面。
#scale_color_discrete("FamilySize")用于修改离散型数据(家属数量)颜色图例的名字为"FamilySize",默认为(as.factor(SibSp + Parch))
#scale_size_discrete("FamilySize")用于修改离散型数据(家属数量)点大小图例的名字为"FamilySize",默认为(as.factor(SibSp +Parch))
题外话:感兴趣的朋友可以试试下面两串代码:
#1)不设置颜色图例和数据点大小图例
titanicCombo[1:891,] %>%
ggplot(aes(x = Age, y = Survived))+
geom_jitter(aes(color =(as.factor(SibSp + Parch)), size = (as.factor(SibSp + Parch)))) +
facet_grid(Pclass~Sex)
#可以看到图例处,名字为(as.factor(SibSp+ Parch)),颜色图例与数据点大小图例是合二为一的状态,只是图例名字美中不足,乍一看不太懂是什么意思。
#2)设置颜色图例名字为"FamilySize",不设置和数据点大小图例名字
titanicCombo[1:891,] %>%
ggplot(aes(x = Age, y = Survived))+
geom_jitter(aes(color =(as.factor(SibSp + Parch)), size = (as.factor(SibSp + Parch)))) +
facet_grid(Pclass~Sex)+scale_color_discrete("FamilySize")
#可见,图形竟然有两个图例!上面的为颜色图例,下面的为数据点大小图例,且数据点大小图例颜色全是黑色,并没有与颜色进行结合,再者数据点大小图例名称为默认的(as.factor(SibSp + Parch)),生涩难懂。
通过以上两组代码对比,我们知道为什么需要同时设置scale_color_discrete("FamilySize") +scale_size_discrete("FamilySize")了吧。
绘制图形:
结论5:不论性别和船舱等级,家属数量在3个及以下的乘客存活率更高;图中还反映出一个特别的现象:第三船舱中,票价在55元以上的男性乘客存活率极低,我们可以细分该群体,看看原因。
4.6绘制家庭成员数量(FamilySize)与存活的关系图
g1 <- titanicCombo[1:891,] %>%
ggplot(aes(x = as.factor(SibSp +Parch))) +
geom_bar(aes(fill =as.factor(SibSp + Parch))) +
labs(x="FamilySize") +
scale_fill_discrete(guide=FALSE)
g2 <-titanicCombo[1:891,] %>%
ggplot(aes(x = as.factor(SibSp +Parch))) +
geom_bar(aes(fill =Survived),position = "fill")+
labs(x="FamilySize") +
scale_fill_discrete()
grid.arrange(g1,g2)
g1注:图g1为家庭成员数量与存活数量的柱状图。
titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数。
ggplot(aes(x =as.factor(SibSp + Parch)))表示先计算家庭成员数量SibSp + Parch,然后将其因子化as.factor(SibSp +Parch),再将因子化结果赋值给x轴,关于y轴的值,ggplot默认进行计数。
geom_bar(aes(fill =as.factor(SibSp + Parch)))中geom_bar表示绘制柱状图,aes(fill = as.factor(SibSp +Parch))表示按照家庭成员数量进行填充颜色。
labs(x="FamilySize")中labs表示轴标签设置,这里只设置了x轴名字为FamilySize"
scale_fill_discrete(guide=FALSE)中scale_fill_discrete()函数用于设置离散型数据的图例参数,guide=FALSE表示不显示图例。
g2注:图g2为家庭成员数量与存活比例的百分比堆积柱状图。
titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数。
ggplot(aes(x =as.factor(SibSp + Parch)))表示先计算家庭成员数量SibSp + Parch,然后将其因子化as.factor(SibSp +Parch),再将因子化结果赋值给x轴,关于y轴的值,ggplot默认进行计数。
geom_bar(aes(fill =Survived),position = "fill")中geom_bar表示绘制柱状图,aes(fill =Survived)表示按照是否存活进行分色填充,position = "fill"表示绘制百分比柱状图。
labs(x="FamilySize")中labs表示轴标签设置,这里只设置了x轴名字为FamilySize"
scale_fill_discrete()表示设置离散型数据图例为默认选项,默认为y轴Survived数据。
绘制图形:
结论6:通过绘制g1,得知家庭成员数量在0个的乘客存活数量最多,存活数量随着家庭成员数量的增加在递减。
通过绘制g2,得知家庭成员数量在3个的乘客,存活比例最高,整体而言,家庭成员数量低于3个的乘客存活比例较其他情况高。
4.7观察是否有座位(Cabin)与性别(Sex)、船舱等级(Pclass)的关系
绘制百分比堆积柱状图
titanicCombo[1:891,] %>%
ggplot(aes(x = Cabin)) +
geom_bar(aes(fill = Survived),position= "fill") +
facet_grid(Sex~Pclass)
注:titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数。
ggplot(aes(x =Cabin)) 表示横轴值为Cabin座位情况,0代表无座位乘客,1代表有座位乘客
geom_bar(aes(fill =Survived),position = "fill")中geom_bar表示绘制柱状图,设置position ="fill",表示百分比模式的柱状图,aes(fill = Survived)表示按照是否存活进行分类颜色填充。
facet_grid(Sex~Pclass)中acet_grid为分面函数,Sex~Pclass表示按照性别和船舱等级组合分面,性别共2个选项,船舱等级有3个选项,因此组合起来共6个情况,分为6面。
绘制图形:
结论7:对于男性群体而言,有座位乘客存活率明显高于无座位乘客,不论船舱等级如何,均呈现该规律。
对于女性群体而言,女性存活率随着船舱等级降低(1为高,3为低),存活率也降低;在第1~2船舱等级中,无座位乘客存活率略高于有座位乘客,在第3船舱中有座位的女性乘客存活率明显高于无座女性乘客。
4.8观察船票价格(Fare)与是否存活(Survived)的关系
titanicCombo[1:891,] %>%
ggplot(aes(x = Fare, y =Survived)) +
geom_jitter(aes(color =(as.factor(SibSp + Parch)), size = (as.factor(SibSp + Parch)))) +
facet_grid(Sex~Pclass, scales ="free") +
scale_color_discrete("FamilySize") +
scale_size_discrete("FamilySize")
注:titanicCombo[1:891,]%>% 表示筛选titanicCombo数据集中前891行数据的所有列,即训练数据集(train),通过管道函数%>%将数据传给后续绘图函数。
ggplot(aes(x =Fare, y = Survived)) 表示横轴为票价,纵轴为是否存活。
geom_jitter(aes(color= (as.factor(SibSp + Parch)), size = (as.factor(SibSp + Parch))))中geom_jitter表示绘制抖动点,以避免数据点重叠,aes(color = (as.factor(SibSp + Parch))中SibSp+ Parch表示计数家庭成员数量,并用as.factor将其转换为因子型数据,最后按照家庭成员数量分类填色;size = (as.factor(SibSp + Parch))表示按照家庭成员数量大小设置抖动点的行状大小;
scale_color_discrete("FamilySize")用于设置离散型数据颜色图例名字为FamilySize;scale_size_discrete("FamilySize") 用于设置离散型数据形状大小图例名字为FamilySize。
绘制图形:
结论8:首先可以看到头等舱(Pclass=1)的票价明显高于2、3等船舱,不论性别差异;1、2、3等舱存活数量相差无几,但是存活比例头等舱明显更高。且3等舱中家庭成员数量在5人及以上的比例更大,大家庭乘客存活率较低。
特殊点:3等船舱中男性乘客,家庭成员数量小于1,且票价高于55元,这个群体,存活率明显高于同为3等船舱的其他男性。
鉴于4.8的结论,我们将3等船舱中男性乘客,家庭成员数量小于1,且票价高于55元的乘客信息单独调出。
library(highr)#为了使用knitr包
library(knitr)#为使用kable函数
possibleGroup <- titanicCombo[1:891,] %>%subset(Pclass==3&Sex=="male"&Fare > 55&(SibSp + Parch)< 1)
knitr::kable(possibleGroup, caption = 'Group of Interest')
| | PassengerId|Survived |Pclass|Name |Sex | Age| SibSp| Parch|Ticket | Fare|Cabin |Embarked |
|:---|-----------:|:--------|:------|:---------------|:----|---:|-----:|-----:|:------|-------:|:-----|:--------|
|75 | 75|1 |3 |Bing, Mr. Lee |male | 32| 0| 0|1601 | 56.4958|0 |S |
|170 | 170|0 |3 |Ling, Mr. Lee |male | 28| 0| 0|1601 | 56.4958|0 |S |
|510 | 510|1 |3 |Lang, Mr. Fang |male | 26| 0| 0|1601 | 56.4958|0 |S |
|644 | 644|1 |3 |Foo, Mr. Choong |male | NA| 0| 0|1601 | 56.4958|0 |S |
|693 | 693|1 |3 |Lam, Mr. Ali |male | NA| 0| 0|1601 | 56.4958|0 |S |
|827 | 827|0 |3 |Lam, Mr. Len |male | NA| 0| 0|1601 | 56.4958|0 |S |
|839 | 839|1 |3 |Chip, Mr. Chang |male | 32| 0| 0|1601 | 56.4958|0 |S |
注:knitr包中的kable函数可以将数据框数据在R中显示为表格样式。调出符合条件的数据共7条,其中仅2位乘客未存活,可见存活比例相对较高。
4.9手动增加各位乘客的Title,观察是否不同Title群体存活率不一致
第1步:手动增加各位乘客的Title
观察原始数据中Name字段,如:Braund, Mr. Owen Harris,我们的目标是提取出字符串"Mr."。
titanicCombo <- titanicCombo %>%
mutate(Title =str_extract(Name,".[a-z]+\\."), Title = as.factor(Title))
注:mutate()函数用于数据修整,这里向数据新增Title字段,字段内容通过向原有字段Name进行文本截取获得。
在使用str_extract()函数进行文本截取过程中使用了简单的正则表达式".[a-z]+\\."
".[a-z]+\\."注释:
#[a-z]表示匹配26个小写字母。
#[a-z]+表示允许字母重复,即待匹配字符串中存在重复字母时仍会将其匹配处理,如:abbc
#\\.中\\为转义符号,将诸如".","^"这样具有特殊意义的字符视为普通字符串并顺利匹配处理,如:Mr.Kuppler。
#最最重要的是开始的那个小点,.[a-z]+\\.,“.”表示任意一个字符,这样我们才能完整地匹配出真正的Title,感兴趣的朋友可以将第一个点去掉,看看运行结果的不同。
Title =as.factor(Title)表示将新增的字段内容Title因子化。
第2步:绘制各个Title频数统计图
titanicCombo[1:891,] %>%
group_by(Title) %>%
summarise(n = n()) %>%
arrange(desc(n)) %>%
ggplot() +
geom_bar(aes(x = reorder(Title,desc(n), FUN = median), y = n, fill = Title),stat = "identity") +
labs(x = "Title", y ="Count") +
scale_fill_discrete(guide=FALSE)
1)数据处理部分解释:
group_by(Title):按Title分组。
summarise(n = n()):n()函数用于计数,summarise(n = n()) 对Title分组计数,并将计数结果传递给字段n。
arrange(desc(n)):desc(n)用于对n字段降序排列。
2)绘图部分解释:
geom_bar表示绘制柱状图。
aes()表示对轴进行参数设置。
aes(x =reorder(Title, desc(n), FUN = median), y = n, fill = Title)中x=reorder(Title,desc(n),FUN = median)表示x轴名称显示顺序按照各个Title的频数中位数进行降序排序后显示,y=n表示y的值为分组汇总后各Title的频数统计值,fill=Title表示按照Title分类填充颜色,因此,每个Title颜色将会不一致。
geom_bar(aes(x,y,fill),stat= "identity")中的stat= "identity"表示对样本点做统计的方式为x、y值一一对应,如Title为Mr.的记录对应n=517,因此在绘制柱状图时,柱子的高度会在y=517处。
labs(x = "Title",y = "Count"):labs()函数用于轴标签设置,这里设置了x轴名字为"Title",y轴名字为"Count"。
scale_fill_discrete(guide=FALSE):scale_fill_discrete()函数用于设置分类型(离散型)数据数据图例名称,guide=FALSE表示不显示图例。
至于不显示图例的原因是显而易见的,因为我们已经对每个Title填充一种颜色进行区分,所以无须再添加图例,意义不大。
附:reorder函数详解:
基本格式:reorder(x,y, FUN = mean, ...,)
基本功能:用于对因子型数据进行排序。
原理:根据字段y的均值/中位数/其他统计指标对x进行排序。x为因子型字段,y为数值型字段,FUN = mean表示默认对y进行求均值,也可以设置FUN = median。
绘制图形:
结论9:Title频数排名前四位分别是Mr,Miss,Mrs,Master,剩下的Title数量微乎其微.