假期閑着無聊,做了一下Kaggle練手的項目--預測泰坦尼克號乘客的存活情況。對於一些函數和算法,剛開始也是懵懵懂懂的,但通過自己查資料,還是明白了許多。然后就是自己寫的時候還看了下別人的做法,特別是國外的文章,寫得很詳細,邏輯特別清晰,還把不同算法的結果給你列出來,最后選擇了最優算法。好佩服,希望自己以后也有這樣的能力。我會把參考資料的網址放在需要查看的地方。train和test兩個文件可以自己上Kaggle上下載,不過需要注冊登錄,在注冊登錄的時候用郵箱驗證需要在谷歌瀏覽器里加載谷歌訪問助手才能看到驗證碼最后才能成功注冊。。。
------------------------------------
我們的整個流程如下:
①數據預處理:數據清洗、可視化、標簽化
②分割訓練數據
③隨機森林分類器及其參數調節
④正式預測test.csv
數據預處理:數據清洗、可視化、標簽化
首先,先導入需要的模塊並讀取數據
#導入包,讀取數據
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns %matplotlib inline data_train = pd.read_csv(r'C:\Users\Administrator\Desktop\456\train.csv') data_test = pd.read_csv(r'C:\Users\Administrator\Desktop\456\test.csv')
導入成功后,我們來看看訓練數集↓
↑發現有如下特征:乘客ID,存活情況,船票級別,乘客姓名,性別,年齡,船上的兄弟姐妹以及配偶的人數,船上的父母以及子女的人數,船票編號,船票費用,所在船艙,登船的港口
查看缺失值。
從以上,我們可以看出:
①訓練集中共有891名乘客
②891名乘客中,714個乘客具有Age的信息,有177個乘客的Age缺失。
③891名乘客中,只有204個乘客具有Cabin的信息,有687個乘客的Cabin缺失。
查看數據的描述性信息
①乘客獲救的概率是0.38
②乘客的平均年齡(沒算缺失值)是29.7歲
來開始進行可視化
#我們要通過繪圖來觀察訓練數集的基本情況哦,我們先從登艙口Embarked開始,來繪制二維柱狀圖,不同性別在不同登艙口的生存情況
sns.barplot(x='Embarked',y='Survived',hue='Sex',data=data_train)
結論:
①無論在哪個港口上船,女性獲救的概率遠遠高出男性。
②C港口上船的乘客獲救較高,可能是住C港口附近的人有共同的什么特征? 但也有可能沒什么關系。。先不管它。
#接着,我們用折線圖從Pclass,也就是幾等艙來觀測下生存概率
sns.pointplot(x='Pclass',y='Survived',hue='Sex',data=data_train,palette={'male':'blue','female':'pink'}, marker=['*',"o"],linestyle=['-','--'])
結論:
①女性的獲救率依舊遠遠高於男性,所以說,很有可能性別Sex對Survived具有顯著的影響
②隨着等級艙的降低,不論是男性或女性,獲救率也在降低。所以可能Pclass也對Surived有顯著影響,
也就是說乘客的身份/地位/財富/背景 對乘客的獲救率可能有顯著影響。
除此之外,我們來看看 堂兄弟/妹,孩子/父母有幾人,對是否獲救的影響
SibSp_info = data_train.groupby(['SibSp','Survived']) SibSp_df = pd.DataFrame(SibSp_info.count()['PassengerId']) print(SibSp_df) Parch_info = data_train.groupby(['Parch','Survived']) Parch_df = pd.DataFrame(Parch_info.count()['PassengerId']) print(Parch_df)
①只能說,大部分的人並沒有攜帶一家老小都來坐船玩
②我暫時看不出,SibSp、Parch對Survived的影響。先不管它吧 。。。。。。。。。。。。。。。
除了Embarked,Pclass等,我還想對年齡、Cabin的首字母(與乘客所在艙口有關)、工資這三種情況與生存情況畫圖分析
但現在的情況是:
①Age是連續型變量,且有些缺失值。對於缺失值的處理有如下:
所以,我的選擇是第三個,把具有缺失值的作為一類別。我們可以按照我們日常生活的邏輯對年齡划區間,對年齡進行分組,再畫圖根據組來對乘客的Survived狀況進行分析。
②Cabin是一個字母+一串數字,但我們可以它們切割了,只留字母。哎,Cabin的缺失值是賊多了,有點麻煩,我選擇了上面第二種方法,依舊把它作為新的類目。用“N”代替
③船票費用也是連續變量,同樣需要分組,我們把他們四分化,分為(最小值到下四分位數),(下四分位數到中位數),(中位數到上四分位數),(上四分位數到最大值)
④我覺得乘客的名字Name、船票編碼Ticket對乘客的Survived情況沒起到顯著的作用,所以我打算刪掉哦!
因此,我們要完成以上的任務哦。也就是說,我們要把原來的data_train轉化成我們需要的、可以方便我們進行數據分析的表格↓
①簡化年齡,就是分組
def simplify_ages(df): #把缺失值補上,方便分組
df.Age = df.Age.fillna(-0.5) #把Age分為不同區間,-1到0,1-5,6-12...,60以上,放到bins里,八個區間,對應的八個區間名稱在group_names那
bins = (-1, 0, 5, 12, 18, 25, 35, 60, 120) group_names = ['Unknown', 'Baby', 'Child', 'Teenager', 'Student', 'Young Adult', 'Adult', 'Senior'] #開始對數據進行離散化,pandas.cut就是這個功能
catagories = pd.cut(df.Age,bins,labels=group_names) df.Age = catagories return df
②簡化Cabin,就是取字母
def simplify_cabin(df): df.Cabin = df.Cabin.fillna('N') df.Cabin = df.Cabin.apply(lambda x:x[0]) return df
③簡化工資,也就是分組
def simplify_fare(df): df.Fare = df.Fare.fillna(-0.5) bins = (-1, 0, 8, 15, 31, 1000) group_names = ['Unknown', '1_quartile', '2_quartile', '3_quartile', '4_quartile'] catagories = pd.cut(df.Fare,bins,labels=group_names) df.Fare = catagories return df
④刪除無用信息
def simplify_drop(df): return df.drop(['Name','Ticket','Embarked'],axis=1)
⑤整合一遍,湊成新表
def transform_features(df): df = simplify_ages(df) df = simplify_cabin(df) df = simplify_fare(df) df = simplify_drop(df) return df
⑥執行讀取新表
#必須要再讀取一遍原來的表,不然會報錯,不僅訓練集要簡化,測試集也要,兩者的特征名稱要一致
data_train = pd.read_csv(r'C:\Users\Administrator\Desktop\456\train.csv') data_train = transform_features(data_train) data_test = transform_features(data_test) data_train.head()
以上任務執行完畢,繼續畫圖!
#好啦,我們根據新的表格來畫圖啦,先來話Age-Survived的圖哦,以Sex分組
sns.barplot(x = 'Age',y = 'Survived',hue='Sex',data = data_train)
①女性獲救概率依舊高於男性,這點就不再闡述了。
②老人小孩獲救率最高,中青年特別是男性,獲救率是最低的。真的是很紳士了。
#再按Cabin-Survived畫
sns.barplot(x = 'Cabin',y = 'Survived',hue='Sex',data = data_train) #根據艙位,其實也可以看出一些端倪哦
#到Fare啦
sns.barplot(x = 'Fare',y = 'Survived',hue='Sex',data = data_train) #果然票費越高的,存活率更高啊,
數據預處理的最后一步:標簽化數據
這里我們會用到Scikit-learn中的LabelEncoder,
Scikit-learn中的LabelEncoder會將每個唯一的字符串值轉換為一個數字
最后的結果會是一張數表哦
關於sklearn的學習資料在這:https://www.jianshu.com/p/cd5a929bec33 《sklearn快速入門》
這里用到了sklearn的preprocessing預處理模塊
還用到pandas的concat功能,concat函數是在pandas底下的方法,可以將數據根據不同的軸作簡單的融合
#test和train一起標簽化,共生死,共沉淪 from sklearn import preprocessing def features_encode(df_train,df_test): features = ['Sex','Age','Fare','Cabin'] df_combined = pd.concat([df_train[features],df_test[features]]) for feature in features: le = preprocessing.LabelEncoder() #非數字型和數字型標簽值標准化 le = le.fit(df_combined[feature]) df_train = le.transform([df_train[feature]]) df_test = le.transform([df_test[feature]]) return df_train,df_test #開始代入函數 data_train,data_test = encode_features(data_train,data_test) data_train.head()
可以看到,像Age,Fare Cabin都標簽化了,都是數字了
分割訓練數據
首先,從標簽(y)中分離出特征(X)。
X_all:所有特征減去我們想要預測的值(Survived)。也就是所有的自變量
y_all:只有我們想要預測的價值,傳說中的目標變量,也就是因變量。
其次,使用Scikit-learn將這些數據隨機洗牌成四個變量。 在這種情況下,訓練80%的數據,然后測試其他20%。
之后,這些數據將重新組織為KFold模式,以驗證訓練算法的有效性。這是在第三步的,只是提前說一下
不懂的話看B站吳恩達視頻的61集 10-3 Model Selection and Train_Validation_Test Sets
不過,這里還是推薦下幾個網址:
https://zhuanlan.zhihu.com/p/30059442 《通俗理解決策樹》
https://segmentfault.com/a/1190000011835617 《隨機森林簡明教程》
https://segmentfault.com/a/1190000008318983 《K-折交叉驗證》
寫得都很好,特別是那個《隨機森林簡明教程》,講的很詳細,自己再動手把相關的參數算一遍,
會大概對隨機森林及其參數有個比較宏觀的理解。
from sklearn.model_selection import train_test_split X_all = data_train.drop(['Survived', 'PassengerId'],axis=1)#主要是乘客ID也沒啥用,刪就刪了吧 y_all = data_train['Survived'] p = 0.2 #用 百分之20作為測試集 """ 穿插一個知識點 train_test_split是交叉驗證中常用的函數,功能是從樣本中隨機的按比例選取train data和test data,形式為: X_train,X_test, y_train, y_test = cross_validation.train_test_split(train_data,train_target,test_size=0.4, random_state=0) """ X_train,X_test, y_train, y_test = train_test_split(X_all,y_all,test_size=p, random_state=23)
隨機森林分類器及其參數調節
再次第100遍推薦:
https://segmentfault.com/a/1190000011835617 《隨機森林簡明教程》
不然到parameter哪里會不太理解。parameter參數的數值我是參考國外一些網站的資料寫的。
from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import make_scorer, accuracy_score from sklearn.model_selection import GridSearchCV #選擇分類器的類型,我沒試過其他的哦,因為在這個案例中,有人做過試驗發現隨機森林模型是最好的,所以選了它。嗚嗚,我下次試試其他的 clf = RandomForestClassifier() #可以通過定義樹的各種參數,限制樹的大小,防止出現過擬合現象哦,也可以通過剪枝來限制,但sklearn中的決策樹分類器目前不支持剪枝 parameters = {'n_estimators': [4, 6, 9], 'max_features': ['log2', 'sqrt','auto'], 'criterion': ['entropy', 'gini'], #分類標准用熵,基尼系數 'max_depth': [2, 3, 5, 10], 'min_samples_split': [2, 3, 5], 'min_samples_leaf': [1,5,8] } #以下是用於比較參數好壞的評分,使用'make_scorer'將'accuracy_score'轉換為評分函數 acc_scorer = make_scorer(accuracy_score) #自動調參,GridSearchCV,它存在的意義就是自動調參,只要把參數輸進去,就能給出最優化的結果和參數 #GridSearchCV用於系統地遍歷多種參數組合,通過交叉驗證確定最佳效果參數。 grid_obj = GridSearchCV(clf,parameters,scoring=acc_scorer) grid_obj = grid_obj.fit(X_train,y_train) #將clf設置為參數的最佳組合 clf = grid_obj.best_estimator_ #將最佳算法運用於數據中 clf.fit(X_train,y_train)
predictions = clf.predict(X_test) print(accuracy_score(y_test,predictions)) #如果把上面多執行幾次,會發現這里的執行的結果都會有所不同,取決於用了parameter的哪些參數
即81%左右與的預測是對的呢
用K-Fold驗證
搞完上面后,我們要想想,
這個模型真的有用嗎?
要不再驗證下?
利用K-FOLD驗證,我們會把數據分成10個桶,然后使用不同的桶作為每次迭代的測試集運行算法。
這跟放回抽樣有點類似,重復放回抽個十次八次那樣
簡單介紹下K-Fold:
K-fold cross-validation (k-CV)是double cross-validation的延伸,作法是將dataset切成k個大小相等的subsets,
每個subset皆分別作為一次test set,其余樣本則作為training set,因此一次k-CV的實驗共需要建立k個models,
並計算k次test sets的平均辨識率。在實作上,k要夠大才能使各回合中的training set樣本數夠多,一般而言k=10算是相當足夠了。
下面的代碼是參考國外網站的寫法,讓我自己寫的話,一下子很難寫出來,但我覺得能先理清思路是最重要的。
from sklearn.cross_validation import KFold def run_kfold(clf): kf = KFold(891,n_folds=10) outcomes = [] fold = 0 for train_index,test_index in kf: fold = fold + 1 X_train,X_test = X_all.values[train_index],X_all.values[test_index] y_train,y_test = y_all.values[train_index],y_all.values[test_index] clf.fit(X_train,y_train) predictions = clf.predict(X_test) accuracy = accuracy_score(y_test,predictions) outcomes.append(accuracy) print("Fold {0} accuracy: {1}".format(fold,accuracy)) mean_outcome = np.mean(outcomes) print("Mean Accuracy:",mean_outcome) run_kfold(clf)
經過十次的抽樣,可以發現這十次的抽樣都有所不同,最小的正確率達到了0.74,最大的時候達到0.865那樣
平均十次下來是0.80.
正式預測test.csv
這個練手項目的要求結果是這樣的:
就是要有乘客的ID,和存活情況
predictions = clf.predict(data_test.drop('PassengerId',axis=1)) output = pd.DataFrame({'Passengers':data_test['PassengerId'],'Survived':predictions}) output.to_csv(r'C:\Users\Administrator\Desktop\456\ceshi2.csv') output.head()
然后要提交預測后的數據
啊,正確預測了0.72的數據,額。。。沒事,安慰自己是第一次提交。
總結下:
這次的試驗還是很粗糙的,比如我粗暴地把缺失的數據直接列為Unknown,其實可以根據缺失值的其他信息,比如說缺失值所在的年齡類別、票價類別等,來擬合可能的缺失值。
然后國外有一篇文章寫得很全,翻譯后的文章收錄在了這篇博客上 了:
http://www.cnblogs.com/zackstang/p/8185531.html