【Kaggle實例分析】Titanic Machine Learning from Disaster

你是不是也學了很久R語言的卻還沒有完完整整的把數據分析的整個流程走一遍。

跟著本文操作練習也許會是個不錯的開始。

看完本文你將了解到:

1. 如何用R語言進行數據分析

2. 數據分析師的主要工作流程

3. 如何參加Kaggle比賽

======================

引言

本文採用Kaggle中比較知名的數據集Titanic MachinenLearning from Disaster作為分析數據源。分析目的是根據訓練集預測部分乘客在沉船事件中是否會存活?

當然不是問你Jack &Rose ^_^

該數據集被評為五大最適合數據分析練手項目之一。Five data science projects to learn data science

說明:本文除了細微處有所改動外,主體部分翻譯借鑒自Megan L. Risdal文章。

本文的基本按照數據分析的整個流程進行:

  • 數據清洗

  • 特徵工程

  • 缺失值

  • 模型設計與預測

下面具體看來

=======================

數據導入與概覽

# 載入相應包nlibrary(ggplot2) # 可視化nlibrary(ggthemes) # 可視化nlibrary(scales) # 可視化nlibrary(dplyr) # 數據處理nlibrary(mice) # 缺失值填補nlibrary(randomForest) # 建模 n

載入完畢後,先將數據導入

【為了保證文章的可讀性,部分代碼運行結果以圖片形式貼出】

train <- read.csv(train.csv, stringsAsFactors = F)ntest <- read.csv(test.csv, stringsAsFactors = F)n#初步觀察數據n# 檢查數據nstr(train)nstr(test)nhead(train)nhead(test)n

#可以看到,除了Survived欄位不同外,其他欄位均相同。合併訓練集與測試集,為下一步數據清洗做準備nfull <- bind_rows(train, test) nstr(full)nsummary(full)n

合併後的數據除了生存情況(Survived)中缺失值NA有418個(需要預測的),年齡(Age)中缺失值有263個,船票費用(Fare)中缺失值有1個。

目前,我們已經對變數,變數類型,及其前幾個取值情況有了初步的了解。n我們知道:我們有1309 個觀測,其中訓練集891個,測試集418個。 我們的目標是要預測生存情況(Survived)——因變數 可供使用的自變數11個

其各個變數對應的含義列示如下

-------------------------------

數據清洗

a. 觀察姓名變數

首先,我注意到在乘客名字(Name)中,有一個非常顯著的特點:乘客頭銜每個名字當中都包含了具體的稱謂或者說是頭銜,將這部分信息提取出來後可以作為非常有用一個新變數,可以幫助我們預測。此外也可以用乘客的姓代替家庭,生成家庭變數。下面開始著手操作!

# 從乘客名字中提取頭銜nfull$Title <- gsub((.*, )|(..*), , full$Name)nn# 查看按照性別劃分的頭銜數量?ntable(full$Sex, full$Title)n

# 對於那些出現頻率較低的頭銜合併為一類 nrare_title <- c(Dona, Lady, the Countess,Capt, Col, Don, Dr, Major, Rev, Sir, Jonkheer)nn# 對於一些稱呼進行重新指定(按含義) 如mlle, ms指小姐, mme 指女士nfull$Title[full$Title ==Mlle]<- Miss nfull$Title[full$Title ==Ms] <- Missnfull$Title[full$Title ==Mme]<- Mrs nfull$Title[full$Title %in% rare_title] <- Rare Titlenn# 重新查看替換後的情況ntable(full$Sex, full$Title)n

# 最後從乘客姓名中,提取姓氏nfull$Surname <- sapply(full$Name, function(x) strsplit(x, split = [,.])[[1]][1])n

我們有 875 唯一姓氏. 有時間的話可以通過發掘乘客姓氏之間的聯繫,也許會有意外發現,這裡就先不做深入探討了.

b.家庭情況是否會影響生存結果?

目前為止我們已經處理完乘客姓名這一變數,並從其中提取了一些新的變數。 下一步考慮衍生一些家庭相關的變數 首先,生成家庭人數family size 這一變數。可以基於已有變數SubSp和Parch(具體含義參照上面)

