探索性数据分析(EDA)
探索性数据分析(EDA)说白了就是通过可视化的方式来看看数据中特征与特征之间,特征与目标之间的潜在关系,看看有什么有用的线索可以挖掘,例如哪些数据是噪声,有哪些特征的相关性比较低,后续可以造出哪些新的特征等。
初窥
当然,在EDA之前先要加载数据,我们不妨先将训练集train.csv读到内存中,并看一看。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
data=pd.read_csv('./Titanic/train.csv')
# 看看data的前5行
data.head()
从图中可以看出数据是由 11 个特征和 1 个标签(Survived)组成的。其中各个特征和标签的意义如下:
特征 | 意义 |
---|---|
Survived | 是否生还,1表示是,0表示否 |
PassengerId | 乘客ID |
Pclass | 船票类型, 总共3种类型:1(一等舱),2(二等舱),3(三等舱) |
Name | 船客姓名 |
Sex | 船客性别:female,male |
Age | 船客年龄 |
SibSp | 船客的兄弟姐妹妻子丈夫的数量 |
Parch | 船客的父母,孩子的数量 |
Ticket | 船票 |
Fare | 船客在船上所花的钱 |
Cabin | 船客的船舱号 |
Embarked | 船客登船的口岸:C,Q,S |
了解了数据种各个属性的含义之后,我们可以看看这个数据集中有没有缺失值。
data.isnull().sum()
可以看出 Age,Cabin 和 Embarked 这三个特征中有缺失值,我们需要处理这些缺失值。怎样处理呢?先不着急,我们可以先看看数据中有哪些信息可以挖掘。
有多少人活了下来
我们首先可以看看训练集中有多少人活了下来。
f,ax=plt.subplots(1,2,figsize=(18,8))
# 生还比例饼图
data['Survived'].value_counts().plot.pie(explode=[0,0.1],autopct='%1.1f%%',ax=ax[0],shadow=True)
ax[0].set_title('Survived')
ax[0].set_ylabel('')
# 生还数量直方图
sns.countplot('Survived',data=data,ax=ax[1])
ax[1].set_title('Survived')
plt.show()
从图中可以看出泰坦尼克沉船事件中还是凶多吉少的。因为在 891 名船客中,只有约 38% 左右的人幸免于难,那么接下来尝试使用数据集中不同的特征,来看看他们的生还率有多少。其实这样一个过程我们可以看出大概有哪些类型的船客活了下来。
性别与生还率的关系
首先,看看不同性别的生还者数量。
data.groupby(['Sex','Survived'])['Survived'].count()
看上去好想女性船客的生还率高一些,我们不妨再可视化一下。
f,ax=plt.subplots(1,2,figsize=(18,8))
data[['Sex','Survived']].groupby(['Sex']).mean().plot.bar(ax=ax[0])
ax[0].set_title('Survived vs Sex')
sns.countplot('Sex',hue='Survived',data=data,ax=ax[1])
ax[1].set_title('Sex:Survived vs Dead')
plt.show()
从图中可以看出一个比较有趣的现象,船上的男人是比女人多了 200 多人,但是女人生还的人数几乎是男人生还的人数的两倍,女人的存活率约为 75% ,而男人的存活率约为 19% 的样子。所以 Sex 这个特征应该是一个能够很好的区分一个人是否生还的特征。而且对于生还来说,好像是女士优先。
船票类型与生还率的关系
船票类型分三个档次,其中 1 为一等舱, 2 为二等舱, 3 为三等舱。既然船舱分三六九等,那么是不是越高级的舱,它的生还率越高呢?
f,ax=plt.subplots(1,2,figsize=(18,8))
data['Pclass'].value_counts().plot.bar(ax=ax[0])
ax[0].set_title('Number Of Passengers By Pclass')
ax[0].set_ylabel('Count')
sns.countplot('Pclass',hue='Survived',data=data,ax=ax[1])
ax[1].set_title('Pclass:Survived vs Dead')
plt.show()
虽然说钱不是万能的,但从可视化结果可以看出,一等舱的生还率最高,大于为 63%,二等舱的生还率约为 48% ,而且虽然三等舱的船客人数是最多的,但生还率确是最低的。所以不难看出,金钱地位还是很重要的,也许一等舱周围有比较多的救生设备。
上流女性与生还率的关系
从前两次可视化结果可以看出,女性,上流人士成为了是否能够活下来的关键,那么上流女性(两者的结合)的生还率会不会很高呢?
sns.factorplot('Pclass','Survived',hue='Sex',data=data)
plt.show()
从这张图可以看出一等舱的女性(上流女性)的生还率非常高!几乎接近了百分之百!而且二等舱和三等舱的女性的生还率也远比男性的生还率高。这也验证了我们的猜测,在沉船后是优先女性和一等舱的船客的。
年龄与生还率的关系
首先可以先看一下训练集中船客的年龄的最值和均值。
print('Oldest Passenger was of:',data['Age'].max(),'Years')
print('Youngest Passenger was of:',data['Age'].min(),'Years')
print('Average Age on the ship:',data['Age'].mean(),'Years')
年纪最大的是80岁的老爷爷或者老太太,最小的是刚出生的小 baby, 平均年龄快 30 岁。这个还是符合常理的。接下来我们看看船舱等级,年龄和生还率的关系,以及性别,年龄和生还率的关系。
f,ax=plt.subplots(1,2,figsize=(18,8))
sns.violinplot("Pclass","Age", hue="Survived", data=data,split=True,ax=ax[0])
ax[0].set_title('Pclass and Age vs Survived')
ax[0].set_yticks(range(0,110,10))
sns.violinplot("Sex","Age", hue="Survived", data=data,split=True,ax=ax[1])
ax[1].set_title('Sex and Age vs Survived')
ax[1].set_yticks(range(0,110,10))
plt.show()
从可视化结果可以看出:
儿童的数量随着船舱等级的增加而增加,10 岁以下的小朋友存活率仿佛都还挺高的,跟船舱等级好像没有太大关系。
来自一等舱的 20-50 岁的船客的存活率很高,而且对女性的生还率一如既往的高。
对于男性来说,年纪越大,生还率越低。
不过我们的年龄是有缺失值的,如果图简单,可以使用平均年龄来填充缺失的年龄。但是这样做并不合适,比如人家只是个 5 岁的小屁孩,但是你把人家强行改成 29 岁显然是不合适的。那有没有能够更加准确地知道缺失的年龄是多少的方法呢?有!我们可以根据姓名来推断缺失的年龄,因为姓名中有很多类似 Mr 或者 Mrs 这样的前缀,所以我们可以根据姓名的前缀来填充缺失的年龄。
填充缺失年龄
外国人的姓名和我们中国人的姓名不太一样,一般都会有 Mr 、 Mrs 、Miss 、Dr 等特殊前缀。所以我们可以先提取姓名中的前缀。
data['Initial']=0
for _ in data:
data['Initial']=data.Name.str.extract('([A-Za-z]+)\.')
这样我们能够提取出诸如:Capt 、Col 、Don 、Lady 、Major 、Sir 等前缀,接着我们可以将这些前缀替换成 Miss 、 Mr 、 Mrs 、 Other 这四个类别,并统计这四个类别的平均年龄。
data['Initial'].replace(['Mlle','Mme','Ms','Dr','Major','Lady','Countess','Jonkheer','Col','Rev','Capt','Sir','Don'],['Miss','Miss','Miss','Mr','Mr','Mrs','Mrs','Other','Other','Other','Mr','Mr','Mr'],inplace=True)
data.groupby('Initial')['Age'].mean()
接着可以根据前缀来填充缺失的年龄。
data.loc[(data.Age.isnull())&(data.Initial=='Mr'),'Age']=33
data.loc[(data.Age.isnull())&(data.Initial=='Mrs'),'Age']=36
data.loc[(data.Age.isnull())&(data.Initial=='Miss'),'Age']=22
data.loc[(data.Age.isnull())&(data.Initial=='Other'),'Age']=46
填充完缺失值后,可以尝试可视化一下。
f,ax=plt.subplots(1,2,figsize=(20,10))
data[data['Survived']==0].Age.plot.hist(ax=ax[0],bins=20,edgecolor='black',color='red')
ax[0].set_title('Survived= 0')
x1=list(range(0,85,5))
ax[0].set_xticks(x1)
data[data['Survived']==1].Age.plot.hist(ax=ax[1],color='green',bins=20,edgecolor='black')
ax[1].set_title('Survived= 1')
x2=list(range(0,85,5))
ax[1].set_xticks(x2)
plt.show()
从图中可以看出 5 岁以下的小屁孩的生还率比较高,80 岁的老人活下来了。
sns.factorplot('Pclass','Survived',col='Initial',data=data)
plt.show()
嗯,女性和小孩的生还率比较高。
登船口岸与生还率的关系
先把口岸和生还率的关系画出来。
sns.factorplot('Embarked','Survived',data=data)
fig=plt.gcf()
fig.set_size_inches(5,3)
plt.show()
可以看出从 C 号口岸上船的生还率最高,最低的是 S 号口岸。嗯,好像并没有什么线索,我们可以再深入一点。
f,ax=plt.subplots(2,2,figsize=(20,15))
sns.countplot('Embarked',data=data,ax=ax[0,0])
ax[0,0].set_title('No. Of Passengers Boarded')
sns.countplot('Embarked',hue='Sex',data=data,ax=ax[0,1])
ax[0,1].set_title('Male-Female Split for Embarked')
sns.countplot('Embarked',hue='Survived',data=data,ax=ax[1,0])
ax[1,0].set_title('Embarked vs Survived')
sns.countplot('Embarked',hue='Pclass',data=data,ax=ax[1,1])
ax[1,1].set_title('Embarked vs Pclass')
plt.subplots_adjust(wspace=0.2,hspace=0.5)
plt.show()
现在能看出很多信息了:
- 上船人数最多的口岸是 S 号口岸,而且在 S 号口岸上船的人大多数都是三等舱的船客。
- C 号口岸上船的生还率最高,可能大部分 C 口岸上船的人是一等舱和二等舱船客吧。
- 虽然有很多一等舱的土豪们基本上都是在 S 口岸上船的,但是 S 口岸的的生还率最低。这是因为 S 口岸上船的人中有很多都是三等舱的船客。
- Q 号口岸上船的人中有 90% 多都是三等舱的船客。
sns.factorplot('Pclass','Survived',hue='Sex',col='Embarked',data=data)
plt.show()
我们可以看出:
- 一等舱和二等舱的女性的生还率几乎为 100%, 这与女性是一等舱还是二等舱没啥关系。
- S 号口岸上船并且是三等舱的,不管是男的还是女的,生还率都很低。金钱决定命运。。。
- Q 号口岸上船的男性几乎团灭,因为Q 号口岸上船的基本上都是三等舱船客。
填充缺失口岸
由于大多数人都是从 S 号口岸上的船,我们可以假设由于人多,所以在 S 口岸登记信息时漏了几位船客,所以不妨用 S 号口岸填充缺失值。
data['Embarked'].fillna('S',inplace=True)
兄弟姐妹的数量与生还率的关系
f,ax=plt.subplots(1,2,figsize=(20,8))
sns.barplot('SibSp','Survived',data=data,ax=ax[0])
ax[0].set_title('SibSp vs Survived')
sns.factorplot('SibSp','Survived',data=data,ax=ax[1])
ax[1].set_title('SibSp vs Survived')
plt.close(2)
plt.show()
从图可以看出,如果一位船客是单独一个人上船旅游,没有兄弟姐妹而且是单身,那么他有大约 34% 的生还率,生还率比较低。如果兄弟姐妹的数量变多,那么生还率还是呈下降趋势的。这其实挺合理的,因为如果是一个家庭在船上的话,可能会设法救他们而不是救自己,这样一来可能谁都救不了。
父母的数量与生还率的关系
f,ax=plt.subplots(1,2,figsize=(20,8))
sns.barplot('Parch','Survived',data=data,ax=ax[0])
ax[0].set_title('Parch vs Survived')
sns.factorplot('Parch','Survived',data=data,ax=ax[1])
ax[1].set_title('Parch vs Survived')
plt.close(2)
plt.show()
从图上看会发现结果和上面的比较相似,父母在船上的船客有更大的生还机会。而且对于那些在船上有 1-3 个父母的人来说,生还率还是比较高的。
花费与生还率的关系
首先,先看一下花费的最值和均值。
print('Highest Fare was:',data['Fare'].max())
print('Lowest Fare was:',data['Fare'].min())
print('Average Fare was:',data['Fare'].mean())
惊奇的发现,居然有人可以享受免费豪华邮轮!!!!
f,ax=plt.subplots(1,3,figsize=(20,8))
sns.distplot(data[data['Pclass']==1].Fare,ax=ax[0])
ax[0].set_title('Fares in Pclass 1')
sns.distplot(data[data['Pclass']==2].Fare,ax=ax[1])
ax[1].set_title('Fares in Pclass 2')
sns.distplot(data[data['Pclass']==3].Fare,ax=ax[2])
ax[2].set_title('Fares in Pclass 3')
plt.show()
从图中可以看出平均花费其实是二等舱的普遍消费水平,但是三等舱的人数是最多的,而三等舱的人群中花费人数最多的是 10 左右,因此平均 32 的花费是被有钱的大佬给提上去的。
简单总结一下
看了这么多特征对于生还的影响,可能有点懵,不妨先简单总结一下根据可视化结果所获得的信息。
- 性别:女性的生还率高
- 船舱等级:越有钱越容易活下来,头等舱的生还率最高,三等舱的生还率最低。
- 年龄:10 岁以下的小朋友的存活率比较高,15-35 岁的年轻人存活率低。可能年轻人就是炮灰吧。
- 口岸:即使大多数一等舱的船客在 S 号口岸上的船, 但生还率不是最高的。 Q 号口岸的基本上是三等舱的船客。
- 兄弟姐妹父母爱人数量:有 1-2 个兄弟姐妹,配偶在船上,或 1-3 个父母的生还率比较高,独自一人或者一个大家庭都在船上的生还率比较低。
特征之间的相关性系数
相关性分为正相关与负相关,正相关指的是:如果特征 A 的数值变大会导致特征 B 的数值变大;负相关指的是:如果特征 A 的数值变小会导致特征 B 的数值变大。通常使用 [-1, 1] 的数值来表示两个特征之间的相关性,这个值称为相关性系数。若该系数为 1 那么表示两个特征之间完全正相关,若为 -1 则表示完全负相关,若为 0 则表示两个特征之间没有相关性(线性的)。
如果现在两个特征高度相关或者完全相关,这就意味着这两个特征都包含高度相似的信息,并且信息的差异非常小,所以其中一个特征是多余的。在构建模型时,我们应该尽量消除这种多余的特征,因为这样能减少训练的时间,也可以在某种程度上缓解过拟合。
所以接下来用热力图对相关性系数进行可视化。
sns.heatmap(data.corr(),annot=True,cmap='RdYlGn',linewidths=0.2) #data.corr()-->correlation matrix
fig=plt.gcf()
fig.set_size_inches(10,8)
plt.show()
从热力图上可以看出这些特征之间没有太大的相关性,最高的也就 SibSp与Parch,值为 0.41 。