1 引言
剛接觸python與大數據不久,這個是學長給出的練習題目。知識積累太少,學習用了不少的時間。盡量詳細的寫,希望對各位的學習有所幫助。
2 背景
2.1 Kaggle
本次數據集來自於Kaggle。Kaggle是一個數據分析建模的應用競賽平台。想要了解詳細資料的小伙伴請自行百度。
2.2 泰坦尼克號
請到Data頁面下載數據集

數據集的各屬性在Data頁面下有詳細介紹。
問題就是以大家熟悉的泰坦尼克號為背景展開的,本次任務的目的就是構建一個可以根據乘客個人信息推測乘客是否生存的數據模型。
3 工具介紹
3.1 Python
我所用的python版本為:Python 3.5.2 。
3.2 Anaconda3
Anaconda 是一個很好用的數據分析工具集,其中的Spyder 與 Jupyter Notebook今后會經常使用,而且使用非常方便。
4 初探數據
相信你已經將數據下載到你的電腦中了,下面我們來將數據經行導入及簡單分析。
請先打開Spyder。
4.1 導入訓練集數據
import pandas as pd #數據分析
import numpy as np #科學計算
from pandas import Series,DataFrame
data_train = pd.read_csv(r'E:\Data\train.csv') #根據數據位置自行修改
運行后我們會在Spyder窗口右上部看到data_train數據。點擊后即可顯示數據表格,如下:

4.2 數據簡單分析
觀察圖表我們可以知道,共有891行、12列。這代表本訓練集共有891條數據,每條數據有12類信息。包括:
• PassengerId => 乘客ID
• Survived => 獲救情況(1為獲救,0為未獲救)
• Pclass => 乘客等級(1/2/3等艙位)
• Name => 乘客姓名
• Sex => 性別
• Age => 年齡
• SibSp => 堂兄弟/妹個數
• Parch => 父母與小孩個數
• Ticket => 船票信息
• Fare => 票價
• Cabin => 客艙
• Embarked => 登船港口
初步觀察,我們會發現一些數據信息值為:nan,這就代表該條數據該類信息缺失。在數據分析中,處理缺失值是一個很重要的步驟。一開始,我們不如統計各類信息的缺失的總體情況,讓自己對該數據集有所了解。
我們可以在IPython console框中輸入:
data_train.info()
回車運行后,即可出現以下內容:

由此可知,Age(年齡)有714人有記錄,Cabin(客艙)有204人有記錄,Embarked(登陸港口)有少量缺失。
我們還可以用下列語句進行數據的總體統計:
data_train.describe()
結果如下:

由此我們可知,乘客的平均年齡為29.7,最大年齡為80.0,最小年齡為0.42。獲救人數為總體的0.383838,等等。
5 數據圖形化分析
進行完總體的初步分析,我們接下來進行數據相關性的分析,為了便於觀察,我們利用圖表展示。
5.1 乘客各屬性
代碼:
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標簽
plt.rcParams['font.family']='sans-serif'
plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負號
fig = plt.figure()
fig.set(alpha=0.2) # 設定圖表顏色alpha參數
plt.subplot2grid((2,3),(0,0)) # 在一張大圖里分列幾個小圖
data_train.Survived.value_counts().plot(kind='bar')# 柱狀圖
plt.title(u"獲救情況 (1為獲救)") # 標題
plt.ylabel(u"人數") # Y軸標簽
plt.subplot2grid((2,3),(0,1))
data_train.Pclass.value_counts().plot(kind="bar") # 柱狀圖顯示
plt.ylabel(u"人數")
plt.title(u"乘客等級分布")
plt.subplot2grid((2,3),(0,2))
plt.scatter(data_train.Survived, data_train.Age) #為散點圖傳入數據
plt.ylabel(u"年齡") # 設定縱坐標名稱
plt.grid(b=True, which='major', axis='y')
plt.title(u"按年齡看獲救分布 (1為獲救)")
plt.subplot2grid((2,3),(1,0), colspan=2)
data_train.Age[data_train.Pclass == 1].plot(kind='kde') # 密度圖
data_train.Age[data_train.Pclass == 2].plot(kind='kde')
data_train.Age[data_train.Pclass == 3].plot(kind='kde')
plt.xlabel(u"年齡")# plots an axis lable
plt.ylabel(u"密度")
plt.title(u"各等級的乘客年齡分布")
plt.legend((u'頭等艙', u'2等艙',u'3等艙'),loc='best') # sets our legend for our graph.
plt.subplot2grid((2,3),(1,2))
data_train.Embarked.value_counts().plot(kind='bar')
plt.title(u"各登船口岸上船人數")
plt.ylabel(u"人數")
plt.show()
結果如下:

如果Spyder無法顯示中文標簽,可以將代碼放入IPython Notebook中運行。
我們可以從這五張圖中形象的了解到乘客的信息,獲救人數少於未獲救人數(之前我們以得到此結論),三等乘客人數最多,獲救人員各年齡段分布(第一列第三張圖,左邊豎列標簽為0,右邊豎列標簽為1),S口岸上船的乘客最多。等等。
這時,我們就要將乘客各屬性與其是否獲救聯系起來:
獲救情況和乘客等級是否有關?
獲救情況和乘客性別、年齡是否有關?(畢竟,婦女、小孩和老人優先搭乘救生艇)
登船口岸是否是獲救因素呢?(雖然感覺關系不大,但是也要考慮全面)
5.2 各屬性與獲救情況的關聯
各乘客等級的獲救情況
Survived_0 = data_train.Pclass[data_train.Survived == 0].value_counts() # 未獲救
Survived_1 = data_train.Pclass[data_train.Survived == 1].value_counts() # 獲救
df = pd.DataFrame({u'獲救':Survived_1,u'未獲救':Survived_0})
df.plot(kind = 'bar', stacked = True)
plt.title(u'各乘客等級的獲救情況')
plt.xlabel(u'乘客等級')
plt.ylabel(u'人數')
plt.show()

根據圖表可以清楚看到第一等級的乘客,獲救人數多於未獲救人數,而其它兩個等級的乘客,獲救人數則少於未獲救人數。
所以,乘客等級與獲救情況有關聯。
各性別的獲救情況
Survived_m = data_train.Survived[data_train.Sex == 'male'].value_counts()
Survived_f = data_train.Survived[data_train.Sex == 'female'].value_counts()
df = pd.DataFrame({u'男性':Survived_m,u'女性':Survived_f})
df.plot(kind = 'bar', stacked = True)
plt.title(u'按性別看獲救情況')
plt.xlabel(u'性別')
plt.ylabel(u'人數')
plt.show()

很明顯看出,未獲救人員中男性乘客比例較大,獲救人員中女性乘客比例較大。
那么,可以確定性別也是獲救情況的一個重要因素。
根據艙等級和性別的獲救情況(第一、二等級為高級艙,第三等級為低級艙)
fig = plt.figure()
plt.title(u'根據艙等級和性別的獲救情況')
ax1 = fig.add_subplot(141) # 將圖像分為1行4列,從左到右從上到下的第1塊
data_train.Survived[data_train.Sex == 'female'][data_train.Pclass != 3].value_counts().plot(kind = 'bar', label = 'female high class', color = '#FA2479')
ax1.set_xticklabels([u'獲救',u'未獲救'], rotation = 0) # 根據實際填寫標簽
ax1.legend([u'女性/高級艙'], loc = 'best')
ax2 = fig.add_subplot(142, sharey = ax1) # 將圖像分為1行4列,從左到右從上到下的第2塊
data_train.Survived[data_train.Sex == 'female'][data_train.Pclass == 3].value_counts().plot(kind = 'bar', label = 'female low class', color = 'pink')
ax2.set_xticklabels([u"未獲救", u"獲救"], rotation=0)
plt.legend([u"女性/低級艙"], loc='best')
ax3 = fig.add_subplot(143, sharey = ax1)
data_train.Survived[data_train.Sex == 'male'][data_train.Pclass != 3].value_counts().plot(kind = 'bar', label = 'male high class', color = 'lightblue')
ax3.set_xticklabels([u'未獲救',u'獲救'], rotation = 0)
plt.legend([u'男性/高級艙'], loc = 'best')
ax4 = fig.add_subplot(144, sharey = ax1)
data_train.Survived[data_train.Sex == 'male'][data_train.Pclass == 3].value_counts().plot(kind = 'bar', label = 'male low class', color = 'steelblue')
ax4.set_xticklabels([u'未獲救',u'獲救'], rotation = 0)
plt.legend([u'男性/低級艙'], loc = 'bast')
plt.show()

很明顯高級艙女性的生還率高於低級艙,低級艙男性的死亡率高於高級艙。在四種情況中,高級艙女性的生還率最高,低級艙男性的死亡率最高。
將與獲救情況相關聯的兩種屬性結合后,所得到的結果可以更明顯知曉他們的相關性。
各登陸港口乘客的獲救情況
fig = plt.figure()
fig.set(alpha = 0.2)
Survived_0 = data_train.Embarked[data_train.Survived == 0].value_counts()
Survived_1 = data_train.Embarked[data_train.Survived == 1].value_counts()
df = pd.DataFrame({u'獲救':Survived_1,u'未獲救':Survived_0})
df.plot(kind = 'bar', stacked = True)
plt.title(u'各登陸港口乘客的獲救情況')
plt.xlabel(u'登陸港口')
plt.ylabel(u'人數')
plt.show()

