红酒数据集预处理实战:缺失值处理的 5 种打开方式,从入门到进阶一步到位
在数据分析与建模流程中,缺失值处理是数据预处理阶段的关键步骤,直接影响后续模型的准确性与稳定性。本文以红酒数据集为研究对象,详细介绍如何通过基础统计方法(均值、中位数、众数)、完整案例分析(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)提供高质量的输入数据。