1. 程式人生 > >資料分析之泰坦尼克號案例

資料分析之泰坦尼克號案例

泰坦尼克號資料處理

【1】 實驗目錄

  • 1 解釋步驟
  • 2 處理資料
  • 3 資料視覺化
  • 4 修改資料
  • 5 訓練模型

【2】 實驗步驟

1 解釋步驟

*  識別和定義問題
 * 獲取訓練和測試資料
 * 質疑,準備,清理資料
 * 分析,識別模式並探索資料
 * 建模,預測並解決問題
 * 視覺化,報告並提出問題解決步驟和最終解決方案
 * 提供並提交結果
首先了解一下問題的定義:在泰坦尼克號沉船事
件中,有人倖存了就有人犧牲,那有什麼因素會影響到成員的倖存機率了?這就是問題所在。
 所以,實驗中會提供兩份資料列表,一份是帶有分類結果的有多項特徵(因素)的資料
然後用前一份資料(之後稱為訓練資料)來訓練學習模型,後一份資料(之後稱為測試資料)
 用在學習模型上得到測試結果(就是分類測試資料)
 這裡有幾條資訊要注意一下: 
 * 1912 年 4 月 15 日,泰坦尼克號首次航行,然後撞上冰山後沉沒,
 2224 名乘客和機組人 員中有 1502 人遇難,大概有 32%的存活率。 
 * 沉船導致生命損失的原因之一是乘客和船員沒有足夠的救生艇。
 *  雖然運氣有一定因素,但一些人比其他人更有可能生存,比如婦女,兒童和上層階級。
 最後一條就分析資料方面可以帶來一些方向
 上面七個流程是為了以下幾個目標:
* 
分類:對樣本進行歸類或分類。也可能要了解不同課程與解決方案目標的含義或相關性 * 關聯:哪些特徵影響到哪些特徵,哪些特徵會更加影響到結果(權重)等,認識到 特徵的關聯性可以幫助另外一些目標的實踐,如建立,完成,糾正特徵等。 * 轉換:在建模階段,可能有些演算法模型需要全部特徵都為數字型別,那就要將分類特 徵轉換為表示正確的數字型別。 * 完成:資料準備時會要求將一些空值或缺失值填上一個值,一些演算法模型會在沒有缺失值 的情況下表現得更好。 * 糾正:有時會對訓練資料中一些錯誤或不自然的資料用其他值代替,或者排除掉。 要糾正就要能檢測出‘離群值’(outlier),還有可以判斷某個特徵對結果沒有多大影響或某個特徵 完全扭曲了結果。 *
建立:有時為了關聯性,轉換和完成的目標,會將幾個特徵合併建立某個特徵。 * 製圖:如何根據資料的性質和解決方案目標來選擇正確的視覺化圖表 後面就按照這幾個目標來處理資料

(一) 資料處理

找出與存活率有關的變數 1 匯入工具包

%python
#資料分析包
import pandas as pd
import numpy as np
import random as rnd

#視覺化包
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

#機器學習包
from sklearn.linear_model import
LogisticRegression from sklearn.svm import SVC,LinearSVC from sklearn.ensemble import RandomForestClassifier from sklearn.neighbors import KNeighborsClassifier from sklearn.naive_bayes import GaussianNB from sklearn.linear_model import Perceptron from sklearn.linear_model import SGDClassifier from sklearn.tree import DecisionTreeClassifier #獲取資料 train_df = pd.read_csv('train.csv') test_df = pd.read_csv('test.csv') #combine變數將兩資料集合並,以備後面用 combine = [train_df,test_df] #檢視資料有哪些特徵 train_df.columns.values #輸出 array(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'], dtype=object)

2 解釋標籤資料 PassengerId:乘客Id Survived:0代表NO,1代表YES Pclass:1,2,3代表成員的經濟社會地位1最高,3最低 Name:乘客姓名 Sex:代表性別 Age:年齡 SibSp:由兩部分組成,Sibling(兄弟姐妹,堂妹都合適),Spouse代表(丈夫或妻子) Parch:父母和孩子組成,若只跟保姆寫0 Ticket:票的數字 Fare:乘客票價 Cabin:船艙數字 Embarked:登船倉:C=Cherbourg,Q=Queenstown,S=Southampton 3 操作資料