# 生成家庭人數變數,包括自己在內nfull$Fsize <- full$SibSp + full$Parch + 1nn# 生成一個家庭變數:以姓_家庭人數 格式nfull$Family <- paste(full$Surname, full$Fsize, sep=_)nn# 使用 ggplot2 繪製家庭人數與生存情況之間的關係nggplot(full[1:891,], aes(x = Fsize, fill = factor(Survived))) +n geom_bar(stat=count, position=dodge) +n scale_x_continuous(breaks=c(1:11)) +n labs(x = Family Size) +n theme_few()n

通過圖形我們可以明顯發現以下特點:

- 1 個人上船和家庭人數>4人的家庭的存活人數小於死亡人數

- 2 家庭人數size在[2:4]的存活人數要高於死亡人數

因此我們可以將家庭人數變數進行分段合併,明顯的可以分為3段:個人,小家庭,大家庭,由此生成新變數.

# 離散化nfull$FsizeD[full$Fsize == 1] <- singletonnfull$FsizeD[full$Fsize < 5 & full$Fsize > 1]<- smallnfull$FsizeD[full$Fsize > 4] <- largenn# 通過馬賽克圖(mosaic plot)查看家庭規模與生存情況之間關係nmosaicplot(table(full$FsizeD,full$Survived), main=Family Size by Survival, shade=TRUE)n

從圖上也可以顯而易見的觀察出來,個人與大家庭不利於生存下來,而相對的小家庭當中生存率相對較高

c. 試著生成更多變數

可以發現在乘客客艙變數 passenger cabin 也存在一些有價值的信息如客艙層數 deck. .

# 可以看出這一變數有很多缺失值nfull$Cabin[1:28]n

# 第一個字母即為客艙層數.如:nstrsplit(full$Cabin[2], NULL)[[1]]nn## [1]n"C" "8" "5"nn# 建立一個層數變數(Deck)變化取值從 A - F:nfull$Deck<-factor(sapply(full$Cabin, function(x) strsplit(x, NULL)[[1]][1]))nsummary(full$Deck)n

這裡有很多可以進一步操作的地方,如有些乘客名下包含很多間房 (e.g., row 28: "C23 C25nC27"), 但是考慮到這一變數數值的稀疏性(sparseness),有1014 個缺失值。 後面就不再進一步考慮。

------------------------

缺失值

現在我們開始對原始數據當中的缺失值進行處理(填補)。

具體做法有很多種,考慮到數據集本身較小,樣本數也不多,因而不能直接整行或者整列刪除缺失值樣本。那麼只能通過現有數據和變數對缺失值進行預估填補 例如:可以用均值中位數模型 填補缺失值,這裡使用後面兩種方式進行。

a. 登船港口缺失——中位數

# 乘客 62 and 830 缺少登船港口信息。nfull[c(62, 830), Embarked]n## [1] "" ""n

