R語(yǔ)言使用特征工程泰坦尼克號(hào)數(shù)據(jù)分析應(yīng)用案例
原文參考:http://tecdat.cn/?p=4491
?
特征工程對(duì)于模型的執(zhí)行非常重要,即使是具有強(qiáng)大功能的簡(jiǎn)單模型也可以勝過復(fù)雜的算法。實(shí)際上,特征工程被認(rèn)為是決定預(yù)測(cè)模型成功或失敗的最重要因素。特征工程真正歸結(jié)為機(jī)器學(xué)習(xí)中的人為因素。通過人類的直覺和創(chuàng)造力,您對(duì)數(shù)據(jù)的了解程度可以帶來不同。
那么什么是特征工程?對(duì)于不同的問題,它可能意味著許多事情,但在泰坦尼克號(hào)的競(jìng)爭(zhēng)中,它可能意味著砍伐,并結(jié)合我們?cè)贙aggle的優(yōu)秀人員給予的不同屬性來從中榨取更多的價(jià)值。通常,機(jī)器學(xué)習(xí)算法可以更容易地從工程學(xué)習(xí)算法中消化和制定規(guī)則,而不是從其導(dǎo)出的變量。
獲得更多機(jī)器學(xué)習(xí)魔力的最初嫌疑人是我們上次從未發(fā)送到?jīng)Q策樹的三個(gè)文本字段。票號(hào),艙位和名稱都是每位乘客獨(dú)有的; 也許可以提取這些文本字符串的一部分以構(gòu)建新的預(yù)測(cè)屬性。讓我們從名稱字段開始。如果我們看一下第一位乘客的名字,我們會(huì)看到以下內(nèi)容:
> train$Name[1]
[1] Braund, Mr. Owen Harris
891 Levels: Abbing, Mr. Anthony Abbott, Mr. Rossmore Edward ... Zimmerman, Mr. Leo
以前我們只通過子集化訪問乘客組,現(xiàn)在我們通過使用行號(hào)1作為索引來訪問個(gè)人。好吧,船上沒有其他人有這個(gè)名字,這幾乎可以肯定,但他們還有什么共享?好吧,我確信船上有很多先生。也許人物頭銜可能會(huì)給我們更多的洞察力。
如果我們滾動(dòng)數(shù)據(jù)集,我們會(huì)看到更多的標(biāo)題,包括Miss,Mrs,Master,甚至是Countess!標(biāo)題“大師”現(xiàn)在有點(diǎn)過時(shí),但在這些日子里,它被保留給未婚男孩。此外,像我們伯爵夫人這樣的貴族也可能對(duì)低級(jí)無產(chǎn)階級(jí)采取不同的行動(dòng)。在這方面似乎有很少的模式可能性比我們之前看過的年齡,性別等組合更深入。
為了提取這些標(biāo)題以創(chuàng)建新變量,我們需要在訓(xùn)練集和測(cè)試集上執(zhí)行相同的操作,以便這些功能可用于增長(zhǎng)我們的決策樹,并對(duì)看不見的測(cè)試數(shù)據(jù)進(jìn)行預(yù)測(cè)。在兩個(gè)數(shù)據(jù)集上同時(shí)執(zhí)行相同過程的簡(jiǎn)單方法是合并它們。在R中我們可以使用rbind,它代表行綁定,只要兩個(gè)數(shù)據(jù)幀具有彼此相同的列。由于我們?cè)跍y(cè)試集中顯然缺少Survived列,讓我們創(chuàng)建一個(gè)完整的缺失值(NAs),然后將兩個(gè)數(shù)據(jù)集行綁定在一起:
> test$Survived <- NA
> combi <- rbind(train, test)
現(xiàn)在我們有了一個(gè)名為“combi”的新數(shù)據(jù)框,其中包含與原始兩個(gè)數(shù)據(jù)集完全相同的行,按照我們指定的順序堆疊:先訓(xùn)練,然后測(cè)試第二。
如果你回顧一下我們對(duì)Owen的調(diào)查結(jié)果,他的名字仍然被編碼為一個(gè)因素。正如我們?cè)诮坛滔盗星懊嫣岬降哪菢?,字符串?huì)自動(dòng)導(dǎo)入R中的因子,即使它沒有意義。所以我們需要將此列轉(zhuǎn)換回文本字符串。要做到這一點(diǎn),我們使用as.character。讓我們這樣做,然后再看看歐文:
> combi$Name <- as.character(combi$Name)
> combi$Name[1]
[1] "Braund, Mr. Owen Harris"
為了分解字符串,我們需要一些鉤子來告訴程序要查找。很好,我們看到人名后面有一個(gè)逗號(hào),并且在他們的頭銜之后有一個(gè)句號(hào)。我們可以很容易地使用函數(shù)strsplit(代表字符串拆分)來區(qū)分這兩個(gè)符號(hào)的原始名稱。讓我們?cè)囋嚥祭实孪壬?/p>
> strsplit(combi$Name[1], split='[,.]')
[[1]]
[1] "Braund" " Mr" " Owen Harris"
好的。在這里,我們發(fā)送strsplit了感興趣的單元格,并在分割字符串時(shí)為其選擇了一些符號(hào),可以是逗號(hào)或句點(diǎn)。方括號(hào)中的那些符號(hào)稱為正則表達(dá)式,雖然這是一個(gè)非常簡(jiǎn)單的符號(hào),如果您打算使用大量文本,我肯定會(huì)建議習(xí)慣使用它們!
我們看到標(biāo)題已經(jīng)單獨(dú)打破了,雖然在它開始之前有一個(gè)奇怪的空間,因?yàn)槎禾?hào)發(fā)生在姓氏的末尾。但是,我們?nèi)绾潍@得這個(gè)標(biāo)題并清除其他我們不想要的東西呢?[[1]]在文本部分之前打印索引。讓我們嘗試通過將所有方括號(hào)附加到原始命令來深入研究這種新類型的容器:
> strsplit(combi$Name[1], split='[,.]')[[1]]
[1] "Braund" " Mr" " Owen Harris"
字符串拆分使用雙重堆疊矩陣,因?yàn)樗肋h(yuǎn)不能確定給定的正則表達(dá)式將具有相同數(shù)量的塊。如果名稱中有更多逗號(hào)或句點(diǎn),則會(huì)創(chuàng)建更多段,因此它會(huì)將它們隱藏得更深,以維護(hù)我們習(xí)慣使用的矩形類型的容器,例如電子表格或現(xiàn)在的數(shù)據(jù)幀!讓我們深入了解索引混亂并提取標(biāo)題。這是這個(gè)嵌套列表中的第二個(gè)項(xiàng)目,所以讓我們深入研究這個(gè)新容器的索引號(hào)2:
> strsplit(combi$Name[1], split='[,.]')[[1]][2]
[1] " Mr"
由于我們不得不深入研究這個(gè)容器以獲得標(biāo)題,只需嘗試combi$Title <- strsplit(combi$Name, split='[,.]')[[1]][2]遍歷整個(gè)名稱向量就會(huì)導(dǎo)致我們所有的行都具有相同的Mr.,所以我們需要更加努力。不出所料,將函數(shù)應(yīng)用于數(shù)據(jù)幀或向量中的大量單元格會(huì)使用applyR的函數(shù)套件:
> combi$Title <- sapply(combi$Name, FUN=function(x) {strsplit(x, split='[,.]')[[1]][2]})
R的應(yīng)用功能都以稍微不同的方式sapply工作,但在這里工作得很好。我們提供sapply了我們剛剛提出的名稱向量和函數(shù)。它遍歷名稱向量的行,并將每個(gè)名稱發(fā)送到函數(shù)。所有這些字符串拆分的結(jié)果都被組合成一個(gè)向量作為sapply函數(shù)的輸出,然后我們將其存儲(chǔ)到原始數(shù)據(jù)幀中的一個(gè)新列,稱為Title。
最后,我們可能希望從標(biāo)題的開頭剝離這些空格。在這里,我們可以用任何東西替換第一次出現(xiàn)的空格。我們可以使用sub這個(gè):
> combi$Title <- sub(' ', '', combi$Title)
好吧,我們現(xiàn)在有一個(gè)很好的新標(biāo)題列,讓我們來看看它:
> table(combi$Title)
Capt Col Don Dona Dr Jonkheer Lady
1 4 1 1 8 1 1
Major Master Miss Mlle Mme Mr Mrs
2 61 260 2 1 757 197
Ms Rev Sir the Countess
2 8 1 1
嗯,這里有一些非常罕見的標(biāo)題,不會(huì)給我們的模型很多,所以讓我們結(jié)合一些最不尋常的。讓我們將它們組合成一個(gè)類別:
> combi$Title[combi$Title %in% c('Mme', 'Mlle')] <- 'Mlle'
我們?cè)谶@做了什么?該%in%運(yùn)營(yíng)商檢查是否值是我們比較它與載體的一部分。所以在這里我們將兩個(gè)標(biāo)題“Mme”和“Mlle”組合成一個(gè)新的臨時(shí)向量,使用c()運(yùn)算符并查看整個(gè)Title列中的任何現(xiàn)有標(biāo)題是否與它們中的任何一個(gè)匹配。然后我們用“Mlle”替換任何一場(chǎng)比賽。
我們一直在尋找冗余。對(duì)于我們這里的集合來說,非常富有似乎是一個(gè)問題。對(duì)于這些男人來說,我們有一些只有一兩個(gè)被祝福的頭銜:船長(zhǎng),少校和先生。所有這些都是軍事頭銜,或者是出生時(shí)擁有大片土地的富裕家伙。
對(duì)于女士們,我們有Dona,Lady,Jonkheer(*見下面的評(píng)論),當(dāng)然還有我們的伯爵夫人。所有這些人都是富人,由于他們的高貴出生,他們的行為可能有些相似。讓我們將這兩個(gè)組合在一起,并將因子級(jí)別的數(shù)量減少到?jīng)Q策樹可能理解的范圍:
< combi$Title[combi$Title %in% c('Dona', 'Lady', 'the Countess', 'Jonkheer')] <- 'Lady'
我們的最后一步是將變量類型更改回一個(gè)因子,因?yàn)檫@些基本上是我們創(chuàng)建的類別:
> combi$Title <- factor(combi$Title)
好的。我們現(xiàn)在已經(jīng)完成了乘客的頭銜。我們還能想到什么呢?那么,有兩個(gè)變量SibSb和Parch表明乘客隨行的家庭成員人數(shù)。似乎有理由認(rèn)為一個(gè)大家庭可能無法追蹤小約翰尼,因?yàn)樗麄兌紶?zhēng)先恐后地下沉沉船,所以讓我們將這兩個(gè)變量合并為一個(gè)新的,F(xiàn)amilySize:
> combi$FamilySize <- combi$SibSp + combi$Parch + 1
很簡(jiǎn)單!我們只是添加乘客與他們?cè)谝黄鸬男值芙忝茫渑?,父母和孩子的?shù)量,當(dāng)然還有一個(gè)用于他們自己的存在,并且有一個(gè)新的變量表明他們旅行的家庭的大小。
更多的東西?好吧,我們只是想到一個(gè)大家庭一起遇到救生艇的問題,但也許特定的家庭比其他家庭更麻煩?我們可以嘗試提取乘客的姓氏并將他們分組以尋找家人,但像約翰遜這樣的常見姓氏可能會(huì)在船上增加一些非相關(guān)人員。事實(shí)上,在一個(gè)3歲的家庭中有三個(gè)約翰遜,另外三個(gè)可能無關(guān)的約翰遜都是獨(dú)自旅行。
將姓氏與家庭大小相結(jié)合可以解決這個(gè)問題。沒有兩個(gè)家族 - 約翰遜應(yīng)該在如此小的船上擁有相同的FamilySize變量。讓我們首先提取乘客的姓氏。這應(yīng)該是我們之前運(yùn)行的標(biāo)題提取代碼的一個(gè)非常簡(jiǎn)單的變化,現(xiàn)在我們只想要strsplit輸出的第一部分:
> combi$Surname <- sapply(combi$Name, FUN=function(x) {strsplit(x, split='[,.]')[[1]][1]})
然后我們想要將FamilySize變量附加到它的前面,但正如我們所看到的那樣,字符串操作需要字符串。因此,讓我們將FamilySize變量臨時(shí)轉(zhuǎn)換為字符串,并將其與Surname結(jié)合使用以獲取新的FamilyID變量:
combi$FamilyID <- paste(as.character(combi$FamilySize), combi$Surname, sep="")
我們使用該函數(shù)paste將兩個(gè)字符串組合在一起,并告訴它通過sep參數(shù)將它們分開。這被存儲(chǔ)到一個(gè)名為FamilyID的新列中。但是那三個(gè)單身的約翰遜人都擁有相同的家庭ID。鑒于我們最初假設(shè)大家庭可能難以在恐慌中堅(jiān)持到一起,讓我們將任何兩個(gè)或更少的家庭大小淘汰,稱之為“小”家庭。這也將解決約翰遜問題。
> combi$FamilyID[combi$FamilySize <= 2] <- 'Small'
讓我們看看我們?nèi)绾巫R(shí)別這些家庭群體:
> table(combi$FamilyID)
11Sage 3Abbott 3Appleton 3Beckwith 3Boulos
11 3 1 2 3
3Bourke 3Brown 3Caldwell 3Christy 3Collyer
3 4 3 2 3
3Compton 3Cornell 3Coutts 3Crosby 3Danbom
3 1 3 3 3 . . .
嗯,有幾個(gè)似乎已經(jīng)從這里的裂縫中滑落。有很多FamilyID只有一兩個(gè)成員,即使我們只想要3或更多的家庭成員。也許有些家庭有不同的姓氏,但無論如何,所有這些一兩個(gè)人群體都是我們?cè)噲D避免的三個(gè)人的截止。讓我們開始清理它:
> famIDs <- data.frame(table(combi$FamilyID))
現(xiàn)在我們將上面的表存儲(chǔ)到數(shù)據(jù)幀中。是的,如果您愿意,可以將大多數(shù)表存儲(chǔ)到數(shù)據(jù)框中,所以讓我們通過在資源管理器中單擊它來查看它:

