在數據分析與建模流程中,缺失值處理是數據預處理階段的關鍵步驟,直接影響后續模型的準確性與穩定性。本文以紅酒數據集為研究對象,詳細介紹如何通過基礎統計方法(均值、中位數、眾數)、完整案例分析(CCA)及機器學習算法(線性回歸、隨機森林)實現缺失值填充,并提供完整代碼實現與結果保存方案,幫助讀者系統化掌握缺失值處理邏輯。
一、項目背景與數據概覽
1.1 數據集介紹
本項目使用的 “紅酒.csv” 數據集包含紅酒的多項理化指標(如酒精含量、酸度、糖分等)、礦物類型標簽及最終質量評分,目標是通過預處理后的數據構建后續質量預測模型。數據中存在部分缺失值,需先進行填充處理。
1.2 核心需求
- 查看數據基本結構(前 10 行、后 10 行),了解數據分布;
- 采用 5 種不同方法處理缺失值,確保數據完整性;
- 拆分訓練集與測試集,按 “訓練集規則填充測試集”(避免數據泄露);
- 保存處理后的數據集,為后續建模提供輸入。
二、項目環境與依賴庫
首先需安裝并導入以下 Python 庫,核心用于數據讀取、缺失值處理與模型構建:
import pandas as pd # 數據處理核心庫
from sklearn.model_selection import train_test_split # 數據集拆分
from sklearn.linear_model import LinearRegression # 線性回歸模型
from sklearn.ensemble import RandomForestRegressor # 隨機森林回歸模型
三、數據預處理核心流程
3.1 數據讀取與初步探索
第一步讀取數據并查看基本結構,同時統計缺失值總量,明確后續處理目標:
# 讀取紅酒數據(根據編碼選擇gbk或utf-8)
data = pd.read_csv('紅酒.csv', encoding='gbk')# 查看數據前10行與后10行,了解數據結構
print("===== 數據前10行 =====")
print(data.head(10))
print("\n===== 數據后10行 =====")
print(data.tail(10))# 統計每列缺失值數量
null_total = data.isnull().sum()
print("\n===== 各列缺失值數量 =====")
print(null_total)# 拆分特征與標簽(質量評分為目標變量,礦物類型為類別特征)
x_whole = data.drop(['質量評分'], axis=1) # 所有特征(含礦物類型)
y_whole = data['質量評分'] # 目標變量:質量評分# 拆分訓練集(75%)與測試集(25%),固定隨機種子確保結果可復現
x_train, x_test, y_train, y_test = train_test_split(x_whole, y_whole, test_size=0.25, random_state=42
)
3.2 缺失值處理方法詳解
為適配不同數據分布場景,本文實現 5 種缺失值處理方法,核心邏輯是 **“按礦物類型分組填充”**(同類紅酒理化指標更相似,填充更精準),且嚴格遵循 “用訓練集規則填充測試集” 原則,避免數據泄露。
所有方法封裝在fill_data.py
文件中,下文分模塊解析核心邏輯。
方法 1:完整案例分析(CCA)—— 直接刪除缺失值行
適用于缺失值占比極低的場景,直接保留無缺失值的完整樣本,優點是簡單無偏差,缺點是會損失數據量。
def cca_train_fill(train_data, train_label):"""訓練集CCA填充:刪除含缺失值的行"""# 合并特征與標簽data = pd.concat([train_data, train_label], axis=1).reset_index(drop=True)# 刪除含缺失值的行df_filled = data.dropna()# 返回填充后的特征與標簽return df_filled.drop('礦物類型', axis=1), df_filled['礦物類型']def cca_test_fill(train_data, train_label, test_data, test_label):"""測試集CCA填充:邏輯與訓練集一致,僅處理測試集"""data = pd.concat([test_data, test_label], axis=1).reset_index(drop=True)df_filled = data.dropna()return df_filled.drop('礦物類型', axis=1), df_filled['礦物類型']
方法 2-4:基礎統計填充(均值 / 中位數 / 眾數)
- 均值填充:適用于數據近似正態分布、無極端值的場景;
- 中位數填充:適用于數據含極端值(如異常高的酒精含量)的場景,抗干擾性更強;
- 眾數填充:適用于類別型或離散型特征(如某類礦物類型的紅酒酸度集中在某個值)。
以均值填充為例,核心代碼邏輯如下(中位數、眾數僅需替換mean()
為median()
/mode()
):
def mean_train_method(data):"""計算單組數據的均值,用于填充"""fill_values = data.mean()return data.fillna(fill_values)def mean_train_fill(train_data, train_label):"""訓練集均值填充:按礦物類型分組后填充"""data = pd.concat([train_data, train_label], axis=1).reset_index(drop=True)# 按礦物類型(0-3)分組groups = [data[data['礦物類型'] == i] for i in range(4)]# 每組單獨用均值填充filled_groups = [mean_train_method(group) for group in groups]# 合并分組數據df_filled = pd.concat(filled_groups).reset_index(drop=True)return df_filled.drop('礦物類型', axis=1), df_filled['礦物類型']def mean_test_fill(train_data, train_label, test_data, test_label):"""測試集均值填充:用訓練集的分組均值填充測試集"""# 合并訓練集與測試集train_all = pd.concat([train_data, train_label], axis=1).reset_index(drop=True)test_all = pd.concat([test_data, test_label], axis=1).reset_index(drop=True)# 按礦物類型分組,用訓練集分組均值填充測試集filled_test_groups = []for mineral_type in range(4):# 訓練集該類別的均值train_group = train_all[train_all['礦物類型'] == mineral_type]# 測試集該類別數據test_group = test_all[test_all['礦物類型'] == mineral_type]# 用訓練集均值填充測試集filled_test = test_group.fillna(train_group.mean())filled_test_groups.append(filled_test)df_filled = pd.concat(filled_test_groups).reset_index(drop=True)return df_filled.drop('礦物類型', axis=1), df_filled['礦物類型']
方法 5:機器學習填充(線性回歸 / 隨機森林)
適用于特征間存在明顯相關性的場景,通過構建預測模型,用其他特征預測缺失值,填充精度更高。核心邏輯是 **“按缺失值數量從小到大處理”**(先填充缺失少的特征,用已填充的特征預測缺失多的特征)。
以隨機森林填充為例,核心代碼如下:
def rf_train_fill(train_data, train_label):"""訓練集隨機森林填充:用其他特征預測缺失值"""# 合并特征與標簽data = pd.concat([train_data, train_label], axis=1).reset_index(drop=True)train_X = data.drop('礦物類型', axis=1)# 按缺失值數量從小到大排序,確定填充順序null_num = train_X.isnull().sum()null_sorted = null_num.sort_values(ascending=True)filling_features = [] # 存儲已處理的特征(用于構建模型輸入)for feat in null_sorted.index:filling_features.append(feat)# 僅處理含缺失值的特征if null_sorted[feat] == 0:continue# 構建模型:以當前特征為目標變量,其他已處理特征為輸入X = train_X[filling_features].drop(feat, axis=1) # 輸入特征y = train_X[feat] # 目標變量(待填充的特征)# 篩選非缺失值樣本作為訓練集,缺失值樣本作為測試集null_rows = train_X[train_X[feat].isnull()].index.tolist()X_train = X.drop(null_rows)y_train = y.drop(null_rows)X_test = X.iloc[null_rows]# 訓練隨機森林模型rf = RandomForestRegressor(n_estimators=100, random_state=42)rf.fit(X_train, y_train)# 預測缺失值并填充y_pred = rf.predict(X_test)train_X.loc[null_rows, feat] = y_predprint(f'完成訓練集「{feat}」列填充')return train_X, data['礦物類型']def rf_test_fill(train_data, train_label, test_data, test_label):"""測試集隨機森林填充:用訓練集訓練的模型邏輯填充測試集"""# 合并訓練集與測試集train_all = pd.concat([train_data, train_label], axis=1).reset_index(drop=True)test_all = pd.concat([test_data, test_label], axis=1).reset_index(drop=True)train_X = train_all.drop('礦物類型', axis=1)test_X = test_all.drop('礦物類型', axis=1)# 按測試集缺失值數量排序,用訓練集規則填充null_num = test_X.isnull().sum()null_sorted = null_num.sort_values(ascending=True)filling_features = []for feat in null_sorted.index:filling_features.append(feat)if null_sorted[feat] == 0:continue# 用訓練集的特征構建模型X_train = train_X[filling_features].drop(feat, axis=1)y_train = train_X[feat]X_test = test_X[filling_features].drop(feat, axis=1)# 篩選測試集缺失值行null_rows = test_X[test_X[feat].isnull()].index.tolist()X_test = X_test.iloc[null_rows]# 訓練模型并預測填充rf = RandomForestRegressor(n_estimators=100, random_state=42)rf.fit(X_train, y_train)y_pred = rf.predict(X_test)test_X.loc[null_rows, feat] = y_predprint(f'完成測試集「{feat}」列填充')return test_X, test_all['礦物類型']
3.3 數據保存與后續使用
填充完成后,需將訓練集與測試集合并為完整 DataFrame,并保存為 Excel 文件,方便后續建模使用。同時對訓練集進行隨機打亂,避免順序對模型訓練的影響:
# 選擇一種填充方法(以CCA為例,其他方法只需替換函數名)
x_train_fill, y_train_fill = fill_data.cca_train_fill(x_train, y_train)
x_test_fill, y_test_fill = fill_data.cca_test_fill(x_train, y_train, x_test, y_test)# 合并特征與標簽,訓練集隨機打亂
data_train = pd.concat([x_train_fill, y_train_fill], axis=1).sample(frac=1, random_state=4)
data_test = pd.concat([x_test_fill, y_test_fill], axis=1)# 保存到本地(需提前創建“數據”文件夾)
data_train.to_excel('./數據/訓練空值刪除填充.xlsx', index=False)
data_test.to_excel('./數據/測試空值刪除填充.xlsx', index=False)
print("數據保存完成!")
四、各方法對比與適用場景
不同缺失值處理方法各有優劣,需根據數據特點選擇,下表為核心對比:
處理方法 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
CCA(刪除行) | 簡單無偏差,不引入額外信息 | 損失數據量,樣本代表性下降 | 缺失值占比 < 5%,數據量充足 |
均值填充 | 計算簡單,保留數據量 | 受極端值影響大 | 數據近似正態分布,無極端值 |
中位數填充 | 抗極端值,穩定性強 | 未利用特征相關性 | 數據含極端值(如酒精含量異常值) |
眾數填充 | 適用于離散型 / 類別型特征 | 不適用于連續型特征 | 離散特征(如某類礦物類型的酸度) |
隨機森林填充 | 利用特征相關性,填充精度高 | 計算復雜,需調參 | 特征間相關性強,數據量中等 |
五、總結與后續優化方向
5.1 項目總結
本項目以紅酒數據集為載體,完整實現了從 “數據探索→缺失值處理→結果保存” 的預處理流程,核心亮點:
- 嚴格遵循 “訓練集規則填充測試集”,避免數據泄露;
- 按 “礦物類型分組填充”,貼合紅酒數據的業務邏輯;
- 覆蓋基礎到進階的 5 種方法,適配不同數據場景。
5.2 后續優化方向
- 填充效果評估:可通過 “插入人工缺失值” 的方式,對比不同方法的填充誤差(如 MAE、RMSE);
- 特征標準化:在機器學習填充前對特征進行標準化(如
StandardScaler
),提升模型預測精度; - 分類任務適配:若目標是 “紅酒質量分級”(如好 / 中 / 差),可將隨機森林回歸改為分類器(
RandomForestClassifier
)。
通過本文的方法,可快速完成紅酒數據集的缺失值處理,為后續的質量預測模型(如線性回歸、隨機森林、SVM)提供高質量的輸入數據。