
大綱
- 序
- 數(shù)據(jù)來源
- 工具介紹
- 數(shù)據(jù)分析
- 數(shù)據(jù)概覽
- 測定基準
- 分析特征重要性
- 數(shù)據(jù)展示(可視化)
- 特征工程
- 數(shù)據(jù)清洗
- 提取新特征
- 特征選取
- 數(shù)據(jù)導出
- 機器學習預測
- 選擇算法
- 訓練及評估
- 預測結(jié)果
- 人物畫像
- 總結(jié)
- 結(jié)合歷史核對結(jié)論
- 一些特別的數(shù)據(jù)
- 結(jié)語
- 參考
序

- 1912年,當時世界上體積最龐大、內(nèi)部設(shè)施最豪華的客運輪船,有“永不沉沒”美譽的泰坦尼克號,在她的處女航中撞冰山沉入大西洋底3700米處,船上1500多人喪生。
- 1985年,美國和法國聯(lián)合搜索隊發(fā)現(xiàn)泰坦尼克號殘骸。
- 1997年,詹姆斯·卡梅隆執(zhí)導美國電影《泰坦尼克號》,將整個驚心動魄的過程首次以電影的方式還原。
- 2012年,《泰坦尼克號》3D版在中國內(nèi)地重映,再次喚起人們對艘富有傳奇色彩的巨輪的緬懷。
- 2017年5月,有科學家表示,細菌正在蠶食泰坦尼克號沉船殘骸。據(jù)最近的估計,到2030年,這艘船可能會迎來它的末日,徹底消失。
假設(shè)如果真有穿越這回事,突然哪一天你的靈魂穿越到了正在航行的泰坦尼克號上的某個乘客身上,你發(fā)現(xiàn)你身處某層客艙內(nèi),被你的同伴稱為Mr/Miss,而你只有現(xiàn)代的記憶。突然一聲巨響同時船身劇烈搖晃,人們驚慌不知所措,那么此時此刻的你,有多大機率可以活下來?
接下來,我會通過數(shù)據(jù)分析,告訴你有哪些特征會影響你的存活機率。
數(shù)據(jù)來源

著名的數(shù)據(jù)分析競賽網(wǎng)站Kaggle上,舉行了很多數(shù)據(jù)分析比賽,其中比較著名的就有 泰坦尼克號乘客生還預測 。
Kaggle提供的數(shù)據(jù)集中,共有1309名乘客數(shù)據(jù),其中891是已知存活情況,剩下418則是需要進行分析預測的。
提供的數(shù)據(jù)特征如下:
PassengerId: 乘客編號
Survived :存活情況(存活:1 ; 死亡:0)
Pclass : 客艙等級
Name : 乘客姓名
Sex : 性別
Age : 年齡
SibSp : 同乘的兄弟姐妹/配偶數(shù)
Parch : 同乘的父母/小孩數(shù)
Ticket : 船票編號
Fare : 船票價格
Cabin :客艙號
Embarked : 登船港口
PassengerId 是數(shù)據(jù)唯一序號;Survived 是存活情況,為預測標記特征;剩下的10個是原始特征數(shù)據(jù)。
下面我將嘗試分析所得原始數(shù)據(jù),通過構(gòu)建特征工程,建模預測乘客的生存情況。
工具介紹

以下數(shù)據(jù)分析過程中,我使用的是我自己用Ruby語言開發(fā)的一套簡單的可通用的數(shù)據(jù)分析工具,集成了數(shù)據(jù)分析報告、數(shù)據(jù)可視化、特征工程構(gòu)建、機器學習分類模型訓練、數(shù)據(jù)導出等。
先看下有哪些命令可以使用:

每個任務(wù)的用途和前置子任務(wù)用途描述如下:

每個任務(wù)都可以單獨執(zhí)行,具體用法會在后面的分析過程中逐個演示。
數(shù)據(jù)分析

說到數(shù)據(jù)挖掘,是把散亂數(shù)據(jù)轉(zhuǎn)換成「有價值」信息的過程,數(shù)據(jù)是可以是數(shù)字或者文本內(nèi)容甚至圖像,而信息是有語義的、人腦可理解的報告、圖表。
數(shù)據(jù)分析的結(jié)果,就是把數(shù)字轉(zhuǎn)化成人類可理解的信息。
數(shù)據(jù)概覽
先看一眼已有的數(shù)據(jù)概括。
第一步,配置好數(shù)據(jù)路徑:

第二步,執(zhí)行數(shù)據(jù)分析任務(wù) profile_data 輸出數(shù)據(jù)摘要信息:
rake app:classifiers:titanic:profile_data

得到基本的數(shù)據(jù)信息:
Rows: 1309, Cols: 12,Columns: ["PassengerId", "Survived", "Pclass", "Name", "Sex", "Age", "SibSp", "Parch", "Ticket", "Fare", "Cabin", "Embarked"]
(注:uniq_proportions 是數(shù)據(jù)唯一值數(shù)量的占比值,下面描述時直接x100,如 0.616 描述為 61.6%,方便理解)
從輸出統(tǒng)計中可以得出以下信息:
-
Survived中549條是0(死亡),342條是1(生還);占比(uniq_proportions)分別是61.6%和38.4,死亡率很高。有418條missing(表示沒有值),是要預測的數(shù)據(jù)量。 -
Pclass的unit_count(唯一數(shù)量)是3,通過uniq_frequencies看出分為1,2,3 個類別,對應(yīng)頭等艙、二等艙和三等艙),其中3占了過半為54.2% - 有幾個特征有缺失數(shù)據(jù)(
missing不為0的),Age為263,Fare為1,Cabin為1014,Embarked為2 -
Sex乘客性別分布中男性占 64.4%,女性 35.6%(人多不一定是好事,后面分析中發(fā)現(xiàn)男性死亡很高) -
Age乘客年齡分布中最小0.17(嬰兒),最大是80歲,平均為29.881 -
Ticket總數(shù)是1309,而唯一數(shù)是929,說明有一票多人使用情況(這個信息在后面的特征提取中有用) -
Fare最高是512.3292,而平均是33.295,貧富分化差距不?。ㄍ瑯?,這個信息在后面的特征提取中會有用) -
Embarked有3組為"S"、"Q"、"C",其中"S"占69.9%,我猜這可能是啟航的港口,登船港口跟存活率有什么關(guān)系這是個疑問
測定基準-沒事跑個分
先跑個分看看,執(zhí)行訓練任務(wù)train:
rake app:classifiers:titanic:train summary=n

