Titanic - Machine Learning from Disaster
-
关于数据集
它包含有关1912年沉没的著名船只泰坦尼克号上的乘客的信息。该数据集通常用于探索数据分析技术和预测建模算法。该数据集包含有关乘客的大量信息,包括他们的姓名、年龄、性别、乘客等级、登船港口以及他们是否幸存下来。有了这些信息,研究人员可以分析和了解在海难期间影响乘客生存的因素。
在对数据进行预处理之前引入一些需要使用的包,以及将数据导入进来。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
from sklearn.preprocessing import RobustScaler,LabelEncoder
file_path_train = r'titanic\Input_data\train.csv'
file_path_test = r'titanic\Input_data\test.csv'
file_path_out = r'titanic\Out_put'
train = pd.read_csv(file_path_train)
test = pd.read_csv(file_path_test)
print(train.head())
数据详细信息如下:
PassengerId 乘客编号 | Survived 幸存 | Pclass Pclass | Name 名字 | Sex | Age | SibSp 西卜斯普 | Parch 帕奇 | Ticket 票 | Fare 票价 | Cabin 舱 | Embarked 已启程 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris 布劳德,欧文·哈里斯先生 | male 雄 | 22.0 | 1 | 0 | A/5 21171 A/5 21171 | 7.2500 | NaN | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... 卡明斯,约翰·布拉德利夫人(弗洛伦斯·布里格斯· | female 女性 | 38.0 | 1 | 0 | PC 17599 邮编 17599 | 71.2833 | C85 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina 海基宁,莱娜小姐 | female 女性 | 26.0 | 0 | 0 | STON/O2. 3101282 砰砰/O2。3101282 | 7.9250 | NaN | S |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) 富特雷尔,雅克·希思夫人(莉莉·梅·皮尔) | female 女性 | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 C123 | S |
4 | 5 | 0 | 3 | Allen, Mr. William Henry 艾伦,威廉·亨利先生 | male 雄 | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S |
以下是泰坦尼克号数据集中列的说明:
PassengerId:乘客 ID:每位乘客的唯一标识符。
Survived: 幸存:表示乘客是幸存 (1) 还是未幸存 (0)。
Pclass: Pclass:乘客的舱位等级:
1 = 1 等舱,2 = 2 等舱,3 = 3 等舱,
Name: 姓名:乘客的姓名。
Sex: 性别:乘客的性别。
Age: 年龄:乘客的年龄。此列可能有缺失值。
SibSp: 与乘客一起旅行的兄弟姐妹或配偶的数量。
Parch: 与乘客一起旅行的父母或儿童人数。
Ticket: 票证:票证号。
Fare: 票价:乘客支付的票价。
Cabin:客舱:乘客所住的客舱编号。此列可能有缺失值。
Embarked: 登船:乘客登船的港口:
C = 瑟堡,法国的一个港口 Q = 皇后镇,爱尔兰的一个港口 S = 南安普敦,英格兰的一个港口
-
观察数据集
使用info函数观察数据集的具体情况,包括特征数据,数据个数,数据类型等。
train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Name 891 non-null object
4 Sex 891 non-null object
5 Age 714 non-null float64
6 SibSp 891 non-null int64
7 Parch 891 non-null int64
8 Ticket 891 non-null object
9 Fare 891 non-null float64
10 Cabin 204 non-null object
11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
-
数据整理
缺失值:先使用isnull()函数检查一下数据集有哪些列数据存在缺失数据。
train.isnull().sum()
主要是Age列和Cabin列存在大量缺失数据,Embarked列仅存在2个缺失数据。
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 177
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2
dtype: int64
缺失数据的图形:为了更直观的观察缺失数据的样子,使用missingno库中的bar函数,将特征个数数据画出来。
import missingno as msno
msno.bar(train)
plt.show()
-
数据预处理:
-
名字数据特征列--Name
先打印前面几行,看看数据的的样子,外国人名字很长,对于中国人来说局的冗余且难以辨别,但是仔细观察,发现这一列中会出现头衔,如“Mr.”,“Mrs.”等,后面都是以“.”结尾,查找了一下资料,外国人喜欢在名字中加上头衔称谓,来表明一个人的身份,故我们只需要头衔来证明一个人的社会身份地位即可,名字对我们分析数据不是那么重要。接来下就是将复杂的名字数据简化为头衔称谓,再将非数值的头衔称谓转化为数值型数据,进行下一步的数据分析。
train['Name'].head()
由于train和test的数据都需要处理,就将两份数据组成一份列表数据,首先从每个人的姓名中提取头衔,$([A-Za-z]+)\.$这个表达式使用正则表达式匹配任何以点号结尾的字母串,例如’Mr.',‘Miss.’等,并返回它们作为一个新的字符串。
然后将提取出的头衔赋值给一个名为“Title”的新列,以便后续分析。
```python
combine = [train,test]
for dataset in combine:
dataset['Title'] = dataset.Name.str.extract('([A-Za-z]+)\.',expand=False)
print(train['Title'])
```
Title列数据都简化成头衔称谓数据,如下所示:
似乎头衔称谓有很多中,检查一下,发现竟然多达17种,其中有常见的“Mr.”,“Mrs.”,“Miss.”等,还要许多罕见的“Sir.”,“Ms”等称谓。代码和头衔称谓如下所示:
```python
print(train['Title'].nunique())
train['Title'].value_counts()
```
Title列数据中的称谓多种多样,其中有些称谓个数极少,不太利于后面的模型训练,为了统一数据,将出现次数很少的那些头衔称谓数据统称为“rare”类型。然后参考了一下其他大神的处理方法,发现我考虑的还是太少了,因为这些称谓中还有一些法语单词,在英语中也同样表示一样的意思,如“Mlle”:法语称呼未婚女性,类似于英语中的“Miss”等。
* 首先将列表中的头衔替换为“Rare”。这个表达式使用replace方法将第一个参数中的任何字符串替换为第二个参数中的字符串。
* 然后将“Mlle”替换为“Miss”,将“Ms”替换为“Miss”,将“Mme”替换为“Mrs”。所以将具有一样意思的称谓也需要合并,具体代码如下所示:
```python
for dataset in combine:
dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess', 'Capt', 'Col','Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss')
dataset['Title'] = dataset['Title'].replace('Ms', 'Miss')
dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs')
train[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
print(train['Title'].value_counts())
```
再次检查一下Title列数据,是否已经全部处理完毕。
```python
train.isnull().sum()
```
-
年龄数据特征列--Age
由于Age是乘客数据,包括“先生”,“夫人”,“小姐”,“主人”等头衔,可以使用此信息来估计年龄。具有相似头衔的人可能具有相似的年龄分布。计算每个标题的年龄中位数,将用它来填充缺失值。
乘客 - “Mr”,其年龄相关数据未填写,将他们的年龄中位数归咎于已经给出的“先生”乘客数据。 乘客 - “Miss/Mrs”,其年龄相关数据未填写,他们估算的年龄中位数已经给出了“小姐/女士/女士/夫人/夫人”乘客数据。 乘客 - “Master”,其年龄相关数据未填写,将他们推算为已经给出的“主人”乘客数据的中位年龄。 乘客 - 未填写年龄相关数据的“Rare”,将他们与已经给出的“稀有”乘客数据的年龄中位数归咎于他们。
因为上面修改了将头衔str值改为数据值了,所以这里也是用到了数字。具体对应可以看这段代码:Title_mapping = {'Master':1,'Miss':2,'Mr':3,'Mrs':4,'Rare':5}
# filling age column according to the passenger data with title based median: combine.loc[(combine['Title'] == 3) & (combine['Age'].isnull()), 'Age'] = combine[combine['Title'] == 3]['Age'].median() combine.loc[(combine['Title'] == 1) & (combine['Age'].isnull()), 'Age'] = combine[combine['Title'] == 1]['Age'].median() combine.loc[(combine['Title'] == 2) & (combine['Age'].isnull()), 'Age'] = combine[combine['Title'] == 2]['Age'].median() combine.loc[(combine['Title'] == 4) & (combine['Age'].isnull()), 'Age'] = combine[combine['Title'] == 4]['Age'].median() combine.loc[(combine['Title'] == 5) & (combine['Age'].isnull()), 'Age'] = combine[combine['Title'] == 5]['Age'].median()
检查一下Age列是否已经处理好了
combine.isnull().sum()
Age列已经没有空值了,处理好了。
为什么选择标题来估算错过的乘客年龄?
由于有各种年龄段的乘客,根据各自的年龄填充他们是我的策略,通过定义年龄的标题来实现。
3. #### 船舱数据特征列--cabin
怀疑泰坦尼克数据集中身份地位更高的人更有可能存活,而购买的船票船舱等级和票价可以间接表明一个人的身份地位,故接下来分析船舱数据。
先观察一下机舱数据列的各等级机舱的个数
train['Pclass'].value_counts()
船舱数据中更多的是三等舱乘客,其次是一等舱乘客,最后是二等舱乘客。根据现实生活中的机舱和人数猜测,人数更多的机舱更有可能是便宜的机舱,所以三等舱是最便宜的船舱,按照顺序排序,一等舱是最昂贵的船舱。
验证我们的猜想,昂贵的一等舱存活率果然远远高于便宜的三等舱,可能是因为一等舱定价更高,质量和安全将超过其他等级船舱,所以,一等舱的存活率很高。
train[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean()
根据船舱等级和票价以及头衔称谓可以知道一个人的身份地位,以及cabin船舱范围,且cabin缺失数据确实是在太多,687/891,高达77.1%,数据缺口太大,处理存在太大的不确定性,故我直接舍去了这列数据。
-
其他数据处理
对于Embarked列,由于缺失值较少,采用前一位乘客数据补全。
for dataset in combine:
dataset['Embarked'] = dataset['Embarked'].fillna(dataset['Embarked'].shift(-1))
对于SibSp和Parch数据,显示的是兄弟姐妹和父母孩子,可以合并为一个Family特征。
for dataset in combine:
dataset['Family'] = dataset['SibSp'] + dataset['Parch']
每个特征变量和标签变量之间的关系:
数据可视化每个特征变量和标签变量之间的关系、
- 首先设置seaborn的绘图风格为白色网格背景,以便于清晰的观察数据的分布和差异。在画布上创建一个2行3列的网格。
- 第一个子图:使用seaborn的countplot函数来绘制一个柱状图,用于显示不同性别的乘客在生存和死亡两种情况下的数量。参数train是一个包含泰坦尼克号乘客数据的数据框。x='Survived’表示将生存情况作为x轴变量,hue='Sex’表示将性别作为不同颜色的分组变量,palette='tab20’表示使用tab20调色板来设置颜色,width=0.3表示设置柱子的宽度为0.3。调整子图之间的水平间距为0.3。给当前的子图添加一个标题,内容为“1. Survival Count by Sex”。
- 第二个子图:用于显示不同舱位等级的乘客在生存和死亡两种情况下的数量。参数x="Survived"表示将生存情况作为x轴变量,hue="Pclass"表示将舱位等级作为不同颜色的分组变量,data=train表示使用train数据框作为数据源,palette="tab20"表示使用tab20调色板来设置颜色,width=0.5表示设置柱子的宽度为0.5。给当前的子图添加一个标题,内容为“2. Survival Count by Pclass”。
- 第三个子图:使用seaborn的countplot函数来绘制一个柱状图,用于显示不同登船地点的乘客在生存和死亡两种情况下的数量。参数x="Survived"表示将生存情况作为x轴变量,hue="Embarked"表示将登船地点作为不同颜色的分组变量,data=train表示使用train数据框作为数据源,palette="tab20"表示使用tab20调色板来设置颜色,width=0.5表示设置柱子的宽度为0.5。给当前的子图添加一个标题,内容为“3. Survival Count by onboarding location”。
- 第四个子图:使用seaborn的histplot函数来绘制一个直方图,用于显示不同年龄段的乘客在生存和死亡两种情况下的数量。参数data=train表示使用train数据框作为数据源,x='Age’表示将年龄作为x轴变量,hue='Survived’表示将生存情况作为不同颜色的分组变量,bins=8表示将x轴分成8个区间,multiple='stack’表示将不同分组的直方图堆叠在一起,kde=True表示在直方图上添加核密度估计曲线,palette='Set1’表示使用Set1调色板来设置颜色。给当前的子图添加一个标题,内容为“4. Distribution of survival with respect to Age”。
- 第五个子图:使用seaborn的histplot函数来绘制一个直方图,用于显示不同票价段的乘客在生存和死亡两种情况下的数量。参数data=train表示使用train数据框作为数据源,x="Fare"表示将票价作为x轴变量,hue="Survived"表示将生存情况作为不同颜色的分组变量,bins=10表示将x轴分成10个区间,multiple='stack’表示将不同分组的直方图堆叠在一起,kde=True表示在直方图上添加核密度估计曲线,palette='Set1’表示使用Set1调色板来设置颜色。给当前的子图添加一个标题,内容为“5. Distribution of survival with respect to Fare”。
- 第六个子图:使用seaborn的histplot函数来绘制一个直方图,用于显示不同家庭人数的乘客在生存和死亡两种情况下的数量。参数train表示使用train数据框作为数据源,x='Family’表示将家庭人数作为x轴变量,hue='Survived’表示将生存情况作为不同颜色的分组变量,bins=8表示将x轴分成8个区间,multiple='stack’表示将不同分组的直方图堆叠在一起,kde=True表示在直方图上添加核密度估计曲线,palette='Set1’表示使用Set1调色板来设置颜色。给当前的子图添加一个标题,内容为“6. Distribution of survival with respect to Family”。
plt.show()
:这行代码是显示所有的子图。
sns.set(style="whitegrid")
plt.figure(figsize=(16,12))
plt.subplot(2,3,1)
sns.countplot(train,x='Survived',hue='Sex',palette='tab20',width=0.3)
plt.subplots_adjust(hspace=0.3)
plt.title('1. Survival Count by Sex')
plt.subplot(2,3,2)
sns.countplot(x="Survived", hue="Pclass", data=train,palette="tab20",width=0.5)
plt.title('2. Survival Count by Pclass')
plt.subplot(2,3,3)
sns.countplot(x="Survived", hue="Embarked", data=train, palette="tab20",width=0.5)
plt.title('3. Survival Count by onboarding location')
plt.subplot(2,3,4)
sns.histplot(data=train,x='Age',hue='Survived',bins=8,multiple='stack',kde=True, palette='Set1')
plt.title('4. Distribution of survival with respect to Age')
plt.subplot(2,3,5)
sns.histplot(data=train, x="Fare", hue="Survived", bins=10,multiple='stack',kde=True,palette='Set1')
plt.title('5. Distribution of survival with respect to Fare')
plt.subplot(2,3,6)
sns.histplot(train, x='Family', hue='Survived',bins=8,multiple='stack',kde=True,palette='Set1')
plt.title('6. Distribution of survival with respect to Family')
plt.show()
通过绘制出来的图形显示可以知道:
-
第一张图:
与女性相比,大多数男性死亡,其数量达到>400。 题目描述,几乎是两倍的女性幸存下来。
-
第二张图:
大多数3等舱乘客死亡,这表明根据票价,将有更多的质量和安全。1等舱乘客票价更高,因此与其他2个乘客舱位相比,质量和安全将更多。
-
第三张图:
大部分乘客在英格兰的一个港口南安普敦登船。图显示,在南安普敦(S)和皇后镇(Q)港口上船的人根据生存情况死亡,但在瑟堡(C)港口上船的人根据生存情况大多还活着。
-
第四张图:
剧情描绘了在泰坦尼克号沉船中死亡的乘客年龄在20-30岁之间(年轻人)。
-
第五张图:
题目描绘了Fare在他们的生存中没有任何作用,而是他们的命运。
-
第六张图
家庭人数在0-2人的乘客,存活率更高。
-
相关矩阵:
选择['Title','Sex', 'Age','Family', 'Pclass', 'Embarked','Survived']这几类列标签进行热力图分析,分析数据之间的相关性。
column_headers = ['Title','Sex', 'Age','Family', 'Pclass', 'Embarked','Survived']
train_df = train[column_headers]
train_after = train_df[:891]
test_after = train_df[892:]
plt.figure(figsize=(12,6))
sns.heatmap(train_after.corr(),annot=True,fmt='.2f')
plt.show()
显示目标变量如何与其他变量关系的协关系矩阵。所有独立特征都显示与目标变量没有关系或否定关系。
从热力图中可以看出,数据框中各个变量之间的相关系数用不同的颜色来表示,颜色越深,表示相关系数越大(无论正负);颜色越浅,表示相关系数越小(无论正负)。
逻辑回归算法预测数据
X_train = train_after.drop('Survived', axis=1)
y_train = train_after['Survived']
X_test = test_after.drop('Survived', axis=1)
y_test = test_after['Survived']
model = LogisticRegression()
model.fit(X_train,y_train)
y_pred = model.predict(X_test)
print(y_pred)
将数据保存起来,然后提交。
predict = [X_test['PassengerId'],y_pred]
predict_df = pd.DataFrame(predict).T
predict_df.to_csv(file_path_out+r'\test.csv')
提交的结果如下:
这次结果较第一次简单处理数据已经产生了挺多的进步,提高了0.5。
第一次处理数据的结果如下图:
当然我的排名也有所提高啦!
对于数据预测,数据是十分重要的,任何一个数据都可能对结果有影响,对于数据的处理,一定要仔细慎重的考虑,正确的处理数据之间的关系。
(ps:看了一些前辈对Titanic数据的处理,有了很多启发,使我更加全面的了解这份数据,写这篇文章的时候,恰逢研究生入学,事务繁多,断断续续才写完了这篇数据分析,今天外面下暴雨。但是看着这份成绩心里说不出的高兴,希望新的三年可以认真对数据进行处理和分析,在毕业之时可以找到一份薪资待遇还不错的工作。)
参考学习链接:📊EDA|🚢Titanic_Survivals🌊🧊|LogisticRegression🚀 | Kaggle --- 📊EDA|🚢Titanic_Survivals🌊🧊|逻辑回归🚀 |卡格尔