train_df.head(5)#輸出前五行
train_df.tail(5)#輸出後五行

這裡寫圖片描述

  • 補缺:比較前五行和後五行發現有的資料缺失,後面將會處理缺失資料
  • 轉換:還發現Ticket特徵的變數是字元和數字的混合,需要轉換

**通過pandas的describe()函式來檢視訓練資料的大概情況

train_df.describe()

這裡寫圖片描述 從中可以看出定義出的見解

  • 泰坦尼克號實際的成員數為 2224,而訓練資料樣本總數目是 891,只佔 40%
  • survived 是一個只具有 0 或 1 值的分類特徵
  • 大概 38%的生存率對比實際中 32%的生存率
  • 有 75%以上的成員沒有帶父母或孩子(從 75%那個數值可以看出
  • 近 3 成的成員有 siblings 和/或 spouse 在船上(從 75%那個數值可以看出)
  • 很少有成員(<1%)的票價是接近最高的$512
  • 很少有年紀大的成員(<1%)的年紀在 65 歲到 80 歲之間。

4 驗證地位高的成員存活率會高於地位低的

先從訓練資料中單獨取出’Pclass’和’Survived’這兩個特徵數 據,然後根據’Pclass’特徵來做分組,
並就每組計算平局值(mean),然後就平均值做一個倒序. 
最後可以看到地位高(Pclass=1)的成員真的存活率高很多(Survived>0.62)
%python
train_df[['Pclass','Survived']].groupby(['Pclass'],as_index=False).mean()\
.sort_values(by='Survived',ascending=False)

這裡寫圖片描述 5 驗證存活率和性別的關係

%python
#首先單獨選出“Sex”和“Survive的”然後根據“Sex”來分組,並就每組計算平均值mean,
然後根據平均值排序(倒序)
train_df[['Sex','Survived']].groupby(['Sex'],as_index=False).mean()\
.sort_values(by='Survived',ascending=False)

這裡寫圖片描述

6 驗證SibSp和Parch兩個特徵值對存貨的影響

%python
train_df[['SibSp','Survived']].groupby(['SibSp'],as_index=False).mean()\
.sort_values(by='Survived',ascending=False)
train_df[['Parch','Survived']].groupby(['Parch'],as_index=False).mean()\
.sort_values(by='Survived',ascending=False)

這裡寫圖片描述

這裡寫圖片描述 可以看出,這兩個特質的值分組後的存貨率都好相近,所以可以證明這兩個值和存活率關 聯性不大,可以放棄或者就這個特徵建立新的特徵.

(二) 資料視覺化

2.1 用Age視覺化直方圖

%python
g = sns.FacetGrid(train_df,col='Survived')
g.map(plt.hist,'Age',bins=20)

Age的圖 從上面可以看出, * 嬰兒(Age <= 4)生存率好高 * 大部分年齡在 15-25 的都犧牲了 * 總體來看,大部分成員的年齡在 15-35 歲之間 從而,可以校驗之前的假設並可以作出一定的結論: * 完善 Age 的空值 * 應該為 Age 特徵做一個分組處理 上面是隻對數字型別的圖例分析,下面會作數字加分類型別數值的圖例分析 2.2 地位Pclass視覺化

%python
grid = sns.FacetGrid(train_df,col='Survived',row='Pclass',size=2.2,aspect=3.8)
grid.map(plt.hist,'Age',alpha=.5,bins=20)
grid.add_legend()

這裡寫圖片描述 可以從上圖看出: * 地位低的(Pclass=3)成員最多,但存活率都不高 * Pclass=2 和 Pclass=3 的嬰兒存活率很高. * Pclass=1 的存活率最高 所以可以得出結論: 應該把 Pclass 特徵放入訓練模型中.

(三)分類特徵的關聯性視覺化

3.1 Embarked和Sex用圖表展示她們和Pclass與存活率的關聯性

%python
grid = sns.FacetGrid(train_df,row='Embarked',size=2.2,aspect=1.6)
grid.map(sns.pointplot,'Pclass','Survived','Sex',palette='deep',\
        hue_order=["female","male"])
grid.add_legend()

這裡寫圖片描述 解析:seaborn包的使用,當前遇到兩種圖示,pointplot為點圖,而hist為柱狀圖,而pointplot中 的引數hue_order是需要設定的,不然legend裡面顯示的順序就會改變。 由圖可知: * 女比男的存活率高 * Embarked=C的男有相對高的存活率,集合了Pclass看的 得出結論如下: * Sex特徵加入到訓練模型裡 * 完善Embarked特徵後,也把它加入到訓練模型中

3.2分類特徵和數值特徵的關聯

%python
#用seanbon畫圖,行(row)是Embarked,列(col)是Survived
grid = sns.FacetGrid(train_df,row='Embarked',col='Survived',size=2.2,aspect=1.6)

grid.map(sns.barplot,'Sex','Fare',alpha=.6,ci=None,order=['female','male'])
grid.add_legend()

這裡寫圖片描述 可以看出: * 高票價(Fare)的成員的生存率高於低的 * 登船口(Embarked)和存活率有關。 * 可以考慮將Fare(票價)分成帶狀

(四) 修改資料

4.1 去掉Cabin和Ticket

%python
train_df = train_df.drop(['Ticket','Cabin'],axis=1)
test_df = test_df.drop(['Ticket','Cabin'],axis=1)
combine = [train_df,test_df]

4.2 處理Name 注:在去掉Name之前,還要建立一個Title頭銜,看Title 是否和存活率有關

%python
#正則表示式”(\w+\.)”表示獲取”.”前第一個詞,用這個詞來分類圖示.expand=False 表示返回 DataFrame 型別. 
for dataset in combine:
    dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.',expand=False)

test_crosstab = pd.crosstab(train_df['Title'],train_df['Sex'])
print(test_crosstab)

這裡寫圖片描述 4.3 將上面得到的頭銜縮小類別

%python
#用一個共同的名字代替一些頭銜,其他的用Rare
for dataset in combine:
    dataset['Title'] = dataset['Title'].replace(['Lady','Countess','Capt','Col',\
                                                'Don','Dr','Major','Rev','Sir',\
                                                'Jonkheer','Dona'],'Rare')
    dataset['Title'] = dataset['Title'].replace('Mlle','Miss')
    dataset['Title'] = dataset['Title'].replace('Ms','Miss')
    dataset['Title'] = dataset['Title'].replace('Mme','Mrs')
print(train_df[['Title','Survived']].groupby(['Title'],as_index=False).mean())

這裡寫圖片描述 將一些好普通的名稱都統一成為”Rare”,然後合併一些統稱,然後作一個分類統計,就會看 到這個頭銜(Title)和存活率還是有一定關係… 然後再將這個 Title 特徵由分類特徵轉變為數字特徵,並且將 None 的值變為 0. 4.4 將Title變成數字特徵

%python
title_mapping = {"Mr":1,"Miss":2,"Mrs":3,"Master":4,"Rare":5}
#將同一個key的map在一起,並將None填充為0
for dataset in combine:
    dataset['Title'] = dataset['Title'].map(title_mapping)
    dataset['Title'] = dataset['Title'].fillna(0)
train_df.head()

這裡寫圖片描述 4.5 去掉Name和PassengerID

%python
train_df = train_df.drop(['Name','PassengerId'],axis=1)
test_df  = test_df.drop(['Name'],axis=1)
combine = [train_df,test_df]
print(train_df.shape,test_df.shape)

4.6 轉換Sex為數字型別

%python
#將female的值換成1,male換成0。
if __name__ == '__main__':
    for dataset in combine:
        dataset['Sex'] = dataset['Sex'].map({'female':1,'male':0}).astype(int)
train_df.head()

這裡寫圖片描述

4.7 處理缺失值或空值 基於Pclass和Sex關聯,處理Age,將其轉換為5個連續的等級

%python
# 處理缺失值和空值
grid = sns.FacetGrid(train_df,row='Pclass',col='Sex',size=2.2,aspect=1.6)
grid.map(plt.hist,'Age',alpha=.5,bins=20)
grid.add_legend()
plt.show()

這裡寫圖片描述 4.8 填充資料用中位數

%python
#用中位數來填充Age的缺失值
guess_ages = np.zeros((2,3))
# print(guess_ages)
for dataset in combine:
    for i in range(0,2):
        for j in range(0,3):

            guess_df = dataset[(dataset['Sex']==i)& \
                               (dataset['Pclass']==j+1)]['Age'].dropna()
#             print(guess_ages)
#         age_mean = guess_df.mean()
#         age_std = guess_df.std()
#         age_guess = rnd.uniform(age_mean - age_std,age_mean+age_std)

            age_guess = guess_df.median()
            guess_ages[i,j] = int(age_guess/0.5 + 0.5) * 0.5

    for i in range(0,2):
        for j in range(0,3):
            dataset.loc[(dataset.Age.isnull())&\
                        (dataset.Sex == i)&(dataset.Pclass==j+1),'Age']\
            =guess_ages[i,j]

    dataset['Age'] = dataset['Age'].astype(int)
print(guess_ages)

這裡寫圖片描述 4.9 將Age切成5段看看與存活率的關係

%python
train_df['AgeBand']=pd.cut(train_df['Age'],5) #切分
train_df[['AgeBand','Survived']].groupby(['AgeBand'],as_index=False).mean()\
.sort_values(by='AgeBand',ascending=True)
for dataset in combine:
    dataset.loc[dataset['Age']<=16,'Age'] = 0
    dataset.loc[(dataset['Age']>16) & (dataset['Age']<=32),'Age']=1
    dataset.loc[(dataset['Age']>32) & (dataset['Age']<=48),'Age']=2
    dataset.loc[(dataset['Age']>48) & (dataset['Age']<=64),'Age']=3
    dataset.loc[(dataset['Age']>64),'Age']=4
train_df.head(5)

這裡寫圖片描述

這裡寫圖片描述 4.10 刪除AgeBand

%python
train_df = train_df.drop(['AgeBand'],axis=1)
combine = [train_df,test_df]
train_df.head()

這裡寫圖片描述 4.11 將SibSp和Parch特徵加起來和存活率做關聯對比

%python
#將SibSp和Parch特徵的數量加起來,然後和存活率做一個關聯對比
for dataset in combine:
    dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1
train_df[['FamilySize','Survived']].groupby(['FamilySize'],as_index=False).mean()\
.sort_values(by='Survived',ascending=False)

這裡寫圖片描述 4.12 建立一個IsAlone特徵值

%python
for dataset in combine:
    dataset['IsAlone'] = 0
    dataset.loc[dataset['FamilySize']==1,'IsAlone'] = 1
train_df[['IsAlone','Survived']].groupby(['IsAlone'],as_index=False).mean()\
.sort_values(by='Survived',ascending=False)

這裡寫圖片描述 4.13 將Parch和SibSp和FamilySize三個特徵去掉

%python
train_df = train_df.drop(['Parch','SibSp','FamilySize'],axis=1)
test_df = test_df.drop(['Parch','SibSp','FamilySize'],axis=1)
combine = [train_df,test_df]
train_df.head()

這裡寫圖片描述 4.14 相乘Pclass和Age特徵值

%python
for dataset in combine:
    dataset['Age*Class'] = dataset.Age * dataset.Pclass
train_df.loc[:,['Age*Class','Age','Pclass']].head(10)

這裡寫圖片描述 4.15 處理Embarked特徵 用最長出現的值填充缺失值,然後在看關聯性

%python
freq_port = train_df.Embarked.dropna().mode()[0]
for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)
train_df[['Embarked','Survived']].groupby(['Embarked'],as_index=False).mean()\
.sort_values(by='Survived',ascending=False)