在這里,我們?cè)俅慰吹剿心切┡c我們的假設(shè)不能很好地合作的頑皮家庭,所以讓我們將這個(gè)數(shù)據(jù)框的子集只顯示那些意外小的FamilyID組。
famIDs <- famIDs[famIDs$Freq <= 2,]
然后,我們需要在數(shù)據(jù)集中覆蓋未正確識(shí)別的組中的任何族ID,并最終將其轉(zhuǎn)換為因子:
我們現(xiàn)在準(zhǔn)備將測(cè)試和訓(xùn)練集分解回原始狀態(tài),用它們帶來我們新奇的工程變量。我們剛剛做的最好的部分是如何在R中處理因子。在幕后,因子基本上存儲(chǔ)為整數(shù),但是用它們的文本名稱掩蓋以供我們查看。如果在單獨(dú)的測(cè)試和訓(xùn)練集上創(chuàng)建上述因子,則無法保證兩組中都存在兩個(gè)組。
例如,先前討論的“3Johnson”族在測(cè)試集中不存在。我們知道他們?nèi)齻€(gè)都從訓(xùn)練集數(shù)據(jù)中幸存下來。如果我們孤立地建立了我們的因素,那么測(cè)試集就沒有因素“3Johnson”。這會(huì)擾亂任何機(jī)器學(xué)習(xí)模型,因?yàn)橛糜跇?gòu)建模型的訓(xùn)練集與要求它預(yù)測(cè)的測(cè)試集之間的因素不一致。即。如果你嘗試,R會(huì)向你拋出錯(cuò)誤。
因?yàn)槲覀冊(cè)趩蝹€(gè)數(shù)據(jù)幀上構(gòu)建了因子,然后在構(gòu)建它們之后將它們拆分,R將為所有新數(shù)據(jù)幀提供所有因子級(jí)別,即使該因子不存在于一個(gè)數(shù)據(jù)幀中也是如此。它仍然具有因子水平,但在集合中沒有實(shí)際觀察。整潔的把戲?qū)??我向您保證,手動(dòng)更新因子水平是一件痛苦的事。
因此,讓我們將它們分開并對(duì)我們新的花哨工程變量做一些預(yù)測(cè):
這里我們介紹R中的另一種子集方法; 有很多取決于您希望如何切割數(shù)據(jù)。我們已根據(jù)原始列車和測(cè)試集的大小隔離了組合數(shù)據(jù)集的某些行范圍。之后的逗號(hào)后面沒有數(shù)字表示我們想要使用此子集獲取所有列并將其存儲(chǔ)到指定的數(shù)據(jù)幀。這為我們提供了原始行數(shù),以及所有新變量,包括一致的因子水平。
是時(shí)候做我們的預(yù)測(cè)了!我們有一堆新變量,所以讓我們將它們發(fā)送到一個(gè)新的決策樹。上次默認(rèn)的復(fù)雜性非常好,所以讓我們用香草控件生成一棵樹,看看它能做什么:

