用Python和Pandas搞定泰坦尼克号数据集:从数据清洗到特征工程的保姆级实战
泰坦尼克号数据集是数据科学领域最经典的入门案例之一。这个包含乘客信息、生存状态等多维特征的数据集,为我们提供了一个绝佳的实战平台,能够完整演练从原始数据到预测模型的整个数据分析流程。本文将手把手带你用Python和Pandas完成这个数据科学项目的全流程,特别适合刚接触数据分析的新手学习。
1. 环境准备与数据初探
在开始分析之前,我们需要搭建好Python环境并导入必要的库。推荐使用Anaconda发行版,它已经集成了我们所需的大部分工具。
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.ensemble import RandomForestClassifier加载数据集后,我们首先需要对数据有个整体认识:
titanic = pd.read_csv('titanic.csv') print(titanic.shape) # 查看数据维度 print(titanic.info()) # 查看数据类型和缺失情况关键发现:
- 数据集包含891条记录和12个特征
- Age(年龄)和Cabin(舱位)有较多缺失值
- 特征类型包括数值型、类别型和文本型
提示:在Jupyter Notebook中,可以使用
titanic.head()快速查看前几行数据,titanic.describe()获取数值特征的统计摘要。
2. 数据清洗:处理缺失值与异常值
真实世界的数据从来不会完美无缺。泰坦尼克号数据集中的缺失值和异常值需要我们谨慎处理。
2.1 缺失值处理策略
不同特征的缺失值需要采用不同的处理方式:
| 特征名 | 缺失比例 | 处理方案 |
|---|---|---|
| Age | 19.9% | 分组中位数填充 |
| Cabin | 77.1% | 标记为缺失类别 |
| Embarked | 0.2% | 众数填充 |
对于Age特征,我们可以基于Title(从姓名中提取)和Pclass(客舱等级)进行分组填充:
# 从姓名中提取称呼 titanic['Title'] = titanic['Name'].str.extract(' ([A-Za-z]+)\.', expand=False) # 按Title和Pclass分组填充Age titanic['Age'] = titanic.groupby(['Title', 'Pclass'])['Age'].apply( lambda x: x.fillna(x.median()))2.2 异常值检测与处理
Fare(票价)特征可能存在异常高值:
sns.boxplot(x='Fare', data=titanic) plt.show()处理方案:
- 对Fare取对数转换,缓解长尾分布
- 对极端高值进行winsorize处理(如99%分位数截断)
3. 特征工程:从原始数据中提取价值
优秀的特征工程往往比模型选择更能提升预测性能。让我们挖掘这个数据集的潜在信息。
3.1 从姓名中提取有用信息
乘客姓名不仅包含称呼,还可能隐含家庭关系:
# 提取姓氏和家庭大小 titanic['Surname'] = titanic['Name'].str.split(',').str[0] titanic['FamilySize'] = titanic['SibSp'] + titanic['Parch'] + 1 # 将称呼标准化 title_mapping = {'Mlle': 'Miss', 'Ms': 'Miss', 'Mme': 'Mrs'} titanic['Title'] = titanic['Title'].replace(title_mapping)3.2 创建新特征
基于现有特征可以构造更有预测力的新特征:
- AgeGroup:将年龄分段(儿童、青年、中年、老年)
- IsAlone:标记单独旅行的乘客
- Deck:从Cabin中提取甲板信息(如A、B、C等)
# 创建IsAlone特征 titanic['IsAlone'] = 0 titanic.loc[titanic['FamilySize'] == 1, 'IsAlone'] = 1 # 提取Deck信息 titanic['Deck'] = titanic['Cabin'].str[0] titanic['Deck'] = titanic['Deck'].fillna('Unknown')4. 探索性数据分析(EDA):发现数据中的模式
可视化是理解数据分布和关系的强大工具。让我们用Seaborn绘制几个关键图表。
4.1 生存率与乘客特征的关系
fig, axes = plt.subplots(1, 2, figsize=(15, 5)) sns.barplot(x='Pclass', y='Survived', data=titanic, ax=axes[0]) sns.barplot(x='Sex', y='Survived', data=titanic, ax=axes[1]) plt.show()关键发现:
- 一等舱乘客生存率显著高于其他舱位
- 女性生存率远高于男性
4.2 年龄与票价分布分析
plt.figure(figsize=(10, 6)) sns.histplot(data=titanic, x='Age', hue='Survived', bins=30, kde=True, alpha=0.6) plt.title('Age Distribution by Survival Status') plt.show()5. 模型构建与评估
经过充分的数据准备后,我们可以开始构建预测模型了。
5.1 数据预处理
首先需要对类别型特征进行编码:
from sklearn.preprocessing import LabelEncoder # 对类别特征进行标签编码 cat_features = ['Sex', 'Embarked', 'Title', 'Deck'] for feature in cat_features: le = LabelEncoder() titanic[feature] = le.fit_transform(titanic[feature])5.2 特征选择与模型训练
使用随机森林进行特征重要性评估:
from sklearn.ensemble import RandomForestClassifier # 准备特征矩阵和目标向量 X = titanic.drop(['PassengerId', 'Survived', 'Name', 'Ticket', 'Cabin'], axis=1) y = titanic['Survived'] # 训练随机森林并评估特征重要性 rf = RandomForestClassifier(n_estimators=100) rf.fit(X, y) # 展示特征重要性 feature_imp = pd.DataFrame({ 'feature': X.columns, 'importance': rf.feature_importances_ }).sort_values('importance', ascending=False)5.3 模型优化与交叉验证
使用网格搜索寻找最优超参数:
from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [50, 100, 200], 'max_depth': [None, 5, 10], 'min_samples_split': [2, 5, 10] } grid_search = GridSearchCV( estimator=RandomForestClassifier(), param_grid=param_grid, cv=5, scoring='accuracy' ) grid_search.fit(X, y) print(f"Best parameters: {grid_search.best_params_}") print(f"Best cross-validation score: {grid_search.best_score_:.3f}")在实际项目中,我发现特征工程的质量往往比模型调参更能提升预测性能。特别是从姓名中提取的Title特征和构造的FamilySize特征,对模型准确率的提升非常明显。