這裡寫圖片描述 4.16 將Embarked轉換成數字型別

%python
for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].map({'S':0,'C':1,'Q':2}).astype(int)
train_df.head()
train_df.head()

這裡寫圖片描述 4.17 處理Fare(票價) 首先處理Fare的缺失值,然後用一箇中間建立的特徵驗證合理性,最後將其分段,並轉換成數字

%python
test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)

train_df['FareBand'] = pd.qcut(train_df['Fare'], 4)
train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean()\
      .sort_values(by='FareBand', ascending=True)

這裡寫圖片描述 4.18 不能將FareBand特徵用在訓練模型中,它是中間特徵而已,可以根據它來轉換‘Fare’為數值型別

%python
for dataset in combine:
    dataset.loc[dataset['Fare'] <= 7.91, 'Fare'] = 0
    dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1
    dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare'] = 2
    dataset.loc[dataset['Fare'] > 31, 'Fare'] = 3
    dataset['Fare'] = dataset['Fare'].astype(int)
train_df = train_df.drop(['FareBand'],axis=1)
combine = [train_df,test_df]
print(train_df.head(5))

這裡寫圖片描述

%python
print(test_df.head(5))

這裡寫圖片描述

(五)訓練模型

  • 首先處理一下資料
%python
X_train = train_df.drop('Survived',axis=1)
Y_train = train_df['Survived']
X_test = test_df.drop('PassengerId',axis=1).copy()
print(X_train.shape,Y_train.shape,X_test.shape)