有趣的是,我們的新變量基本上管理著我們的樹。這是我上次沒有提到的決策樹的另一個(gè)缺點(diǎn):它們偏向于支持多層次的因素。看看我們的61級(jí)FamilyID因素在這里是如此突出,并且樹挑出了所有比其他家庭更偏向的家庭。這樣,決策節(jié)點(diǎn)可以將數(shù)據(jù)切割并改變?yōu)橐韵鹿?jié)點(diǎn)的純度的最佳可能組合。
但除此之外,您應(yīng)該知道如何從決策樹創(chuàng)建提交,所以讓我們看看它是如何執(zhí)行的!

太棒了,我們的排名幾乎減半了!所有這一切都是通過從我們已經(jīng)擁有的東西中榨取更多的價(jià)值。這只是您可以在此數(shù)據(jù)集中找到的示例。
繼續(xù)嘗試創(chuàng)建更多工程變量!和以前一樣,我也非常鼓勵(lì)你玩復(fù)雜性參數(shù),也許可以嘗試修剪一些更深的樹,看它是否有助于或阻礙你的等級(jí)。您甚至可以考慮從樹中排除一些變量,看看它是否也發(fā)生了變化。
但在大多數(shù)情況下,由于決策樹的貪婪性,標(biāo)題或性別變量將決定第一個(gè)決策。對(duì)于多層次因素的偏見也不會(huì)消失,如果沒有實(shí)際提交意見書,過度擬合問題很難衡量,但良好的判斷力可能會(huì)有所幫助。
有問題歡迎下方留言!