在什么數(shù)據(jù)都沒改動的情況下,使用所有特征數(shù)據(jù)來跑訓練任務(wù)(任務(wù)沒有配置時,默認使用樸素貝葉斯分類)和CV(Cross Validation,交叉驗證),得到平均的 F1 Score 是 0.75889,意味這個是我們的模型的最低基準,最終構(gòu)建出的模型分數(shù)要高于這個之上才是可靠的模型。
分析特征重要性
這么多特征,有哪些特征是跟存活率比較相關(guān)的?
可以使用這個數(shù)據(jù)分析任務(wù),再做進一步分析:使用任務(wù)參數(shù) label_column=Survived 來指明哪個是要預測的標記特征,再執(zhí)行一次數(shù)據(jù)分析任務(wù):
rake app:classifiers:titanic:profile_data label_column=Survived

這次的輸出就有些差異,可以看到是針對 Survived 有值的數(shù)據(jù)來統(tǒng)計:
Profile 891 data which Survived is not nil
還多了特征分布占比統(tǒng)計:
Calcualte proportions of "Survived"
從而能知道各個特征值與標簽特征Survived的對比,得出符合該特征時的“生存率”。
例如 Pclass 的統(tǒng)計:
+-------------+-------+------------+--------------------------------------------------------------+--------------------------------------------------------------+
| | count | uniq_count | Survived=0 | Survived=1 |
+-------------+-------+------------+--------------------------------------------------------------+--------------------------------------------------------------+
| Pclass | 891 | 3 | {3=>[372, "75.8%"], 1=>[80, "37.0%"], 2=>[97, "52.7%"]} | {3=>[119, "24.2%"], 1=>[136, "63.0%"], 2=>[87, "47.3%"]} |
Pclass=3(Survived=0)有372條,占75.8%,意思是三等艙的乘客中死亡率是75.8%;
Pclass=1(Survived=1)有136條,占63.0%,意味頭等艙的乘客中有63.0%是存活了下來。
以此類推,性別數(shù)據(jù)中:
+-------------+-------+------------+--------------------------------------------------------------+--------------------------------------------------------------+
| | count | uniq_count | Survived=0 | Survived=1 |
+-------------+-------+------------+--------------------------------------------------------------+--------------------------------------------------------------+
| Sex | 891 | 2 | {"male"=>[468, "81.1%"], "female"=>[81, "25.8%"]} | {"male"=>[109, "18.9%"], "female"=>[233, "74.2%"]} |
- 男性死亡率是81.1%,女性的死亡率是25.8%;
- 男性存活率是18.9%,女性的存活率是74.2%。
不用建模光是根據(jù)這2組數(shù)據(jù)就已經(jīng)可以想象到,如果當時是女性,并入駐在頭等艙,存活下來的機率是很高的;反之,如果是男性,又是入駐在三等艙區(qū)的,差不多5個人中只能活1個。。。
這些分類結(jié)果比較少、存活率傾斜比較明顯的,顯然是比較重要的特征。那么其他特征值分布比較離散的,對最終的存活率影響是怎樣的呢?
在這里可以先使用 evaluate_features (特征評估)任務(wù)來對每個特征進行一次初步評估,原理是單獨使用每個特征來自動訓練(默認使用了樸素貝葉斯做分類算法)并進行Cross Validation(交叉驗證),得出Accuracy(準確率)、Precision(精確率)、Recall(召回值),用來評估該特征對預測標記特征的重要程度。
執(zhí)行特征評估任務(wù),并使用選項label_column指明Survived 為標記特征:
rake app:classifiers:titanic:evaluate_features label_column=Survived

留意提示:
Tips: if Average Accuracy > 0.8 means it's an important feature; if Precision is 0 means it's not reliable
從分析結(jié)果中可以看到:
-
Sex的Accuracy大于0.8,屬于比較重要的特征。 -
PassengerId以及Name的Precision是0,表示特征值太離散,不適合用來做預測的特征,而事實上這2個特征的值都是唯一不重復的(Name只有2條重復),確實不適合做為分類特征。 -
Age、SibSp、Parch、Cabin、和Embarked的Precision值都偏低(< 0.3),說明這個幾項相關(guān)性不足(有部分是受到缺失值得影響)。
數(shù)據(jù)展示(可視化)
人是視覺生物,天生對圖像信息比數(shù)字、文字信息更敏感,為了能更直觀的表達統(tǒng)計數(shù)據(jù)所體現(xiàn)的意義,可將數(shù)據(jù)以各種圖表的形式來可做視化展示,輔助對數(shù)據(jù)的理解。
我編寫的這套工具中集成了一些圖表的Web UI,可以直接使用數(shù)據(jù)分析任務(wù)的輸出報表來生成圖表,可以查看每一個特征的統(tǒng)計。
例如性別數(shù)量統(tǒng)計:

男多女少!那人多力量就大嗎?讓數(shù)據(jù)回答,加入存活狀態(tài)對比看看:

很明顯,不用看數(shù)字都看得明白:女性存活率更高!
乘客的年齡與人數(shù)的分布統(tǒng)計圖:

從圖中可看出在乘客中,人數(shù)最多的是24歲、有47人,整體呈現(xiàn)出中間層年齡段人數(shù)居多,人群以年輕、中年人為主。年輕力壯就會更有利嗎?未必!
在接下來的特征工程的后半段中會有更多的圖表展示,這里先不展開了。
特征工程

