專案例項---隨機森林在Kaggle例項:Titanic中的應用(一)
在前兩篇文章介紹了決策樹和隨機森林的原理、流程和演算法之後,這裡將以隨機森林為例,探索資料探勘演算法在專案中的應用,尤其是在kaggle專案中進行資料探勘的一般流程。
1、分析專案背景
專案背景的熟悉對於我們理解影響分析目標的因素十分重要,甚至可以發覺一些常規資料中沒有提出來的因素。
該專案的頁面
泰坦尼克的沉沒發生在1912年4月15日,包括海員在內,船上共2224個人,遇難1502年,倖存722人。這麼多人遇難的原因之一是事故發生後船上沒有足夠的救生艇。事後的調查資料顯示,似乎有一些型別的人倖存的可能性更大,比如婦女、孩子、上層階級的乘客。
這個專案的目標就是通過這些調查資料,利用資料探勘演算法來建立船上人員生存概率的模型,並利用訓練的模型對測試資料中乘客的生存情況進行預測(調查資料已經過處理,並分成訓練集-這部分帶標籤,及測試集-這部分標籤被抹去),提交預測結果後系統會給出預測精度。
插入點題外話:泰坦尼克上有8箇中國人,其中6人生還,對比其他人這是一個極高的生還率;當時的英美媒體出於轉移人們注意力的需要,強行汙衊他們是卑鄙竊取上救生艇機會的人,他們一生都被扣上“自私的種族”的帽子,這6箇中國人成了英美兩國家喻戶曉的“醜陋人物”。中國在當時是積分積弱的國家,船上的8個人都是勞工,共用一張船票,擠在三等艙裡。他們的主要工作是燒鍋爐。他們怎麼可能有優先上救生艇的機會?被汙衊後他們根本沒有發聲的機會,他們更可能連自己被汙衊都不知道,因為回國後他們就要回到當時那種水深火熱的生活中去。後來的調查還原了真相,這6箇中國人裡,有5個是乘坐一艘已經破了的小船逃命的。當時因為這條船已經破了,人們都認為沒法救命,只有這5 個人在萬般無奈之下想努力試一試。他們將這條破船扔下海,然後跳上去,緊緊抱住這條船,把它當成大海中的一塊浮木,漂浮在大海上。幸運的是,他們終於遇上了一艘救生艇,被救了上來。而另外一箇中國人,則是緊緊抱住了一塊門板,直到遇上救生艇獲救。
本文中的主要流程及程式參考該文。
2、訓練資料基本特點的統計分析
2.1樣本資料初探
資料在這裡,我們用到的兩個檔案分別是train.csv和test.csv,分別存放著訓練資料集(有標籤)和測試資料集(無標籤)。下載資料後可以看到資料如下的樣子:
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22 | 1 | 0 | A/5 21171 | 7.25 | S | |
2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Thayer) | female | 38 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26 | 0 | 0 | STON/O2. 3101282 | 7.925 | S |
訓練集資料共有891個樣本,測試集資料418個樣本,這裡測試集資料佔總樣本約1/3。這個比例是不固定的,建議在1/5~1/3之間;同時可以看到總共有1309個樣本,而事故發生時船上共有2224人,因而不是所有人的資料都被包含進本專案中。
下面看看各特徵代表的意思:
變數名稱 | PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked |
---|---|---|---|---|---|---|---|---|---|---|---|---|
中文翻譯 | 乘客ID | 是否倖存 | 艙位等級 | 姓名 | 性別 | 年齡 | 兄弟姐妹個數 | 父母子女個數 | 船票 | 票價 | 船艙名稱 | 登船港口 |
變數型別 | int | int | int | chr | chr | num | int | int | chr | num | chr | chr |
變數描述 | 按順序編號,如1,2,3……可能是資料據錄入資料庫時產生的主鍵,無意義 | 樣本標籤,取0,1,0代表遇難,1代表倖存 | 艙位等級包括1,2,3級,艙位等級高艙內設施可能會更好一點,便利性可能也會更好,因而可能和倖存概率很有關係 | 姓名可能能一定程度反映出一定人的身份,身份與他的經濟能力即與艙位好壞等掛鉤,在發生事故後可能與受保護的程式有關,比如為社會做出特殊貢獻的人先走,比如特殊人才先走 | 性別可能也與倖存率有關,社會的普遍做法是女士優先 | 年齡也可能與倖存率有關,社會普遍做法是老人小孩優先,更可能是小孩優先 | 船上兄弟姐妹的個數也可能與倖存率有關,家裡獨子的可能更容易獲得上救生艇的機會 | 船上父母子女的個數也可能與倖存率有關,也是人多獲得上救生艇機會更大,而父母一般會將機會給子女 | 船票反映出來的也是位置艙位等情況,道理是一樣的 | 票價同船票和艙位等級的效果 | 船艙名稱反映出的也是設施等條件,比如好的船舶可能有更便利的逃生門或者配備了更多的救生艇 | 登船港口可能也能與一個人的身份國籍等有關聯進而可能與逃生率關聯 |
以上便是對資料的最初步的認識,如果有興趣可以更深入瞭解當時船上人員的背景資訊及特徵變數各型別的具體意思,比如哪個字母代表哪個港口,從這個港口一般登船的會是什麼人,等等,為上表中的特徵變數可能對分析目標(倖存率)的影響做支撐。
2.2各特徵的基本統計分析
2.2.1 載入資料
# 載入資料
##### 載入訓練和測試資料
#####--------------------------------------------------------------------------------------------------
#這裡讀入資料的時候我們沒有做任何的處理(像去除空值這些)
train=pd.read_csv("./train.csv")
test=pd.read_csv("./test.csv")
#檢視樣本數和特徵數
#train_num,train_var_num=np.shape(train)
#test_num,test_var_num=np.shape(test)
#print("訓練集:有",train_num,"個樣本","每個樣本有",train_var_num,"個變數.")
#print("測試集:有",test_num,"個樣本","每個樣本有",test_var_num,"個變數.")
print(train.info())
print(test.info())
我們可以對資料進行進一步的檢視,如下:
#訓練集
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId 891 non-null int64
Survived 891 non-null int64
Pclass 891 non-null int64
Name 891 non-null object
Sex 891 non-null object
Age 714 non-null float64
SibSp 891 non-null int64
Parch 891 non-null int64
Ticket 891 non-null object
Fare 891 non-null float64
Cabin 204 non-null object
Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB
None
#測試集
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId 418 non-null int64
Pclass 418 non-null int64
Name 418 non-null object
Sex 418 non-null object
Age 332 non-null float64
SibSp 418 non-null int64
Parch 418 non-null int64
Ticket 418 non-null object
Fare 417 non-null float64
Cabin 91 non-null object
Embarked 418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
None
可以看到訓練訓練集有 891 個樣本 每個樣本有 12 個變數;測試集有 418 個樣本 每個樣本有 11 個變數。
從變數型別來有浮點型有2個,這類特徵為連續型變數,後續在統計分佈和缺失值的處理上與整型和符號型是不一樣的;整型特徵有4個、符號型有5個,這兩類在統計分佈和缺失值處理上大體相似。
從缺失值來看,Age和Cabin在訓練集和測試集中都有較多的缺失值,而Embarked在訓練集中有兩個缺失值,Fare在測試集中有一個缺少值;對於缺少值的處理也有不同的方法,比如如果某一特徵缺失值很多,通過初步分析又分析跟分析目標關聯性不大,可以考慮剔除該特徵,或者用其他與他相關的特徵來代替;對於少量的連續型特徵可以考慮用均值、中值代替,也可以找與它相關性很強的特徵中對應樣本的值來替代等方法;對於少量的離散形或符號型特徵缺失值,可考慮使用出現頻率最高的符號值來替代。後面在特徵工程中有專門的內容來處理缺失值。
2.2.2 去除離群點
離群點對後面的資料建模有比較大的影響,尤其是在迴歸建模中。因而應當儘量去除資料中的離群點。
在這個專案中由於測試資料用來考核的,不能刪除任何一個數據,因而我們只需要在訓練集中去除離群點。
離群點的去除本身就是一個學問,有很多方法,這裡只採用一種比較簡單的方法,四分位極差法(比上分位小於1.5倍四分位極差或比下分位大1.5倍四分位極差則為離散點)。
去除離群點
這裡選取了”Age”,”SibSp”,”ParCh”,”Fare”四個數值型變數;另一個數值型變數艙位等級沒選是因為該變數只有1、2、3級不可能有離群點,其他符號型變數諸如性別、登入港口,也只有有限的型別,一般不可能離群,也沒有必要分析是否離群。
#去除離群點
#####--------------------------------------------------------------------------------------------------
#離群點檢測
def detect_outliers(df,n,features):
'''
輸入:
df:資料框,為需要檢測的樣本集
n:正整數,樣本特徵超出四分位極差個數的上限,有這麼多個特徵超出則樣本為離群點
features:列表,用於檢測是否離群的特徵
輸出:
'''
outlier_indices=[]
outlier_list_col_index=pd.DataFrame()
#對每一個變數進行檢測
for col in features:
#計算四分位數相關資訊
Q1=np.percentile(df[col],25)
Q3=np.percentile(df[col],75)
IQR=Q3-Q1
#計算離群範圍
outlier_step=1.5*IQR
#計算四分位數時如果資料上有空值,這些空值也是參與統計的,所以統計出來的Q1、Q3、IQR這些資料有可能是NAN,但是這並不要緊,在判斷是否大於或小於的時候跟NAN比較一定是false,因而樣本並不會因為空值而被刪除掉
#空值會在後面特徵工程時再做處理
#找出特徵col中顯示的離群樣本的索引
outlier_list_col=df[(df[col]<Q1-outlier_step)|(df[col]>Q3+outlier_step)].index
#額外儲存每一個特徵在各樣本中的離群判斷
temp=pd.DataFrame((df[col]<Q1-outlier_step)|(df[col]>Q3+outlier_step),columns=[col])
#將索引新增到一個綜合列表中,如果某個樣本有多個特徵出現離群點,則該樣本的索引會多次出現在outlier_indices裡
outlier_indices.extend(outlier_list_col)
#額外儲存每一個特徵在各樣本中的離群判斷,方便檢視資料
outlier_list_col_index=pd.concat(objs=[outlier_list_col_index,temp],axis=1)
#選出有n個以上特徵存在離群現象的樣本
outlier_indices=Counter(outlier_indices)
multiple_outliers=list(k for k,v in outlier_indices.items() if v>n)
return multiple_outliers,outlier_list_col_index
#獲取離群點
outliers_to_drop,outlier_col_index=detect_outliers(train,2,["Age","SibSp","Parch","Fare"])
#這裡選取了"Age","SibSp","ParCh","Fare"四個數值型變數;另一個數值型變數艙位等級沒選是因為該變數只有1、2、3級不可能有離群點,其他符號型變數諸如性別、登入港口,也只有有限的型別,一般不可能離群,也沒有必要分析是否離群。
離群點詳細資訊:
print(train.loc[outliers_to_drop])
print(outlier_col_index.loc[outliers_to_drop])#檢視哪個特徵對樣本成為離群點有決定作用.
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
27 | 28 | 0 | 1 | Fortune, Mr. Charles Alexander | male | 19.0 | 3 | 2 | 19950 | 263.00 | C23 C25 C27 | S |
88 | 89 | 1 | 1 | Fortune, Miss. Mabel Helen | female | 23.0 | 3 | 2 | 19950 | 263.00 | C23 C25 C27 | S |
159 | 160 | 0 | 3 | Sage, Master. Thomas Henry | male | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
180 | 181 | 0 | 3 | Sage, Miss. Constance Gladys | female | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
201 | 202 | 0 | 3 | Sage, Mr. Frederick | male | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
324 | 325 | 0 | 3 | Sage, Mr. George John Jr | male | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
341 | 342 | 1 | 1 | Fortune, Miss. Alice Elizabeth | female | 24.0 | 3 | 2 | 19950 | 263.00 | C23 C25 C27 | S |
792 | 793 | 0 | 3 | Sage, Miss. Stella Anna | female | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
846 | 847 | 0 | 3 | Sage, Mr. Douglas Bullen | male | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
863 | 864 | 0 | 3 | Sage, Miss. Dorothy Edith “Dolly” | female | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
下面對樣本成為離群點的原因進行分析
PassengerId | Age | SibSp | Parch | Fare | |
---|---|---|---|---|---|
27 | 28 | False | True | True | True |
88 | 89 | False | True | True | True |
159 | 160 | False | True | True | True |
180 | 181 | False | True | True | True |
201 | 202 | False | True | True | True |
324 | 325 | False | True | True | True |
341 | 342 | False | True | True | True |
792 | 793 | False | True | True | True |
846 | 847 | False | True | True | True |
863 | 864 | False | True | True | True |
從上表中可以看到,在10個離群樣本中,Age並不是原因,而SibSp,Parch,Fare三個特徵在每一個樣本中都表現出離群。詳情可利用簡單的統計資訊檢視。
print(train.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% | 0.000000 | 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 |
從上表中可以看到,在訓練資料中:
Survived上看,Survived只能取0、1,到中位數的時候仍然還是0,下分位數才到1,說明訓練資料樣本中,倖存率在1/4~1/2之間,結合均值0.383838,倖存率大概在1/3左右;
Pclass上看,Pclass只能取1、2、3級,但均值為2.308642,說明絕大數乘客為三等票,結合四位數,可知這一比例在大於5/8;
Age上看,891個樣本只有714個統計數字,說明有183個缺失值;最小值是0.420000週歲,最大值是80.000000週歲,說明船上有剛出生沒多久的小孩,也有年紀很大的老人;20.125000-38.000000週歲佔了一半,且平均年齡是29.699118週歲,說明青壯年為主;
SibSp上看,超過一半的人沒有兄弟姐妹或配偶在船上,但也有比較多的,最多的是8個;1個以下的佔了3/4,但均值是0.523008,說明有不少人有1個兄弟姐妹在船上,可能也有一些大於1個的。
Parch上看,超3/4的人沒有父母或子女在船上,也有個別比較多的,最大值是6個,但均值是0.381594,說明這種情況很少,更可能是1個;
Fare上看,最小值是0,說明有沒花錢上船的;3/4的人的票價低於31,但均值卻是32.204208,主要原因是高票價的人雖然少,但它們的數值較大,最大的是512.329200,這些票價拉高了整體票價;
離群分析:
Age上看,沒有樣本表現出離群;儘管有183個缺失值,這此缺失值並不影響離群分析,或者說離群分析的時候不會把缺失值排除掉;
SibSp上看,10個樣本在SibSp上均表現出離群,對比SibSp值及SibSp在訓練集中的統計資訊,SibSp大於等3個就離群了,在這一特徵上有很多樣本表現出離群,而最終離群點的確定是幾個特徵綜合選出來的;所以並不代表刪除離群點後SibSp只會小於等於2個;
Parch上看,10個樣本在Parch上均表現出離群,對比Parch的具體值及Parch在訓練集中的統計資訊,Parch大於等於2個就離群了,同樣,在這一特徵上有很多樣本表現出離群,而最終離群點的確定是幾個特徵綜合選出來的;所以並不代表說明刪除離群點後Parch只會小於等於1個,尤其是Parch的四分位數都為0,四分位極差也為0,這種情況下,只要大於0的值就會被視為離群樣本;
Fare上看,10個樣本在Fare上均表現出離群,對比Fare的具體值及Fare在訓練集中的統計資訊,Fare大於等於69.55時就離群了;
可以看到這種定義離群的方法還需要改進。
刪除離群點:
#train = train.drop(outliers_to_drop, axis = 0).reset_index(drop=True)
2.2.3 整合訓練集和測試集
這一步沒有特別意義,只是為了後面在有的內容統計和值的處理上更方便,也可以不整合而對每個資料集單獨處理。
但是整合需要在訓練集剔除離群點後再做,因為測試集是不需要剔除離群點。
train_len,train_var_num=np.shape(train)
dataset=pd.concat(objs=[train,test],axis=1).reset_index(drop=True)
#資料集缺失值用NAN填充
dataset=dataset.fillna(np.nan)
2.2.4 刪除離群點後整體檢視資料
檢視整個資料集缺失值
#整個資料集缺失值
Age 256 0.197075
Cabin 1007 0.775212
Embarked 2 0.001540
Fare 1 0.000770
Name 0 0.000000
Parch 0 0.000000
PassengerId 0 0.000000
Pclass 0 0.000000
Sex 0 0.000000
SibSp 0 0.000000
Survived 418 0.321786
Ticket 0 0.000000
dtype: int64
#訓練樣本缺失值
PassengerId 0 0.000000
Survived 0 0.000000
Pclass 0 0.000000
Name 0 0.000000
Sex 0 0.000000
Age 170 0.192963
SibSp 0 0.000000
Parch 0 0.000000
Ticket 0 0.000000
Fare 0 0.000000
Cabin 680 0.771850
Embarked 2 0.002270
dtype: int64
從整個資料集和訓練集的缺失值來看,仍然是Age和Cabin缺失值較多,並且在訓練樣本中的比例與整個資料集中的比例基本一致;訓練中2個樣本的Embarked缺失,測試集中一個Fare值缺失;整體資料集中Survived有418個缺失值是因為測試集樣本類別標籤已被抹除。
再重點看看訓練集
print(train.info())
特徵詳情如下:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 881 entries, 0 to 890
Data columns (total 12 columns):
PassengerId 881 non-null int64
Survived 881 non-null int64
Pclass 881 non-null int64
Name 881 non-null object
Sex 881 non-null object
Age 711 non-null float64
SibSp 881 non-null int64
Parch 881 non-null int64
Ticket 881 non-null object
Fare 881 non-null float64
Cabin 201 non-null object
Embarked 879 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 89.5+ KB
None
去除離群點後訓練集基本統計資訊:
print(train.describe())
PassengerId | Survived | Pclass | Age | SibSp | Parch | Fare | |
---|---|---|---|---|---|---|---|
count | 881.000000 | 881.000000 | 881.000000 | 711.000000 | 881.000000 | 881.000000 | 881.000000 |
mean | 446.713961 | 0.385925 | 2.307605 | 29.731603 | 0.455165 | 0.363224 | 31.121566 |
std | 256.617021 | 0.487090 | 0.835055 | 14.547835 | 0.871571 | 0.791839 | 47.996249 |
min | 1.000000 | 0.000000 | 1.000000 | 0.420000 | 0.000000 | 0.000000 | 0.000000 |
25% | 226.000000 | 0.000000 | 2.000000 | 20.250000 | 7.895800 | 0.000000 | 7.910400 |
50% | 448.000000 | 0.000000 | 3.000000 | 28.000000 | 0.000000 | 0.000000 | 14.454200 |
75% | 668.000000 | 1.000000 | 3.000000 | 38.000000 | 1.000000 | 0.000000 | 30.500000 |
max | 891.000000 | 1.000000 | 3.000000 | 80.000000 | 5.000000 | 6.000000 | 512.329200 |
從以上的基本統計資訊可以看到,去除離群點後對Age和Fare這類連續變數的分佈幾乎沒有影響,但對SibSp和Parch的分佈產生一定改變,這其實也是我們刪除離群點的原因。
2.2.5 特徵之間的相關性初探
目前只能對數值型特徵計息相關係數,初步探尋特徵之間的關聯,尤其是與是否倖存的關聯性。
g=sns.heatmap(train[["Survived","Age","SibSp","Parch","Pclass","Fare"]].corr(),annot=True,fmt = ".2f",cmap = "coolwarm")
plt.show()
結果如下
從上圖可以看到,Pclass和Survived成負相關,即3等票的倖存率要小於1等票;Fare和 Survived成正相關,即票價越高,可能越能倖存;Age,SibSp,Parch與Survived幾乎不相關,但是並不意味著這些特徵就沒有用處,整體上的不相關可能是因為資料的“正”“負”抵消產生而看起來不相關;比如年齡,雖然整體上與Survived相關性不大,但可能區域性會產生相關性,比如小孩就很容易倖存。
Age和SibSp、Parch、Pclass、Fare都有一定的相關性,因為生育有年齡特點,年齡在一定程式上也能跟經濟能力相關,經濟能力又跟船票等級和票價這些相關,因為這些資料展現出來的特點跟我們的生活經驗是吻合的。
SibSp和Age、Parch、Fare有一定相關性。
Parch和Age、SibSp、Fare相關
Pclass和Age、Fare相關
Fare和Age、SibSp、Parch、Pclass相關,這其他成其以Fare和Pclass的相關性最大。
雖然特徵之前的相關性分析對於確定特徵和最終的倖存率沒有直接關係,但這對於特徵工程很有指導意義。
2.3 各特徵與分析目標的關聯分析
2.3.1 年齡Age與生存率的關係
# 特徵之間的相關性初探
#####--------------------------------------------------------------------------------------------------
#年齡Age與生存率的關係
g=sns.kdeplot(train["Age"][(train["Age"].notnull())&(train["Survived"]==0)],color="Red",shade=True)
g=sns.kdeplot(train["Age"][(train["Age"].notnull())&(train["Survived"]==1)],color="Blue",shade=True,ax=g)
g.set_xlabel("Age")
g.set_ylabel("Frequency")
g=g.legend(["Not Survived","Survived"])
plt.show()
結果如下圖
從上圖中可以看到,在倖存都中年齡較小的乘客所佔比重明顯要高於遇難者年齡較小的乘客所佔比重,而遇難者中20幾歲乘客所佔比重明顯要高於倖存者中20幾歲乘客所佔比重,另一段在另外遇難者中60-80歲乘客所佔比重也要高於倖存者中60-80乘客所佔比重。
所有說,儘管從Age與Survived的相關係數看,兩者幾乎不相關,但不代表,倖存率在年齡中沒有區別。
2.3.2 性別Sex與生存率的關係
#性別Sex與生存率的關係
g=sns.barplot(data=train,x="Sex",y="Survived")
g.set_ylabel("Survival Probability")
plt.show()
結果如下
從圖上可以看到女性倖存率遠遠高於男姓倖存率,性別將是對結果的預測產生比較大的權重。
#進一步檢視
print(train[["Sex","Survived"]].groupby("Sex").mean())
Survived
Sex
female 0.747573
male 0.190559
2.3.3 船上兄弟姐妹或配偶數量SibSp與生存率的關係
# 船上兄弟姐妹或配偶數量SibSp與生存率的關係
g=sns.barplot(data=train,x="SibSp",y="Survived")
g.set_ylabel("Survival Probability")
plt.show()
結果如下
從圖上可以看到,似乎船上兄弟姐妹人數比較少的時候倖存概率更大。
2.3.4 船上父母或子女數量Parch與生存率的關係
#船上父母或子女數量Parch與生存率的關係
g=sns.barplot(data=train,x="Parch",y="Survived")
g.set_ylabel("Survival Probability")
plt.show()
結果如下
從圖上看,船上父母或子女數量為1到3個的時候倖存率會更高一些,沒有或有很多個時倖存率會低一點。
2.3.5 船票等級Pclass與生存率的關係
#船票等級Pclass與生存率的關係
g=sns.barplot(data=train,x="Pclass",y="Survived")
g.set_ylabel("Survival Probability")
plt.show()
結果如下
從圖上可以明顯看到不同的船票等級中倖存率是不同的,而且大致成反比例關係。
2.3.6 票價Fare與生存率的關係
#票價Fare與生存率的關係
g=sns.kdeplot(train["Fare"],color="Green",shade=True)
g=sns.kdeplot(train["Fare"][train["Survived"]==0],color="red",shade=True,ax=g)
g=sns.kdeplot(train["Fare"][train["Survived"]==1],color="blue",shade=True,ax=g)
g.set_xlabel("Fare")
g.set_ylabel("Frequency")
g=g.legend(["All","Not Survived","Survived"])
plt.show()
結果如下
從圖上可以明顯看到不同票價在倖存率上的區別,主要分為兩段,遇難者在低票價區的概率相對倖存者在低票價區的概率明顯要高,而倖存者在高票價區的概率明顯要比遇難者在高票價區的概率要高。
同時從圖上可以看到票價的分佈存在長尾,相對正態分佈嚴重偏斜,這種分佈在很多演算法中會導致該特徵產生過高的權重(高於它應該享有的待遇),對該特徵進行log變換可以糾正偏斜。
我們同時在訓練集和測試集進行變換,由於測試集中存在一個缺失值,可以先將該缺失值補上,這裡採用中值填充。
#Fare特徵的缺失值進行填充
dataset["Fare"]=dataset["Fare"].fillna(dataset["Fare"].median())#這裡用到了整個資料集
test["Fare"]=test["Fare"].fillna(dataset["Fare"].median())
#利用柱形圖來檢視log變換前Fare在整個資料集中的分佈
g=sns.distplot(dataset["Fare"],color="M",label="Skewness:%.2f"%(dataset["Fare"].skew()))
g.legend(loc="best")
plt.show()
結果如下:
可以看到很明顯的長尾,偏斜度達4.51
#下面利用log函式進行資料變換
dataset["Fare"]=dataset["Fare"].map(lambda i:np.log(i) if i>0 else 0)#map()函式具體將元素進行對映的功能
#檢視變換後的資料分佈
g=sns.distplot(dataset["Fare"],color="M",label="Skewness:%.2f"%(dataset["Fare"].skew()))
g.legend(loc="best")
plt.show()
結果如下:
可以看到資料的偏斜得到很大程度的改善。
2.3.7 登入港口Embarked與生存率的關係
#訓練集中資料存在兩個缺失值,我們可以採用出現頻率最大的港口來填充缺失值。因而需要先檢視資料整體情況
print(dataset["Embarked"].describe())
結果如下:
count 1297
unique 3
top S
freq 904
Name: Embarked, dtype: object
可以看到出現頻率最大的港口“S”
#缺失值填充
dataset["Embarked"]=dataset["Embarked"].fillna(dataset["Embarked"].describe().top)
train["Embarked"]=train["Embarked"].fillna(dataset["Embarked"].describe().top)
print(dataset["Embarked"].isnull().sum())
print(train["Embarked"].isnull().sum())
可以看到填充後缺失值為0。
下面來檢視Embarked與生存率的關係。
#檢視Embarked與生存率的關係
g=sns.barplot(data=train,x="Embarked",y="Survived")
g.set_ylabel("Survival Probability")
plt.show()
結果如下:
可以看到從港口“C”登船的乘客生存率最高。可能是從Cherbourg(C)港口登船的乘客比Queenstown (Q)和Southampton (S)登船的乘客買一等票的比例更高。可以進一步核查。
# 進一步檢視 Pclasst和Embarked的關係
g=sns.factorplot(data=train,x="Pclass",col="Embarked",size=6,kind="count",palette="muted")
g.despine(left=True)
g = g.set_ylabels("Count")
plt.show()
結果如下:
可以看到“C”港口登船的乘客中一等票的比例確實要比其他兩個港口高很多。
2.3.8 船艙Cabin與生存率的關係
船艙是符號型變數,可以試著檢視其變數(最好之前先檢視該變數值有多少類,如果類別太多就不適合直接統計,需要進一步處理)。
print(train["Cabin"].describe())
結果如下:
count 201
unique 147
top B96 B98
freq 4
Name: Cabin, dtype: object
唯一值有147個,因而不能直接統計,將在後面特徵工程中做處理。
2.3.8 姓名和船票資訊由於是比較散亂的符號型變數不便於統計,需要做進一步處理.
3、 缺失值填充
這裡包括缺失值的填充和一些符號值的數值化(符號值不需要急著數值化,後面的特徵工程會進行啞變數處理,但有時為了分析的需要會提前處理)。
前面在分析相關性的時候,我們已經對Fare、Embarked缺失值進行了填充,現在對其他特徵的缺失值填充。
3.1 對年齡Age進行填充
Age在整個資料集中有1007個缺失值,但通過前面的分析知道,年齡可能與生存率存在一定程度的相關性。所有直接把這一特徵丟棄可能對於後面模型的預測不利。
這裡可以通過找到與年齡相近的特徵,然後檢視相近特徵對應位置的值,再找到相近特徵中其他位置中與該位置值相等的位置,我們要填充的特徵可能在這些位置上有值,利用這些值來對缺失值進行填充是一個可行的辦法。
下面我們需要找到與Age相近的特徵。
為了度量Age與性別Sex的相關性,將Age數值化(即將符號變數轉換為啞變數)
#性別Sex的數值化
dataset["Sex"]=dataset["Sex"].map({"male":0,"female":1})
train["Sex"]=train["Sex"].map({"male":0,"female":1})
考慮到Embarked與年齡關聯不大,暫時不進行處理。這裡需要考察與Age相關聯的變數有Sex、SibSp、Parch、Plcass,從之前的相關性分析可以看到,票價Fare與年齡Age也存在一定相關性,但比較SibSp、Parch、Plcass與Age的相關性會小很多,而且Fare是連續型變數,要找到該特徵上的兩個相等值會更困難一點,當然可以做一定的近似來匹配位置。但這裡沒有將其放入Age的關聯分析裡面。
#Age與Sex的相關性分析
g=sns.factorplot(data=dataset,x="Sex",y="Age",kind="box")
plt.show()
結果如下:
從上可以看到年齡的分佈與性別幾乎沒有關係。
#Age與SibSp的相關性分析
g=sns.factorplot(data=dataset,x="SibSp",y="Age",kind="box")
plt.show()
結果如下:
從圖上可以看到,兄弟姐妹不同的個數下年齡的分佈是不一樣的,可以將SibSp的值與Age做對應來匹配Age中的缺失值。這種匹配是有誤差和一定浮動範圍的,但如果通過多個特徵一起來
#Age與Parch的相關性分析
g=sns.factorplot(data=dataset,x="Parch",y="Age",kind="box")
plt.show()
從圖上可以看到,父母子女在船上的個數不同,年齡的分佈也有區分,這個也可以用來和Age做對應。
#Age與Pclass的相關性分析
g=sns.factorplot(data=dataset,x="Pclass",y="Age",kind="box")
plt.show()
結果如下:
從圖上可以看到,不同船票等級中年齡分佈是不一樣的,雖然有重疊,還是可以用來和Age做對應。
#填充Age缺失值
#獲取Age缺失值索引
index_NaN_age=list(dataset["Age"][dataset["Age"].isnull()].index)
for i in index_NaN_age:
age_med=dataset["Age"].median()#如果通過關聯特徵找不到匹配的值,則用整個資料的中值填充
age_pred=dataset["Age"][((dataset["SibSp"]==dataset.iloc[i]["SibSp"])&(dataset["Parch"]==dataset.iloc[i]["Parch"])&(dataset["Pclass"]==dataset.iloc[i]["Pclass"]))].median()
if not np.isnan(age_pred):
dataset["Age"].iloc[i]=age_pred
else:
dataset["Age"].iloc[i]=age_med
#填充值後再看一次Age在不同Survived下的分佈情況
g=sns.factorplot(data=dataset,x="Survived",y="Age",kind="violin")
plt.show()
結果如下:
從圖上還是可以看到在年經較小的人群中倖存概率會更大一點(更寬),年輕人中遇難可能性更大(中部更寬),遇難和倖存的人年齡中值其實也是有輕微差別。
利用密度圖會更清楚一點。
從圖