? 今日目標
- 熟練處理數據中的缺失值(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 輔助判斷效果最佳
- 清洗結果直接影響后續的建模質量