什么是特征工程?
數(shù)據(jù)和特征決定了機器學習的上限,而模型和算法只是逼近這個上限而已。而特征工程的目的是最大限度地從原始數(shù)據(jù)中提取特征以供算法和模型使用。顧名思義,特征工程其實就是一項工程活動,它是能夠?qū)?shù)據(jù)像藝術(shù)一般展現(xiàn)的技術(shù)。這樣說的原因是好的特征工程很好的混合了專業(yè)領(lǐng)域知識、直覺和基本的數(shù)學能力。
數(shù)據(jù)清洗
通過前面的數(shù)據(jù)分析知道有一些數(shù)據(jù)有缺失,需要先填充。
這里可以使用console=y 選項來以交互模式來執(zhí)行數(shù)據(jù)清洗任務(wù)clean_data:
rake app:classifiers:titanic:clean_data console=y

填補 Fare
看下 Fare 數(shù)據(jù)的缺失情況:

只有一條缺失記錄,而這個乘客的Ticket只有他一個人在使用,無法通過查找相同票號的價格來填補。
船票的價格顯然是跟 Pclass (客艙等級)及 Cabin(客艙號)有關(guān)的,因此使用具有相同Pclass和Cabin的中位數(shù)來填補:
df.where(df['Fare'].not_eq(nil) & df['Pclass'].eq(3) & df['Cabin'].eq(nil) )['Fare'].median
=> 8.05
填補 Age
df['Age'].missing
=> 263
發(fā)現(xiàn)所有 Name 中有“Mr.” “Mrs.” 等頭銜字眼,因此提取出來可以作為一個輔助特征Title,然后再使用具有相同Title和Sex的中位數(shù)來填補。
填補 Cabin
Cabin 缺失的比較多,但發(fā)現(xiàn)相同Cabin的Ticket也相同,因此可以反推具有相同Ticket的乘客也會住在同一個Cabin內(nèi),因此可以找出具有相同Ticket但Cabin不為空的乘客數(shù)據(jù)來填補:
df['Cabin'].missing
=> 1014
same_cabins = 0
df.where(df['Cabin'].eq(nil)).each_row_with_index do |row, idx|
same_cabin = df.where( df['Cabin'].not_eq(nil) & df['Ticket'].eq(row['Ticket']) )
if same_cabin.size != 0
same_cabins += 1
end
end
same_cabins
# => 16
發(fā)現(xiàn)這類數(shù)據(jù)也不多,只有16條。
其他的不確定性太多放棄填補。Cabin 數(shù)據(jù)雖然不完整,但還可以用來提取新的特征,下文有說明。
填補 Embarked
+-----+-------------+----------+--------+-------------------------------------------+--------+-----+-------+-------+--------+------+-------+----------+
| | PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked |
+-----+-------------+----------+--------+-------------------------------------------+--------+-----+-------+-------+--------+------+-------+----------+
| 62 | 62 | 1 | 1 | Icard, Miss. Amelie | female | 38 | 0 | 0 | 113572 | 80 | B28 | |
| 830 | 830 | 1 | 1 | Stone, Mrs. George Nelson (Martha Evelyn) | female | 62 | 0 | 0 | 113572 | 80 | B28 | |
+-----+-------------+----------+--------+-------------------------------------------+--------+-----+-------+-------+--------+------+-------+----------+
Embarked 的缺失只有2條,觀察數(shù)據(jù)后發(fā)現(xiàn)是2位女士,共用同一張Ticket “113572”,住在同一個Cabin “B28”,在同一個Pclass 1,因此推斷她們可能是認識的,因此也非常有可能是一起出行并在同一個港口登船,所以不用分別處理。另外我按常理推斷相同港口出售的票是相連的,可取Ticket前4位數(shù)字相同的表示是相同港口,最后找出有相同Pclass和相似Ticket中 Embarked 的眾數(shù):
df.filter_rows { |p| p['Ticket'].to_s.first(4) == '113572'.first(4) && p['Pclass'] == 1 } ['Embarked'].frequencies.max_index
=> #<Daru::Vector(1)>
C 7
得出是 “C”。
完成數(shù)據(jù)清洗后,查看現(xiàn)有的數(shù)據(jù)概況:

可以看到只有 Cabin 還有998條是缺失的了,至此數(shù)據(jù)清洗完畢,接下來做新特征提取。
提取新特征

問題:應(yīng)該提取出哪些特征?
面對這樣的群體災難事件場景中,有什么因素會影響到個人生存機會呢?是個人的年齡、體力、社會身份、財富、擁有的權(quán)力?是因為你是女性、帶著孩子的母親?小孩和老人行動緩慢會很不利?人緣好朋友多會更有優(yōu)勢?拖家?guī)Э谑抢圪樳€是助力,還是獨善其身更有利?亦或是你當時所處的位置導致了最后的結(jié)局?
設(shè)想
結(jié)合已有的數(shù)據(jù),我覺得有如下這些因素可以嘗試:
- Sex(性別):性別是人類個體之間最大的生理特征區(qū)別,影響到體力、社交地位,在群體中往往也受到優(yōu)待(女士優(yōu)先),而且前面的特征評估已說明這個特征很重要
- AgeLevel(年齡層):年齡影響到體力、社交地位,年紀小的、或者年紀大的在群體中往往也受到優(yōu)待(尊老愛幼);這里可以分組為(child, young, midlife, aged),基于
Age提取 - SocialTitle(社交頭銜):在群體活動中,不同身份顯然也會影響到群體決策 ;這里分組為 Mrs, Miss, Mr and Master,基于
Name提取 - IncomeClass(收入水平): 收入決定了所買的船票類型,也就決定了客艙所在的位置,以及社交地位;分組為 no-income, lower-income, middle-income, upper-income), 可基于
Fare和Pclass提取 - SocialClass(社交地位):一個人可以沒有很多錢,但他從事的職業(yè)、貴族身份在群體活動中是有影響力的;分為 lower-class, middle-class, upper-class,基于
Title,IncomeClass提取 - FamilyRole(家庭角色):如果你是位父親,你所肩負的責任能讓你愿意作出巨大犧牲;一位母親在災難面前為了保護孩子往往迸發(fā)超出常人的生存欲望;可分組為grandparent, couple, father, mother, child;可基于
Sex,AgeLevel,SibSp,Parch - CabinArea(艙位所在區(qū)域):顯然這會影響到逃生的時機,基于
Cabin - Mates(伙伴數(shù)量):這次航行中的同行人數(shù)(有多少人使用相同的
Ticket) - FamilySize(同船的家庭成員數(shù)量): 家人在一起互助肯定比一群陌生人有更多的信任和生存機會;基于
SibSp和Parch - TravelAlone(是否孤身上路):
Mates+FamilySize,無論是朋友還是家人,多一個熟人在身邊也許就比別人多一份生存機率
實現(xiàn)

