当前位置: 首页 > news >正文

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)

🧪 今日练习建议

  1. 对学生数据进行缺失值统计

  2. 使用多种方法填充缺失成绩

  3. 使用标准差(Z-Score)和 IQR 方法检测异常值

  4. 对异常值进行合理处理(删除 / 替换)

  5. 输出处理前后的对比数据统计信息

    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 辅助判断效果最佳
  • 清洗结果直接影响后续的建模质量

相关文章:

  • MFC获取本机所有IP、局域网所有IP、本机和局域网可连接IP
  • 深度学习框架入门指南:PyTorch 核心实战
  • Discrete Audio Tokens: More Than a Survey
  • 修复opensuse 风滚草rabbitmq的Error: :plugins_dir_does_not_exist问题
  • 随机生成的乱码域名”常由**域名生成算法(DGA)** 产生
  • Flutter基础(UI监听)
  • MyBatis 缓存机制详解
  • Nestjs框架: nestjs-bull的使用与相关queue的规划
  • 【RAG面试题】LLMs已经具备了较强能力,存在哪些不足点?
  • day49-硬件学习之I2C(续)
  • TTvideo免费开源PC录屏软件
  • UE--Slate 焦点、捕获,输入处理与玩家控制器的关系
  • 【 MyBatis-Plus | 精讲 】
  • 1 Studying《Is Parallel Programming Hard》1-5
  • 【网络安全】密码学知识普及
  • leetcode.2014 重复k次的最长子序列
  • Unity 脚本自动添加头部注释
  • 不同信创系统如何集中远程运维?贝锐向日葵提供稳定方案
  • 科技如何影响我们的生活?
  • word中如何保存高清图片,并保存为高质量的pdf文件(图像不失真)