資料分析之泰坦尼克號案例
泰坦尼克號資料處理
【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 <= 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】感謝三盟科技提供案例筆記