提取Sex(性別)
使用原有數(shù)據(jù),不需要改動。
提取 AgeLevel(年齡層)
按如下年齡劃分:
age_level = lambda { |age, default="unknown"|
{
0..14.5 => 'child', # 少年0-14
15..35.5 => 'young', # 青年15-35
36..60.5 => 'midlife', # 中年36-60
61..90.5 => 'aged', # 老年61-90
}.select { |range| range === age }.values[0] || default
}
df['AgeLevel'] = df['Age'].map { |age| age_level.call(age) }
提取 SocialTitle(社交頭銜)
# extract "Title" from "Name"
df['NameTitle'] = df['Name'].map { |name| name.match(/, ([^.]+)./)[1] }
# extract SocialTitle: what was his social title?
social_title = lambda { |title, sex|
title = title.sub("the ", "")
title = 'Mrs' if title == 'Dr' && sex == 'female' # a special case
{
%w{Mr Don Major Capt Jonkheer Rev Col Dr Sir}=> 'Mr',
%w{Miss Mlle Ms Lady Dona} => 'Miss',
%w{Mrs Countess Mme} => 'Mrs',
%w{Master} => 'Master',
}.select { |_| _.include? title }.values[0]
}
df['SocialTitle'] = df['NameTitle', 'Sex'].map(:row) { |row| social_title.call(row["NameTitle"], row["Sex"]) }
這樣把一些數(shù)量較少的NameTitle都歸類起來,另外提取這個特征時,發(fā)現(xiàn)有一條特殊記錄,有一個Dr是女性,因此做了特殊處理:
title = 'Mrs' if title == 'Dr' && sex == 'female' # a special case
提取 IncomeClass(收入水平)
考慮到游船還是需要一定的經(jīng)濟收入,我將上層階級、中產(chǎn)階級、下層階級 按照 1:2:7 的比例(下層等級的數(shù)量是上層的數(shù)量之和),獲取對應(yīng)的唯一票價的分位數(shù),作為分層的水平線:
uniq_fare = df['Fare'].uniq
income_classes = {"no-income": 0, "lower-income": uniq_fare.percentile(100-70), "middle-income": uniq_fare.percentile(100-20), "upper-income": uniq_fare.percentile(100-10) }.stringify_keys
# => {"no-income"=>0, "lower-income"=>10.5, "middle-income"=>57.75, "upper-income"=>82.2667}
得出的數(shù)據(jù)統(tǒng)計如下:
df['IncomeClass'].uniq_frequencies
# => {"middle-income"=>587, "lower-income"=>513, "upper-income"=>192, "no-income"=>17}
df['IncomeClass'].uniq_proportions
# => {"middle-income"=>0.448, "lower-income"=>0.392, "upper-income"=>0.147, "no-income"=>0.013}
參考資料:
提取 SocialClass(社交地位)
社交地位中“名”與”利“起主要作用(別不服氣,現(xiàn)實社會的游戲規(guī)則就是這樣)。乘客數(shù)據(jù)中的頭銜信息很重要,因此可以把有貴族頭銜以及IncomeClass是高級的都視作upper-class;中產(chǎn)收入的歸為middle-class;其他的就是lower-class。
代碼簡單不展示了,只是有些英國的貴族頭銜信息我不熟悉需要查下參考資料。
參考資料:
提取 FamilyRole(家庭角色)
這里判別規(guī)則比較復雜,看圖理解

提取 CabinArea(艙位所在區(qū)域)
這里要先提取一個Deck(甲板)特征,這個是根據(jù)Cabin的首位字母得出的,可以分為A、B、C、D、E、F、G和T,未知的則標記為N/A。
# extract "Deck" from "Cabin"
df['Deck'] = df['Cabin'].map { |cabin| cabin && cabin[0] || 'N/A' }
可以參考泰坦尼克號的剖面圖:

