Python 数据分析与可视化 Day 9 - 缺失值与异常值处理技巧
✅ 今日目标
- 熟练处理数据中的缺失值(NaN、None)
- 学会识别和处理异常值(outliers)
- 掌握常用的处理方法:填充、删除、替换、标准差法、箱型图法等
- 为后续机器学习建模打好数据清洗基础
📚 一、缺失值处理(Missing Data)
1. 检查缺失值
df.isnull().sum() # 每列缺失值数量
df.info() # 查看字段类型和非空计数
2. 删除缺失值
df.dropna() # 删除包含缺失值的行
df.dropna(axis=1) # 删除包含缺失值的列
df.dropna(how="all") # 删除整行全为空
3. 填充缺失值(推荐)
df["成绩"].fillna(df["成绩"].mean(), inplace=True) # 均值填充
df["成绩"].fillna(method="ffill", inplace=True) # 前向填充
df["成绩"].fillna(method="bfill", inplace=True) # 后向填充
4. 替换无效数据为 NaN
import numpy as np
df["成绩"].replace("缺考", np.nan, inplace=True)
⚠️ 二、异常值检测与处理
1. 基于统计分布检测异常值(Z-Score)
score_mean = df["成绩"].mean()
score_std = df["成绩"].std()df["Z值"] = (df["成绩"] - score_mean) / score_std# 筛选 Z 分数绝对值大于 3 的异常点
outliers = df[abs(df["Z值"]) > 3]
print(outliers)
2. 使用箱型图(IQR)识别异常值
Q1 = df["成绩"].quantile(0.25)
Q3 = df["成绩"].quantile(0.75)
IQR = Q3 - Q1lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR# 判断异常值
outliers_iqr = df[(df["成绩"] < lower_bound) | (df["成绩"] > upper_bound)]
print(outliers_iqr)
3. 替换或删除异常值
# 方法一:直接删除
df_cleaned = df[(df["成绩"] >= lower_bound) & (df["成绩"] <= upper_bound)]# 方法二:用边界值替代异常值
df["成绩"] = df["成绩"].clip(lower_bound, upper_bound)
🧪 今日练习建议
-
对学生数据进行缺失值统计
-
使用多种方法填充缺失成绩
-
使用标准差(Z-Score)和 IQR 方法检测异常值
-
对异常值进行合理处理(删除 / 替换)
-
输出处理前后的对比数据统计信息
import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt import os# 设置中文支持(根据系统配置选择) plt.rcParams['font.family'] = 'Arial Unicode MS' # macOS # plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows plt.rcParams['axes.unicode_minus'] = False# 加载数据 data_path = "data/students_dirty.csv" if not os.path.exists(data_path):raise FileNotFoundError("❌ 缺少数据文件:students_dirty.csv,请确保数据文件存在于 data 目录下。")df = pd.read_csv(data_path, encoding="utf-8", header=0) print("✅ 原始数据预览:") print(df)# ------------------ 一、缺失值处理 ------------------print("\n🔍 缺失值统计:") print(df.isnull().sum())# 示例:将“缺考”字符串替换为 NaN df.replace({"缺考": np.nan}, inplace=True) df["成绩"] = pd.to_numeric(df["成绩"], errors="coerce")# 填充缺失值(用平均数填充) mean_score = df["成绩"].mean() df.fillna({"成绩": mean_score}, inplace=True) print(f"\n✅ 缺失值已填充为平均数:{mean_score:.2f}")# ------------------ 二、异常值检测(Z-Score) ------------------mean = df["成绩"].mean() std = df["成绩"].std() df["Z值"] = (df["成绩"] - mean) / std# 绝对值大于 3 视为异常 outliers_z = df[abs(df["Z值"]) > 3] print(f"\n📈 Z-Score 异常值数量:{len(outliers_z)}") print(outliers_z)# ------------------ 三、异常值检测(IQR) ------------------Q1 = df["成绩"].quantile(0.25) Q3 = df["成绩"].quantile(0.75) IQR = Q3 - Q1 lower = Q1 - 1.5 * IQR upper = Q3 + 1.5 * IQRoutliers_iqr = df[(df["成绩"] < lower) | (df["成绩"] > upper)] print(f"\n📊 IQR 异常值数量:{len(outliers_iqr)}") print(outliers_iqr)# ------------------ 四、异常值处理 ------------------# 方式一:删除异常值 df_filtered = df[(df["成绩"] >= lower) & (df["成绩"] <= upper)]# 方式二:clip 限制在边界范围内 df_clipped = df.copy() df_clipped["成绩"] = df_clipped["成绩"].clip(lower, upper)# ------------------ 五、可视化箱型图 ------------------sns.boxplot(df["成绩"]) plt.title("成绩箱型图") plt.tight_layout() os.makedirs("charts", exist_ok=True) plt.savefig("charts/成绩箱型图_异常值检测.png") plt.close() print("📊 箱型图已保存至 charts/成绩箱型图_异常值检测.png")# ------------------ 六、保存结果 ------------------df_filtered.to_csv("data/students_no_outliers.csv", index=False) df_clipped.to_csv("data/students_clipped.csv", index=False)print("\n✅ 异常值处理后的数据已保存:") print("✔️ 删除异常值版本:data/students_no_outliers.csv") print("✔️ 边界裁剪版本:data/students_clipped.csv")
成绩箱型图_异常值检测:
将异常值裁剪为上下限后的数据:
删除异常值后的数据:
🧾 今日总结
- 缺失值和异常值是数据清洗中的核心问题
- 多种处理方法需视场景选择,避免信息损失或过度拟合
- 数据分布图与 Z 分数 / IQR 辅助判断效果最佳
- 清洗结果直接影响后续的建模质量