我估計對於有相同艙位等級(passenger class票價(Fare的乘客也許有著相同的 登船港口位置embarkment .我們可以看到他們支付的票價分別為: $ 80 和 $ 80 同時他們的艙位等級分別是: 1 和 1 . 那麼他們最有可能是在哪個港口登船的呢?

# 去除缺失值乘客的IDnembark_fare <- full %>% filter(PassengerId != 62 & PassengerId != 830)nn# 用 ggplot2 繪製embarkment, passenger class, & median fare 三者關係圖nggplot(embark_fare, aes(x = Embarked, y = Fare, fill = factor(Pclass))) +n geom_boxplot() +n geom_hline(aes(yintercept=80), n colour=red, linetype=dashed, lwd=2) +n scale_y_continuous(labels=dollar_format()) +n theme_few()n

很明顯!

從港口 (C)出發的頭等艙支付的票價的中位數為80。因此我們可以放心的把處於頭等艙且票價在$80的乘客62和830 的出發港口缺失值替換為C.

# 因為他們票價為80且處於頭等艙,因而他們很有可能都是從港口C登船的。nfull$Embarked[c(62, 830)] <- Cn

b. 票價缺失 ——中位數

這裡發現1044行的乘客票價為空值

# 提取1044行數據nfull[1044, ]n

這是從港口Southampton (S)出發的三等艙乘客。 從相同港口出發且處於相同艙位的乘客數目為 (n = 494).

ggplot(full[full$Pclass == 3 & full$Embarked == S, ], n aes(x = Fare)) +n 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()n

從得到的圖形上看,將缺失值用中位數進行替換是合理的。替換數值為$8.05.

# 基於出發港口和客艙等級,替換票價缺失值nfull$Fare[1044] <- median(full[full$Pclass == 3 & full$Embarked == S, ]$Fare, na.rm = TRUE)n

c. 年齡缺失——預測填補

最後,正如我們之前觀察到的,在用戶年齡(Age 中有大量的缺失存在。 這裡我們將基於年齡和其他變數構建一個預測模型對年齡缺失值進行預測 nn

# S統計缺失數量nsum(is.na(full$Age))n## [1] 263n

通常我們會使用 rpartn(recursive partitioning for regression) 包來做缺失值預測 在這裡我將使用 mice 包進行處理。具體理由,你可以通過閱讀關於基於鏈式方程 Chained Equations多重插補法Multiple Imputation(MICE)的內容MICEn(PDF). 在這之前我們先要對因子變數(factor variables)因子化,然後再進行多重插補法。 nn

# 使因子變數因子化nfactor_vars <- c(PassengerId,Pclass,Sex,Embarked,n Title,Surname,Family,FsizeD)nnfull[factor_vars] <- lapply(full[factor_vars],function(x) as.factor(x))nn# 設置隨機種子nset.seed(129)nn# 執行多重插補法,剔除一些沒什麼用的變數:nmice_mod <- mice(full[, !names(full) %in% c(PassengerId,Name,Ticket,Cabin,Family,Surname,Survived)], method=rf) nn# 保存完整輸出 nmice_output <- complete(mice_mod)n

讓我們對比數據填補前與填補後的數據分布情況。確保數據分布沒用發生偏移

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

結果看起來不錯,那麼下面可以用mice模型的結果對原年齡數據進行替換。

# MICE模型結果替換年齡變數.nfull$Age <- mice_output$Agenn# 檢查缺失值是否被完全替換了nsum(is.na(full$Age))nn## [1] 0n

現在,我們已經完成了對所有重要變數的缺失值的替換工作。 但是這一切還沒結束,我們可以對年齡變數進一步對的劃分 ..

-----------------------------------

特徵工程2

現在我們知道每一位乘客的年齡,那麼我們可以基於年齡生成一些變數如兒童(Child)和 母親(Mother).

劃分標準:

- 兒童 : 年齡Age < 18

- 母親 : 1 女性; 2 年齡 > 18; 3n擁有超過1個子女 4 頭銜不是Miss.

# 首先我們來看年齡與生存情況之間的關係nggplot(full[1:891,], aes(Age, fill = factor(Survived))) + n geom_histogram() + n # 分性別來看,因為前面我們知道 性別對於生存情況有重要影響n facet_grid(.~Sex) + n theme_few()n

# 生成兒童(child)變數, 並且基於此劃分兒童child與成人adultnfull$Child[full$Age < 18] <- Childnfull$Child[full$Age >= 18] <- Adultnn# 展示對應人數ntable(full$Child, full$Survived)n## n## 0 1n## Adult 484 274n## Child 65 68n

從結果看,兒童的生存率要高於成人但是這並不意味著作為兒童就一定可以生還。正如我們當年看《泰坦尼克號》電影時,最後船員要求母親和兒童先上船一樣。

下面來生成母親這個變數. nn

# 生成母親變數nfull$Mother <- NotMothernnfull$Mother[full$Sex ==female & full$Parch > 0 & full$Age > 18 & full$Title != Miss] <- Mothernn# 統計對於數量ntable(full$Mother, full$Survived)nn## n## 0 1n## Mother 16 39n## Not Mother 533 303nn# 對新生成的兩個變數完成因子化。nfull$Child <- factor(full$Child)nfull$Mother <- factor(full$Mother)n

至此,所有我們需要的變數都已經生成,並且其中沒有缺失值。 為了保險起見,我們進行二次確認。

#這個起到什麼作用?nmd.pattern(full)n

現在我們終於完成對泰坦尼克數據集(the Titanic dataset)中所有的變數缺失值的填補,並基於原有變數構建了一些新變數,希望這些可以在最終的生存情況預測時起到幫助。

----------------------

模型設定與預測

在完成上面的工作之後,我們進入到最後一步:預測泰坦尼克號上乘客的生存狀況。 在這裡我們使用隨機森林分類演算法(The RandomForestnClassification Algorithm) 我們前期那麼多工作都是為了這一步服務的。

a. 拆分訓練集與測試集

我們第一步需要將數據變回原先的訓練集與測試集.

# 將數據拆分為訓練集與測試集ntrain <- full[1:891,]ntest <- full[892:1309,]n

b. 建立模型

我們利用訓練集訓練建立隨機森林 randomForest 模型.

# 設置隨機種子nset.seed(754)nn# 建立模型l (注意: 不是所有可用變數全部加入)nrf_model <- randomForest(factor(Survived) ~ Pclass + Sex + Age + SibSp + Sex*Parch + Fare + Embarked + Title + FsizeD + Child + Mother, data = train)nn# 顯示模型誤差nplot(rf_model, ylim=c(0,0.36))nlegend(topright, colnames(rf_model$err.rate), col=1:3, fill=1:3)n

黑色那條線表示:整體誤差率(the overall error rate)低於20% 紅色和綠色分別表示:遇難與生還的誤差率 至此相對於生還來說,我們可以更準確的預測出死亡。

c.變數重要性

通過計算Gini係數得到相應變數的重要性排序

# 獲取重要性係數nimportance <- importance(rf_model)nvarImportance <- data.frame(Variables = row.names(importance), Importance = round(importance[ ,MeanDecreaseGini],2))nn# 基於重要性係數排列變數nrankImportance <- varImportance %>% mutate(Rank = paste0(#,dense_rank(desc(Importance))))nn#通過 ggplot2 繪製相關重要性變數圖nggplot(rankImportance, aes(x = reorder(Variables, Importance), n y = Importance, fill = Importance)) +n geom_bar(stat=identity) + n geom_text(aes(x = Variables, y = 0.5, label = Rank),n hjust=0, vjust=0.55, size = 4, colour= red) +n labs(x = Variables) +n coord_flip() + n theme_few()n

我們從圖上可以看出哪些變數才是對我們預測最重要的變數 從圖上看頭銜和性別對於生存情況影響最大,其次是船票價格和年齡。而相應的乘客艙位排第五。 而最出乎我意料的是母親和孩子對於生存與否的影響最小排在11和10. 這個我小時候看泰坦尼克號的印象相差甚遠。

d.預測

下面到了最後一步了----預測結果! 在這裡可以把剛才建立的模型直接應用在測試集上。 但為了達到最佳的預測結果,我們也可以重新構建不同的模型,或者用不同的變數進行組合。

# 基於測試集進行預測nprediction <- predict(rf_model, test)nn# 將結果保存為數據框,按照Kaggle提交文檔的格式要求。[兩列:PassengerId and Survived (prediction)]nsolution <- data.frame(PassengerID = test$PassengerId, Survived = prediction)nn# 將結果寫入文件nwrite.csv(solution, file = rf_mod_Solution1.csv, row.names = F)n

得到的文件大家就可以上傳Kaggle獲取自己的排名情況啦~

當然前提你得有個Kaggle賬號。註冊kaggle

比賽頁面:Titanic: Machine Learning from Disaster

Ps:Kaggle上不光可以參加比賽,還可以學習其他優秀選手分享的經驗、以及一些代碼等。

藉此機會,好好逛逛吧~

-----------------------------

總結

本次案例講解到這裡就全部結束了。 後面大家想要繼續提高排名,提高預測的準確率,則需要構建一些新的變數或是構建新的模型,大家可以自由探索和發揮。

感謝你花時間閱讀這樣一篇基於Kaggle 數據集的數據分析流程的介紹,希望對你有幫助~

【如果你有提高預測準確率的方法請留言或者私信告訴我~】

參考資料:

Titanic: Machine Learning from Disaster -by Megan L. Risdal


推薦閱讀:

乾貨--線性回歸模型與CART樹的比較
利用R語言做可重複性報告研究
R語言可視化——多邊形與地圖填充
Rpy|字元串處理|綜述

TAG:R编程语言 | 数据分析 | Kaggle |