從圖上可以看出從字母越前越靠近輪船上層,意味逃生的路徑更短(這個和后面做新特征分析得到的統(tǒng)計信息是吻合的)。
這也是為什么Cabin即使有缺失數(shù)據(jù),也可以換個姿勢用來提取新特征。
CabinArea 則由Pclass和Deck組合而成,如 "P-1 D-C",意思是“頭等艙的C區(qū)”
# extract CabinArea: where was he? () based on Pclass, Deck
# e.g.: "P-1 D-C"
df['CabinArea'] = df['Pclass', 'Deck'].map(:row) { |x| "P-#{x['Pclass']} D-#{x['Deck']}" }
提取 Mates(伙伴數(shù)量)
tickets = df['Ticket'].to_a.group_by(&:itself)
df['Mates'] = df['Ticket'].map { |ticket| tickets[ticket].size }
df['Mates'].uniq_proportions
# => {1=>0.545, 2=>0.202, 3=>0.112, 4=>0.049, 5=>0.027, 7=>0.027, 6=>0.018, 8=>0.012, 11=>0.008}
發(fā)現(xiàn)有近55%的乘客是獨自出行,而后面做新數(shù)據(jù)分析發(fā)現(xiàn),這樣的人的生存率,不到3成。
提取 FamilySize(同船的家庭成員數(shù)量)
很簡單,SibSp + Parch
# extract "FamilySize" from "SibSp" and "Parch"
df['FamilySize'] = df['SibSp'] + df['Parch']
df['FamilySize'].uniq_proportions
# {0=>0.604, 1=>0.18, 2=>0.121, 3=>0.033, 5=>0.019, 4=>0.017, 6=>0.012, 10=>0.008, 7=>0.006}
有6成是不帶家人,然而,后面的數(shù)據(jù)分析顯示,這個不是好主議。
提取 TravelAlone(是否孤身上路)
是否有伙伴或親人,只有個人則標記為1,否則取0
df['TravelAlone'] = df['Mates', 'FamilySize'].map(:row) { |row| row.sum == 1 ? 1 : 0 }
df['TravelAlone'].uniq_frequencies
# => {1=>0.506, 0=>0.494}
各占一半。
把所有特征提取相關(guān)代碼添加到 extract_features子任務(wù)中保存,至此,新的特征構(gòu)建完畢!
至此我得到以下特征的數(shù)據(jù):
- Sex(性別)
- AgeLevel(年齡層)
- SocialTitle(社交頭銜)
- IncomeClass(收入水平)
- SocialClass(社交地位)
- FamilyRole(家庭角色)
- CabinArea(艙位所在區(qū)域)
- Mates(伙伴數(shù)量)
- FamilySize(同船的家庭成員數(shù)量)
- TravelAlone(是否孤身上路)
這些新特征是否真的對預測存活有幫助,接下來讓看下新特征的分析和評估。
特征選取 - 評估及選取新特征
重新跑一次數(shù)據(jù)分析任務(wù)profile_data,這次加上任務(wù)參數(shù)feature_columns 來指定要分析的特征數(shù)據(jù): feature_columns=Sex,AgeLevel,SocialTitle,IncomeClass,SocialClass,FamilyRole,CabinArea,Mates,FamilySize,TravelAlone
執(zhí)行的完整命令為:
rake app:classifiers:titanic:profile_data label_column=Survived feature_columns=Sex,AgeLevel,SocialTitle,IncomeClass,SocialClass,FamilyRole,CabinArea,Mates,FamilySize,TravelAlone


可以了解新增特征數(shù)據(jù)的整體概況,下面接著逐一分析每個新特征的數(shù)據(jù)統(tǒng)計。
新特征數(shù)據(jù)分析
要針對某個feature 分析,可以增加參數(shù)profile_feature=$feature_name 來執(zhí)行數(shù)據(jù)分析任務(wù)profile_data,例如:
rake app:classifiers:titanic:profile_data label_column=Survived feature_columns=Sex,AgeLevel,SocialTitle,IncomeClass,SocialClass,FamilyRole,CabinArea,Mates,FamilySize,TravelAlone profile_feature=Sex
得到:

Sex(性別):
結(jié)合存活狀態(tài)分布圖:

在這場事故中,女性擁有更高的生存機率:74.2%;男性僅有18.9%。
性別為什么在這樣的災難中其關(guān)鍵性作用?從事后幸存者的口中得知,這是因為一個男人的一個命令。我們帶著疑問繼續(xù)看下其他特征的統(tǒng)計分析。
AgeLevel(年齡層):
年齡層統(tǒng)計結(jié)果:
Profile feature "AgeLevel"
+----------+-------+-------------+----------------+----------------+
| AgeLevel | count | proportions | Survived=0 | Survived=1 |
+----------+-------+-------------+----------------+----------------+
| young | 591 | 0.663 | [379, "64.1%"] | [212, "35.9%"] |
| midlife | 196 | 0.22 | [118, "60.2%"] | [78, "39.8%"] |
| child | 82 | 0.092 | [35, "42.7%"] | [47, "57.3%"] |
| aged | 22 | 0.025 | [17, "77.3%"] | [5, "22.7%"] |
+----------+-------+-------------+----------------+----------------+
按生理階段分為4組:
-
child:少年(0-14) -
young:青年(15-35) -
midlife:中年(36-60) -
aged:老年(61-90)
生存率最高的是少年們,存活率是57.3%;老年人有最高的死亡率:77.3%;身強力壯的青年死亡率第二,為64.1%;更有生活經(jīng)驗的中年存活率不到4成,為39.8%。
看圖更直觀:

從這些數(shù)據(jù)對比可以看出,年齡與存活率不是線性的關(guān)系,不是越低越好,也不是越高越差。思考下,年齡的分組,實際上也形成社會關(guān)系、家庭角色的分組差異,因此在這里統(tǒng)計表現(xiàn)可能只是表面結(jié)果,并不是深層原因。
SocialTitle(社交頭銜):
Profile feature "SocialTitle"
+-------------+-------+-------------+----------------+----------------+
| SocialTitle | count | proportions | Survived=0 | Survived=1 |
+-------------+-------+-------------+----------------+----------------+
| Mr | 537 | 0.603 | [451, "84.0%"] | [86, "16.0%"] |
| Miss | 186 | 0.209 | [55, "29.6%"] | [131, "70.4%"] |
| Mrs | 128 | 0.144 | [26, "20.3%"] | [102, "79.7%"] |
| Master | 40 | 0.045 | [17, "42.5%"] | [23, "57.5%"] |
+-------------+-------+-------------+----------------+----------------+
被稱呼為 Mrs(太太)的存活率最高為79.7%,這個只是簡單的稱呼信息,別人尊稱你一聲“先生”、“小姐”或者“太太”,不會決定你的生死,但你可以據(jù)此快速了解到,當有人稱呼你為Mr或Miss時,你在這場事故中的存活概率。

IncomeClass(收入水平):
Profile feature "IncomeClass"
+---------------+-------+-------------+----------------+----------------+
| IncomeClass | count | proportions | Survived=0 | Survived=1 |
+---------------+-------+-------------+----------------+----------------+
| middle-income | 403 | 0.452 | [222, "55.1%"] | [181, "44.9%"] |
| lower-income | 348 | 0.391 | [273, "78.4%"] | [75, "21.6%"] |
| upper-income | 125 | 0.14 | [40, "32.0%"] | [85, "68.0%"] |
| no-income | 15 | 0.017 | [14, "93.3%"] | [1, "6.7%"] |
+---------------+-------+-------------+----------------+----------------+
存活率最低的是no-income只有6.7%,最高的是upper-income為68.0%,收入水平跟存活率有很明顯的線性關(guān)系:收入水平越高生存機率越高。這樣的結(jié)果很殘酷也很現(xiàn)實,更富有則擁有更多的生存優(yōu)勢,從來如此。

