摘要:船上每位旅客的身份證明乘客等級。簡單地用平均年齡或中位年齡替換它們可能不是最佳解決方案,因為年齡可能因乘客的類別和類別而不同。例如如果乘客是女性,則來自和來自王室,中位年齡為歲。失蹤的年齡已被取代。此函數(shù)將值替換為表示。
由 Kaggle 主持的泰坦尼克號生存挑戰(zhàn)賽是一項競賽,其目標是基于一組描述乘客的變量,例如他的年齡,性別或乘客在船上的等級,來預測特定乘客是生存或死亡。
我一直在玩 Titanic 數(shù)據(jù)集,我最近在公共排行榜上獲得了0.8134的準確度分數(shù)。當我寫這篇文章時,我在所有 Kagglers 中排名前4%。
這篇文章將分享我的解決方案。
為了使本教程更具“學術(shù)性”以便任何人都能從中受益,我將首先從探索性數(shù)據(jù)分析(EDA)開始,然后我將遵循特征工程并最終呈現(xiàn)我設(shè)置的預測模型。
在這個 jupyter 筆記本中,我將在每個級別的管道中使用 Python。
本教程涉及的主要庫是:
Pandas 用于數(shù)據(jù)操作和接入(ingestion)
Matplotlib 和 seaborn 用于數(shù)據(jù)可視化
Numpy 用于多維數(shù)組計算
sklearn 用于機器學習和預測建模
安裝過程安裝這些軟件包的一種非常簡單的方法是下載并安裝 Conda,它是將以上所有包封裝起來的發(fā)行版。此發(fā)行版適用于所有平臺(Windows,Linux 和 Mac OSX)。
特別注意這是我作為博主和機器學習從業(yè)者的第一次嘗試。
如果您對我所做的代碼或假設(shè)有疑問,請不要猶豫,在下面的評論部分發(fā)表評論。
如果您對如何改進筆記本電腦也有建議,請聯(lián)系我。
本教程可在我的 github 帳戶中找到。
譯者注:本翻譯在 qiwihui 下。
希望你已經(jīng)在計算機上設(shè)置了所有內(nèi)容。讓我們開始吧。
I - 探索性數(shù)據(jù)分析正如在不同的數(shù)據(jù)項目中,我們將首先開始深入研究數(shù)據(jù)并建立我們的第一個直覺。
在本節(jié)中,我們將做四件事。
數(shù)據(jù)提取:我們將加載數(shù)據(jù)集并首先查看它。
清潔:我們將填寫缺失值。
繪圖:我們將創(chuàng)建一些有趣的圖表,這些圖表(希望)可以發(fā)現(xiàn)數(shù)據(jù)中的相關(guān)性和隱藏的見解。
假設(shè):我們將從圖表中提出假設(shè)。
我們稍微調(diào)整了這款筆記本的風格,以便畫圖居中。
from IPython.core.display import HTML HTML(""" """);
導入有用的包。
%matplotlib inline import warnings warnings.filterwarnings("ignore") warnings.filterwarnings("ignore", category=DeprecationWarning) import pandas as pd pd.options.display.max_columns = 100 from matplotlib import pyplot as plt import numpy as np import seaborn as sns import pylab as plot params = { "axes.labelsize": "large", "xtick.labelsize": "x-large", "legend.fontsize": 20, "figure.dpi": 150, "figure.figsize": [25, 7] } plot.rcParams.update(params)
有兩個數(shù)據(jù)集:訓練集和測試集。
我們將使用訓練集來構(gòu)建我們的預測模型,用測試集來對其進行評分并生成輸出文件以在Kaggle評估系統(tǒng)上提交。
我們將在本文末尾看到這個過程是如何完成的。
現(xiàn)在讓我們開始加載訓練集。
data = pd.read_csv("./data/train.csv")
print data.shape
(891, 12)
我們得到:
891 行
12 列
Pandas 允許你鳥瞰數(shù)據(jù)。
data.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S |
Survived 列是 目標變量。 如果 Survived 為 1,乘客幸免于難,否則他已經(jīng)死了。這是我們要預測的變量。
其他變量描述了乘客。 它們是 特征。
PassengerId:船上每位旅客的身份證明
Pclass:乘客等級。 它有三個可能的值:1,2,3(第一,第二和第三類)
Name:Passeger的名字
Sex:性別
Age:年齡
SibSp:與乘客一起旅行的兄弟姐妹和配偶的數(shù)量
Parch:與乘客一起旅行的父母和孩子的數(shù)量
Ticket:船票號碼
Fare:票價
Cabin:船艙號碼
Embarked:這描述了人們登上的泰坦尼克號的三個可能區(qū)域。 三個可能的值 S,C,Q
Pandas 允許您對數(shù)字特征進行高級簡單的統(tǒng)計描述。
這可以使用 describe 方法完成。
data.describe()
PassengerId | Survived | Pclass | Age | SibSp | Parch | Fare | |
---|---|---|---|---|---|---|---|
count | 891.000000 | 891.000000 | 891.000000 | 714.000000 | 891.000000 | 891.000000 | 891.000000 |
mean | 446.000000 | 0.383838 | 2.308642 | 29.699118 | 0.523008 | 0.381594 | 32.204208 |
std | 257.353842 | 0.486592 | 0.836071 | 14.526497 | 1.102743 | 0.806057 | 49.693429 |
min | 1.000000 | 0.000000 | 1.000000 | 0.420000 | 0.000000 | 0.000000 | 0.000000 |
25% | 223.500000 | 0.000000 | 2.000000 | 20.125000 | 0.000000 | 0.000000 | 7.910400 |
50% | 446.000000 | 0.000000 | 3.000000 | 28.000000 | 0.000000 | 0.000000 | 14.454200 |
75% | 668.500000 | 1.000000 | 3.000000 | 38.000000 | 1.000000 | 0.000000 | 31.000000 |
max | 891.000000 | 1.000000 | 3.000000 | 80.000000 | 8.000000 | 6.000000 | 512.329200 |
count 變量顯示 Age 列中缺少177個值。
一種解決方案是用中值年齡填充空值。我們也可以用平均年齡來估算,但中位數(shù)對異常值更為穩(wěn)健。
data["Age"] = data["Age"].fillna(data["Age"].median())
讓我們看一下結(jié)果。
data.describe()
PassengerId | Survived | Pclass | Age | SibSp | Parch | Fare | |
---|---|---|---|---|---|---|---|
count | 891.000000 | 891.000000 | 891.000000 | 891.000000 | 891.000000 | 891.000000 | 891.000000 |
mean | 446.000000 | 0.383838 | 2.308642 | 29.361582 | 0.523008 | 0.381594 | 32.204208 |
std | 257.353842 | 0.486592 | 0.836071 | 13.019697 | 1.102743 | 0.806057 | 49.693429 |
min | 1.000000 | 0.000000 | 1.000000 | 0.420000 | 0.000000 | 0.000000 | 0.000000 |
25% | 223.500000 | 0.000000 | 2.000000 | 22.000000 | 0.000000 | 0.000000 | 7.910400 |
50% | 446.000000 | 0.000000 | 3.000000 | 28.000000 | 0.000000 | 0.000000 | 14.454200 |
75% | 668.500000 | 1.000000 | 3.000000 | 35.000000 | 1.000000 | 0.000000 | 31.000000 |
max | 891.000000 | 1.000000 | 3.000000 | 80.000000 | 8.000000 | 6.000000 | 512.329200 |
完美。
我們現(xiàn)在制作一些圖表。讓我們根據(jù)性別來看待生存。
data["Died"] = 1 - data["Survived"]
data.groupby("Sex").agg("sum")[["Survived", "Died"]].plot(kind="bar", figsize=(25, 7), stacked=True, colors=["g", "r"]);
看起來男性乘客更容易死亡。讓我們繪制相同的圖形,但用比例代替。
data.groupby("Sex").agg("mean")[["Survived", "Died"]].plot(kind="bar", figsize=(25, 7), stacked=True, colors=["g", "r"]);
性別變量似乎是一種歧視性特征。女性更有可能生存。
現(xiàn)在讓我們將生存與年齡變量聯(lián)系起來。
fig = plt.figure(figsize=(25, 7)) sns.violinplot(x="Sex", y="Age", hue="Survived", data=data, split=True, palette={0: "r", 1: "g"} );
正如我們在上面的圖表中看到并通過以下方式驗證:
女性的生存率高于男性,如較大的女性綠色直方圖所示
現(xiàn)在,我們看到:
年齡為男性乘客的生存:
年輕的男性傾向于生存
??* 20至40歲之間的大量乘客死亡
年齡似乎沒有對女性生存產(chǎn)生直接影響
以下小提琴情節(jié)證實,在遇到威脅的情況下,水手和船長遵守一條舊的行為準則:“婦女和兒童優(yōu)先!”。
對嗎?
現(xiàn)在讓我們關(guān)注每位乘客的票價,看看它如何影響生存。
figure = plt.figure(figsize=(25, 7)) plt.hist([data[data["Survived"] == 1]["Fare"], data[data["Survived"] == 0]["Fare"]], stacked=True, color = ["g","r"], bins = 50, label = ["Survived","Dead"]) plt.xlabel("Fare") plt.ylabel("Number of passengers") plt.legend();
票價較低的乘客更容易死亡。
換句話說,擁有更昂貴門票,因此更重要的社會地位的乘客似乎首先獲救。
好的,這很好。 現(xiàn)在讓我們將年齡,票價和生存結(jié)合在一張圖表上。
plt.figure(figsize=(25, 7)) ax = plt.subplot() ax.scatter(data[data["Survived"] == 1]["Age"], data[data["Survived"] == 1]["Fare"], c="green", s=data[data["Survived"] == 1]["Fare"]) ax.scatter(data[data["Survived"] == 0]["Age"], data[data["Survived"] == 0]["Fare"], c="red", s=data[data["Survived"] == 0]["Fare"]);
圓圈的大小與票價成正比。
在 x 軸上,我們有年齡,在 y 軸,我們考慮票價。
我們可以觀察不同的集群:
x = 20 和 x = 45 之間的大綠點:票價最高的成人
x = 10 和 x = 45 之間的小紅點,船上較低級別的成年人
x = 0 和 x = 7 之間的小密集點:這些是被保存的孩子
事實上,票價與我們在下面的圖表中看到的類別相關(guān)。
ax = plt.subplot() ax.set_ylabel("Average fare") data.groupby("Pclass").mean()["Fare"].plot(kind="bar", figsize=(25, 7), ax = ax);
現(xiàn)在讓我們看看登船地點如何影響生存。
fig = plt.figure(figsize=(25, 7)) sns.violinplot(x="Embarked", y="Fare", hue="Survived", data=data, split=True, palette={0: "r", 1: "g"});
似乎登船地點 C 的票價范圍更廣,因此支付最高價格的乘客是那些幸存的乘客。
我們也看到這種情況發(fā)生在登船地點 S 而不是登船地點 Q。
現(xiàn)在讓我們停止數(shù)據(jù)探索并切換到下一部分。
II - 特征工程在前一部分中,我們調(diào)查了數(shù)據(jù)并發(fā)現(xiàn)了一些有趣的相關(guān)性。
在這一部分中,我們將看到如何處理和轉(zhuǎn)換這些變量,使數(shù)據(jù)變得可以通過機器學習算法進行管理。
我們還將創(chuàng)建或“設(shè)計”在構(gòu)建模型時有用的其他功能。
我們將在此過程中看到如何處理文本變量(如乘客姓名)并將此信息集成到我們的模型中。
為了更加清晰,我們將代碼分散在多帶帶的函數(shù)中。
但首先,讓我們定義一個打印函數(shù),斷言是否已經(jīng)處理了一個特征。
def status(feature): print "Processing", feature, ": ok"加載數(shù)據(jù)
啟動機器學習問題的一個技巧是將訓練集一起附加到測試集。
我們將使用訓練集進行特征工程以防止信息泄漏。然后我們將這些變量添加到測試集中。
讓我們加載訓練集和測試集并將它們合在一起。
def get_combined_data(): # reading train data train = pd.read_csv("./data/train.csv") # reading test data test = pd.read_csv("./data/test.csv") # extracting and then removing the targets from the training data targets = train.Survived train.drop(["Survived"], 1, inplace=True) # merging train data and test data for future feature engineering # we"ll also remove the PassengerID since this is not an informative feature combined = train.append(test) combined.reset_index(inplace=True) combined.drop(["index", "PassengerId"], inplace=True, axis=1) return combined
combined = get_combined_data()
讓我們看一下數(shù)據(jù)的維度。
print combined.shape
(1309, 10)
訓練集和測試集被合并。您可能會注意到總行數(shù)(1309)是訓練集和測試集中行數(shù)的精確總和。
combined.head()
Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
2 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
3 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
4 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S |
在查看乘客姓名時,人們可能想知道如何處理它們以提取有用的信息。
如果你仔細看看這些第一個例子:
Braund, Mr. Owen Harris
Heikkinen, Miss. Laina
Oliva y Ocana, Dona. Fermina
Peter, Master. Michael J
你會注意到每個名字都有一個稱謂!這可能是一個簡單的小姐(Miss.)或太太(Mrs.),但它有時可能像 Master,Sir 或 Dona 那樣更復雜。在這種情況下,我們可以通過簡單地解析稱謂并提取標題并轉(zhuǎn)換為二進制變量來引入有關(guān)社會地位的其他信息。
讓我們看看我們將如何在下面的函數(shù)中執(zhí)行此操作。
讓我們先來看看在訓練集中有什么不同的稱謂。
titles = set() for name in data["Name"]: titles.add(name.split(",")[1].split(".")[0].strip())
print titles
set(["Sir", "Major", "the Countess", "Don", "Mlle", "Capt", "Dr", "Lady", "Rev", "Mrs", "Jonkheer", "Master", "Ms", "Mr", "Mme", "Miss", "Col"])
Title_Dictionary = { "Capt": "Officer", "Col": "Officer", "Major": "Officer", "Jonkheer": "Royalty", "Don": "Royalty", "Sir" : "Royalty", "Dr": "Officer", "Rev": "Officer", "the Countess":"Royalty", "Mme": "Mrs", "Mlle": "Miss", "Ms": "Mrs", "Mr" : "Mr", "Mrs" : "Mrs", "Miss" : "Miss", "Master" : "Master", "Lady" : "Royalty" } def get_titles(): # we extract the title from each name combined["Title"] = combined["Name"].map(lambda name:name.split(",")[1].split(".")[0].strip()) # a map of more aggregated title # we map each title combined["Title"] = combined.Title.map(Title_Dictionary) status("Title") return combined
此函數(shù)解析名稱并提取稱謂。 然后,它將稱謂映射到稱謂類別。
我們選擇:
Officer
Royalty
Mr
Mrs
Miss
Master
讓我們運行一下!
combined = get_titles()
Processing Title : ok
combined.head()
Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | Title | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S | Mr |
1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C | Mrs |
2 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S | Miss |
3 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S | Mrs |
4 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S | Mr |
讓我們檢查一下稱謂是否填寫正確。
combined[combined["Title"].isnull()]
Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | Title | |
---|---|---|---|---|---|---|---|---|---|---|---|
1305 | 1 | Oliva y Ocana, Dona. Fermina | female | 39.0 | 0 | 0 | PC 17758 | 108.9 | C105 | C | NaN |
在1305行中確實存在 NaN 值。實際上相應的名稱是 Oliva y Ocana, **Dona**. Fermina。
在訓練數(shù)據(jù)集中沒有遇到這個標題。
很好,現(xiàn)在我們有一個名為 Title 的附加列來包含這些信息。
處理年齡(Age)我們在第一部分中看到 Age 變量缺少177個值。這是一個很大的數(shù)字(約占數(shù)據(jù)集的13%)。簡單地用平均年齡或中位年齡替換它們可能不是最佳解決方案,因為年齡可能因乘客的類別和類別而不同。
為了理解原因,讓我們按性別(Sex),稱謂(Title)和乘客類(Pclass)對我們的數(shù)據(jù)集進行分組,并為每個子集計算中位數(shù)年齡。
為了避免測試集中的數(shù)據(jù)泄漏,我們使用訓練集填寫訓練中的缺失年齡,并且我們使用從訓練集計算的值來填充測試集中的年齡。
訓練級中缺少的年齡數(shù)
print combined.iloc[:891].Age.isnull().sum()
177
測試集中缺少的年齡數(shù)
print combined.iloc[891:].Age.isnull().sum()
86
grouped_train = combined.iloc[:891].groupby(["Sex","Pclass","Title"]) grouped_median_train = grouped_train.median() grouped_median_train = grouped_median_train.reset_index()[["Sex", "Pclass", "Title", "Age"]]
grouped_median_train.head()
Sex | Pclass | Title | Age | |
---|---|---|---|---|
0 | female | 1 | Miss | 30.0 |
1 | female | 1 | Mrs | 40.0 |
2 | female | 1 | Officer | 49.0 |
3 | female | 1 | Royalty | 40.5 |
4 | female | 2 | Miss | 24.0 |
此 dataframe 將幫助我們根據(jù)不同的標準估算缺失的年齡值。
查看中位年齡列,看看這個值如何根據(jù) Sex,Pclass 和 Title 組合在一起。
例如:
如果乘客是女性,則來自 Pclass 1 和來自王室(royalty),中位年齡為40.5歲。
如果乘客是男性,來自 Pclass 3,擁有 Mr 稱謂,則年齡中位數(shù)為26歲。
讓我們創(chuàng)建一個函數(shù),根據(jù)這些不同的屬性填充 組合 中的缺失年齡。
def fill_age(row): condition = ( (grouped_median_train["Sex"] == row["Sex"]) & (grouped_median_train["Title"] == row["Title"]) & (grouped_median_train["Pclass"] == row["Pclass"]) ) return grouped_median_train[condition]["Age"].values[0] def process_age(): global combined # a function that fills the missing values of the Age variable combined["Age"] = combined.apply(lambda row: fill_age(row) if np.isnan(row["Age"]) else row["Age"], axis=1) status("age") return combined
combined = process_age()
Processing age : ok
完美。失蹤的年齡已被取代。
但是,我們注意到票價(Fare)中缺少1個值,登船位置(Embarked)有兩個缺失值,而船艙位置(Cabin)有很多缺失值。我們稍后會處理這些變量。
我們現(xiàn)在處理名字。
def process_names(): global combined # we clean the Name variable combined.drop("Name", axis=1, inplace=True) # encoding in dummy variable titles_dummies = pd.get_dummies(combined["Title"], prefix="Title") combined = pd.concat([combined, titles_dummies], axis=1) # removing the title variable combined.drop("Title", axis=1, inplace=True) status("names") return combined
此函數(shù)會刪除 Name 列,我們不再使用它,因為我們創(chuàng)建了 Title 列。
然后我們使用虛擬編碼(dummy encoding)對稱謂值進行編碼。
您可以了解虛擬編碼以及如何在 Pandas 中輕松完成此操作。
combined = process_names()
Processing names : ok
combined.head()
Pclass | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | Title_Master | Title_Miss | Title_Mr | Title_Mrs | Title_Officer | Title_Royalty | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3 | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S | 0 | 0 | 1 | 0 | 0 | 0 |
1 | 1 | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C | 0 | 0 | 0 | 1 | 0 | 0 |
2 | 3 | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S | 0 | 1 | 0 | 0 | 0 | 0 |
3 | 1 | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S | 0 | 0 | 0 | 1 | 0 | 0 |
4 | 3 | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S | 0 | 0 | 1 | 0 | 0 | 0 |
如你看到的 :
不再有名字特征。
出現(xiàn)了新的變量(Title_X)。這些特征是二進制的。
例如,如果 Title_Mr = 1,則相應的稱謂為 Mr。
處理票價(Fare)讓我們通過在訓練集上計算的平均票價估算缺失的票價值。
def process_fares(): global combined # there"s one missing fare value - replacing it with the mean. combined.Fare.fillna(combined.iloc[:891].Fare.mean(), inplace=True) status("fare") return combined
此函數(shù)用平均值替換一個缺失的票價(Fare)值。
combined = process_fares()
Processing fare : ok處理登船位置(Embarked)
def process_embarked(): global combined # two missing embarked values - filling them with the most frequent one in the train set(S) combined.Embarked.fillna("S", inplace=True) # dummy encoding embarked_dummies = pd.get_dummies(combined["Embarked"], prefix="Embarked") combined = pd.concat([combined, embarked_dummies], axis=1) combined.drop("Embarked", axis=1, inplace=True) status("embarked") return combined
此函數(shù)用最常用的 Embarked 值替換了兩個缺失的 Embarked 值。
combined = process_embarked()
Processing embarked : ok
combined.head()
Pclass | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Title_Master | Title_Miss | Title_Mr | Title_Mrs | Title_Officer | Title_Royalty | Embarked_C | Embarked_Q | Embarked_S | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3 | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
1 | 1 | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
2 | 3 | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
3 | 1 | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
4 | 3 | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
train_cabin, test_cabin = set(), set() for c in combined.iloc[:891]["Cabin"]: try: train_cabin.add(c[0]) except: train_cabin.add("U") for c in combined.iloc[891:]["Cabin"]: try: test_cabin.add(c[0]) except: test_cabin.add("U")
print train_cabin
set(["A", "C", "B", "E", "D", "G", "F", "U", "T"])
print test_cabin
set(["A", "C", "B", "E", "D", "G", "F", "U"])
我們在測試集中沒有任何不存在于訓練集中的船艙位置字母。
def process_cabin(): global combined # replacing missing cabins with U (for Uknown) combined.Cabin.fillna("U", inplace=True) # mapping each Cabin value with the cabin letter combined["Cabin"] = combined["Cabin"].map(lambda c: c[0]) # dummy encoding ... cabin_dummies = pd.get_dummies(combined["Cabin"], prefix="Cabin") combined = pd.concat([combined, cabin_dummies], axis=1) combined.drop("Cabin", axis=1, inplace=True) status("cabin") return combined
此函數(shù)將 NaN 值替換為 U(表示 Unknow )。 然后它將每個 Cabin 值映射到第一個字母。
然后它再次使用虛擬編碼對艙位值進行編碼。
combined = process_cabin()
Processing cabin : ok
好了,沒有缺失值了。
combined.head()
Pclass | Sex | Age | SibSp | Parch | Ticket | Fare | Title_Master | Title_Miss | Title_Mr | Title_Mrs | Title_Officer | Title_Royalty | Embarked_C | Embarked_Q | Embarked_S | Cabin_A | Cabin_B | Cabin_C | Cabin_D | Cabin_E | Cabin_F | Cabin_G | Cabin_T | Cabin_U | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3 | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
1 | 1 | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 3 | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
3 | 1 | female | 35.0 | 1 | 0 | 113803 | 53.1000 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 3 | male | 35.0 | 0 | 0 | 373450 | 8.0500 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
def process_sex(): global combined # mapping string values to numerical one combined["Sex"] = combined["Sex"].map({"male":1, "female":0}) status("Sex") return combined
此函數(shù)將字符串值 male 和 female 分別映射到1和0。
combined = process_sex()
Processing Sex : ok處理乘客等級(Pclass)
def process_pclass(): global combined # encoding into 3 categories: pclass_dummies = pd.get_dummies(combined["Pclass"], prefix="Pclass") # adding dummy variable combined = pd.concat([combined, pclass_dummies],axis=1) # removing "Pclass" combined.drop("Pclass",axis=1,inplace=True) status("Pclass") return combined
此函數(shù)使用虛擬編碼對 Pclass(1,2,3)的值進行編碼。
combined = process_pclass()
Processing Pclass : ok處理船票號碼(Ticket)
讓我們首先看看我們的數(shù)據(jù)集中不同的船票號碼前綴
def cleanTicket(ticket): ticket = ticket.replace(".", "") ticket = ticket.replace("/", "") ticket = ticket.split() ticket = map(lambda t : t.strip(), ticket) ticket = list(filter(lambda t : not t.isdigit(), ticket)) if len(ticket) > 0: return ticket[0] else: return "XXX"
tickets = set() for t in combined["Ticket"]: tickets.add(cleanTicket(t))
print len(tickets)
37
def process_ticket(): global combined # a function that extracts each prefix of the ticket, returns "XXX" if no prefix (i.e the ticket is a digit) def cleanTicket(ticket): ticket = ticket.replace(".","") ticket = ticket.replace("/","") ticket = ticket.split() ticket = map(lambda t : t.strip(), ticket) ticket = filter(lambda t : not t.isdigit(), ticket) if len(ticket) > 0: return ticket[0] else: return "XXX" # Extracting dummy variables from tickets: combined["Ticket"] = combined["Ticket"].map(cleanTicket) tickets_dummies = pd.get_dummies(combined["Ticket"], prefix="Ticket") combined = pd.concat([combined, tickets_dummies], axis=1) combined.drop("Ticket", inplace=True, axis=1) status("Ticket") return combined
combined = process_ticket()
Processing Ticket : ok處理家庭
這部分包括根據(jù)家庭的大小創(chuàng)建新變量(大小是我們創(chuàng)建的另一個變量)。
這種新變量的創(chuàng)建是在一個現(xiàn)實的假設(shè)下完成的:大家庭聚集在一起,因此他們比多帶帶旅行的人更有可能獲救。
def process_family(): global combined # introducing a new feature : the size of families (including the passenger) combined["FamilySize"] = combined["Parch"] + combined["SibSp"] + 1 # introducing other features based on the family size combined["Singleton"] = combined["FamilySize"].map(lambda s: 1 if s == 1 else 0) combined["SmallFamily"] = combined["FamilySize"].map(lambda s: 1 if 2 <= s <= 4 else 0) combined["LargeFamily"] = combined["FamilySize"].map(lambda s: 1 if 5 <= s else 0) status("family") return combined
此函數(shù)引入了4個新特征:
FamilySize:包括乘客(他/她)自己在內(nèi)的親屬總數(shù)。
Sigleton:描述 size = 1 的家庭的布爾變量
SmallFamily:一個布爾變量,描述 2 <= size <= 4 的家庭
LargeFamily:一個布爾變量,描述 5 < size 的家庭
combined = process_family()
Processing family : ok
print combined.shape
(1309, 67)
最后我們得到了67個特征。
combined.head()
Sex | Age | SibSp | Parch | Fare | Title_Master | Title_Miss | Title_Mr | Title_Mrs | Title_Officer | Title_Royalty | Embarked_C | Embarked_Q | Embarked_S | Cabin_A | Cabin_B | Cabin_C | Cabin_D | Cabin_E | Cabin_F | Cabin_G | Cabin_T | Cabin_U | Pclass_1 | Pclass_2 | Pclass_3 | Ticket_A | Ticket_A4 | Ticket_A5 | Ticket_AQ3 | Ticket_AQ4 | Ticket_AS | Ticket_C | Ticket_CA | Ticket_CASOTON | Ticket_FC | Ticket_FCC | Ticket_Fa | Ticket_LINE | Ticket_LP | Ticket_PC | Ticket_PP | Ticket_PPP | Ticket_SC | Ticket_SCA3 | Ticket_SCA4 | Ticket_SCAH | Ticket_SCOW | Ticket_SCPARIS | Ticket_SCParis | Ticket_SOC | Ticket_SOP | Ticket_SOPP | Ticket_SOTONO2 | Ticket_SOTONOQ | Ticket_SP | Ticket_STONO | Ticket_STONO2 | Ticket_STONOQ | Ticket_SWPP | Ticket_WC | Ticket_WEP | Ticket_XXX | FamilySize | Singleton | SmallFamily | LargeFamily | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 22.0 | 1 | 0 | 7.2500 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 1 | 0 |
1 | 0 | 38.0 | 1 | 0 | 71.2833 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 1 | 0 |
2 | 0 | 26.0 | 0 | 0 | 7.9250 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |
3 | 0 | 35.0 | 1 | 0 | 53.1000 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 0 | 1 | 0 |
4 | 1 | 35.0 | 0 | 0 | 8.0500 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
在這一部分中,我們根據(jù)我們創(chuàng)建的特征建立統(tǒng)計模型。您可以將此模型視為一個盒子,它可以處理任何新乘客的信息,并決定他是否能夠幸存。
有各種各樣的模型可供使用,從邏輯回歸到?jīng)Q策樹,以及更復雜的模型,如隨機森林和梯度提升樹。
我們將使用隨機森林。Random Froests 在 Kaggle 比賽中證明了很高的有效性。
有關(guān)為什么集合方法表現(xiàn)良好的更多詳細信息,您可以參考這些帖子:
http://mlwave.com/kaggle-ense...
http://www.overkillanalytics....
回到我們的問題,我們現(xiàn)在必須:
1.將組合數(shù)據(jù)集分成訓練集和測試集。
2.使用訓練集建立預測模型。
3.使用訓練集評估模型。
4.使用測試集測試模型,并生成并輸出提交文件。
請記住,我們必須重復 2 和 3 直到達到可接受的評估分數(shù)。
讓我們首先導入需要用到的函數(shù)包。
from sklearn.pipeline import make_pipeline from sklearn.ensemble import RandomForestClassifier from sklearn.ensemble.gradient_boosting import GradientBoostingClassifier from sklearn.feature_selection import SelectKBest from sklearn.model_selection import StratifiedKFold from sklearn.model_selection import GridSearchCV from sklearn.model_selection import cross_val_score from sklearn.feature_selection import SelectFromModel from sklearn.linear_model import LogisticRegression, LogisticRegressionCV
為了評估我們的模型,我們將使用5折交叉驗證(5-fold cross validation),因為它是在比賽排行榜中使用的指標。
為此,我們將定義一個小的評分函數(shù)。
def compute_score(clf, X, y, scoring="accuracy"): xval = cross_val_score(clf, X, y, cv = 5, scoring=scoring) return np.mean(xval)
從組合數(shù)據(jù)集中恢復訓練集和測試集是一項簡單的任務。
def recover_train_test_target(): global combined targets = pd.read_csv("./data/train.csv", usecols=["Survived"])["Survived"].values train = combined.iloc[:891] test = combined.iloc[891:] return train, test, targets
train, test, targets = recover_train_test_target()特征選擇
到目前為止,我們已經(jīng)提出了30多個特征。這個數(shù)字非常大。
在完成特征工程時,我們通常傾向于通過選擇捕獲基本特征的“正確”數(shù)量的特征來減少維度。
事實上,特征選擇帶來許多好處:
它減少了數(shù)據(jù)之間的冗余
它加快了訓練過程
它減少過擬合
基于樹的估算器可用于計算特征重要性,而這些重要性又可用于丟棄不相關(guān)的特征。
clf = RandomForestClassifier(n_estimators=50, max_features="sqrt") clf = clf.fit(train, targets)
讓我們看看每個特征的重要性。
features = pd.DataFrame() features["feature"] = train.columns features["importance"] = clf.feature_importances_ features.sort_values(by=["importance"], ascending=True, inplace=True) features.set_index("feature", inplace=True)
features.plot(kind="barh", figsize=(25, 25))
正如您可能注意到的那樣,與 Title_Mr,Age,Fare 和 Sex 相關(guān)聯(lián)非常重要。
與 Passenger_Id 也有重要的相關(guān)性。
現(xiàn)在讓我們將我們的訓練集和測試集轉(zhuǎn)換為更緊湊的數(shù)據(jù)集。
model = SelectFromModel(clf, prefit=True) train_reduced = model.transform(train) print train_reduced.shape
(891, 12)
test_reduced = model.transform(test) print test_reduced.shape
(418, 12)
好極了! 現(xiàn)在我們的特征減少了很多。
我們將看看我們是否會使用訓練集的減少版或完整版。
讓我們嘗試不同的基礎(chǔ)模型logreg = LogisticRegression() logreg_cv = LogisticRegressionCV() rf = RandomForestClassifier() gboost = GradientBoostingClassifier() models = [logreg, logreg_cv, rf, gboost]
for model in models: print "Cross-validation of : {0}".format(model.__class__) score = compute_score(clf=model, X=train_reduced, y=targets, scoring="accuracy") print "CV score = {0}".format(score) print "****"
Cross-validation of :超參數(shù)調(diào)整CV score = 0.818195097715 **** Cross-validation of : CV score = 0.81818240172 **** Cross-validation of : CV score = 0.808183171282 **** Cross-validation of : CV score = 0.824917697684 ****
正如建模部分的開頭所提到的,我們將使用隨機森林模型。它可能不是這項任務的最佳模型,但我們將展示如何調(diào)整。這項工作可以應用于不同的模型。
隨機森林非常方便。然而,它們會帶有一些參數(shù)進行調(diào)整,以便為預測任務獲得最佳模型。
要了解有關(guān)隨機森林的更多信息,請參閱此 鏈接。
此外,我們將使用全部訓練集。
# turn run_gs to True if you want to run the gridsearch again. run_gs = False if run_gs: parameter_grid = { "max_depth" : [4, 6, 8], "n_estimators": [50, 10], "max_features": ["sqrt", "auto", "log2"], "min_samples_split": [2, 3, 10], "min_samples_leaf": [1, 3, 10], "bootstrap": [True, False], } forest = RandomForestClassifier() cross_validation = StratifiedKFold(n_splits=5) grid_search = GridSearchCV(forest, scoring="accuracy", param_grid=parameter_grid, cv=cross_validation, verbose=1 ) grid_search.fit(train, targets) model = grid_search parameters = grid_search.best_params_ print("Best score: {}".format(grid_search.best_score_)) print("Best parameters: {}".format(grid_search.best_params_)) else: parameters = {"bootstrap": False, "min_samples_leaf": 3, "n_estimators": 50, "min_samples_split": 10, "max_features": "sqrt", "max_depth": 6} model = RandomForestClassifier(**parameters) model.fit(train, targets)
現(xiàn)在通過掃描超參數(shù)的幾個組合來構(gòu)建模型,我們可以生成一個輸出文件以在 Kaggle 上提交。
output = model.predict(test).astype(int) df_output = pd.DataFrame() aux = pd.read_csv("./data/test.csv") df_output["PassengerId"] = aux["PassengerId"] df_output["Survived"] = output df_output[["PassengerId","Survived"]].to_csv("./predictions/gridsearch_rf.csv", index=False)[BONUS] 混合不同模型
我沒有親自上傳基于模型混合的提交,但這是你可以這么做:
trained_models = [] for model in models: model.fit(train, targets) trained_models.append(model) predictions = [] for model in trained_models: predictions.append(model.predict_proba(test)[:, 1]) predictions_df = pd.DataFrame(predictions).T predictions_df["out"] = predictions_df.mean(axis=1) predictions_df["PassengerId"] = aux["PassengerId"] predictions_df["out"] = predictions_df["out"].map(lambda s: 1 if s >= 0.5 else 0) predictions_df = predictions_df[["PassengerId", "out"]] predictions_df.columns = ["PassengerId", "Survived"]
predictions_df.to_csv("./predictions/blending_base_models.csv", index=False)
為了獲得良好的混合提交,基本模型應該是不同的,并且它們的相關(guān)性是不相關(guān)的。
IV - 結(jié)論在本文中,我們探討了 Kaggle 帶給我們的一個有趣的數(shù)據(jù)集。
我們?yōu)g覽了數(shù)據(jù)科學管道的基本要點:
數(shù)據(jù)探索和可視化:制定假設(shè)的第一步
數(shù)據(jù)清理
特征工程
特征選擇
超參數(shù)調(diào)整
提交
混合
如果您想測試和使用它,可以將此博客下載為筆記本:我的 github repo
譯者注:此中文翻譯地址為: qiwihui 的 github repo
關(guān)于這一挑戰(zhàn)的文章很多,所以顯然還有改進的余地。
以下是我建議的后續(xù)步驟:
挖掘更多數(shù)據(jù)并最終構(gòu)建新特征。
嘗試不同的模型:邏輯回歸,Gradient Boosted Tree,XGboost 等。
嘗試集成學習技巧(堆疊)
運行 auto-ML 框架
如果你能找到改善我的解決方案的方法,我會非常高興。這可以讓我更新文章,絕對給你信任。所以請隨時發(fā)表評論。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/43161.html
摘要:訓練集是用來訓練你的機器學習模型的。但機器學習,你也要教它一些事實,比如長得像圖片的就是狗,長得像圖片的就是貓。好了,這樣我們整體的一個機器學習的簡單項目就完成,但我們還是要看一下效果。 最近寫了Kaggle的一個playground項目——預測科比投籃是否命中https://www.kaggle.com/c/kobe...,主要使用python的pandas和sklearn包。 這里...
摘要:內(nèi)容來自,人工智能數(shù)據(jù)科學比賽整理平臺。大賽面向全球高校在校生開放,旨在提升高校學生對數(shù)據(jù)分析與處理的算法研究與技術(shù)應用能力,探索大數(shù)據(jù)的核心科學與技術(shù)問題,嘗試創(chuàng)新大數(shù)據(jù)技術(shù),推動大數(shù)據(jù)的產(chǎn)學研用,本次大賽鼓勵高校教師參與指導。 內(nèi)容來自 DataSciComp,人工智能/數(shù)據(jù)科學比賽整理平臺。Github:iphysresearch/DataSciComp 本項目由 ApacheC...
摘要:內(nèi)容來自,人工智能數(shù)據(jù)科學比賽整理平臺。本項目由強力支持。每篇文章識別最多三個核心實體,并分別判斷文章對上述核心實體的情感傾向積極中立消極三種。 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1760&h=200); 內(nèi)容來自 DataSciComp,人工智能/數(shù)據(jù)科學比賽...
showImg(https://segmentfault.com/img/bVbkB4E?w=800&h=400); 背景 關(guān)于 Kaggle https://www.kaggle.com/ 這是一個為你提供完美數(shù)據(jù),為你提供實際應用場景,可以與小伙伴在數(shù)據(jù)挖掘領(lǐng)域 high 的不要不要的的地方啊!!! Kaggle 是一個用來學習、分享和競賽的線上數(shù)據(jù)實驗平臺,有點類似 KDD—CUP(國際...
閱讀 2566·2021-10-11 10:58
閱讀 1149·2021-09-29 09:34
閱讀 1486·2021-09-26 09:46
閱讀 3830·2021-09-22 15:31
閱讀 730·2019-08-30 15:54
閱讀 1458·2019-08-30 13:20
閱讀 1251·2019-08-30 13:13
閱讀 1486·2019-08-26 13:52