這裡寫圖片描述

5.1 邏輯迴歸

%python
logreg = LogisticRegression()
Logreg.fit(X_train,Y_train)
Y_pred = logreg.predict(X_test)
acc_log = round(logreg.score(X_train,Y_train) * 100,2)
acc_log
#輸出:81.26

5.2 支援向量機

%python
svc = SVC()
svc.fit(X_train,Y_train)
Y_pred = svc.predict(X_test)
acc_svc = round(svc.score(X_train,Y_train) * 100,2)
print(acc_svc)

#輸出:83.5

5.3 K最鄰近

%python
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train,Y_train)
Y_pred = knn.predict(X_test)
acc_knn = round(knn.score(X_train,Y_train)*100,2)
print(acc_knn)
#輸出:84.06

5.4 樸素貝葉斯

%python
#樸素貝葉斯
gaussian = GaussianNB()
gaussian.fit(X_train,Y_train)
Y_pred = gaussian.predict(X_test)
acc_gaussian = round(gaussian.score(X_train,Y_train)*100,2)
print(acc_gaussian)
#輸出:76.88

5.5 傳導器

%python
# 傳導器
perceptron = Perceptron()
perceptron.fit(X_train,Y_train)
Y_pred = perceptron.predict(X_test)
acc_perceptron = round(perceptron.score(X_train,Y_train)*100,2)
print(acc_perceptron)
#輸出:78.79

5.6 線性核的向量機

%python
#線性核的向量機
linear_svc = LinearSVC()
linear_svc.fit(X_train,Y_train)
Y_pred = linear_svc.predict(X_test)
acc_linear_svc = round(linear_svc.score(X_train,Y_train)*100,2)
print(acc_linear_svc)
#輸出:79.46

5.7 隨機梯度下降

%python
#隨機梯度下降
sgd = SGDClassifier()
sgd.fit(X_train,Y_train)
Y_pred = sgd.predict(X_test)
acc_sgd = round(sgd.score(X_train,Y_train)*100,2)
print(acc_sgd)
#輸出:76.09

5.8 決策樹

%python
decision_tree = DecisionTreeClassifier()
decision_tree.fit(X_train,Y_train)
Y_pred = decision_tree.predict(X_test)
acc_decision_tree = round(decision_tree.score(X_train,Y_train)*100,2)
acc_decision_tree
#輸出:86.64

5.9 隨機森林

%python
# 隨機森林
random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(X_train,Y_train)
Y_pred = random_forest.predict(X_test)
acc_random_forest = round(random_forest.score(X_train,Y_train)*100,2)
print(acc_random_forest)
#輸出:86.64

(六)致謝

【1】感謝三盟科技提供案例筆記