Kaggle 入門:探索泰坦尼克號事故倖存情況分析

原文地址

作者:梅根·麗思達(Megan L. Risdal)

---

文章目錄:

  • 1 介紹
    • 1.1 載入並檢查數據
  • 2 特徵工程
    • 2.1 名字分析
    • 2.2 家庭存活情況分析
    • 2.3 處理更多變數
  • 3 缺失數據處理
    • 3.1 觀測值插補
    • 3.2 預測性插補
    • 3.3 特徵工程:第二階段
  • 4 預測
    • 4.1 從源數據分離出訓練數據和測試數據
    • 4.2 建立模型
    • 4.3 變數重要性分析
    • 4.4 開始預測!
  • 5 結語

---

1 介紹

這是我第一次嘗試使用 Kaggle 腳本。經過一段時間在 Kaggle 上的瀏覽和閱讀其他用戶寫的腳本後,我決定對泰坦尼克號事件情況數據集進行分析。在此期間我也將生成一些數據可視化圖片。這個實例中我用來預測倖存者的模型是基於隨機森林(randomForest)方法建立的。我才剛入門機器學習這個領域,還有很多東西要學,歡迎大家在評論區進行反饋!

在我腳本中主要包含三個部分:

  • 特徵工程
  • 缺失數據插補
  • 預測!

1.1 載入並檢查數據

# 載入分析所用的包# 這些是用於數據可視化的包library("ggplot2")library("ggthemes")library("scales")library("dplyr") # 數據處理包library("mice") # 數據插補包library("randomForest") # 隨機森林分類演算法

載入需要的包後,我們要讀入數據:

train <- read.csv("../input/train.csv", stringsAsFactors = F)test <- read.csv("../input/test.csv", stringsAsFactors = F)full <- bind_rows(train, test) # 結合訓練數據和測試數據# 檢查數據str(full)

代碼運行得出的結果:

## "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 May Peel)" ...## $ 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" ...

目前我們大概了解了所要分析的變數,它們的類型,以及一部分觀測值。我們發現我們將要分析12個變數的1309個觀測值。由於我們無法從一些變數名完全看出其意思,以下列表將幫助我們理解各個變數名所帶有的含義:

2 特徵工程

2.1 名字分析

我首先注意到的變數是乘客名字,因為我們可以將這個變數切片出更多變數來用於幫助我們的預測,或是用於創建新的變數。比如,乘客名字中包含了乘客頭銜,我們還可以利用姓氏來代表其家庭。現在就開始特徵工程分析吧!

# 從乘客名字中分離出頭銜full$Title <- gsub("(.*, )|(\..*)", "", full$Name)# 根據性別顯示頭銜table(full$Sex, full$Title)

運行結果:

## ## Capt Col Don Dona Dr Jonkheer Lady Major Master Miss Mlle Mme## female 0 0 0 1 1 0 1 0 0 260 2 1## male 1 4 1 0 7 1 0 2 61 0 0 0## ## Mr Mrs Ms Rev Sir the Countess## female 0 197 2 0 0 1## male 757 0 0 8 1 0

處理特別頭銜:

# 將數量較少的頭銜歸類於「稀有頭銜」(rare_title)rare_title <- c("Dona", "Lady", "the Countess","Capt", "Col", "Don", "Dr", "Major", "Rev", "Sir", "Jonkheer")# 對 Mlle, Ms, Mme 這類頭銜重新歸類,因為這類頭銜意思重複full$Title[full$Title == "Mlle"] <- "Miss" full$Title[full$Title == "Ms"] <- "Miss"full$Title[full$Title == "Mme"] <- "Mrs" full$Title[full$Title %in% rare_title] <- "Rare Title"# 再次根據性別顯示頭銜數量統計table(full$Sex, full$Title)

運行結果:

## ## Master Miss Mr Mrs Rare Title## female 0 264 0 198 4## male 61 0 757 0 25

接下來,從乘客名字中提取姓氏:

full$Surname <- sapply(full$Name, function(x) strsplit(x, split = "[,.]")[[1]][1])

於是我們得出一共有875個不一樣的姓氏。

2.2 家庭存活情況分析

現在乘客名字已經被處理成新的變數了,我們可以將其更進一步創建家庭變數。首先我們要基於兄弟姐妹、伴侶(也許有人會有多個伴侶)、雙親及兒女數量來創建家庭規模變數。

# 創建家庭規模變數,其規模包括乘客自己,所以最後有+1full$Fsize <- full$SibSp + full$Parch + 1# 創建家庭變數full$Family <- paste(full$Surname, full$Fsize, sep="_")

家庭規模變數是怎樣的呢?為了更好了解其對倖存情況的影響,我們先根據訓練數據中的這個變數來繪圖:

# 使用 ggplot2 來創建家庭規模和倖存情況之間的關係圖ggplot(full[1:891,], aes(x = Fsize, fill = factor(Survived))) + geom_bar(stat="count", position="dodge") + scale_x_continuous(breaks=c(1:11)) + labs(x = "Family Size") + theme_few()

啊哈。現在我們能看出孤身一人在船上的和家庭規模在四人以上在船上的人倖存率較低。我們可以將這個變數歸為三個等級,因為大型家庭的數量相對較少。現在創建離散家庭規模變數。

# 將家庭規模變數離散化,分成個人、小型家庭、大型家庭三類full$FsizeD[full$Fsize == 1] <- "singleton"full$FsizeD[full$Fsize < 5 & full$Fsize > 1] <- "small"full$FsizeD[full$Fsize > 4] <- "large"# 通過使用馬賽克圖(mosaic plot)來展示不同家庭規模倖存情況mosaicplot(table(full$FsizeD, full$Survived), main="Family Size by Survival", shade=TRUE)

馬賽克圖再次證明了我們之前的觀點,即孤身一人在船上的和家庭中有四人以上在船上的人倖存率較低,而小型家庭倖存率則更高。我打算對年齡變數進行分析,然而數據集中有263行的年齡變數是缺失值,所以我們必須先處理好缺失值才能分析年齡。

2.3 處理更多變數

還有哪些變數可以進行特徵工程分析呢?也許艙位號也是很有價值的變數,因為艙位號中包含了甲板編號。接下來就研究一下。

# 可以看出,這項變數包含了大量的缺失值full$Cabin[1:28]

運行結果:

## [1] "" "C85" "" "C123" "" ## [6] "" "E46" "" "" "" ## [11] "G6" "C103" "" "" "" ## [16] "" "" "" "" "" ## [21] "" "D56" "" "A6" "" ## [26] "" "" "C23 C25 C27"

# 艙位號的首個字元便是甲板編號。例如:strsplit(full$Cabin[2], NULL)[[1]]

運行結果:

## [1] "C" "8" "5"

接下來創建甲板編號變數,獲取從A - F的甲板編號:

full$Deck<-factor(sapply(full$Cabin, function(x) strsplit(x, NULL)[[1]][1]))

這部分我們還可以做很多,比如研究列出多個房間的船艙(例如,row 28: 「C23 C25 C27」),但考慮到這項變數比較少,我們不會深入研究這個。

3 缺失數據處理

現在我們將要開始研究缺失數據,通過插補來對其進行處理。我們有很多方法處理缺失數據。考慮到該數據集的規模較小,我們不會將帶有缺失數據的觀測(即行)或是變數(即列)完全刪除。我們可以根據數據分布情況用合適的值替換缺失數據,這類值包括均值、中位數或是眾數,或者,我們也可以用預測值。我們將使用後兩種方法處理缺失值,而我會根據可視化數據來做出決定。

3.1 合理值插補

62號乘客和830號乘客的登船港口是缺失的:

full[c(62, 830), "Embarked"]

運行結果:

## [1] "" ""

經過一番考慮,我推斷當前數據中有兩個變數和登船港口有關,這兩個變數是乘客等級和票價:

# 排除帶有缺失值的乘客IDembark_fare <- full %>% filter(PassengerId != 62 & PassengerId != 830)# 使用ggplot2來可視化登船港口情況,乘客等級和中位數票價ggplot(embark_fare, aes(x = Embarked, y = Fare, fill = factor(Pclass))) + geom_boxplot() + geom_hline(aes(yintercept=80), colour="red", linetype="dashed", lwd=2) + scale_y_continuous(labels=dollar_format()) + theme_few()

從圖中我們可以看出,在 Charbourg ("C") 登船的頭等艙乘客的票價中位數大概是$80,符合我們的缺失登船港口乘客數據的情況。所以,我們可以將這兩個乘客的登船港口缺失值替換為"C"。

full$Embarked[c(62, 830)] <- "C"

我們繼續處理缺失數據,第1044行的乘客信息的票價是缺失的。

full[1044, ]

運行結果:

## PassengerId Survived Pclass Name Sex Age SibSp Parch## 1044 1044 NA 3 Storey, Mr. Thomas male 60.5 0 0## Ticket Fare Cabin Embarked Title Surname Fsize Family FsizeD## 1044 3701 NA S Mr Storey 1 Storey_1 singleton## Deck## 1044 <NA>

這是名由 Southampton("S") 登船的三等艙乘客。根據相同船艙等級和登船港口,我們對其他乘客的票價情況進行可視化處理。

ggplot(full[full$Pclass == "3" & full$Embarked == "S", ], aes(x = Fare)) + geom_density(fill = "#99d6ff", alpha=0.4) + geom_vline(aes(xintercept=median(Fare, na.rm=T)), colour="red", linetype="dashed", lwd=1) + scale_x_continuous(labels=dollar_format()) + theme_few()

