相應的數據集和可執行文件均可以在本文中獲取。以下各段代碼也均可在jupyter中運行,可以隨時查看結果。
題目描述:
1912年,泰坦尼克號在第一次航行中就與冰山相撞沉沒,導致了大部分乘客和船員身亡。在這個項目中,我們將探索部分泰坦尼克號旅客名單,來確定哪些特徵可以最好地預測一個人是否會生還。
分析數據
import numpy as np #導入包
import pandas as pd
# 加載數據集
full_data = pd.read_csv('titanic_data.csv')
print(full_data.describe()) #打印數據集
表中是每行:
- count:總數
- mean:平均值
- std:是方差
- min:最小值
- 25%、50%、75%:位於總數據分別爲25%、50%、75%的數據
- max:最大值
表中每列是數據裏面旅客的特徵:
- PassengerId:乘客ID
- Survived:是否存活(0代表否,1代表是)
- Pclass:社會階級(1代表上層階級,2代表中層階級,3代表底層階級)
- Age:船上乘客的年齡(可能存在 NaN)
- SibSp:乘客在船上的兄弟姐妹和配偶的數量
- Parch:乘客在船上的父母以及小孩的數量
- Fare:乘客爲船票支付的費用
這和我們直接在csv表裏面看到的數據不一樣,那是因爲打印的時候沒有處理字符列。
- Survived:是否存活(0代表否,1代表是)
- Pclass:社會階級(1代表上層階級,2代表中層階級,3代表底層階級)
- Name:船上乘客的名字
- Sex:船上乘客的性別
- Age:船上乘客的年齡(可能存在
NaN
) - SibSp:乘客在船上的兄弟姐妹和配偶的數量
- Parch:乘客在船上的父母以及小孩的數量
- Ticket:乘客船票的編號
- Fare:乘客爲船票支付的費用
- Cabin:乘客所在船艙的編號(可能存在
NaN
) - Embarked:乘客上船的港口(C 代表從 Cherbourg 登船,Q 代表從 Queenstown 登船,S 代表從 Southampton 登船)
我們單獨把數據集的前幾行打印出來看下
display(full_data.head())#顯示前5行
這裏顯示的就正確了。
從上面這些表中可以看到年齡(Age)列只有714行,缺失了105行。所以我們要對缺失值進行處理。
考慮到年齡最小值是0.42,最大值80,這裏用中位數填充缺失值,因爲中位數不受極端變量值的影響。
# 年齡缺失值處理
full_data['Age']=full_data['Age'].fillna( full_data['Age'].median())# 用中位數填充
# 再次查看數據完整性
print(full_data.describe())
這回數據都完整了。
因爲我們感興趣的是每個乘客或船員是否在事故中活了下來。可以將 Survived 這一特徵從這個數據集移除,並且用一個單獨的變量 outcomes
來存儲。它也做爲我們要預測的目標。
# 從數據集中移除 'Survived' 這個特徵,並將它存儲在一個新的變量中。
outcomes = full_data['Survived']
data = full_data.drop('Survived', axis = 1)
# 顯示已移除 'Survived' 特徵的數據集前5行
display(data.head())
此時泰坦尼克號的 Survived 數據從 DataFrame 移除。 data
(乘客數據)和 outcomes
(是否存活)現在已經匹配好。也就是對於任何乘客的 data.loc[i]
都有對應的存活的結果 outcome[i]
。
定義評判標準(計算準確率)
爲了驗證我們預測的結果,我們需要一個標準來給我們的預測打分。預測的準確率,既正確預測乘客存活的比例。
創建我們的 accuracy_score
函數以對前五名乘客的預測來做測試。
假設預測他們全部都存活,求預測的準確率。
def accuracy_score(truth, pred):
# 確保預測的數量與結果的數量一致
if len(truth) == len(pred):
# 計算預測準確率(百分比)
return "Predictions have an accuracy of {:.2f}%.".format((truth == pred).mean()*100)
else:
return "Number of predictions does not match number of outcomes!"
# 測試 'accuracy_score' 函數
predictions = pd.Series(np.ones(5, dtype = int)) #把五個人的生存都置爲1,既存活
print(accuracy_score(outcomes[:5], predictions))#打印準確率
Predictions have an accuracy of 60.00%.
假設所有人都遇難了,求此時的準確率
def predictions_0(data):
predictions = []
for _, passenger in data.iterrows():#遍歷總數據的個數
# 把所有人的生存都置爲0
predictions.append(0)
# 返回設定好的遇難結果
return pd.Series(predictions)
# 進行預測
predictions = predictions_0(data)#傳入當前數據
print (accuracy_score(outcomes, predictions))#打印準確率
Predictions have an accuracy of 61.62%.
以上的測試完畢
考慮使用一個特徵進行預測
我們來看看 Sex 這一特徵對乘客的存活率有多大影響。
運行下面的代碼繪製出依據乘客性別計算存活率的柱形圖。
import matplotlib.pyplot as plt
# Merge data and outcomes into single dataframe
all_data = pd.concat([data, outcomes], axis = 1)
# Create outcomes DataFrame
all_data = all_data[['Sex', 'Survived']]
# Create plotting figure
plt.figure(figsize=(8,6))
values = ['male', 'female']
frame = pd.DataFrame(index = np.arange(len(values)), columns=('Sex','Survived','NSurvived'))
for i, value in enumerate(values):
frame.loc[i] = [value, \
len(all_data[(all_data['Survived'] == 1) & (all_data['Sex'] == value)]), \
len(all_data[(all_data['Survived'] == 0) & (all_data['Sex'] == value)])]
# Set the width of each bar
bar_width = 0.4
# Display each category's survival rates
for i in np.arange(len(frame)):
nonsurv_bar = plt.bar(i-bar_width, frame.loc[i]['NSurvived'], width = bar_width, color = 'r')
surv_bar = plt.bar(i, frame.loc[i]['Survived'], width = bar_width, color = 'g')
plt.xticks(np.arange(len(frame)), values)
plt.legend((nonsurv_bar[0], surv_bar[0]),('Did not survive', 'Survived'), framealpha = 0.8)
# Common attributes for plot formatting
plt.xlabel('Sex')
plt.ylabel('Number of Passengers')
plt.title('Passenger Survival Statistics With \'%s\' Feature'%('Sex'))
plt.show()
觀察泰坦尼克號上乘客存活的數據統計,我們可以發現大部分男性乘客在船沉沒的時候都遇難了。相反的,大部分女性乘客都在事故中生還。讓我們以此改進先前的預測:如果乘客是男性,那麼我們就預測他們遇難;如果乘客是女性,那麼我們預測他們在事故中活了下來。
用訪問 dictionary(字典)的方法來訪問船上乘客的每個特徵對應的值。例如, passenger['Sex']
返回乘客的性別,並計算準確率。
def predictions_1(data):
""" 只考慮一個特徵,如果是女性則生還 """
predictions = []
for _, passenger in data.iterrows():
# 輸入預測條件
if passenger['Sex'] == 'female':
predictions.append(1)
else:
predictions.append(0)
# 返回預測結果
return pd.Series(predictions)
# 進行預測
predictions = predictions_1(data)
#打印準確率
print (accuracy_score(outcomes, predictions))
Predictions have an accuracy of 78.68%.
當我們預測船上女性乘客全部存活,而剩下的人全部遇難,那麼我們預測的準確率是78.68%.
考慮兩個特徵進行預測
僅僅使用乘客性別(Sex)這一特徵,我們預測的準確性就有了明顯的提高。現在再看一下使用額外的特徵能否更進一步提升我們的預測準確度。例如,綜合考慮所有在泰坦尼克號上的男性乘客:我們是否找到這些乘客中的一個子集,他們的存活概率較高。我們來看看每位男性乘客的年齡(Age)。
下面這段代碼,把男性基於年齡的生存結果繪製出來。
#保存性別爲男性的數據
all_data = pd.concat([data, outcomes], axis = 1)
all_data = all_data[all_data['Sex'] == 'male']
all_data = all_data[['Age', 'Survived']]
# Divide the range of data into bins and count survival rates
min_value = all_data['Age'].min()
max_value = all_data['Age'].max()
value_range = max_value - min_value
bins = np.arange(0, all_data['Age'].max() + 10, 10)
# Overlay each bin's survival rates
nonsurv_vals = all_data[all_data['Survived'] == 0]['Age'].reset_index(drop = True)
surv_vals = all_data[all_data['Survived'] == 1]['Age'].reset_index(drop = True)
plt.hist(nonsurv_vals, bins = bins, alpha = 0.6,
color = 'red', label = 'Did not survive')
plt.hist(surv_vals, bins = bins, alpha = 0.6,
color = 'green', label = 'Survived')
# Add legend to plot
plt.xlim(0, bins.max())
plt.legend(framealpha = 0.8)
# Common attributes for plot formatting
plt.xlabel('Age')
plt.ylabel('Number of Passengers')
plt.title('Passenger Survival Statistics With \'%s\' Feature'%('Age'))
plt.show()
仔細觀察泰坦尼克號存活的數據統計,在船沉沒的時候,大部分小於10歲的男孩都活着,而大多數10歲以上的男性都隨着船的沉沒而遇難。讓我們繼續在先前預測的基礎上構建:如果乘客是女性,那麼我們就預測她們全部存活;如果乘客是男性並且小於10歲,我們也會預測他們全部存活;所有其它我們就預測他們都沒有幸存。
用之前 predictions_1
的代碼作爲開始來修改代碼,實現新的預測函數predictions_2。
def predictions_2(data):
""" 考慮兩個特徵:
- 如果是女性則生還
- 如果是男性並且小於10歲則生還 """
predictions = []
for _, passenger in data.iterrows():
if passenger['Sex'] == 'female':
predictions.append(1)
else:
if passenger['Age'] < 10:
predictions.append(1)
else:
predictions.append(0)
# 返回預測結果
return pd.Series(predictions)
# 進行預測
predictions = predictions_2(data)
print(accuracy_score(outcomes, predictions))
Predictions have an accuracy of 79.35%.
當預測所有女性以及小於10歲的男性都存活的時候,預測的準確率會達到79.35%。
添加年齡(Age)特徵與性別(Sex)的結合比單獨使用性別(Sex)也提高了不少準確度。
考慮多個特徵進行預測
在不同的條件下多次使用相同的特徵,使得預測結果提高到80%以上。
def predictions_3(data):
predictions = []
for _, passenger in data.iterrows():
# 輸入你自己的預測條件
if passenger['Sex'] == 'female':
predictions.append(1)
else:
if passenger['Age'] < 10:
predictions.append(1)
else:
if passenger['Fare']>=90 :
predictions.append(1)
else:
predictions.append(0)
# 返回預測結果
return pd.Series(predictions)
# 進行預測
predictions = predictions_3(data)
print(accuracy_score(outcomes, predictions))
Predictions have an accuracy of 78.79%.
結論
經過了數次對數據的探索和分類,創建了一個預測泰坦尼克號乘客存活率的有用的算法。在這個項目中我們手動地實現了一個簡單的機器學習模型——決策樹(decision tree)。決策樹每次按照一個特徵把數據分割成越來越小的羣組(被稱爲 nodes)。每次數據的一個子集被分出來,如果分割後新子集之間的相似度比分割前更高(包含近似的標籤),我們的預測也就更加準確。電腦來幫助我們做這件事會比手動做更徹底,更精確。這個鏈接提供了另一個使用決策樹做機器學習入門的例子。
決策樹是許多監督學習算法中的一種。在監督學習中,我們關心的是使用數據的特徵並根據數據的結果標籤進行預測或建模。也就是說,每一組數據都有一個真正的結果值,不論是像泰坦尼克號生存數據集一樣的標籤,或者是連續的房價預測。