Python泰坦尼克号数据分析实战:从数据清洗到模型预测全流程

发布时间:2026/10/1 1:34:45
Python泰坦尼克号数据分析实战:从数据清洗到模型预测全流程 简介这份资源是面向Python数据科学初学者与Kaggle竞赛入门者的泰坦尼克号生存预测完整实践包围绕经典赛题串联数据加载、探索性分析、特征工程、模型训练与评估全流程适合希望系统掌握Pandas、Numpy、Matplotlib、Seaborn及Scikit-learn等工具的学习者。压缩包共4个文件包含2个csv数据集、1个py实验代码和1份doc实验报告整体约2.35MB其中csv用于训练与测试建模py承载核心分析逻辑doc则记录实验过程与结果总结。目前已有5207人学习下载说明该案例在入门群体中认可度较高。读者可借此获得一套可复用的赛题实现思路理解缺失值处理、分组统计、特征构造、交叉验证与网格搜索调参等关键环节并通过实验报告对照复盘模型选择与评估指标快速建立数据科学项目的完整认知。1. 从「python泰坦尼克号.zip」说起一份压缩包背后到底藏着什么很多人第一次在搜索引擎里敲下「python泰坦尼克号.zip」其实心里想的是另一件事我手上有一份泰坦尼克号的数据想用 Python 把它跑通从读数据到出结果最好还能看到一张像样的图。这个压缩包名字听起来像个现成项目但真正值钱的不是那个 zip 文件本身而是它背后那条完整链路——数据读取、缺失值处理、特征工程、模型训练、结果输出。这条链路跑通了你换任何一份 CSV 都能复用跑不通换十个压缩包还是卡在第一步。泰坦尼克号数据集是 Kaggle 上最经典的入门竞赛数据一共 891 行训练数据、418 行测试数据字段包括乘客编号、舱位等级、姓名、性别、年龄、兄弟姐妹数、父母子女数、船票号、票价、客舱号、登船港口和是否幸存。它小到能在笔记本上秒级跑完又脏到能让你把数据清洗的坑踩一遍年龄有 177 个缺失值客舱号缺失率超过 77%票价和舱位等级强相关。对刚接触 Python 数据分析的人来说这是一份性价比极高的练手材料对已经工作的人来说它是验证一套特征处理流程是否可靠的试金石。这篇笔记不假设你手上已经有一份现成的 zip而是把「拿到泰坦尼克号数据之后怎么用 Python 跑出一份能看的结果」这件事从头讲清楚。适合三类人刚装完 Python 想找个真实数据集练手的新手、想复习 pandas 和 sklearn 基本用法的从业者、以及需要一份可复现基线来对比自己模型的人。全程只依赖 pandas、numpy、scikit-learn、matplotlib 这几个常见库不需要 GPU不需要联网下载模型。2. 环境与数据准备把 zip 变成能读的 DataFrame2.1 先确认 Python 环境和四个核心库不管你是用 python 官网下载的安装包还是通过 Anaconda 装的第一步都是确认版本和库。我一般会在终端里跑一遍下面这段确认没有报错再往下走。很多人卡在「python 不是内部或外部命令」本质是安装时没勾选 Add Python to PATH或者装了多个版本导致 PATH 指向了 Microsoft Store 的占位程序。python --version pip list | grep -E pandas|numpy|scikit-learn|matplotlib如果某个库缺失直接 pip 安装即可。国内网络环境下如果下载慢可以临时指定镜像源但不要长期写死在配置里否则换网络后反而出问题。pip install pandas numpy scikit-learn matplotlib逻辑说明python --version确认解释器可用pip list配合 grep 快速筛出四个库是否已装。参数上没有需要调的唯一要注意的是如果你用 vscode 配置 python 环境记得在右下角选对解释器否则终端里装好了、编辑器里还是报 ModuleNotFoundError。2.2 解压 zip 并确认文件结构拿到python泰坦尼克号.zip之后先解压到一个纯英文路径下。中文路径在 Windows 上偶尔会让 pandas 读取时报编码错误虽然新版已经改善很多但没必要冒这个险。解压后常见结构是train.csv、test.csv、gender_submission.csv三个文件也可能只有合并后的一份titanic.csv。unzip python泰坦尼克号.zip -d titanic_project cd titanic_project ls -lh逻辑说明-d指定解压目录ls -lh用人类可读格式看文件大小。如果压缩包里嵌套了多层目录用find . -name *.csv定位真实数据文件位置。这一步的坑在于有些压缩包用的是 GBK 编码的文件名在 Linux 上解压会出现乱码此时用unzip -O GBK指定编码即可。2.3 用 pandas 读入并做第一轮体检读进来之后不要急着建模先做一次结构体检。下面这段代码会输出行列数、字段类型、缺失值分布和前五行基本能让你对数据有个整体判断。import pandas as pd import numpy as np train pd.read_csv(train.csv) test pd.read_csv(test.csv) print(train shape:, train.shape) print(test shape:, test.shape) print(train.dtypes) print(train.isnull().sum().sort_values(ascendingFalse).head(10)) print(train.head())逻辑说明shape确认行列数dtypes看字段类型是否符合预期比如 Age 应该是 floatPclass 应该是 intisnull().sum()按缺失数量降序排列一眼看出哪些字段需要重点处理。参数上read_csv默认用逗号分隔、UTF-8 编码如果遇到UnicodeDecodeError加encodinglatin-1或encodinggbk试一下。体检完你会发现几个典型问题Age 缺 177 个Cabin 缺 687 个Embarked 缺 2 个。Cabin 缺失率太高直接整列丢掉比强行填充更合理Age 缺失比例约 20%可以用中位数或按性别舱位分组填充Embarked 只缺 2 个用众数填掉即可。这一步的判断直接决定后面模型的上限很多人一上来就fillna(0)结果把年龄信息全毁了。3. 特征工程把原始字段变成模型能吃的数字3.1 缺失值处理的三种策略与选择依据缺失值处理没有万能公式核心是看缺失比例和字段重要性。我一般按下面这张表来决策你可以直接套用。字段缺失比例处理策略理由Age约 20%分组中位数填充年龄与性别、舱位相关分组填充比全局中位数更准Cabin约 77%提取首字母后丢弃原列缺失太多但首字母代表甲板位置有信息量Embarked约 0.2%众数填充缺失极少众数影响可忽略Fare测试集缺 1 个中位数填充单点缺失中位数最稳train[Age] train.groupby([Sex, Pclass])[Age].transform( lambda x: x.fillna(x.median()) ) train[Embarked] train[Embarked].fillna(train[Embarked].mode()[0]) train[Cabin] train[Cabin].fillna(U) train[CabinDeck] train[Cabin].str[0] train.drop(Cabin, axis1, inplaceTrue) test[Age] test.groupby([Sex, Pclass])[Age].transform( lambda x: x.fillna(x.median()) ) test[Fare] test[Fare].fillna(test[Fare].median()) test[Cabin] test[Cabin].fillna(U) test[CabinDeck] test[Cabin].str[0] test.drop(Cabin, axis1, inplaceTrue)逻辑说明groupby加transform保证填充后的索引和原表对齐这是很多人用apply时会踩的坑——apply返回的索引可能错位。str[0]提取客舱号首字母U 代表未知。参数上median()对偏态分布比mean()更稳健票价这种右偏数据尤其明显。3.2 从姓名和票号里榨出可用特征Name 字段看起来是纯文本但里面藏着称呼Mr、Mrs、Miss、Master这个称呼和生存率强相关——Master 指小男孩生存率明显高于成年男性。Ticket 字段格式混乱但前缀能反映票的类型。这两列处理好了比多调几轮模型参数管用。train[Title] train[Name].str.extract(r ([A-Za-z])\.) test[Title] test[Name].str.extract(r ([A-Za-z])\.) rare_titles [Lady, Countess, Capt, Col, Don, Dr, Major, Rev, Sir, Jonkheer, Dona] for df in [train, test]: df[Title] df[Title].replace(rare_titles, Rare) df[Title] df[Title].replace({Mlle: Miss, Ms: Miss, Mme: Mrs}) train[FamilySize] train[SibSp] train[Parch] 1 test[FamilySize] test[SibSp] test[Parch] 1 for df in [train, test]: df[IsAlone] (df[FamilySize] 1).astype(int)逻辑说明正则r ([A-Za-z])\.匹配姓名中最后一个点号前的单词这是称呼的标准位置。稀有称呼合并成 Rare 是为了避免独热编码后维度爆炸。FamilySize把兄弟姐妹、父母子女和本人加在一起IsAlone是它的二值化版本。参数上要注意正则里的空格少了这个空格会把名字里的缩写也匹配进来。3.3 类别字段编码与数值字段分箱Sex、Embarked、Title、CabinDeck 都是类别字段需要转成数字。Pclass 虽然是数字但它本质是等级做独热编码比当连续值更合理。Age 和 Fare 做分箱可以捕捉非线性关系。from sklearn.preprocessing import LabelEncoder for col in [Sex, Embarked, Title, CabinDeck]: le LabelEncoder() train[col] le.fit_transform(train[col].astype(str)) test[col] le.transform(test[col].astype(str)) train[AgeBin] pd.cut(train[Age], bins[0, 12, 18, 35, 60, 100], labelsFalse) test[AgeBin] pd.cut(test[Age], bins[0, 12, 18, 35, 60, 100], labelsFalse) train[FareBin] pd.qcut(train[Fare], 4, labelsFalse, duplicatesdrop) test[FareBin] pd.qcut(test[Fare], 4, labelsFalse, duplicatesdrop)逻辑说明LabelEncoder对训练集 fit、对测试集 transform顺序不能反否则类别映射不一致。pd.cut按固定边界分箱适合年龄这种有明确分段的字段pd.qcut按分位数分箱适合票价这种分布不均的字段。duplicatesdrop是防止分位点重复导致报错票价里有很多相同值不加这个参数会翻车。4. 建模与验证从逻辑回归到随机森林的完整对比4.1 选哪些特征、怎么切训练验证集特征工程做完后把无关列丢掉只保留对预测有用的字段。PassengerId、Name、Ticket 这些标识类字段直接排除。然后用train_test_split切出验证集固定随机种子保证可复现。from sklearn.model_selection import train_test_split features [Pclass, Sex, Age, SibSp, Parch, Fare, Embarked, Title, FamilySize, IsAlone, CabinDeck, AgeBin, FareBin] X train[features] y train[Survived] X_train, X_val, y_train, y_val train_test_split( X, y, test_size0.2, random_state42, stratifyy )逻辑说明stratifyy保证训练集和验证集的生存率比例一致数据量小的时候不加这个参数验证集可能全是幸存或全是不幸存评估结果毫无意义。random_state42是习惯用法换成任何固定整数都行关键是全流程统一。4.2 逻辑回归基线先跑通再谈优化逻辑回归是泰坦尼克号任务的标准基线训练快、可解释性强。先用它跑通全流程确认没有数据泄漏和维度错误再换复杂模型。from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.metrics import accuracy_score, classification_report pipe_lr Pipeline([ (scaler, StandardScaler()), (clf, LogisticRegression(max_iter1000, random_state42)) ]) pipe_lr.fit(X_train, y_train) pred_lr pipe_lr.predict(X_val) print(LR accuracy:, accuracy_score(y_val, pred_lr)) print(classification_report(y_val, pred_lr))逻辑说明Pipeline把标准化和分类器串在一起避免手动对训练集和验证集分别 fit 导致的数据泄漏。max_iter1000是防止默认 100 次迭代不收敛特征标准化后通常 200 次内就收敛了。classification_report输出精确率、召回率和 F1比单看准确率更能发现问题——如果某一类召回率特别低说明模型偏向多数类。4.3 随机森林与梯度提升参数怎么设、结果怎么比逻辑回归跑通后换随机森林和梯度提升看提升空间。随机森林对缺失值和异常值不敏感梯度提升在小数据集上通常表现更好但更容易过拟合。from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier rf RandomForestClassifier( n_estimators300, max_depth6, min_samples_split4, min_samples_leaf2, random_state42 ) rf.fit(X_train, y_train) pred_rf rf.predict(X_val) gb GradientBoostingClassifier( n_estimators200, learning_rate0.05, max_depth3, random_state42 ) gb.fit(X_train, y_train) pred_gb gb.predict(X_val) print(RF accuracy:, accuracy_score(y_val, pred_rf)) print(GB accuracy:, accuracy_score(y_val, pred_gb))逻辑说明随机森林的max_depth6是防止树太深导致过拟合min_samples_leaf2保证叶子节点至少有两个样本。梯度提升的learning_rate0.05配合n_estimators200是经典组合学习率低就需要更多树但泛化更好。参数没有绝对最优建议用网格搜索在验证集上选但不要用测试集调参那是自欺欺人。三个模型跑完后把准确率列个表对比。通常逻辑回归在 0.80 左右随机森林 0.82 上下梯度提升 0.83 到 0.85 之间。如果某个模型明显低于 0.78先检查特征工程有没有出错而不是急着调参。5. 避坑与排查那些让结果原地踏步的细节5.1 训练集和测试集特征列不一致现象模型在验证集上表现正常一跑测试集就报ValueError: feature_names mismatch或者预测结果全是 0。原因训练集做了独热编码后列数变多测试集没有对齐或者测试集多了一列训练集没有的类别。解决用pd.get_dummies时加reindex(columnstrain_columns, fill_value0)或者用LabelEncoder时确保测试集出现的类别都在训练集里出现过没出现过的映射到未知类。5.2 用测试集调参导致过拟合现象在本地验证集上准确率 0.85提交到平台只有 0.76。原因反复用验证集调参验证集的信息已经泄漏到模型选择里。解决切出三份数据——训练集、验证集、测试集调参只看验证集测试集只在最后跑一次。数据量小的时候用交叉验证代替单次切分cross_val_score的均值比单次结果可靠得多。5.3 年龄填充用了全局中位数现象模型对儿童乘客的预测准确率明显偏低。原因全局中位数填充把不同性别、不同舱位的年龄差异抹平了Master 称呼对应的小男孩年龄被填成了 28 岁。解决按 Sex 和 Pclass 分组填充或者用随机森林回归预测缺失年龄。分组填充的代码在第 3 章已经给过核心是groupby加transform。5.4 忘记处理 Fare 的极端值现象逻辑回归系数异常大标准化后仍然不收敛。原因Fare 字段有少量超过 500 的极端值标准化后仍然拉偏分布。解决对 Fare 做对数变换np.log1p或者用分位数分箱代替原始值。np.log1p是log(1x)能处理 0 值比直接log安全。5.5 随机种子不固定导致结果无法复现现象同样的代码跑两次准确率差 2 个百分点。原因train_test_split、随机森林、梯度提升都有随机成分没固定种子。解决所有涉及随机的函数都加random_state42包括train_test_split、RandomForestClassifier、GradientBoostingClassifier。这不是玄学是工程可复现的基本要求。6. 把结果落成文件从预测到提交的最后一公里跑完模型只是半成品真正交付的是能提交的结果文件。Kaggle 的泰坦尼克号竞赛要求提交两列PassengerId 和 Survived。用测试集跑预测然后写成 CSV。test_features test[features] test_pred gb.predict(test_features) submission pd.DataFrame({ PassengerId: test[PassengerId], Survived: test_pred }) submission.to_csv(submission.csv, indexFalse) print(submission[Survived].value_counts())逻辑说明indexFalse是防止把 DataFrame 索引也写进 CSV多一列会导致提交格式错误。value_counts()检查预测分布如果幸存比例和训练集差太多训练集约 38%说明模型可能有偏。参数上to_csv默认 UTF-8 编码平台一般都能识别。写完之后别急着提交先做两件事。第一用submission.head()和submission.shape确认行数是 418、列数是 2。第二把整个流程从头到尾再跑一遍确认没有随机性导致的差异。我自己的习惯是把所有代码整理成一个.py文件从读数据到写提交文件一气呵成中间不依赖任何手动步骤。这样下次拿到新数据改一下文件名和字段映射就能复用。还有一个容易被忽略的点特征重要性。随机森林和梯度提升都能输出feature_importances_花两分钟看一眼你会发现 Title、Sex、Fare、Age 排在前列而 PassengerId 这种字段重要性接近零。如果某个你精心构造的特征重要性极低要么是构造方式有问题要么是这个数据集里它确实没用别硬塞。import matplotlib.pyplot as plt importances pd.Series(gb.feature_importances_, indexfeatures) importances.sort_values().plot(kindbarh, figsize(8, 6)) plt.title(Feature Importances) plt.tight_layout() plt.savefig(feature_importance.png, dpi150)逻辑说明sort_values()让条形图从低到高排列barh是水平条形图字段名长的时候比垂直柱状图好读。dpi150保证图片清晰度tight_layout防止标签被裁掉。这张图不是给别人看的是给你自己看的——下次做新数据集时你会知道哪些类型的特征值得花时间构造。最后说个我踩过的坑有次我把AgeBin用pd.cut分箱后忘了转成整数pandas 返回的是 category 类型sklearn 直接报错。解决办法是加.astype(int)或者用labelsFalse参数。这种错误不涉及算法但能卡你半小时。希望帮到你。本文还有配套的精品资源点击获取