根據這個數據可視化圖,由於他們的船艙等級和登船港口一致,我們可以將該缺失值替換為該圖中中位數的數值,即$8.05。

full$Fare[1044] <- median(full[full$Pclass == "3" & full$Embarked == "S", ]$Fare, na.rm = TRUE)

3.2 預測性插補

就如我們在之前所說,該數據集中有一些缺失的年齡數據。我們將在插補年齡的缺失數據時採用一些更有意思的方法。我們會創建根據其他變數預測年齡值的模型。

顯示缺失年齡值的數量:

sum(is.na(full$Age))

運行結果:

## [1] 263

我們也可以使用rpart包(遞歸劃分回歸)來預測缺失年齡值,但在這裡我會使用mice包。你可以在這個鏈接里閱讀到更多有關使用R中的鏈式方程來進行多重插補的內容。我們先要分解因子變數,然後使用mice包來插補缺失數據。

# 分解因子變數factor_vars <- c("PassengerId","Pclass","Sex","Embarked", "Title","Surname","Family","FsizeD")full[factor_vars] <- lapply(full[factor_vars], function(x) as.factor(x))# 設置隨機種子set.seed(129)# 使用mice包進行插補,除去無用變數,這裡使用了隨機森林方法mice_mod <- mice(full[, !names(full) %in% c("PassengerId","Name","Ticket","Cabin","Family","Surname","Survived")], method="rf")

運行結果:

## ## iter imp variable## 1 1 Age Deck## 1 2 Age Deck## 1 3 Age Deck## 1 4 Age Deck## 1 5 Age Deck## 2 1 Age Deck## 2 2 Age Deck## 2 3 Age Deck## 2 4 Age Deck## 2 5 Age Deck## 3 1 Age Deck## 3 2 Age Deck## 3 3 Age Deck## 3 4 Age Deck## 3 5 Age Deck## 4 1 Age Deck## 4 2 Age Deck## 4 3 Age Deck## 4 4 Age Deck## 4 5 Age Deck## 5 1 Age Deck## 5 2 Age Deck## 5 3 Age Deck## 5 4 Age Deck## 5 5 Age Deck

保存輸出結果:

mice_output <- complete(mice_mod)

現在對比一下原數據中乘客年齡分布和我們得出的結果,確保沒出現太大偏差:

# 繪出年齡分布圖par(mfrow=c(1,2))hist(full$Age, freq=F, main="Age: Original Data", col="darkgreen", ylim=c(0,0.04))hist(mice_output$Age, freq=F, main="Age: MICE Output", col="lightgreen", ylim=c(0,0.04))

看起來效果不錯,接下來我們利用mice模型中的數據來替換原數據里的年齡值:

# 替換數據full$Age <- mice_output$Age# 顯示新數據中缺失數據的情況sum(is.na(full$Age))

運行結果:

## [1] 0

終於完成了對需要變數的所有缺失數據的處理!現在我們有了完整的年齡變數值,接下來我打算做些收尾工作。我們之後可以利用年齡值做更多的特徵工程。

3.3 特徵工程:第二階段

現在我們獲得了所有人的年齡,我們可以創建一些基於年齡生成的變數:母親孩子。判斷孩子的條件是18歲以下,而母親的條件是:1)是女性,2)超過18歲,3)至少有一個小孩,4)頭銜不是"小姐"。

我們首先了解一下年齡和倖存情況的關係:

ggplot(full[1:891,], aes(Age, fill = factor(Survived))) + geom_histogram() + # 我把性別也包括在內,因為我們知道性別也是一個很重要的因素 facet_grid(.~Sex) + theme_few()

創建分辨孩子的列,並判斷是孩子還是成人:

full$Child[full$Age < 18] <- "Child"full$Child[full$Age >= 18] <- "Adult"# 查看統計table(full$Child, full$Survived)

運行結果:

## ## 0 1## Adult 484 274## Child 65 68

看來孩子的倖存率確實會高一些,但即使是孩子也無法幸免於難。接下來,我們的特徵工程還需要完成創建母親變數。也許我們可以期待在泰坦尼克號上的母親們能有更大的倖存幾率。

# 加入母親變數full$Mother <- "Not Mother"full$Mother[full$Sex == "female" & full$Parch > 0 & full$Age > 18 & full$Title != "Miss"] <- "Mother"# 顯示統計table(full$Mother, full$Survived)

運行結果:

## ## 0 1## Mother 16 39## Not Mother 533 303

我們需要的變數現在都已經處理好了,裡面也沒有缺失數據。謹慎起見,我再檢查一下:

md.pattern(full)

運行結果:

## Warning in data.matrix(x): NAs introduced by coercion## Warning in data.matrix(x): NAs introduced by coercion## Warning in data.matrix(x): NAs introduced by coercion## PassengerId Pclass Sex Age SibSp Parch Fare Embarked Title Surname## 150 1 1 1 1 1 1 1 1 1 1## 61 1 1 1 1 1 1 1 1 1 1## 54 1 1 1 1 1 1 1 1 1 1## 511 1 1 1 1 1 1 1 1 1 1## 30 1 1 1 1 1 1 1 1 1 1## 235 1 1 1 1 1 1 1 1 1 1## 176 1 1 1 1 1 1 1 1 1 1## 92 1 1 1 1 1 1 1 1 1 1## 0 0 0 0 0 0 0 0 0 0## Fsize Family FsizeD Child Mother Ticket Survived Deck Name Cabin ## 150 1 1 1 1 1 1 1 1 0 0 2## 61 1 1 1 1 1 1 0 1 0 0 3## 54 1 1 1 1 1 0 1 1 0 0 3## 511 1 1 1 1 1 1 1 0 0 0 3## 30 1 1 1 1 1 0 0 1 0 0 4## 235 1 1 1 1 1 1 0 0 0 0 4## 176 1 1 1 1 1 0 1 0 0 0 4## 92 1 1 1 1 1 0 0 0 0 0 5## 0 0 0 0 0 352 418 1014 1309 1309 4402

太棒了!我們現在已經把所有泰坦尼克號數據集里的相關缺失值處理好了,其中用到的mice包也榜上了大忙。我們現在可以創建一些變數來幫我們建立預測倖存情況的模型。

4 預測

我們終於能根據我們處理好的數據開始預測在泰坦尼克號上的乘客倖存情況了。我們將使用隨機森林randomForest分類演算法來進行預測。

4.1 分離出訓練數據和測試數據

我們的第一步是分離用於訓練的數據和用於測試的數據。

train <- full[1:891,]test <- full[892:1309,]

4.2 建立模型

然後對訓練數據使用隨機森林randomForest。

# 設置隨機種子set.seed(754)# 建立模型(注意:不是所有變數都要使用)rf_model <- randomForest(factor(Survived) ~ Pclass + Sex + Age + SibSp + Parch + Fare + Embarked + Title + FsizeD + Child + Mother, data = train)# 顯示模型誤差plot(rf_model, ylim=c(0,0.36))legend("topright", colnames(rf_model$err.rate), col=1:3, fill=1:3)

黑線表示總體誤差率,大概在20%以下。紅線和綠線分別表示死亡和倖存的誤差率。現在我們就可以知道,我們預測死亡情況比預測倖存情況更為準確。那意味著什麼呢?

4.3 變數重要性分析

我們來通過繪製平均精度下降圖來看看變數重要性分布。

# 得到重要性importance <- importance(rf_model)varImportance <- data.frame(Variables = row.names(importance), Importance = round(importance[ ,"MeanDecreaseGini"],2))# 創建基於重要性的排名變數rankImportance <- varImportance %>% mutate(Rank = paste0("#",dense_rank(desc(Importance))))# 使用ggplot2來繪製相關變數重要性示意圖ggplot(rankImportance, aes(x = reorder(Variables, Importance), y = Importance, fill = Importance)) + geom_bar(stat="identity") + geom_text(aes(x = Variables, y = 0.5, label = Rank), hjust=0, vjust=0.55, size = 4, colour = "red") + labs(x = "Variables") + coord_flip() + theme_few()

哇,幸虧我們創建了頭銜變數!頭銜變數是這些變數中相關重要性最高的一個。同時我對乘客等級排在第五位感到驚奇,也許這是因為我們看了電影《泰坦尼克號》得到的偏見。

4.4 開始預測!

我們終於來到了最後一步:做出預測!但我們依舊可以重複前面的步驟來對預測進行調整,比如採用別的模型或是變數的不同組合,來達到更好的預測效果。

# 使用測試數據進行預測prediction <- predict(rf_model, test)# 用兩個列把結果保存到數據框中:乘客ID(PassengerId)和預測倖存情況(Survived (prediction))solution <- data.frame(PassengerID = test$PassengerId, Survived = prediction)# 將結果寫入文件中write.csv(solution, file = "rf_mod_Solution.csv", row.names = F)

5 結論

感謝你閱讀我對Kaggle數據集的第一次嘗試。我打算在這方面能做得更多,當然了,歡迎大家對這個新手的筆記進行評論和建議。

註:本文由 Excelsior vcvc 翻譯自 Megan L. Risdal. Exploring the Titanic Dataset

推薦閱讀:

大數據分析作業-怎麼從導演及演員判斷電影值不值得看?
用大數據精準預測地震,每年將有1.3萬人免於受難!
R和Python數據結構對比
一篇文章告訴你,該學R還是Python
第一份數據報告的誕生

TAG:Kaggle | 数据分析 | 「泰坦尼克号」沉没事故 |