練習(xí):泰坦尼克號獲救數(shù)據(jù)預(yù)測,預(yù)測人員獲救與哪些因素有關(guān)。
1. 讀取數(shù)據(jù)
import pandas as pd
df = pd.read_csv('titanic_train.csv')
print(df.head())
print(df.columns)
print(df.isnull().sum())
print(df.describe())
運(yùn)行結(jié)果如圖1所示:

從圖中可以看出數(shù)據(jù)大小為891 * 12,列名有['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket','Fare', 'Cabin', 'Embarked'],有些列的數(shù)據(jù)存在著缺失,我們先看看每一列代表什么意思。
'PassengerId':乘客的ID
'Survived':乘客是否獲救,0表示未獲救,1表示獲救
'Pclass':乘客艙位等級
'Name':乘客名字
'Sex':乘客性別
'Age':乘客年齡
'SibSp':乘客兄弟姐妹數(shù)
'Parch':乘客攜帶的老人以及小孩數(shù)量
'Ticket':乘客船票編號
'Fare':乘客當(dāng)前船票價(jià)格
'Cabin':缺失值太多,忽略
'Embarked':乘客上船地點(diǎn),分別有C、S、Q
2.數(shù)據(jù)預(yù)處理
- 從圖1看出數(shù)據(jù)中'Age'列和'Carbin'列存在缺失值,所以我們先要對'Age'列進(jìn)行填充,這里使用均值填充,而'Carbin'列在分析的時(shí)候可以忽視它。
df['Age'] = df['Age'].fillna(df['Age'].median())
print(df.describe())
運(yùn)行結(jié)果如圖2所示:

從圖2可以看出,'Age'列缺失的數(shù)據(jù)已經(jīng)被填充好了。
- 觀察'Sex'這一列,發(fā)現(xiàn)里面的數(shù)據(jù)是male和female,分別代表男性和女性,在機(jī)器學(xué)習(xí)上無法識別str,我們需要將str轉(zhuǎn)換為0,1,即male用0表示,female用1表示。
print(df['Sex'].unique())
df.loc[df['Sex'] == 'male','Sex'] = 0
df.loc[df['Sex'] == 'female','Sex'] = 1
print(df['Sex'].unique())
運(yùn)行結(jié)果如圖3所示:

從圖3可以看出,已經(jīng)將male和female轉(zhuǎn)換為0和1.
- 觀察Embarked這一列,發(fā)現(xiàn)里面的數(shù)據(jù)是C、Q、S,所以也要和上面的一樣,進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換。這里將S用0表示,C用1表示,Q用2表示。同時(shí)發(fā)現(xiàn)Embarked列也存在著缺失值,所以需要對缺失值進(jìn)行填充,先統(tǒng)計(jì)C、Q、S中出現(xiàn)頻率最高的,然后將出現(xiàn)頻率最高的填入缺失值。
print(df['Embarked'].unique())
print(dict(df['Embarked'].value_counts()))
df['Embarked'] = df['Embarked'].fillna('S')
df.loc[df['Embarked'] == 'S','Embarked'] = 0
df.loc[df['Embarked'] == 'C','Embarked'] = 1
df.loc[df['Embarked'] == 'Q','Embarked'] = 2
print(df['Embarked'].unique())
運(yùn)行結(jié)果如圖4所示:

從圖4中可以看出S的頻率最高,所以這里講S填入缺失值,經(jīng)過處理后的數(shù)據(jù)變?yōu)?、1、2。
3.模型建立
- 用線性回歸預(yù)測
# 利用線性回歸預(yù)測
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold
# 設(shè)置標(biāo)簽
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
alg = LinearRegression()
kf = KFold(n_splits=3, random_state=1)
predictions = []
for train, test in kf.split(df[predictors]):
train_predictors = df[predictors].iloc[train, :]
train_target = df['Survived'].iloc[train]
alg.fit(train_predictors, train_target)
# 對測試集進(jìn)行預(yù)測
test_predictions = alg.predict(df[predictors].iloc[test, :])
predictions.append(test_predictions)
predictions = np.concatenate(predictions, axis=0)
predictions[predictions > 0.5] = 1 # 大于0.5表示獲救
predictions[predictions <= 0.5] = 0 # 小于0.5表示未獲救
accuracy = len(predictions[predictions == df['Survived']]) / len(predictions)
print(accuracy)
運(yùn)行結(jié)果為:0.7833894500561167
可見預(yù)測的準(zhǔn)確率并不是很高。同時(shí)還要注意,由于版本的變換,KFold模塊現(xiàn)在改為從model_selection中導(dǎo)入。還有,KFold的使用方法也改了,下面是之前版本的使用方式。
kf = KFold(df.shape[0], n_folds=3, random_state=1)
predictions = []
for train, test in kf:
train_predictors = (df[predictors].iloc[train, :]
- 用邏輯回歸預(yù)測
from sklearn import model_selection
from sklearn.linear_model import LogisticRegression
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
alg_1 = LogisticRegression(random_state=1)
scores = model_selection.cross_val_score(alg_1, df[predictors], df['Survived'], cv=3)
print(scores.mean())
運(yùn)行結(jié)果為:0.7878787878787877
可見預(yù)測的準(zhǔn)確率之比線性回歸高了那么一點(diǎn)點(diǎn),但還是不高。在運(yùn)行程序時(shí)發(fā)現(xiàn)了警告,警告內(nèi)容如圖5所示:

雖然警告信息并不影響代碼運(yùn)行,但輸出窗口異常明顯的幾行紅字提醒,總覺得不爽。
FutureWarning是語言或者庫中將來可能改變的有關(guān)警告。
根據(jù)報(bào)警信息和參考相關(guān)文檔,“Default will change from 'liblinear' to 'lbfgs' in 0.22.”,默認(rèn)的solver參數(shù)在0.22版本中,將會由“l(fā)iblinear”變?yōu)椤發(fā)bfgs”,且指定solver參數(shù)可以消除該warning。
這是代碼在發(fā)出警告,將來代碼運(yùn)行時(shí)如果沒有及時(shí)關(guān)注到版本的問題,可能solver的參數(shù)會發(fā)生改變。所以,最安全的方法并不是通過ignore消除警告,而是指定一個(gè)solver參數(shù)。
解決辦法:
# 版本問題,需要在后面添加 solver='liblinear',否則會有警告,雖然不影響運(yùn)行
alg_1 = LogisticRegression(random_state=1, solver='liblinear')
另一種思路是:
# 這種方法可以消除任何警告信息
import warnings
warnings.filterwarnings("ignore")
- 隨機(jī)森林模型進(jìn)行預(yù)測
同時(shí)我們要考慮上述標(biāo)簽中,到底是哪個(gè)標(biāo)簽的權(quán)重對獲救的概率影響高一點(diǎn),哪個(gè)權(quán)重會使得獲救的概率降低,這一點(diǎn)需要考慮。這里引進(jìn)隨機(jī)森林模型,能夠綜合的利用標(biāo)簽,降低過擬合的風(fēng)險(xiǎn),提高預(yù)測的準(zhǔn)確性。
# 使用隨機(jī)森林模型
from sklearn import model_selection
from sklearn.ensemble import RandomForestClassifier
# 設(shè)置標(biāo)簽
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
# 通過隨機(jī)森林進(jìn)行預(yù)測,這里隨機(jī)森林的參數(shù)只是隨便設(shè)定的,具體參數(shù)需要建立一個(gè)隨機(jī)森林模型
# n_estimators指樹的個(gè)數(shù),min_samples_split指內(nèi)部節(jié)點(diǎn)再劃分所需最小樣本數(shù),min_samples_leaf葉子節(jié)點(diǎn)最少樣本數(shù)
alg_2 = RandomForestClassifier(random_state=1, n_estimators=50, min_samples_split=4, min_samples_leaf=2)
kf = model_selection.KFold(n_splits=3, random_state=1, shuffle=True)
scores_1 = model_selection.cross_val_score(alg_2, df[predictors], df['Survived'], cv=kf)
print(scores_1.mean())
運(yùn)行結(jié)果為:0.8260381593714926
從圖中的結(jié)果可以看出預(yù)測的準(zhǔn)確率又比前面的高了一些,但還是不夠。由于這里的隨機(jī)森林模型是隨便設(shè)定的,沒有設(shè)定好參數(shù),所以需要對模型進(jìn)行優(yōu)化,建立合適的樹模型,優(yōu)化數(shù)的個(gè)數(shù)和深度。
RandomForestClassifier用法參考:https://www.cnblogs.com/pinard/p/6160412.html
- 自己構(gòu)造特征
前面我們還有一些標(biāo)簽沒有使用上,在這里我們將剩下的一些標(biāo)簽給用上,將標(biāo)簽進(jìn)行數(shù)字化處理。其中,構(gòu)建一個(gè)FamilySize標(biāo)簽,它是由SibSp標(biāo)簽和Parch標(biāo)簽相加得到;構(gòu)建一個(gè)NameLength標(biāo)簽,它是乘客的名字長度;再構(gòu)建Title標(biāo)簽,它是每個(gè)乘客的稱號,如Mr、Mrs、Miss、Dr...。然后將標(biāo)簽的數(shù)據(jù)轉(zhuǎn)換為數(shù)字進(jìn)行處理。
# 自己構(gòu)建特征
df['FamilySize'] = df['SibSp'] + df['Parch'] # 兄弟姐妹和老人小孩
df['NameLength'] = df['Name'].apply(lambda x: len(x)) # 名字的長度
import re
# 每個(gè)人都有自己的身份的詞,如Miss, Mr...
def get_title(name):
title_search = re.search(' ([A-Za-z]+)\.', name)
if title_search:
return title_search.group(1)
return ''
titles = df['Name'].apply(get_title)
print(pd.value_counts(titles))
# 將稱號用數(shù)字表示
title_mapping = {'Mr': 1, 'Miss': 2, 'Mrs': 3, 'Master': 4, 'Dr': 5, 'Rev': 6, 'Col': 7, 'Major': 8, 'Mlle': 9,
'Countess': 10, 'Ms': 11, 'Lady': 12, 'Jonkheer': 13, 'Don': 14, 'Mme': 15, 'Capt': 16, 'Sir': 17}
for k, v in title_mapping.items():
titles[titles == k] = v
print(pd.value_counts(titles))
df['Title'] = titles
運(yùn)行結(jié)果如圖6、7所示:


至此,3個(gè)新的標(biāo)簽就已經(jīng)構(gòu)建好了,我們先將數(shù)據(jù)可視化,觀察哪個(gè)標(biāo)簽所占的權(quán)重較高,然后使用隨機(jī)森林模型進(jìn)行預(yù)測。
from sklearn.feature_selection import SelectKBest, f_classif
predictors_new = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked', 'FamilySize', 'NameLength', 'Title']
selector = SelectKBest(f_classif, k=5)
selector.fit(df[predictors_new], df['Survived'])
scores_2 = -np.log10(selector.pvalues_)
plt.bar(range(len(predictors_new)), scores_2)
plt.xticks(range(len(predictors_new)), predictors_new, rotation='vertical')
plt.show()
SelectKBest用法參考:http://www.itdecent.cn/p/586ba8c96a3d
運(yùn)行結(jié)果如圖8所示:

通過上述圖8我們選擇5個(gè)最重要的標(biāo)簽,接下來用隨機(jī)森林模型進(jìn)行預(yù)測。
predictors_1 = ['Pclass', 'Sex', 'Fare', 'Title', 'NameLength']
alg_3 = RandomForestClassifier(random_state=1, n_estimators=50, min_samples_split=8, min_samples_leaf=4)
kf = model_selection.KFold(n_splits=3, random_state=1, shuffle=True)
scores_1 = model_selection.cross_val_score(alg_3, df[predictors_1], df['Survived'], cv=kf)
print(scores_1.mean())
運(yùn)行結(jié)果為:0.8215488215488215
可見,預(yù)測的結(jié)果并沒有多大的改變,可見隨機(jī)森林的預(yù)測效果還不夠好,看能否優(yōu)化一下樹結(jié)構(gòu)。