有錢就一定能活下來嗎?
SocialClass(社交地位):
Profile feature "SocialClass"
+--------------+-------+-------------+----------------+----------------+
| SocialClass | count | proportions | Survived=0 | Survived=1 |
+--------------+-------+-------------+----------------+----------------+
| lower-class | 361 | 0.405 | [286, "79.2%"] | [75, "20.8%"] |
| middle-class | 357 | 0.401 | [201, "56.3%"] | [156, "43.7%"] |
| upper-class | 173 | 0.194 | [62, "35.8%"] | [111, "64.2%"] |
+--------------+-------+-------------+----------------+----------------+
存活率最低的是lower-class 為20.8%,最高的是upper-class為64.2%,社交地位跟存活率有很明顯的線性關(guān)系:社交地位越高生存機率越高。這是說沒有利,也要有名嗎?數(shù)據(jù)不會撒謊,同樣很現(xiàn)實。

FamilyRole(家庭角色):
不看外因看內(nèi)因,從家庭的角度看看:
Profile feature "FamilyRole"
+-------------+-------+-------------+----------------+----------------+
| FamilyRole | count | proportions | Survived=0 | Survived=1 |
+-------------+-------+-------------+----------------+----------------+
| single | 530 | 0.595 | [363, "68.5%"] | [167, "31.5%"] |
| mother | 87 | 0.098 | [27, "31.0%"] | [60, "69.0%"] |
| child | 82 | 0.092 | [35, "42.7%"] | [47, "57.3%"] |
| husband | 72 | 0.081 | [57, "79.2%"] | [15, "20.8%"] |
| wife | 49 | 0.055 | [8, "16.3%"] | [41, "83.7%"] |
| father | 49 | 0.055 | [42, "85.7%"] | [7, "14.3%"] |
| grandparent | 22 | 0.025 | [17, "77.3%"] | [5, "22.7%"] |
+-------------+-------+-------------+----------------+----------------+
存活率最低的是father父親(14.3%),緊接是husband丈夫(20.8%);最高的不是mother母親(69.%),而是沒有孩子的wife太太(83.7%)。

作為男性,明明是群體中最具有強壯的體魄,又有相對更豐富的生存經(jīng)驗,怎么反而在這場事故中就成了生存機率最低的?這里再提出一個疑問。
CabinArea(艙位所在區(qū)域):
“天時不如地利”,艙位所在區(qū)域是否影響乘客到達甲板的時間、登上救生艇的機會?
Profile feature "CabinArea"
+-----------+-------+-------------+----------------+----------------+
| CabinArea | count | proportions | Survived=0 | Survived=1 |
+-----------+-------+-------------+----------------+----------------+
| P-3 D-N/A | 478 | 0.536 | [366, "76.6%"] | [112, "23.4%"] |
| P-2 D-N/A | 167 | 0.187 | [93, "55.7%"] | [74, "44.3%"] |
| P-1 D-C | 67 | 0.075 | [26, "38.8%"] | [41, "61.2%"] |
| P-1 D-B | 48 | 0.054 | [12, "25.0%"] | [36, "75.0%"] |
| P-1 D-N/A | 31 | 0.035 | [19, "61.3%"] | [12, "38.7%"] |
| P-1 D-D | 29 | 0.033 | [7, "24.1%"] | [22, "75.9%"] |
| P-1 D-E | 25 | 0.028 | [7, "28.0%"] | [18, "72.0%"] |
| P-1 D-A | 15 | 0.017 | [8, "53.3%"] | [7, "46.7%"] |
| P-2 D-F | 8 | 0.009 | [1, "12.5%"] | [7, "87.5%"] |
| P-3 D-F | 6 | 0.007 | [4, "66.7%"] | [2, "33.3%"] |
| P-2 D-E | 5 | 0.006 | [2, "40.0%"] | [3, "60.0%"] |
| P-2 D-D | 4 | 0.004 | [1, "25.0%"] | [3, "75.0%"] |
| P-3 D-G | 4 | 0.004 | [2, "50.0%"] | [2, "50.0%"] |
| P-3 D-E | 3 | 0.003 | [0, "0.0%"] | [3, "100.0%"] |
| P-1 D-T | 1 | 0.001 | [1, "100.0%"] | [0, "0.0%"] |
+-----------+-------+-------------+----------------+----------------+
- 存活率最低的是
P-1 D-T,頭等艙T層甲板,只有1個人,存活率只有0%; - 存活率最高的是
P-3 D-E,頭等艙E層甲板,有3個人,都活了下來,存活率100%; - 幸存人數(shù)最多的是
P-3 D-N/A,三等艙其他區(qū)域,112人活了下來,而存活率只有23.4%; - 人數(shù)次之的是
P-2 D-N/A,二等艙其他區(qū)域,存活率44.3%; - 第三的是
P-1 D-C,頭等艙C層甲板,存活率61.2%;

觀察泰坦尼克號的艙室布局圖:

可以發(fā)現(xiàn)頭等艙分布在船體的各段部位,比二等、三等艙有更好的出入口、更接近甲板,因此應(yīng)該擁有更有利的生存機會,跟得出的統(tǒng)計信息是吻合的。
你在哪里出發(fā)也就決定了你能走到哪里?輸在起跑線上,又有誰愿意呢。
Mates(伙伴數(shù)量):
中國古語云:“地利不如人和”,“在家靠父母,出門靠朋友”,看看出行的伙伴數(shù)量:
Profile feature "Mates"
+-------+-------+-------------+----------------+----------------+
| Mates | count | proportions | Survived=0 | Survived=1 |
+-------+-------+-------------+----------------+----------------+
| 1 | 481 | 0.54 | [351, "73.0%"] | [130, "27.0%"] |
| 2 | 181 | 0.203 | [88, "48.6%"] | [93, "51.4%"] |
| 3 | 101 | 0.113 | [35, "34.7%"] | [66, "65.3%"] |
| 4 | 44 | 0.049 | [12, "27.3%"] | [32, "72.7%"] |
| 7 | 24 | 0.027 | [19, "79.2%"] | [5, "20.8%"] |
| 5 | 21 | 0.024 | [14, "66.7%"] | [7, "33.3%"] |
| 6 | 19 | 0.021 | [15, "78.9%"] | [4, "21.1%"] |
| 8 | 13 | 0.015 | [8, "61.5%"] | [5, "38.5%"] |
| 11 | 7 | 0.008 | [7, "100.0%"] | [0, "0.0%"] |
+-------+-------+-------------+----------------+----------------+
- 1個獨自出行是最多的,但存活率只有 27.0%;
- 2個人結(jié)伴出行是數(shù)量排第二,存活率為 51.4%;
- 人數(shù)最多的有11個,有存活狀態(tài)的有7人,然后一個也沒活下來,存活率是0%;
- 存活率最高的是4個人一組的,有44個,存活率是72.7%;
- 除了0%以外存活率最低的是7個人一組的,有24人,存活率比單人還低,僅有20.8%;
從數(shù)據(jù)看,伙伴數(shù)量跟存活率并非是線性關(guān)系,而是呈現(xiàn)“凸函數(shù)”特征,2、3、4這樣的中間值都有過半的存活機率;1及6、7、11 兩端數(shù)值的生存率均不過3成。這是否是表明在出行時,3到4人是最佳的團隊人數(shù),人數(shù)過少或過多都不利團隊生存。

FamilySize(同船的家庭成員數(shù)量):
家庭規(guī)模對生存的影響:
Profile feature "FamilySize"
+------------+-------+-------------+----------------+----------------+
| FamilySize | count | proportions | Survived=0 | Survived=1 |
+------------+-------+-------------+----------------+----------------+
| 0 | 537 | 0.603 | [374, "69.6%"] | [163, "30.4%"] |
| 1 | 161 | 0.181 | [72, "44.7%"] | [89, "55.3%"] |
| 2 | 102 | 0.114 | [43, "42.2%"] | [59, "57.8%"] |
| 3 | 29 | 0.033 | [8, "27.6%"] | [21, "72.4%"] |
| 5 | 22 | 0.025 | [19, "86.4%"] | [3, "13.6%"] |
| 4 | 15 | 0.017 | [12, "80.0%"] | [3, "20.0%"] |
| 6 | 12 | 0.013 | [8, "66.7%"] | [4, "33.3%"] |
| 10 | 7 | 0.008 | [7, "100.0%"] | [0, "0.0%"] |
| 7 | 6 | 0.007 | [6, "100.0%"] | [0, "0.0%"] |
+------------+-------+-------------+----------------+----------------+
- 沒有與親人同行的有537人,存活率30.4%;
- 存活率最低的去到0%,是成員數(shù)最大的7和10;
- 存活率最高是72.4%,是成員數(shù)為3;而人數(shù)到了4時生存率急劇下降到20.0%;
3人加上自己,也就是4,跟前面的Mates=4時生存率最高的結(jié)果重疊了,這應(yīng)該是團體票都是同一個票號的緣故。

TravelAlone(是否孤身上路):
Profile feature "TravelAlone"
+-------------+-------+-------------+----------------+----------------+
| TravelAlone | count | proportions | Survived=0 | Survived=1 |
+-------------+-------+-------------+----------------+----------------+
| 1 | 448 | 0.503 | [327, "73.0%"] | [121, "27.0%"] |
| 0 | 443 | 0.497 | [222, "50.1%"] | [221, "49.9%"] |
+-------------+-------+-------------+----------------+----------------+
當你一個人時,只有27.0%的機率存活;你有伙伴時,有49.9%的機會活下去。

所以出遠門時,還是結(jié)伴出行吧。
Mates、FamilySize、TravelAlone 這幾組數(shù)據(jù)表明,組隊是有必要的,但人數(shù)不宜過多,4個人在一起是最有優(yōu)勢的組合。
數(shù)據(jù)導出
導出需要的特征數(shù)據(jù)進行存檔,執(zhí)行數(shù)據(jù)導出任務(wù)export_features:
rake app:classifiers:titanic:export_features label_column=Survived feature_columns=Sex,AgeLevel,SocialTitle,IncomeClass,SocialClass,FamilyRole,CabinArea,Mates,FamilySize,TravelAlone
Save to tmp/classifiers_output/titanic_survival_prediction-export.csv
導出數(shù)據(jù)在這里可以下載: titanic_survival_prediction-export.csv
機器學習預測

選擇算法
訓練數(shù)據(jù)有帶標注的特征、需要預測是否存活,顯然這是一個非常典型的二元分類問題,屬于監(jiān)督學習分類問題;特征數(shù)據(jù)中有連續(xù)數(shù)值(continuous)和離散值(discrete),數(shù)據(jù)量也不大,這里我選擇了決策樹算法(Decisiontree)。另外,選擇這個算法,這也有種命運在做決策的意味。

訓練及評估
為了讓要預測特征比較語義,我加了一個新的特征 Survival,只是簡單把1和0值映射為Survived和No:
# extract "Survival" so that can use it as the label_column
df['Survival'] = df['Survived'].map { |survived| survived && (survived == 1 ? 'Survived' : 'No') }
然后配置模型參數(shù):

訓練及交叉驗證:
執(zhí)行訓練任務(wù)train會自動把有標記的數(shù)據(jù)集按比例拆分成訓練集和測試集,進行交叉驗證:
rake app:classifiers:titanic:train summary=n

模型評估結(jié)果的F1 Score平均分是 0.81489,高于最開始的評分基準值,說明新增的特征整體上有更高的相關(guān)性。
要評估每個新特征的相關(guān)性,執(zhí)行特征評估任務(wù) evaluate_features:
rake app:classifiers:titanic:evaluate_features

