本文是參考寒小陽大神的博客:http://blog.csdn.net/han_xiaoyang/article/details/49797143
中的實現所做的記錄,想通過這個例子學習學習拿到數據后怎樣進行分析,整個分析和分類的流程是什么樣的。
泰坦尼克號之災主要是一個二分類問題。
因為感覺光看不動手寫一寫只知道個原理,結果還是不知道怎么寫。因此我又按照那篇博文擼了一遍代碼。
整個過程分兩部分:第一步是先對原始數據進行分析;第二步才是進行學習建模。
該篇記錄先來進行第一部分,也就是數據分析的部分。拿到一組數據后,我們首先要做的是對這些數據進行初步的分析,看看哪些數據值得我們需要。
當然首先要做的就是加載數據來看一看,這里用pandas庫,一個非常好的數據分析庫:
# 首先引入需要用的庫
import pandas as pd # pandas專門用來進行數據分析,蠻有用的
import numpy as np
from pandas import Series,DataFrame
# 加載數據,看看數據長什么樣
data_train = pd.read_csv('Train.csv')
data_train.head() # 打印頭五個出來看看
可以看到如下輸出:
有哪些屬性都一目了然。可以看到有10多個字段(并不是所有字段都是特征屬性哦):
- PassengerId:乘客ID
- Survived:是否獲救
- Pclass:乘客等級
- Name:乘客姓名
- Sex:性別
- Age:年齡
- SibSp:堂兄弟妹個數
- Parch:父母與小孩個數
- Ticket:船票信息
- Fare:票價
- Cabin:客艙
- Embarked:登船港口
接著看看這些數據的基本信息:
data_train.info()
打印如下:
可以看到,RangeIndex是說總共有891個數據,列數有12列;并且能夠看到每列數據的類型,而且能看出有些數據是不全的,比如Age只有714個數,Cabin只有204個數。
再來看看詳細點的描述呢:
data_train.describe()
輸出:
以上函數更能描述數據的一些事情。我們可以看到,上面的表格中將特征中數值型數據的一些屬性計算了出來,可以一目了然的看到每類數值型特征的平均值,標準差,最小值等屬性。比如Age一欄,看到乘客的平均年齡為29.6歲。
接下來繼續分析數據。看完了每個數據單獨的關系,現在應該結合我們最終的目的來看看數據間相互的關系了。我們最終是分類,也就是想找出哪些特征與最終是否獲救有關。因此我們可以看看每個或多個屬性和最后獲救Survived數據有什么樣的關系。
先來畫畫圖吧:
import matplotlib.pyplot as plt
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"人數")
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"年齡")
plt.ylabel(u"密度")
plt.title(u"各等級的乘客年齡分布")
plt.legend((u'頭等艙',u'2等艙',u'3等艙'),loc='best')
plt.subplot2grid((2,3),(1,2))
data_train.Embarked.value_counts().plot(kind='bar')
plt.title(u"各登船口岸人數")
plt.ylabel(u"人數")
plt.show()
圖如下:
由上面的圖我們又可以看到一些情況,比如獲救情況不容樂觀,300多人,連一半都沒到;
3等艙的人蠻多的;
從年齡來看,遇難和獲救的人跨度都很廣;
然后開始分析主要屬性對獲救的關系,先看看乘客等級:
# 再來看看各乘客等級的獲救情況
fig = plt.figure()
fig.set(alpha=0.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()
圖如下:
可以看到在1等艙里面獲救的幾率就要大得多呢!果然有錢有權的就是好。
接著看港口位置:
fig = plt.figure()
fig.set(alpha=0.2)
Survived_E0 = data_train.Embarked[data_train.Survived == 0].value_counts()
Survived_E1 = data_train.Embarked[data_train.Survived == 1].value_counts()
df = pd.DataFrame({u"獲救":Survived_E1,u"未獲救":Survived_E0})
df.plot(kind='bar',stacked=True)
plt.title(u"各登陸港口乘客的獲救情況")
plt.xlabel(u"登陸港口")
plt.ylabel(u"人數")
plt.show()
好像并沒有什么有價值的。
再來看性別:
fig = plt.figure()
fig.set(alpha=0.2)
Survived_sex0 = data_train.Sex[data_train.Survived == 0].value_counts()
Survived_sex1 = data_train.Sex[data_train.Survived == 1].value_counts()
df = pd.DataFrame({u'獲救':Survived_sex1,u'未獲救':Survived_sex0})
df.plot(kind='bar',stacked=True)
plt.title(u"按性別看獲救情況")
plt.xlabel(u"性別")
plt.ylabel(u"人數")
plt.show()
這幅圖上明顯可以看出,船上的人確實都蠻紳士的,貫徹落實副船長所說的“讓小孩和女士先走”政策。
再來細些,看看每個艙里的性別情況呢?
fig=plt.figure()
fig.set(alpha=0.65) # 設置圖像透明度,無所謂
plt.title(u"根據艙等級和性別的獲救情況")
ax1 = fig.add_subplot(141)
data_train.Survived[data_train.Sex == 'female'][data_train.Pclass != 3].value_counts().plot(kind='bar',label='female highclass',color='#FA2479')
ax1.set_xticklabels([u"獲救",u"未獲救"],rotation=0)
ax1.legend([u"女性/高級艙"], loc='best')
ax2=fig.add_subplot(142, sharey=ax1)
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='best')
plt.show()
然后是堂兄妹等等:
# 再看看其他的獲救情況
fig = plt.figure()
fig.set(alpha=0.2)
Survived_S0 = data_train.SibSp[data_train.Survived == 0].value_counts()
Survived_S1 = data_train.SibSp[data_train.Survived == 1].value_counts()
df = pd.DataFrame({u'獲救':Survived_S1,u'未獲救':Survived_S0})
df.plot(kind='bar',stacked=True)
plt.title(u"按堂兄妹看獲救情況")
plt.xlabel(u"堂兄妹個數")
plt.ylabel(u"人數")
plt.show()
簡化版(Parch屬性):
g = data_train.groupby(['Parch','Survived'])
df = pd.DataFrame(g.count()['PassengerId'])
df
好像并沒啥關系吧。
好吧,數據里還有一些比如船票應該是1人1張的,和最后是否獲救應該沒啥關系,因此暫時不用看了。
還有就是有些數據不全的值,比如說Cabin,只有204個數據,這也是我們需要分析分析的,先來看看它的分布:
data_train.Cabin.value_counts()
這Cabin數據也是。。一眼看上去感覺很多又亂,而且每個就那么幾個。。一個一個看也太麻煩了。。就先看看Cabin這個數據有或沒有兩種情況和獲救的關系吧。
# Cabin值太分散了,很多都是只出現了1次,因此先看看這個值的有無,對于survival的分布狀況
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()
看樣子好像有船艙記錄的獲救比例高些啊,那么就先這樣把Cabin分成Yes和No吧,作為一個特征。
接下來還有一個擁有缺失值的屬性Age年齡,其實這個屬性應該還是有些關鍵的,因為副船長說過嘛,小孩也是優先級高的,因此Age或許是個比較大的影響因子。但是作為候選特征的Age數據卻不全,這怎么辦呢?
遇到缺值的情況,一般有幾種常見的處理方式:
- 如果缺失值的樣本占總數的比例極高,我們可能直接丟棄,因為它本來就沒幾個嘛,或許是噪點呢。
- 如果缺失值的樣本適中,而且該屬性不是連續值的特征(就像剛才的Cabin,可以看做類目屬性),就可以把NaN作為一個新類別,加入到類別特征中去;(剛才是因為Cabin里類目太分散了,每個的數量又很稀少,因此就定了有和無兩個類目)
- 如果缺失值的樣本適中,而該屬性又是連續型的,有時候就會考慮給定一個step(比如這里的age,我們可以考慮每隔2/3歲為一個步長),然后把它離散化,之后把NaN作為一個type加到屬性類目中。
- 有些情況下,缺失的值個數并不是特別多,那我們也可以試著根據已有的值,擬合一下數據,補充上。
在Age值的處理上,這里可以采用擬合補全的方法。(比如用機器學習里面的回歸):
from sklearn.ensemble import RandomForestRegressor
# 使用RandomForestRegressor填補缺失的年齡屬性
def set_missing_ages(df):
# 把已有的數值型特征取出丟進RandomForestRegressor中
age_df = df[['Age','Fare','Parch','SibSp','Pclass']]
known_age = age_df[age_df.Age.notnull()].as_matrix()
unknown_age = age_df[age_df.Age.isnull()].as_matrix()
y = known_age[:,0]
X = known_age[:,1:]
# fit到RandomForestRegressor中
rfr = RandomForestRegressor(random_state=0,n_estimators=2000,n_jobs=-1)
rfr.fit(X,y)
# 用模型進行未知年齡結果預測
predictedAges = rfr.predict(unknown_age[:,1::])
# 用預測結果補全原缺失數據
df.loc[(df.Age.isnull()),'Age'] = predictedAges
return df,rfr
def set_Cabin_type(df):
df.loc[(df.Cabin.notnull()),'Cabin'] = 'Yes'
df.loc[(df.Cabin.isnull()),'Cabin'] = 'No'
return df
data_train,rfr = set_missing_ages(data_train)
data_train = set_Cabin_type(data_train)
data_train
訓練出來了,現在沒有缺失值了,看看:
data_train.info()
可以看到已經沒有缺失值了,當然那個Embarked少了兩個,可以直接忽略。。
那么特征分析的差不多了,要開始進行學習分類了。但還有一步需要做做。在這些特征中,有些特征是類目型特征并不是數值,而我們通常機器學習是需要用到的是數值型的特征,那么我們就該把對應的類目型特征轉換為數值型特征。怎么做呢?
一般來說可以因子化/one-hot編碼。
而且pandas里面有個get_dummies函數可以干這個事哦。
# Cabin,原本是一個屬性,取值為['Yes','No'],現在因子化后,就變成兩個屬性’Cabin_yes‘和’Cabin_No‘
# 原本Cabin取值為yes的,在此處的'Cabin_yes'下取值為1,在'Cabin_no'下取值為0;反之亦然
# 其它類目型同理
dummies_Cabin = pd.get_dummies(data_train['Cabin'],prefix='Cabin')
dummies_Embarked = pd.get_dummies(data_train['Embarked'],prefix='Embarked')
dummies_Sex = pd.get_dummies(data_train['Sex'],prefix='Sex')
dummies_Pclass = pd.get_dummies(data_train['Pclass'],prefix='Pclass')
df = pd.concat([data_train,dummies_Cabin,dummies_Embarked,dummies_Sex,dummies_Pclass],axis=1)
# 丟掉一些不需要的值了
df.drop(['Pclass','Name','Sex','Ticket','Cabin','Embarked'],axis=1,inplace=True)
df
現在可以看到特征全是數值型的,并且都是我們需要的特征了。
# 再次打印數據描述來看看
df.describe()
我們可以看到Age和Fare的范圍跨度有點大啊,分別是0.4280,0512。這應該要處理一下了,不然會影響到收斂速度的,因此需要對這兩個數進行所說的歸一化。話說還有PassengerId呢,這屬性一看就是個id,不是特征,就不管了。
# 數據分析完,就開始對數據進行預處理了,比如歸一化。
import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
age_scale_param = scaler.fit(df['Age'])
df['Age_scaled'] = scaler.fit_transform(df['Age'],age_scale_param)
fare_scale_param = scaler.fit(df['Fare'])
df['Fare_scaled'] = scaler.fit_transform(df['Fare'],fare_scale_param)
df
數據預處理的處理完了,接下來就可以進行相應的建模了。一般就使用sklearn來進行建模了。
泰坦尼克號的初步數據分析基本到這就完成了,下一篇就根據以上處理過的數據進行分類模型的建立以及后續的分析。