项目:泰坦尼克沉船事件乘客幸存预测¶
事件背景¶
泰坦尼克号(RMS Titanic)是一艘奥林匹克级邮轮,于1912年4月首航时撞上冰山后沉没。泰坦尼克号是同级的3艘超级邮轮中的第2艘,与姐妹船奥林匹克号和不列颠号为白星航运公司的乘客们提供大西洋旅行。
泰坦尼克号由位于北爱尔兰贝尔法斯特的哈兰·沃尔夫船厂兴建,是当时最大的客运轮船,由于其规模相当一艘现代航空母舰,因而号称“上帝也沉没不了的巨型邮轮”。在泰坦尼克号的首航中,从英国南安普敦出发,途经法国瑟堡-奥克特维尔以及爱尔兰昆士敦,计划横渡大西洋前往美国纽约市。但因为人为错误,于1912年4月14日船上时间夜里11点40分撞上冰山;2小时40分钟后,即4月15日凌晨02点20分,船裂成两半后沉入大西洋,死亡人数超越1500人,堪称20世纪最大的海难事件,同时也是最广为人知的海难之一。
项目目标¶
基于泰坦尼克号乘客的性别和船舱等级等属性,对幸存情况进行逻辑回归分析,挖掘影响乘客幸存的重要特征,并利用回归模型预测未知幸存情况的乘客是否从沉船事件中幸存。
数据描述¶
数据集包括两个数据表:train.csv和test.csv
train.csv:记录了891位泰坦尼克号乘客在沉船事件后的幸存情况,用于进行模型训练,具体字段信息如下
- PassengerId:乘客ID
- survival:是否幸存
- 0 否
- 1 是
- pclass:船舱等级
- 1 一等舱
- 2 二等舱
- 3 三等舱
- name:姓名
- sex:性别
- Age:年龄
- sibsp:同乘伴侣/同胞数量
- parch:同乘父母/孩子数量
- ticket:船票号
- fare:票价金额
- cabin:船舱号
- embarked:登船港口
- C 瑟堡
- Q 皇后镇
- S 南安普敦
test.csv:记录泰坦尼克号上其他418名乘客的相关信息,用于预测乘客是否幸存(字段含义与train.csv相同,但不包含survival)
补充项目中扩展的新特征:
HasCabin:是否有登记船舱信息- 0 无登记船舱信息
- 1 有登记船舱信息
FamilyNum:乘客在船上的家庭成员个数
结论前置¶
泰坦尼克号沉船事件中,乘客生还几率受年龄、家庭规模、船舱等级、性别以及是否有船舱记录影响显著:
- 年龄越大,生还几率越低:年龄每增加1岁,生还几率降低 3.69%
- 家庭规模越大,生还几率越低:每多1名同乘家庭成员,生还几率降低 20.55%
- 船舱越高级,生还几率越高:三等舱乘客的生还几率比一等舱乘客低 70.52%
- 优先救援女性乘客:男性乘客的生还几率比女性乘客低 93.70%
- 优先救援有登记船舱信息的乘客:有登记船舱信息的乘客生还几率比无登记乘客高 257.12%
读取数据¶
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
import statsmodels.api as sm
origin_train = pd.read_csv("train.csv") # 读入训练集文件
数据评估与清洗¶
clean_train = origin_train.copy() # 复制一份训练集,用于进行数据清洗
数据整齐度评估¶
clean_train.head() # 查看文件前5行
| PassengerId | Survived | 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 | 7.2500 | NaN | S |
| 1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
| 2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/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 | S |
| 4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S |
从总体来看,这份训练集结构清晰,数据整齐,不存在结构性问题。
数据干净度评估¶
clean_train.info() # 获取数据集概览
<class 'pandas.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 str 4 Sex 891 non-null str 5 Age 714 non-null float64 6 SibSp 891 non-null int64 7 Parch 891 non-null int64 8 Ticket 891 non-null str 9 Fare 891 non-null float64 10 Cabin 204 non-null str 11 Embarked 889 non-null str dtypes: float64(2), int64(5), str(5) memory usage: 83.7 KB
数据干净度分析¶
- 训练数据集共891个数据(891个人)
- 每个人都有12个特征
- 存在缺失值:
- Age(年龄)特征缺失了117个数据
- Cabin(船舱号)特征缺失了687个数据
- Embarked(登船港口)特征缺失了2个数据
- 数据类型异常:
- PassengerId(乘客ID)是乘客的唯一标识符,应该是字符串类型数据,而不是整型
- Survived(是否幸存)、Pclass(船舱等级)、Sex(性别)、Embarked(登船港口)都是分类特征,但这些特征的数据类型各异
缺失值处理¶
Age缺失值处理¶
- Age(年龄)特征缺失了117个数据,占总体数据比例的13%。
- 因缺失比例适中且其他特征信息完整,采用整体均值填充,在保证样本量的同时避免引入额外偏差。
clean_train[clean_train['Age'].isnull()] # 查看Age列存在缺失值的表格数据
| PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5 | 6 | 0 | 3 | Moran, Mr. James | male | NaN | 0 | 0 | 330877 | 8.4583 | NaN | Q |
| 17 | 18 | 1 | 2 | Williams, Mr. Charles Eugene | male | NaN | 0 | 0 | 244373 | 13.0000 | NaN | S |
| 19 | 20 | 1 | 3 | Masselmani, Mrs. Fatima | female | NaN | 0 | 0 | 2649 | 7.2250 | NaN | C |
| 26 | 27 | 0 | 3 | Emir, Mr. Farred Chehab | male | NaN | 0 | 0 | 2631 | 7.2250 | NaN | C |
| 28 | 29 | 1 | 3 | O'Dwyer, Miss. Ellen "Nellie" | female | NaN | 0 | 0 | 330959 | 7.8792 | NaN | Q |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 859 | 860 | 0 | 3 | Razi, Mr. Raihed | male | NaN | 0 | 0 | 2629 | 7.2292 | NaN | C |
| 863 | 864 | 0 | 3 | Sage, Miss. Dorothy Edith "Dolly" | female | NaN | 8 | 2 | CA. 2343 | 69.5500 | NaN | S |
| 868 | 869 | 0 | 3 | van Melkebeke, Mr. Philemon | male | NaN | 0 | 0 | 345777 | 9.5000 | NaN | S |
| 878 | 879 | 0 | 3 | Laleff, Mr. Kristo | male | NaN | 0 | 0 | 349217 | 7.8958 | NaN | S |
| 888 | 889 | 0 | 3 | Johnston, Miss. Catherine Helen "Carrie" | female | NaN | 1 | 2 | W./C. 6607 | 23.4500 | NaN | S |
177 rows × 12 columns
clean_train['Age'] = clean_train['Age'].fillna(clean_train['Age'].mean()) # 用 Age 列数据的平均值填充 Age 列的 NaN 值
clean_train['Age'].isnull().sum() # 验证 Age 列的 NaN 值是否已被填充
np.int64(0)
Cabin缺失值处理¶
- Cabin(船舱号)特征缺失687条(占77%),缺失率极高,无法有效填充。
- 考虑到Cabin可能反映乘客距救生艇的远近,与生存率存在潜在关联,因此不直接删除该特征。
- 为此,本项目根据乘客船舱信息是否缺失,将该特征重构为“HasCabin”,定义“有登记船舱信息为1,无登记船舱信息为0”,将“缺失”本身转化为有效信号。
clean_train[clean_train['Cabin'].isnull()] # 查看Cabin列存在缺失值的表格数据
| PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.000000 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
| 2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.000000 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
| 4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.000000 | 0 | 0 | 373450 | 8.0500 | NaN | S |
| 5 | 6 | 0 | 3 | Moran, Mr. James | male | 29.699118 | 0 | 0 | 330877 | 8.4583 | NaN | Q |
| 7 | 8 | 0 | 3 | Palsson, Master. Gosta Leonard | male | 2.000000 | 3 | 1 | 349909 | 21.0750 | NaN | S |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 884 | 885 | 0 | 3 | Sutehall, Mr. Henry Jr | male | 25.000000 | 0 | 0 | SOTON/OQ 392076 | 7.0500 | NaN | S |
| 885 | 886 | 0 | 3 | Rice, Mrs. William (Margaret Norton) | female | 39.000000 | 0 | 5 | 382652 | 29.1250 | NaN | Q |
| 886 | 887 | 0 | 2 | Montvila, Rev. Juozas | male | 27.000000 | 0 | 0 | 211536 | 13.0000 | NaN | S |
| 888 | 889 | 0 | 3 | Johnston, Miss. Catherine Helen "Carrie" | female | 29.699118 | 1 | 2 | W./C. 6607 | 23.4500 | NaN | S |
| 890 | 891 | 0 | 3 | Dooley, Mr. Patrick | male | 32.000000 | 0 | 0 | 370376 | 7.7500 | NaN | Q |
687 rows × 12 columns
# 构造一个新的列,有登记船舱信息记为1,无登记船舱信息记为0
clean_train['HasCabin'] = clean_train['Cabin'].notnull().astype("int") # 构造出 HasCabin 列
clean_train[['Cabin','HasCabin']]
| Cabin | HasCabin | |
|---|---|---|
| 0 | NaN | 0 |
| 1 | C85 | 1 |
| 2 | NaN | 0 |
| 3 | C123 | 1 |
| 4 | NaN | 0 |
| ... | ... | ... |
| 886 | NaN | 0 |
| 887 | B42 | 1 |
| 888 | NaN | 0 |
| 889 | C148 | 1 |
| 890 | NaN | 0 |
891 rows × 2 columns
clean_train = clean_train.drop("Cabin",axis=1) # 将无用的 Cabin 列从原表中删除
clean_train
| PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Embarked | HasCabin | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.000000 | 1 | 0 | A/5 21171 | 7.2500 | S | 0 |
| 1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.000000 | 1 | 0 | PC 17599 | 71.2833 | C | 1 |
| 2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.000000 | 0 | 0 | STON/O2. 3101282 | 7.9250 | S | 0 |
| 3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.000000 | 1 | 0 | 113803 | 53.1000 | S | 1 |
| 4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.000000 | 0 | 0 | 373450 | 8.0500 | S | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 886 | 887 | 0 | 2 | Montvila, Rev. Juozas | male | 27.000000 | 0 | 0 | 211536 | 13.0000 | S | 0 |
| 887 | 888 | 1 | 1 | Graham, Miss. Margaret Edith | female | 19.000000 | 0 | 0 | 112053 | 30.0000 | S | 1 |
| 888 | 889 | 0 | 3 | Johnston, Miss. Catherine Helen "Carrie" | female | 29.699118 | 1 | 2 | W./C. 6607 | 23.4500 | S | 0 |
| 889 | 890 | 1 | 1 | Behr, Mr. Karl Howell | male | 26.000000 | 0 | 0 | 111369 | 30.0000 | C | 1 |
| 890 | 891 | 0 | 3 | Dooley, Mr. Patrick | male | 32.000000 | 0 | 0 | 370376 | 7.7500 | Q | 0 |
891 rows × 12 columns
Embarked 缺失值处理¶
- Embarked(登船港口)特征缺失了2个数据,而且Embarked是一个分类变量,所以采用众数填充方式对缺失值进行填充。
clean_train[clean_train['Embarked'].isnull()] # 查看Embarked列存在缺失值的表格数据
| PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Embarked | HasCabin | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 61 | 62 | 1 | 1 | Icard, Miss. Amelie | female | 38.0 | 0 | 0 | 113572 | 80.0 | NaN | 1 |
| 829 | 830 | 1 | 1 | Stone, Mrs. George Nelson (Martha Evelyn) | female | 62.0 | 0 | 0 | 113572 | 80.0 | NaN | 1 |
embarked_mode = clean_train['Embarked'].mode() # 计算Embarked的众数
clean_train['Embarked'] = clean_train['Embarked'].fillna(embarked_mode[0]) # 把众数填充进缺失位置
clean_train['Embarked'].isnull().sum() # 重新统计缺失值个数,确保正确填充缺失值
np.int64(0)
处理异常类型¶
将 PassengerId 转为字符串类型¶
- PassengerId 表示每个乘客的唯一 ID,不应被用于数值计算,因此需要将其转位字符串类型
clean_train['PassengerId'] = clean_train['PassengerId'].astype("str") # 把 PassengerId 列的数据类型设置为 str
clean_train.info()
<class 'pandas.DataFrame'> RangeIndex: 891 entries, 0 to 890 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 PassengerId 891 non-null str 1 Survived 891 non-null int64 2 Pclass 891 non-null int64 3 Name 891 non-null str 4 Sex 891 non-null str 5 Age 891 non-null float64 6 SibSp 891 non-null int64 7 Parch 891 non-null int64 8 Ticket 891 non-null str 9 Fare 891 non-null float64 10 Embarked 891 non-null str 11 HasCabin 891 non-null int64 dtypes: float64(2), int64(5), str(5) memory usage: 83.7 KB
Survived、Pclass、Sex、Embarked、HasCabin转为分类类型¶
- Survived、Pclass、Sex、Embarked、HasCabin都是分类特征,但这些特征的数据类型各异,所以需要更改为 category 类型
clean_train['Survived'] = clean_train['Survived'].astype('category') # 更改 Survived 列
clean_train['Survived']
0 0
1 1
2 1
3 1
4 0
..
886 0
887 1
888 0
889 1
890 0
Name: Survived, Length: 891, dtype: category
Categories (2, int64): [0, 1]
clean_train['Pclass'] = clean_train['Pclass'].astype('category') # 更改 Pclass 列
clean_train['Pclass']
0 3
1 1
2 3
3 1
4 3
..
886 2
887 1
888 3
889 1
890 3
Name: Pclass, Length: 891, dtype: category
Categories (3, int64): [1, 2, 3]
clean_train['Sex'] = clean_train['Sex'].astype('category') # 更改 Sex 列
clean_train['Sex']
0 male
1 female
2 female
3 female
4 male
...
886 male
887 female
888 female
889 male
890 male
Name: Sex, Length: 891, dtype: category
Categories (2, str): ['female', 'male']
clean_train['Embarked'] = clean_train['Embarked'].astype('category') # 更改 Embarked 列
clean_train['Embarked']
0 S
1 C
2 S
3 S
4 S
..
886 S
887 S
888 S
889 C
890 Q
Name: Embarked, Length: 891, dtype: category
Categories (3, str): ['C', 'Q', 'S']
clean_train['HasCabin'] = clean_train['HasCabin'].astype('category') # 更改 HasCabin 列
clean_train['HasCabin']
0 0
1 1
2 0
3 1
4 0
..
886 0
887 1
888 0
889 1
890 0
Name: HasCabin, Length: 891, dtype: category
Categories (2, int64): [0, 1]
数据重复评估¶
- PassengerId 表示每个乘客的唯一 ID,不能存在重复,需要检查该列是否有异常
clean_train["PassengerId"].duplicated().sum()
np.int64(0)
小结:输出为 0,说明不存在重复值
数据一致性评估¶
- 不一致数据可能存在于所有分类特征中,需要查看是否存在不同值实际指代同一目标的情况
- 分类特征:Survived、Pclass、Sex、Embarked、HasCabin
clean_train['Survived'].value_counts() # 幸存结果分类
Survived 0 549 1 342 Name: count, dtype: int64
clean_train['Pclass'].value_counts() # 船舱等级分类
Pclass 3 491 1 216 2 184 Name: count, dtype: int64
clean_train['Sex'].value_counts() # 性别分类
Sex male 577 female 314 Name: count, dtype: int64
clean_train['Embarked'].value_counts() # 登船港口分类
Embarked S 646 C 168 Q 77 Name: count, dtype: int64
clean_train['HasCabin'].value_counts() #是否有登记船舱分类
HasCabin 0 687 1 204 Name: count, dtype: int64
小结:所有分类特征数据都一致
无效或错误数据评估¶
- 无效或错误数据可能导致模型预测准确性降低,需要先行处理
clean_train.describe()
| Age | SibSp | Parch | Fare | |
|---|---|---|---|---|
| count | 891.000000 | 891.000000 | 891.000000 | 891.000000 |
| mean | 29.699118 | 0.523008 | 0.381594 | 32.204208 |
| std | 13.002015 | 1.102743 | 0.806057 | 49.693429 |
| min | 0.420000 | 0.000000 | 0.000000 | 0.000000 |
| 25% | 22.000000 | 0.000000 | 0.000000 | 7.910400 |
| 50% | 29.699118 | 0.000000 | 0.000000 | 14.454200 |
| 75% | 35.000000 | 1.000000 | 0.000000 | 31.000000 |
| max | 80.000000 | 8.000000 | 6.000000 | 512.329200 |
小结:不存在异常值
- 乘客年龄平均为30岁左右,最大值为80岁,最小值为0.42岁。
- 同乘伴侣/同胞数量最大值为8个,最小为0个。
- 同乘父母/孩子数量最大值为6个,最小值为0个。
- 船票价格平均为32元,最大值为512元,最小值为0元【0元有可能是赠票,所以不算异常值】。
特征扩展¶
- SibSp 表示同乘的伴侣/同胞数,Parch 表示同乘的父母/孩子数,这个两个特征表示的都是这个乘客的家人。
- 假设这个乘客家人很多,那他因为拯救家人而遇难的可能性就会大增,因此认为家人的多少是影响乘客幸存的重要因素。
- 由此,可以构造成一个新特征,表示“乘客在船上的家庭成员个数”,指定特征名为“FamilyNum”
clean_train["FamilyNum"] = clean_train["SibSp"] + clean_train["Parch"] # 家庭成员人数 = 同伴或同胞数 + 父母或孩子数
clean_train.head(10)
| PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Embarked | HasCabin | FamilyNum | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.000000 | 1 | 0 | A/5 21171 | 7.2500 | S | 0 | 1 |
| 1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.000000 | 1 | 0 | PC 17599 | 71.2833 | C | 1 | 1 |
| 2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.000000 | 0 | 0 | STON/O2. 3101282 | 7.9250 | S | 0 | 0 |
| 3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.000000 | 1 | 0 | 113803 | 53.1000 | S | 1 | 1 |
| 4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.000000 | 0 | 0 | 373450 | 8.0500 | S | 0 | 0 |
| 5 | 6 | 0 | 3 | Moran, Mr. James | male | 29.699118 | 0 | 0 | 330877 | 8.4583 | Q | 0 | 0 |
| 6 | 7 | 0 | 1 | McCarthy, Mr. Timothy J | male | 54.000000 | 0 | 0 | 17463 | 51.8625 | S | 1 | 0 |
| 7 | 8 | 0 | 3 | Palsson, Master. Gosta Leonard | male | 2.000000 | 3 | 1 | 349909 | 21.0750 | S | 0 | 4 |
| 8 | 9 | 1 | 3 | Johnson, Mrs. Oscar W (Elisabeth Vilhelmina Berg) | female | 27.000000 | 0 | 2 | 347742 | 11.1333 | S | 0 | 2 |
| 9 | 10 | 1 | 2 | Nasser, Mrs. Nicholas (Adele Achem) | female | 14.000000 | 1 | 0 | 237736 | 30.0708 | C | 0 | 1 |
数据探索¶
探索幸存比例¶
sns.set_palette("pastel") # 设置图表的配色方案
plt.rcParams['figure.figsize'] = (7, 3.5) # 设置全局图表宽、高
plt.rcParams["figure.autolayout"] = True # 自动调整子图间距
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号 '-' 显示为方块
survived_count = clean_train['Survived'].value_counts() # 统计Survived中每个类别的个数
survived_count
Survived 0 549 1 342 Name: count, dtype: int64
plt.pie(survived_count, labels=['遇难','幸存'], autopct="%.2f%%")
plt.show()
小结:泰坦尼克号遇难乘客远多于幸存乘客,比例约为3:2
- 幸存342人,占总人数的38.38%
- 遇难549人,占总人数的61.62%
探索船舱等级与是否幸存之间的相关关系¶
pclass_count_all = clean_train['Pclass'].value_counts() # 统计不同等级船舱的人数
pclass_count_all
Pclass 3 491 1 216 2 184 Name: count, dtype: int64
# 统计不同等级船舱的幸存人数
survived_df = clean_train[clean_train['Survived'] == 1] # 筛选出所有幸存乘客信息
pclass_count_survived = survived_df['Pclass'].value_counts() # 统计不同等级船舱的幸存人数
pclass_count_survived
Pclass 1 136 3 119 2 87 Name: count, dtype: int64
fig, axes = plt.subplots(1, 2) # 绘制1行2列的子图图框
axes[0].pie(pclass_count_all, labels=['3', '1', '2'], autopct="%.2f%%") # 绘制饼图,展示不同等级船舱的人数比例
sns.countplot(clean_train, x="Pclass", hue="Survived", ax=axes[1]) # 绘制计数图,展现不同船舱等级的人员的幸存人数对比【0 表示‘遇难’,1 表示‘幸存’】
plt.show()
小结:船舱越高级,乘客幸存率越高,船舱等级与是否幸存之间存在正相关关系
- 一等舱216人,占总人数的24.24%,幸存136人,幸存率63.96%
- 二等舱184人,占总人数的20.65%,幸存87人,幸存率47.28%
- 三等舱491人,占总人数的55.11%,幸存119人,幸存率24.23%
探索性别与是否幸存之间的相关关系¶
sex_count_all = clean_train['Sex'].value_counts() # 统计不同性别的人数
sex_count_all
Sex male 577 female 314 Name: count, dtype: int64
# 统计不同性别的幸存人数
survived_df = clean_train[clean_train['Survived'] == 1] # 筛选出所有幸存乘客信息
sex_count_survived = survived_df['Sex'].value_counts() # 统计不同性别的幸存人数
sex_count_survived
Sex female 233 male 109 Name: count, dtype: int64
fig, axes = plt.subplots(1, 2) # 绘制1行2列的子图图框
axes[0].pie(sex_count_all, labels=['male', 'female'], autopct="%.2f%%") # 绘制饼图,展示不同性别人数占比
sns.countplot(clean_train, x='Sex', hue='Survived', ax=axes[1]) # 绘制计数图,展示不同性别人员幸存人数对比【0 表示‘遇难’,1 表示‘幸存’】
plt.show()
小结:女性乘客的幸存率远高于男性乘客,由此推测性别与是否幸存之间存在相关关系
- 女性乘客314人,占总人数的35.2%,幸存233人,幸存率74.20%
- 男性乘客577人,占总人数的64.8%,幸存109人,幸存率18.89%
探索乘客的年龄分布情况¶
fig, axes = plt.subplots(1, 2, gridspec_kw={"width_ratios": [1, 5]}) # 绘制1行2列的子图图框
sns.boxplot(clean_train['Age'], ax=axes[0]) # 绘制箱形图,用于展示乘客年龄的集中趋势
sns.histplot(clean_train, x='Age') # 绘制直方图,用于展示乘客年龄的总体分布情况
plt.show()
小结:大多数乘客年龄位于22岁到40岁之间,且有不少婴幼儿及老年乘客
- 乘客年龄主要集中于22岁到40岁之间,呈双峰右偏分布,一峰代表婴幼儿,另一峰代表青壮年乘客,长尾代表老年乘客
探索年龄与是否幸存之间的相关关系¶
sns.histplot(clean_train, x='Age', hue='Survived', alpha=0.4) # 绘制直方图,展示不同年龄人员幸存人数对比【0 表示‘遇难’,1 表示‘幸存’】
plt.show()
小结:幸存比例在婴幼儿峰中较高,青壮年峰及老年长尾所对应的年龄段遇难人数均占多数,因此,年龄与是否幸存之间存在相关关系
探索乘客的年龄分布情况¶
fig, axes = plt.subplots(1, 2, figsize=(10, 5), gridspec_kw={"width_ratios": [1, 4]}) # 绘制1行2列的子图图框
sns.boxplot(clean_train['Fare'], ax=axes[0]) # 绘制箱形图,用于展示票价金额的集中趋势
sns.histplot(clean_train, x="Fare") # 绘制直方图,用于展示乘客船票金额的总体分布情况
plt.show()
小结:船票金额呈右偏态分布,多数票价集中在0–40美元的低价区间,少量高价票构成右侧长尾,从而拉高整体平均值
探索票价金额与是否幸存之间的相关关系¶
sns.histplot(clean_train, x="Fare", hue="Survived") # 绘制直方图,用于展示不同船票金额对应人员的幸存人数对比【0 表示‘遇难’,1 表示‘幸存’】
plt.show()
小结:购买低价票(0-40美元)的乘客遇难人数均占多数,购买中高价票(40美元以上)的乘客幸存人数均占多数,因此,票价金额与是否幸存之间存在相关关系
探索登船港口与是否幸存之间的相关关系¶
embarked_count_all = clean_train['Embarked'].value_counts() # 统计不同登船港口乘客的人数
embarked_count_all
Embarked S 646 C 168 Q 77 Name: count, dtype: int64
# 统计不同登船港口乘客的幸存人数
survived_df = clean_train[clean_train['Survived'] == 1] # 筛选出所有幸存乘客信息
embarked_count_survived = survived_df['Embarked'].value_counts() # 统计不同登船港口乘客的幸存人数
embarked_count_survived
Embarked S 219 C 93 Q 30 Name: count, dtype: int64
fig, axes = plt.subplots(1, 2) # 绘制1行2列的子图图框
axes[0].pie(embarked_count_all, labels=['S', 'C', 'Q'], autopct='%.2f%%') # 绘制饼图,用于表示不同港口登船的乘客的比例
sns.countplot(clean_train, x='Embarked', hue='Survived') # 绘制计数图,用于展示不同登船港口对应乘客人数的幸存人数对比【0 表示‘遇难’,1 表示‘幸存’】
plt.show()
小结:从瑟堡登船的乘客,幸存数量大于遇难数量,而皇后镇和南安普敦则相反,因此,登船港口与是否幸存之间存在相关关系
- 从 S(南安普敦)港口登船的乘客有646人,占总人数的72.50%,幸存219人,幸存率33.90%
- 从 C(瑟堡)港口登船的乘客有168人,占总人数的18.86%,幸存93人,幸存率55.35%
- 从 Q(皇后镇)港口登船的乘客有77人,占总人数的8.64%,幸存30人,幸存率38.96%
探索是否有登记船舱信息与是否幸存之间的相关关系¶
has_cabin_count_all = clean_train['HasCabin'].value_counts() # 统计有登记船舱信息和无登记船舱信息的人员的个数
has_cabin_count_all
HasCabin 0 687 1 204 Name: count, dtype: int64
# 统计有登记船舱信息和无登记船舱信息的幸存人员的个数
survived_df = clean_train[clean_train['Survived'] == 1] # 筛选出所有幸存乘客信息
has_cabin_count_survived = survived_df['HasCabin'].value_counts() # 统计有登记船舱信息和无登记船舱信息的幸存人员的个数
has_cabin_count_survived
HasCabin 0 206 1 136 Name: count, dtype: int64
fig, axes = plt.subplots(1, 2) # 绘制1行2列的子图图框
axes[0].pie(has_cabin_count_all, labels=['0', '1'], autopct='%.2f%%') # 绘制饼图,用于展示有无登记船舱信息人员的比例【0 表示‘无登记’,1 表示‘有登记’】
sns.countplot(clean_train, x='HasCabin', hue='Survived') # 绘制计数图,用于展示有无登记船舱信息人员的幸存人数对比【0 表示‘遇难’,1 表示‘幸存’】
plt.show()
小结:有登记船舱信息的乘客幸存人数多于遇难人数,无登记船舱信息的乘客遇难人数多余幸存人数,因此,是否有登记船舱信息与是否幸存之间存在相关关系
- 无登记船舱信息的乘客有687人,占总人数的77.10%,幸存206人,幸存率29.99%
- 有登记船舱信息的乘客有204人,占总人数的22.90%,幸存136人,幸存率66.67%
探索家庭成员数量与是否幸存之间的相关关系¶
family_num_count_all = clean_train['FamilyNum'].value_counts() # 统计不同家庭成员数量乘客的人数
family_num_count_all
FamilyNum 0 537 1 161 2 102 3 29 5 22 4 15 6 12 10 7 7 6 Name: count, dtype: int64
# 统计不同家庭成员数量乘客的幸存人数
survived_df = clean_train[clean_train['Survived'] == 1] # 筛选出所有幸存乘客信息
family_num_count_survived = survived_df['FamilyNum'].value_counts() # 统计不同家庭成员数量乘客的幸存人数
family_num_count_survived
FamilyNum 0 163 1 89 2 59 3 21 6 4 5 3 4 3 Name: count, dtype: int64
fig, axes = plt.subplots(1, 2) # 绘制1行2列的子图图框
axes[0].pie(family_num_count_all, labels=['0', '1', '2', '3', '5', '4', '6', '10', '7']) # 绘制饼图,用于展示不同家庭成员数量的乘客的比例
sns.countplot(clean_train, x='FamilyNum', hue='Survived') # 绘制计数图,用于展示不同家庭成员数量乘客的幸存人数对比【0 表示‘遇难’,1 表示‘幸存’】
plt.show()
小结:有1-3位其他家庭成员的乘客幸存人数多余遇难人数,无其他家庭成员的乘客与有4位以上家庭成员的乘客均遇难人数多于幸存人数,因此,家庭成员数量与是否幸存之间存在相关关系
- 无其他家庭成员的乘客有537人,占总人数的60.27%,幸存163人,幸存率30.35%
- 有1-3位其他家庭成员的乘客有292人,占总人数的32.77%,幸存169人,幸存率57.88%
- 有4位以上家庭成员的乘客有62人,占总人数的6.96%,幸存10人,幸存率16.13%
分析数据¶
lr_titanic_train = clean_train.copy() # 备份数据集
lr_titanic_train.head()
| PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Embarked | HasCabin | FamilyNum | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | S | 0 | 1 |
| 1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C | 1 | 1 |
| 2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | S | 0 | 0 |
| 3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | S | 1 | 1 |
| 4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | S | 0 | 0 |
删除无关特征:
- 乘客的唯一ID(PassengerId):用于唯一标识用户,与用户是否幸存无相关关系
- 乘客的姓名(Name):用户专属名称,与用户是否幸存无相关关系
- 船票号(Ticket):船票的唯一标识,与用户是否幸存无相关关系
lr_titanic_train = lr_titanic_train.drop(['PassengerId', 'Name', 'Ticket'], axis=1) # 删除'PassengerId'、'Name'和'Ticket'三列
lr_titanic_train.head()
| Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Embarked | HasCabin | FamilyNum | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | 0 | 1 |
| 1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | 1 | 1 |
| 2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | 0 | 0 |
| 3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.1000 | S | 1 | 1 |
| 4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.0500 | S | 0 | 0 |
虚拟变量构建:将所有分类变量构建位虚拟变量
- 分类变量包含:Pclass、Sex、Embarked、HasCabin
lr_titanic_train = pd.get_dummies(lr_titanic_train, columns=['Pclass', 'Sex', 'Embarked', 'HasCabin'], dtype=int, drop_first=True)
lr_titanic_train.head()
| Survived | Age | SibSp | Parch | Fare | FamilyNum | Pclass_2 | Pclass_3 | Sex_male | Embarked_Q | Embarked_S | HasCabin_1 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 22.0 | 1 | 0 | 7.2500 | 1 | 0 | 1 | 1 | 0 | 1 | 0 |
| 1 | 1 | 38.0 | 1 | 0 | 71.2833 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| 2 | 1 | 26.0 | 0 | 0 | 7.9250 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
| 3 | 1 | 35.0 | 1 | 0 | 53.1000 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
| 4 | 0 | 35.0 | 0 | 0 | 8.0500 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
构造自变量与因变量:提取出自变量 X 和因变量 y,用于逻辑回归模型训练
y = lr_titanic_train['Survived'] # 提取出因变量列
y
0 0
1 1
2 1
3 1
4 0
..
886 0
887 1
888 0
889 1
890 0
Name: Survived, Length: 891, dtype: category
Categories (2, int64): [0, 1]
X = lr_titanic_train.drop('Survived', axis=1) # 把表示因变量的'Survived'列移除,其他列都是自变量列
X.head()
| Age | SibSp | Parch | Fare | FamilyNum | Pclass_2 | Pclass_3 | Sex_male | Embarked_Q | Embarked_S | HasCabin_1 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 22.0 | 1 | 0 | 7.2500 | 1 | 0 | 1 | 1 | 0 | 1 | 0 |
| 1 | 38.0 | 1 | 0 | 71.2833 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| 2 | 26.0 | 0 | 0 | 7.9250 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
| 3 | 35.0 | 1 | 0 | 53.1000 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
| 4 | 35.0 | 0 | 0 | 8.0500 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
检查变量与变量间的相关性:
sns.heatmap(X.corr().abs(), annot=True) # 用热力图直观展示变量间相关性的大小
plt.show()
由热力图可以看出:FamilyNum 和 SibSp 强相关
操作:移除 SibSp
X = X.drop(['SibSp'], axis=1) # 删除强相关特征
X.head()
| Age | Parch | Fare | FamilyNum | Pclass_2 | Pclass_3 | Sex_male | Embarked_Q | Embarked_S | HasCabin_1 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 22.0 | 0 | 7.2500 | 1 | 0 | 1 | 1 | 0 | 1 | 0 |
| 1 | 38.0 | 0 | 71.2833 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| 2 | 26.0 | 0 | 7.9250 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
| 3 | 35.0 | 0 | 53.1000 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
| 4 | 35.0 | 0 | 8.0500 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
项目迭代:第一次模型训练后,发现 ‘Fare、Parch、Pclass_2、Embarked_Q’ 三个特征对模型没有显著预测效果,所以在此移除这三个特征
X = X.drop(['Fare', 'Parch', 'Pclass_2','Embarked_Q'], axis=1) # 删除对模型无显著预测效果的特征
X.head()
| Age | FamilyNum | Pclass_3 | Sex_male | Embarked_S | HasCabin_1 | |
|---|---|---|---|---|---|---|
| 0 | 22.0 | 1 | 1 | 1 | 1 | 0 |
| 1 | 38.0 | 1 | 0 | 0 | 0 | 1 |
| 2 | 26.0 | 0 | 1 | 0 | 1 | 0 |
| 3 | 35.0 | 1 | 0 | 0 | 1 | 1 |
| 4 | 35.0 | 0 | 1 | 1 | 1 | 0 |
项目迭代:第二次模型训练后,发现模型效果仍不够理想,由于 ‘Embarked_S’ 特征对,对模型没有显著预测效果,所以在此移除这个特征
X = X.drop(['Embarked_S'], axis=1) # 删除对模型无显著预测效果的特征【后加的】
X.head()
| Age | FamilyNum | Pclass_3 | Sex_male | HasCabin_1 | |
|---|---|---|---|---|---|
| 0 | 22.0 | 1 | 1 | 1 | 0 |
| 1 | 38.0 | 1 | 0 | 0 | 1 |
| 2 | 26.0 | 0 | 1 | 0 | 0 |
| 3 | 35.0 | 1 | 0 | 0 | 1 |
| 4 | 35.0 | 0 | 1 | 1 | 0 |
自变量构建补充:为了保证截距不会被模型忽略,需要给自变量表格增加一个自变量列,其值全为 1
X = sm.add_constant(X)
X.head()
| const | Age | FamilyNum | Pclass_3 | Sex_male | HasCabin_1 | |
|---|---|---|---|---|---|---|
| 0 | 1.0 | 22.0 | 1 | 1 | 1 | 0 |
| 1 | 1.0 | 38.0 | 1 | 0 | 0 | 1 |
| 2 | 1.0 | 26.0 | 0 | 1 | 0 | 0 |
| 3 | 1.0 | 35.0 | 1 | 0 | 0 | 1 |
| 4 | 1.0 | 35.0 | 0 | 1 | 1 | 0 |
构建逻辑回归模型¶
model = sm.Logit(y, X) # 建立模型
result = model.fit() # 拟合模型
result.summary() # 输出模型拟合结果
Optimization terminated successfully.
Current function value: 0.440651
Iterations 6
| Dep. Variable: | Survived | No. Observations: | 891 |
|---|---|---|---|
| Model: | Logit | Df Residuals: | 885 |
| Method: | MLE | Df Model: | 5 |
| Date: | Mon, 13 Apr 2026 | Pseudo R-squ.: | 0.3383 |
| Time: | 15:21:59 | Log-Likelihood: | -392.62 |
| converged: | True | LL-Null: | -593.33 |
| Covariance Type: | nonrobust | LLR p-value: | 1.470e-84 |
| coef | std err | z | P>|z| | [0.025 | 0.975] | |
|---|---|---|---|---|---|---|
| const | 2.8511 | 0.345 | 8.254 | 0.000 | 2.174 | 3.528 |
| Age | -0.0376 | 0.008 | -4.852 | 0.000 | -0.053 | -0.022 |
| FamilyNum | -0.2301 | 0.066 | -3.501 | 0.000 | -0.359 | -0.101 |
| Pclass_3 | -1.2214 | 0.214 | -5.716 | 0.000 | -1.640 | -0.803 |
| Sex_male | -2.7654 | 0.199 | -13.907 | 0.000 | -3.155 | -2.376 |
| HasCabin_1 | 1.2729 | 0.244 | 5.209 | 0.000 | 0.794 | 1.752 |
模型第一次拟合:
- 模型的 Pseudo R-squ = 0.3455,达到正常的拟合水平
- Fare 的 P 值为 0.392,说明 Fare 对模型没有显著预测效果,需要移除 Fare 特征
- Parch 的 P 值为 0.280,说明 Parch 对模型没有显著预测效果,需要移除 Parch 特征
- Pclass_2 的 P 值为 0.634,说明 Pclass_2 对模型没有显著预测效果,需要移除 Pclass_2 特征
- Embarked_Q 的 P 值为 0.815,说明 Embarked_Q 对模型没有显著预测效果,需要移除 Embarked_Q 特征
模型第二次拟合:
- 模型的 Pseudo R-squ = 0.3442,拟合水平少量提升。
- Embarked_S 的 P 值为 0.044,为提高模型精度,尝试移除 Embarked_S
- 其他剩余特征均对模型存在显著预测效果。
模型第三次拟合:
- 模型的 Pseudo R-squ = 0.3383,拟合水平再次提升。
模型结果总结¶
模型结果解读
print(f"年龄每增加1岁,生还几率降低 {(1-np.exp(-0.0376))*100:.2f}%")
print(f"每多1名同乘家庭成员,生还几率降低 {(1-np.exp(-0.2301))*100:.2f}%")
print(f"三等舱乘客的生还几率比一等舱乘客低 {(1-np.exp(-1.2214))*100:.2f}%")
print(f"男性乘客的生还几率比女性乘客低 {(1-np.exp(-2.7654))*100:.2f}%")
print(f"有登记船舱信息的乘客生还几率比无登记乘客高 {(np.exp(1.2729)-1)*100:.2f}%")
年龄每增加1岁,生还几率降低 3.69% 每多1名同乘家庭成员,生还几率降低 20.55% 三等舱乘客的生还几率比一等舱乘客低 70.52% 男性乘客的生还几率比女性乘客低 93.70% 有登记船舱信息的乘客生还几率比无登记乘客高 257.12%
总结:泰坦尼克号沉船事件中,乘客生还几率受年龄、家庭规模、船舱等级、性别以及是否有船舱记录影响显著
- 年龄越大,生还几率越低:年龄每增加1岁,生还几率降低 3.69%
- 家庭规模越大,生还几率越低:每多1名同乘家庭成员,生还几率降低 20.55%
- 船舱越高级,生还几率越高:三等舱乘客的生还几率比一等舱乘客低 70.52%
- 优先救援女性乘客:男性乘客的生还几率比女性乘客低 93.70%
- 优先救援有登记船舱信息的乘客:有登记船舱信息的乘客生还几率比无登记乘客高 257.12%
使用逻辑回归模型¶
导入测试集数据¶
origin_test = pd.read_csv('test.csv') # 读取测试集 CSV 文件
origin_test.head()
| PassengerId | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 892 | 3 | Kelly, Mr. James | male | 34.5 | 0 | 0 | 330911 | 7.8292 | NaN | Q |
| 1 | 893 | 3 | Wilkes, Mrs. James (Ellen Needs) | female | 47.0 | 1 | 0 | 363272 | 7.0000 | NaN | S |
| 2 | 894 | 2 | Myles, Mr. Thomas Francis | male | 62.0 | 0 | 0 | 240276 | 9.6875 | NaN | Q |
| 3 | 895 | 3 | Wirz, Mr. Albert | male | 27.0 | 0 | 0 | 315154 | 8.6625 | NaN | S |
| 4 | 896 | 3 | Hirvonen, Mrs. Alexander (Helga E Lindqvist) | female | 22.0 | 1 | 1 | 3101298 | 12.2875 | NaN | S |
对测试集数据进行预处理¶
clean_test = origin_test.copy() # 将原始数据集复制一份,以防改错
clean_test.head()
| PassengerId | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 892 | 3 | Kelly, Mr. James | male | 34.5 | 0 | 0 | 330911 | 7.8292 | NaN | Q |
| 1 | 893 | 3 | Wilkes, Mrs. James (Ellen Needs) | female | 47.0 | 1 | 0 | 363272 | 7.0000 | NaN | S |
| 2 | 894 | 2 | Myles, Mr. Thomas Francis | male | 62.0 | 0 | 0 | 240276 | 9.6875 | NaN | Q |
| 3 | 895 | 3 | Wirz, Mr. Albert | male | 27.0 | 0 | 0 | 315154 | 8.6625 | NaN | S |
| 4 | 896 | 3 | Hirvonen, Mrs. Alexander (Helga E Lindqvist) | female | 22.0 | 1 | 1 | 3101298 | 12.2875 | NaN | S |
把已知的不需要用到的特征列删掉¶
clean_test = clean_test.drop(['Fare', 'PassengerId', 'Name', 'Ticket'], axis=1)
clean_test.head()
| Pclass | Sex | Age | SibSp | Parch | Cabin | Embarked | |
|---|---|---|---|---|---|---|---|
| 0 | 3 | male | 34.5 | 0 | 0 | NaN | Q |
| 1 | 3 | female | 47.0 | 1 | 0 | NaN | S |
| 2 | 2 | male | 62.0 | 0 | 0 | NaN | Q |
| 3 | 3 | male | 27.0 | 0 | 0 | NaN | S |
| 4 | 3 | female | 22.0 | 1 | 1 | NaN | S |
评估剩下的特征¶
clean_test.info()
<class 'pandas.DataFrame'> RangeIndex: 418 entries, 0 to 417 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Pclass 418 non-null int64 1 Sex 418 non-null str 2 Age 332 non-null float64 3 SibSp 418 non-null int64 4 Parch 418 non-null int64 5 Cabin 91 non-null str 6 Embarked 418 non-null str dtypes: float64(1), int64(3), str(3) memory usage: 23.0 KB
小结:
- Age 存在缺失
- Cabin 存在缺失
- Pclass(船舱等级)、Sex(性别)、Embarked(登船港口)都是分类特征,但这些特征的数据类型各异
clean_test['Age'] = clean_test['Age'].fillna(clean_test['Age'].mean()) # 用 Age 列数据的平均值填充 Age 列的 NaN 值
clean_test['Age'].isnull().sum() # 验证 Age 列的 NaN 值是否已被填充
np.int64(0)
# 构造一个新的列,有登记船舱信息记为1,无登记船舱信息记为0
clean_test['HasCabin'] = clean_test['Cabin'].notnull().astype("int") # 构造出 HasCabin 列
clean_test[['Cabin','HasCabin']]
| Cabin | HasCabin | |
|---|---|---|
| 0 | NaN | 0 |
| 1 | NaN | 0 |
| 2 | NaN | 0 |
| 3 | NaN | 0 |
| 4 | NaN | 0 |
| ... | ... | ... |
| 413 | NaN | 0 |
| 414 | C105 | 1 |
| 415 | NaN | 0 |
| 416 | NaN | 0 |
| 417 | NaN | 0 |
418 rows × 2 columns
clean_test['Pclass'] = clean_test['Pclass'].astype('category') # 更改 Pclass 列
clean_test['Pclass']
0 3
1 3
2 2
3 3
4 3
..
413 3
414 1
415 3
416 3
417 3
Name: Pclass, Length: 418, dtype: category
Categories (3, int64): [1, 2, 3]
clean_test['Sex'] = clean_test['Sex'].astype('category') # 更改 Pclass 列
clean_test['Sex']
0 male
1 female
2 male
3 male
4 female
...
413 male
414 female
415 male
416 male
417 male
Name: Sex, Length: 418, dtype: category
Categories (2, str): ['female', 'male']
clean_test['Embarked'] = clean_test['Embarked'].astype('category') # 更改 Pclass 列
clean_test['Embarked']
0 Q
1 S
2 Q
3 S
4 S
..
413 S
414 C
415 S
416 S
417 C
Name: Embarked, Length: 418, dtype: category
Categories (3, str): ['C', 'Q', 'S']
构造新特征¶
clean_test["FamilyNum"] = clean_test["SibSp"] + clean_test["Parch"] # 家庭成员人数 = 同伴或同胞数 + 父母或孩子数
clean_test.head(10)
| Pclass | Sex | Age | SibSp | Parch | Cabin | Embarked | HasCabin | FamilyNum | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 3 | male | 34.5 | 0 | 0 | NaN | Q | 0 | 0 |
| 1 | 3 | female | 47.0 | 1 | 0 | NaN | S | 0 | 1 |
| 2 | 2 | male | 62.0 | 0 | 0 | NaN | Q | 0 | 0 |
| 3 | 3 | male | 27.0 | 0 | 0 | NaN | S | 0 | 0 |
| 4 | 3 | female | 22.0 | 1 | 1 | NaN | S | 0 | 2 |
| 5 | 3 | male | 14.0 | 0 | 0 | NaN | S | 0 | 0 |
| 6 | 3 | female | 30.0 | 0 | 0 | NaN | Q | 0 | 0 |
| 7 | 2 | male | 26.0 | 1 | 1 | NaN | S | 0 | 2 |
| 8 | 3 | female | 18.0 | 0 | 0 | NaN | C | 0 | 0 |
| 9 | 3 | male | 21.0 | 2 | 0 | NaN | S | 0 | 2 |
把因变量的分类变量构造成虚拟变量¶
- 由于要进行‘逻辑回归’,所以此处需要把所有因变量的分类变量构造成虚拟变量【Survived:是预测结果,不能改虚拟变量】
- 分类变量包含:Pclass、Sex、Embarked、HasCabin
clean_test = pd.get_dummies(clean_test, columns=['Pclass', 'Sex', 'Embarked', 'HasCabin'], dtype=int, drop_first=True)
clean_test.head()
| Age | SibSp | Parch | Cabin | FamilyNum | Pclass_2 | Pclass_3 | Sex_male | Embarked_Q | Embarked_S | HasCabin_1 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 34.5 | 0 | 0 | NaN | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
| 1 | 47.0 | 1 | 0 | NaN | 1 | 0 | 1 | 0 | 0 | 1 | 0 |
| 2 | 62.0 | 0 | 0 | NaN | 0 | 1 | 0 | 1 | 1 | 0 | 0 |
| 3 | 27.0 | 0 | 0 | NaN | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
| 4 | 22.0 | 1 | 1 | NaN | 2 | 0 | 1 | 0 | 0 | 1 | 0 |
删除无关变量¶
clean_test = clean_test.drop(['SibSp', 'Parch', 'Cabin', 'Pclass_2', 'Embarked_Q', 'Embarked_S'], axis=1)
clean_test.head()
| Age | FamilyNum | Pclass_3 | Sex_male | HasCabin_1 | |
|---|---|---|---|---|---|
| 0 | 34.5 | 0 | 1 | 1 | 0 |
| 1 | 47.0 | 1 | 1 | 0 | 0 |
| 2 | 62.0 | 0 | 0 | 1 | 0 |
| 3 | 27.0 | 0 | 1 | 1 | 0 |
| 4 | 22.0 | 2 | 1 | 0 | 0 |
为了保证截距不会被模型忽略,需要给自变量表格增加一个自变量列,其值全为 1¶
clean_test = sm.add_constant(clean_test)
clean_test.head()
| const | Age | FamilyNum | Pclass_3 | Sex_male | HasCabin_1 | |
|---|---|---|---|---|---|---|
| 0 | 1.0 | 34.5 | 0 | 1 | 1 | 0 |
| 1 | 1.0 | 47.0 | 1 | 1 | 0 | 0 |
| 2 | 1.0 | 62.0 | 0 | 0 | 1 | 0 |
| 3 | 1.0 | 27.0 | 0 | 1 | 1 | 0 |
| 4 | 1.0 | 22.0 | 2 | 1 | 0 | 0 |
再看一眼概览,确保数据集正确清洗完成¶
clean_test.info()
<class 'pandas.DataFrame'> RangeIndex: 418 entries, 0 to 417 Data columns (total 6 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 const 418 non-null float64 1 Age 418 non-null float64 2 FamilyNum 418 non-null int64 3 Pclass_3 418 non-null int64 4 Sex_male 418 non-null int64 5 HasCabin_1 418 non-null int64 dtypes: float64(2), int64(4) memory usage: 19.7 KB
使用预处理后的测试集数据进行模型预测¶
result_test = result.predict(clean_test) # 对测试集数据进行预测
result_test
0 0.080819
1 0.409658
2 0.095998
3 0.104366
4 0.585002
...
413 0.093425
414 0.934593
415 0.070340
416 0.093425
417 0.061072
Length: 418, dtype: float64
构建要上传到竞赛平台的结果数据集¶
- PassengerID 列:存放原来的测试集的 PassengerID 列
- Survived 列:存放预测结果【0:遇难】【1:幸存】
# 定义‘预测概率>0.61’为‘幸存’,否则为‘遇难’
result_test = result_test > 0.61 # 把预测结果转为布尔值
result_test = result_test.astype(int) # 把预测结果的布尔值转为整数类型的0和1【0:遇难】【1:幸存】
result_test
0 0
1 0
2 0
3 0
4 0
..
413 0
414 1
415 0
416 0
417 0
Length: 418, dtype: int64
# 构建测试结果 DataFrame
result_test_dataframe = pd.DataFrame({'PassengerId':origin_test['PassengerId'], 'Survived':result_test})
result_test_dataframe.head()
| PassengerId | Survived | |
|---|---|---|
| 0 | 892 | 0 |
| 1 | 893 | 0 |
| 2 | 894 | 0 |
| 3 | 895 | 0 |
| 4 | 896 | 0 |
保存测试集的预测结果¶
result_test_dataframe.to_csv("result_test.csv", index=False)
泰坦尼克号幸存预测 Kaggle 竞赛网址:https://www.kaggle.com/competitions/titanic/overview
文件提交格式:
- 提交一个恰好有418条条目加上一行头部的预测结果csv文件。
- 该文件只能包含2列(PassengerId和Survived)
项目评分:0.77990