獲救情況與上船口岸的聯系,根據上圖感覺相關性並不強。C港的獲救率高一些。
堂兄弟/妹,孩子/父母有幾人,對是否獲救的影響
g = data_train.groupby(['SibSp','Survived']) # 數據分組
df = pd.DataFrame(g.count()['PassengerId'])
print (df)
g = data_train.groupby(['Parch','Survived'])
df = pd.DataFrame(g.count()['PassengerId'])
print (df)
堂兄弟/妹,孩子/父母有幾人,對是否獲救的影響並不明顯。
按Cabin有無看獲救情況
fig = plt.figure()
fig.set(alpha = 0.2)
Survived_cabin = data_train.Survived[pd.notnull(data_train.Cabin)].value_counts()
Survived_nocabin = data_train.Survived[pd.isnull(data_train.Cabin)].value_counts()
df = pd.DataFrame({u'有':Survived_cabin, u'無':Survived_nocabin}).transpose()
df.plot(kind = 'bar', stacked = True)
plt.title(u'按Cabin有無看獲救情況')
plt.xlabel(u'Cabin有無')
plt.ylabel(u'人數')
plt.show()

有客艙信息的獲救情況較高一點。但情況復雜,比如生還者上岸后進行信息統計的話,就會影響信息有無對獲救情況的關系。
6 簡單數據預處理
#我們將測試集導入,再將刪除Survived數據的訓練集與測驗集進行合並,這樣便於進行數據處理
data_test = pd.read_csv(r'E:\Data\test.csv') # 導入測驗集數據
y = data_train['Survived'] # 將訓練集Survived 數據存儲在y中
del data_train['Survived'] # 刪除訓練集Survived數據
sum_id = data_test['PassengerId'] # 存儲測試集乘客ID
df = pd.merge(data_train, data_test,how='outer') # 合並無Survived數據的訓練集與測驗集,how = ‘outer’ 意為並集
#刪掉無關因素
df = df.drop(['Name','PassengerId','Ticket','Cabin'],axis=1) # 刪除姓名、ID、船票信息、客艙信息,axis=0 刪除行,=1 刪除列
#缺失數據填充
df['Age'] = df['Age'].fillna(df['Age'].mean()) # 用平均值填充空值
df['Fare'] = df['Fare'].fillna(df['Fare'].mean())
df['Embarked'] = df['Embarked'].fillna( df['Embarked'].value_counts().index[0]) # 用數量最多項填充
#將性別與港口用啞變量表示
dumm = pd.get_dummies(df[['Sex','Embarked']]) # '啞變量'矩陣
df = df.join(dumm)
del df['Sex'] // 刪除
del df['Embarked']
#數據降維
df['Age'] = (df['Age']-df['Age'].min()) /(df['Age'].max()-df['Age'].min())
df['Fare'] = (df['Fare']-df['Fare'].min()) /(df['Fare'].max()-df['Fare'].min())
#訓練模型
data_train = df[:len(data_train)] # 將合並后的數據分離
data_test = df[len(data_train):]
7 訓練並預測
from sklearn.cross_validation import train_test_split
X_train, X_val, y_train,y_val = train_test_split(data_train,y,test_size=0.3, random_state=42) # 以7:3(0.3)將訓練集與獲救結果隨機拆分,隨機種子為42
from sklearn.linear_model import LogisticRegression # 引入邏輯回歸
LR = LogisticRegression()
LR.fit(X_train, y_train) # 訓練數據
print('訓練集准確率:\n',LR.score(X_train, y_train)) # 分數
print('驗證集准確率:\n',LR.score(X_val, y_val))
#預測測驗集
pred= LR.predict(data_test) # pred 為預測結果
pred = pd.DataFrame({'PassengerId':sum_id.values, 'Survived':pred}) # 格式化預測結果
pred.to_csv('pred_LR.csv',index=None) # 導出數據
結果:

最后,也可以將導出的csv文件在Kaggle中進行提交。
8 結語
這是個不太復雜的數據處理題目,預測結果只有兩種,但是題目所給因素較多,需要經行無關因素排除,排除后要將數據經行預處理。預處理時,先將缺失值進行補全,再進行降維處理,本題在降維前要將分類變量Sex、Embarked變為啞變量。最后進行train_test_split方法拆分數據,利用邏輯回歸訓練模型。
附:佳句
來了,愛了,給了她一顆星星,走了。
——雲天明《三體》
以上

