数据清洗踩坑实录:用Python画年龄分布直方图前,你必须处理的缺失值问题(以泰坦尼克号数据为例)

张开发
2026/6/5 2:53:24 15 分钟阅读
数据清洗踩坑实录:用Python画年龄分布直方图前,你必须处理的缺失值问题(以泰坦尼克号数据为例)
数据清洗实战从泰坦尼克号年龄缺失值处理到高级可视化技巧第一次用Python分析泰坦尼克号数据集时我兴冲冲地写下了plt.hist(df[Age])结果屏幕上跳出了一串红色错误——原来数据里藏着不少缺失值。这个看似简单的直方图绘制实际上需要经历完整的数据清洗流程。本文将带你从数据清洗的坑里爬出来一路升级到核密度曲线和分组可视化的高级技巧。1. 数据清洗直方图绘制前的必修课1.1 缺失值检测与处理打开泰坦尼克号数据集后别急着画图。先用这行代码检查年龄列的完整性import pandas as pd df pd.read_csv(titanic.csv) print(df[Age].isnull().sum())你会看到类似这样的输出177这意味着有177条年龄记录缺失约占全部数据的20%。直接绘制直方图会导致错误或扭曲的分布。我们有几种处理方案简单删除法适合缺失比例小的情况clean_df df.dropna(subset[Age])均值/中位数填充保持数据量但可能引入偏差median_age df[Age].median() df[Age].fillna(median_age, inplaceTrue)高级填充法基于其他特征预测年龄如用姓名中的称呼推断提示删除前先评估缺失机制是否随机非随机缺失需要特殊处理1.2 异常值检测与处理清洗缺失值后还要检查异常值print(df[Age].describe())重点关注最小值是否合理如年龄不可能为负最大值是否超出合理范围如150岁四分位距IQR外的潜在异常点处理异常值的常用方法方法代码示例适用场景IQR过滤Q1 df[Age].quantile(0.25)Q3 df[Age].quantile(0.75)df df[~((df[Age] (Q1-1.5*IQR))(df[Age] (Q31.5*IQR)))]标准差过滤df df[np.abs(df[Age]-df[Age].mean()) 3*df[Age].std()]近似正态分布人工修正df.loc[df[Age]100, Age] 100已知明确上限2. 基础可视化从直方图到核密度曲线2.1 Matplotlib基础绘制处理完脏数据后终于可以绘制基础直方图了import matplotlib.pyplot as plt plt.figure(figsize(10,6)) plt.hist(clean_df[Age], bins30, colorskyblue, edgecolorblack) plt.title(乘客年龄分布, fontsize15) plt.xlabel(年龄, fontsize12) plt.ylabel(频数, fontsize12) plt.grid(axisy, alpha0.3) plt.show()关键参数解析bins决定分组粗细太少会掩盖细节太多会出现锯齿range限定显示范围如range(0,100)densityTrue时显示概率密度而非频数2.2 进阶核密度估计直方图受分组影响大核密度曲线KDE能提供更平滑的分布估计from scipy.stats import gaussian_kde kde gaussian_kde(clean_df[Age].dropna()) x_vals np.linspace(clean_df[Age].min(), clean_df[Age].max(), 1000) kde_vals kde(x_vals) plt.figure(figsize(10,6)) plt.hist(clean_df[Age], bins30, densityTrue, colorlightblue, edgecolorblack, alpha0.7) plt.plot(x_vals, kde_vals, r-, linewidth2) plt.title(年龄分布直方图与核密度曲线, fontsize15) plt.xlabel(年龄, fontsize12) plt.ylabel(密度, fontsize12) plt.legend([核密度曲线, 直方图]) plt.show()3. 高级技巧分组对比可视化3.1 性别分组分析使用Seaborn可以轻松实现分组可视化import seaborn as sns plt.figure(figsize(12,6)) sns.histplot(dataclean_df, xAge, hueSex, elementstep, statdensity, common_normFalse, palette[pink,lightblue]) plt.title(按性别分组的年龄分布, fontsize15) plt.xlabel(年龄, fontsize12) plt.ylabel(密度, fontsize12) plt.show()3.2 舱位等级分组分析plt.figure(figsize(12,6)) sns.kdeplot(dataclean_df, xAge, huePclass, fillTrue, paletteviridis, alpha0.3) plt.title(按舱位等级分组的年龄分布, fontsize15) plt.xlabel(年龄, fontsize12) plt.ylabel(密度, fontsize12) plt.show()4. 实战完整分析流程4.1 数据探索与清洗完整的分析应该从数据探索开始# 探索性分析 print(df.info()) print(df[[Age,Fare]].describe()) # 可视化缺失情况 import missingno as msno msno.matrix(df) plt.show() # 多维度填充缺失年龄 title_mapping {Mr: 30, Miss: 22, Mrs: 35, Master: 5} df[Title] df[Name].str.extract( ([A-Za-z])\., expandFalse) df[Age] df.apply(lambda row: title_mapping[row[Title]] if pd.isnull(row[Age]) else row[Age], axis1)4.2 交互式可视化使用Plotly创建交互式图表import plotly.express as px fig px.histogram(clean_df, xAge, colorSex, marginalrug, hover_dataclean_df.columns, title乘客年龄分布) fig.update_layout(barmodeoverlay) fig.update_traces(opacity0.75) fig.show()4.3 分布特征量化除了可视化还需要量化描述分布特征from scipy import stats print(f偏度: {stats.skew(clean_df[Age]):.2f}) print(f峰度: {stats.kurtosis(clean_df[Age]):.2f}) print(f正态检验p值: {stats.normaltest(clean_df[Age]).pvalue:.4f}) # 分位数计算 quantiles clean_df[Age].quantile([0.25, 0.5, 0.75]) print(f25分位数: {quantiles[0.25]:.1f}) print(f中位数: {quantiles[0.5]:.1f}) print(f75分位数: {quantiles[0.75]:.1f})5. 专业级可视化技巧5.1 多图组合展示fig, axes plt.subplots(2, 2, figsize(15,12)) # 总体分布 sns.histplot(dataclean_df, xAge, kdeTrue, axaxes[0,0]) axes[0,0].set_title(总体年龄分布) # 按性别分布 sns.violinplot(dataclean_df, xSex, yAge, axaxes[0,1]) axes[0,1].set_title(性别年龄分布) # 按舱位分布 sns.boxplot(dataclean_df, xPclass, yAge, axaxes[1,0]) axes[1,0].set_title(舱位年龄分布) # 生存率与年龄 sns.histplot(dataclean_df, xAge, hueSurvived, elementstep, statdensity, common_normFalse, palette{0:red, 1:green}, axaxes[1,1]) axes[1,1].set_title(生存/遇难年龄分布) plt.tight_layout() plt.show()5.2 动态可视化使用IPython交互功能创建动态效果from ipywidgets import interact interact(bins(5,50,5), bandwidth(0.1,2,0.1)) def update_plot(bins20, bandwidth0.5): plt.figure(figsize(10,6)) sns.histplot(dataclean_df, xAge, binsbins, kdeTrue, kde_kws{bw_method:bandwidth}) plt.title(f年龄分布 (bins{bins}, bandwidth{bandwidth})) plt.show()5.3 高级样式定制plt.style.use(seaborn-talk) fig plt.figure(figsize(14,8)) gs fig.add_gridspec(2, 2, hspace0.3, wspace0.2) ax1 fig.add_subplot(gs[0, :]) ax2 fig.add_subplot(gs[1, 0]) ax3 fig.add_subplot(gs[1, 1]) # 主图 sns.histplot(dataclean_df, xAge, hueSurvived, elementstep, statdensity, common_normFalse, palette{0:#ff6361, 1:#58508d}, axax1) ax1.set_title(生存/遇难年龄分布对比, pad20) # 子图1 sns.boxplot(dataclean_df, xPclass, yAge, paletteBlues, axax2) ax2.set_title(舱位等级年龄分布) # 子图2 sns.kdeplot(dataclean_df, xAge, hueSex, fillTrue, paletteReds, axax3) ax3.set_title(性别年龄分布) plt.suptitle(泰坦尼克号乘客年龄多维度分析, y1.02, fontsize16) plt.tight_layout() plt.show()6. 实际项目中的经验分享在金融风控项目中处理客户年龄数据时我发现直接删除缺失值会导致样本偏差——年轻客户更可能不填写年龄。后来改用基于其他特征的预测填充法模型效果提升了8%。这让我明白数据清洗不是简单的技术操作而是需要结合业务理解的决策过程。几个实用建议保存清洗前后的数据版本方便回溯对关键变量做敏感性分析比较不同处理方式的影响在报告中标明数据清洗方法保证结果可复现处理泰坦尼克号数据时最有意思的发现是头等舱乘客的平均年龄比三等舱大12岁但生存率却高出40%。这种交叉分析的价值远超过单纯的年龄分布直方图。

更多文章