對比原始數(shù)據(jù)的評估結(jié)果,新特征的相關(guān)性更高些(CabinArea 由于有數(shù)據(jù)缺失,精確度不高)。
訓練結(jié)束后選擇 Persist trained data?(y/n):y 保存訓練結(jié)果,任務(wù)會自動保存模型圖像,可得到一棵決定存活與否的命運決策之樹:

預測結(jié)果

認為模型可信后,就可以對待預測數(shù)據(jù)進行預測,執(zhí)行預測任務(wù) predict:
rake app:classifiers:titanic:predict summary=n

至此,完成了從數(shù)據(jù)分析到數(shù)據(jù)清洗、特征工程、機器學習建模以及最后的乘客生存狀態(tài)預測。
人物畫像

有了特征提取的規(guī)則,以及預測的模型,最后我們可以做下簡單的人物畫像,構(gòu)建一個persona任務(wù),運行:
rake app:classifiers:titanic:persona
輸出如下:
... ...
"Klasen, Miss. Gertrud Emilia" was a child Miss.
She was 1 years old.
She was a child, with 2 families.
She was in a middle-income family since She spent $12.1833 to buy a 3rd Class ticket.
She embarked from Southampton port.
> Did She survive? Probably No.
"Parker, Mr. Clifford Richard" was a young Mr.
He was 28 years old.
He was single.
He was in a lower-income family since He spent $10.5 to buy a 2nd Class ticket.
He embarked from Southampton port.
> Did He survive? Probably No.
"Chaffee, Mrs. Herbert Fuller (Carrie Constance Toogood)" was a midlife Mrs.
She was 47 years old.
She was a wife, with Her husband.
She was in a upper-income family since She spent $61.175 to buy a 1st Class ticket.
She embarked from Southampton port.
Her cabin was on E Deck.
> Did She survive? Probably Survived.
... ...
那么,現(xiàn)在在船上的“你”,可以對照“自己”的特征,預測下自己有多少機會可以活下來了。
總結(jié)

結(jié)合歷史核對結(jié)論
數(shù)據(jù)分析的目的:通過數(shù)據(jù),找出事件背后的原因、規(guī)律,用來改進、預防未來相似的事件。
在先前的分析數(shù)據(jù)時提出的疑問:
性別為什么在這樣的災難中其關(guān)鍵性作用?
為什么婦女兒童這類“弱者”反而更能生存?
根據(jù)泰坦尼克號唯一存活副船長查爾斯·萊特勒,事后描述,面對沉船災難時,船長愛德華·約翰·史密斯(Edward J. Smith)在最后的時刻下命令,命令先讓婦女和兒童上救生艇,許多乘客顯得十分平靜,一些人則拒絕與家人分開。

在生命面前,一切都是平等的。是什么抑制了生存的本能,讓人能作出如此犧牲讓步?顯然,是社會文明發(fā)展的結(jié)果。
是不是可以有個結(jié)論:災難發(fā)生時,如果跟高素質(zhì)的人群聚集,弱者能得到優(yōu)待?
再看另外一個問題:
有錢就一定能活下來嗎?
回憶錄中有描述:
亞斯特四世(當時世界第一首富)把懷著五個月身孕的妻子送上4號救生艇后,站在甲板上,帶著他的狗,點燃一根雪茄煙,對劃向遠處的小艇最后呼喊:"我愛你們?。?/p>
還有一個問題:
作為男性,明明是群體中最具有強壯的體魄,又有相對更豐富的生存經(jīng)驗,怎么反而在這場事故中就成了生存機率最低的?
作為男人、作為孩子的父親、作為妻子的丈夫,肩頭上扛的一邊感情,另一邊是責任,面對災難是作出了何種抉擇,其實不用多說,看一段回憶錄:
一名叫那瓦特列的法國商人把兩個孩子送上了救生艇,委托幾名婦女代為照顧,自己卻拒絕上船。
兩個兒子得救后,世界各地的報紙紛紛登載兩個孩子的照片,直到他們的母親從照片上認出了他們。不幸的是,孩子們永遠失去了父親。
新婚燕爾的麗德帕絲同丈夫去美國度蜜月,她死死抱住丈夫不愿獨自逃生。
丈夫在萬般無奈中一拳將她打昏。麗德帕絲醒來時,她已在一條在海上漂浮的救生艇上了。
此后,她終生未再嫁,以此懷念亡夫。

一些特別數(shù)據(jù)
- 人數(shù)最多的家庭:

是 Sage一家,有11個人,大部分沒有存活。
- 年紀最小的乘客:

一個不足2個月的女嬰。
- 年紀最大的乘客:

是一位80高齡的老先生,當時幸存了下來。
- 唯一的女博士:

- 沒有家人的小孩:

不知為何沒有登記父母記錄,還是真的沒有父母陪同出行。
- 同名的人:

不確定是真的巧合同名,還是乘客登記信息有誤。
結(jié)語

泰坦尼克號事件發(fā)生距今已有105年,即使是年齡最小的幸存者也早已不在人世,這個事件留下給世人的教訓不應(yīng)該只有對影視作品的唏噓與緬懷。
在冰冷沒有情感的數(shù)據(jù)上進行分析解讀,我們發(fā)現(xiàn)“物競天擇,適者生存”這樣的大自然生存法則,在泰坦尼克這樣的災難事件上完全失去了作用。
隨著科技的發(fā)展、更為先進的探測、預警工具的研發(fā),人工智能駕駛技術(shù)的投入,以后這樣大型的意外事件可能會越來少發(fā)生,但一旦發(fā)生了,影響個體存活的因素,除了科技手段,還有群體的文明程度。很慶幸,我們生活在一個科技、文明都在高速發(fā)展的時代。
那么當科技發(fā)展的速度超過文明發(fā)展,由沒有感性只有理性的機器、人工智能來定最佳的生存選擇的策略時,又會是一種什么局面?