☰
红酒数据集分析全流程:从数据清洗到模型解释的避坑指南
2026/10/13 23:21:45 网站建设 项目流程

简介:面向数据分析初学者与统计建模学习者,这份以经典红酒品质评分为对象的数据分析大作业,完整演示了从数据预处理、相关性分析、多元线性回归到主成分回归与KNN分类的典型分析流程。内容覆盖11项物理属性与评分的关系,通过Spearman相关分析识别关键变量,给出回归方程与预测区间,并对数据降维和主成分解释做了清晰说明。资源共1个docx文件,约202KB,属纯文档型资料,适合用于课程设计、期末大作业参考或自学统计建模方法。目前已有4265人学习下载。通过文档可掌握红酒数据集的分析思路、R语言与Excel的配合用法,以及从模型构建到结果解读的完整报告写法,对完成类似数据分析任务有直接借鉴价值。

1. 红酒数据集的大作业,真正卡人的是“分析深度”

很多人拿到“数据分析大作业-红酒数据集的分析”这个题目,第一反应是找个 Notebook 跑一遍回归、画几张热力图,然后发现结论翻来覆去就是“酒精度越高,评分越高”,交上去拿个及格分,回头还不知道哪里不够。这恰好是这类数据集最典型的陷阱:它太规整、太少脏数据,看起来谁都能做,但作业的好差差距几乎全部体现在你有没有把每一步做成“有依据的决定”,而不是默认参数跑到底。

这个数据集我做过不止一次,带过的人里翻车最多的不是不会调包,而是三个问题:不知道 quality 是序数而不是连续数、不知道相关性分析和建模结论根本是两回事、不知道报告里每个结论都应该能说出“我怎么验证过”。这篇文章就按一个完整可交付的分析流程来写:读入清洗、探索性分析、建模找关键变量、避坑、以及最后怎么把结论写扎实。适合做大作业的学生,也适合刚入职需要交分析报告的分析师。读完你能直接照着一套流程复现,并且知道每一步为什么这么走。

2. 读入与清洗红酒数据:三个步骤决定分析下限

2.1 读入数据:分号分隔符和数据类型检查

公开渠道能下载到的红酒数据集通常是 CSV 格式,但它的分隔符不是逗号而是分号,这一点是第一个翻车点。我见过有人用 Excel 打开直接双击,结果整份数据堆在第一列里,还以为是文件坏了。

import pandas as pd df = pd.read_csv("winequality-red.csv", sep=";") print(df.shape) # (1599, 12) print(df.info()) print(df.head(3))

sep=";" 必须显式指定,漏了它或者误写成 sep=",",read_csv 会认为整个文件只有一列,后面所有字段名都取不到。df.shape 返回 (1599, 12),意思是 1599 条红葡萄酒样本,12 个字段:固定酸度、挥发性酸度、柠檬酸、残糖、氯化物、游离二氧化硫、总二氧化硫、密度、pH、硫酸盐、酒精度,以及最后的质量评分 quality。df.info() 看每一列的非空计数和数据类型,df.head(3) 快速扫一眼数值范围,能不能对上常识,比如 pH 在 3 左右、酒精度在 9 到 14 之间,这一步不能省。

读入这一步没有太多参数好调,唯一要养成的习惯是读完立刻看一眼 shape 和 info。后续所有清洗和建模都建立在“字段名没读错、行数没少”的假设上,默认 pandas 读进来的结构是对的,后面出问题会非常难查。

2.2 缺失值、重复行与类型转换:清洗的三板斧

红酒数据集在绝大多数公开版本里没有缺失值,但重复行是真实存在的。有人统计过,1599 行里大约有 240 行重复,这个比例不小。如果不处理,后面做训练集和测试集划分时,重复样本会同时出现在两边,模型的评估指标会虚高,这是典型的“自我感动式准确率”。

# 缺失值检查:正常情况下每列都是 0 print(df.isnull().sum()) # 重复行检查与删除 print("重复行数:", df.duplicated().sum()) df = df.drop_duplicates().reset_index(drop=True) print("删除后行数:", df.shape[0]) # 类型转换:quality 是评分,转成 int df["quality"] = df["quality"].astype(int)

duplicated() 默认按整行所有列完全一致来判断重复,只要有一个字段不同就不算重复。drop_duplicates() 默认保留第一条出现的记录,reset_index(drop=True) 是为了把删行后乱掉的索引重新排成 0 到 N-1,避免后面按索引切片时出现空洞。quality 转成 int 是因为它本质是品酒师打的整数分,虽然 CSV 里读进来可能是 int64,但你无法保证每个版本的文件都是这样,显式转一次是低成本高收益的习惯。

清洗还有一个容易被忽略的动作:用 describe() 看每列的 min 和 max,确认没有离谱的异常值。

print(df.describe().T)

比如残糖的 max 如果高达十几,先别急着当异常值删掉,这可能是某种半干型或甜型葡萄酒,它本来就是这个量级。红酒数据集的分析里,残糖和酒精度天然就有较宽的合法范围,清洗时最忌讳“数值大就是脏数据”。

2.3 离群值处理:能不动就不动,动了要记账

我见过两种极端处理方式:一种是完全不管离群值,另一种是用 IQR 规则把超过上下界 1.5 倍四分位距的行全部删掉。前者在建模时会被个别极端样本带偏,后者会把整整一个类别的样本误伤。

# 以 volatile acidity 为例,计算 IQR Q1 = df["volatile acidity"].quantile(0.25) Q3 = df["volatile acidity"].quantile(0.75) IQR = Q3 - Q1 outlier_mask = (df["volatile acidity"] < Q1 - 1.5 * IQR) | (df["volatile acidity"] > Q3 + 1.5 * IQR) print("离群数量:", outlier_mask.sum())

正确做法是:先算离群数量,再单独拉出这些样本看它们的 quality 分布。如果离群样本里高分酒占比不低,说明这个“离群”其实是某种真实存在的风格,删掉反而丢失信息。红酒数据集总共就 1599 条,删掉 5% 就是 80 条,对训练集的影响不小。我的习惯是:只有同时满足“数值明显违背理化常识”和“样本量占比小于 2%”两个条件才删,否则留着重采样或靠模型鲁棒性吸收。这一步做完,把删除的行数和理由写在分析报告的数据预处理部分,能让作业上一个档次。

3. 探索性分析:好酒差酒在指标上差在哪

3.1 单变量分布:quality 是序数,不是连续数

探索性分析的第一步是看每个变量的分布,尤其是 quality 的分布。常见做法是画直方图,但直方图有个隐患:它是按数值区间分箱,如果把 3 到 8 分的整数评分当成连续变量画图,会让人误以为 4.5 分这个中间值也是有意义的。

import matplotlib.pyplot as plt import seaborn as sns sns.histplot(df["quality"], bins=10, kde=False, discrete=True) plt.title("Quality Distribution") plt.show()

discrete=True 是 seaborn 专门为整数型离散变量设计的参数,它保证每个整数分单独占一个箱体,不会出现某个箱体横跨两个分数的情况。从分布上你能看到 5 分和 6 分占了大头,3 分和 8 分是少数,这说明数据本身是不均衡的。这个不均衡直接决定了后面建模时用回归还是分类、用什么指标评估,所以探分布不是走流程,是给后续决策提供依据。

其他指标,比如 alcohol、pH、sulphates,用 histplot 画连续分布,同时叠加 KDE 曲线,能看出是否偏态。偏态明显的变量在建模前考虑做对数变换或分位数变换。注意这里的目的是“发现”,不是“处理”,先记录下来,清洗阶段有没有必要动,要等看完变量间关系再决定。

3.2 组间差异检验:好酒和差酒到底差在哪

光看分布只能知道每个变量长什么样,回答不了“好酒和差酒在指标上有没有显著差异”这个问题。大作业里常见的做法是把 quality >= 7 定义为好酒、quality <= 5 定义为差酒、6 分作为中间档,然后对每个指标做两组间的显著性检验。

from scipy.stats import mannwhitneyu good = df[df["quality"] >= 7] bad = df[df["quality"] <= 5] test_cols = ["alcohol", "volatile acidity", "sulphates", "citric acid", "pH", "density"] results = [] for col in test_cols: stat, p = mannwhitneyu(good[col], bad[col], alternative="two-sided") results.append({"指标": col, "好酒均值": good[col].mean(), "差酒均值": bad[col].mean(), "p值": p}) results = pd.DataFrame(results).sort_values("p值") print(results)

为什么用 Mann-Whitney U 检验而不是 t 检验?因为好酒和差酒的样本量差很多,而且很多理化指标的分布并不满足正态性假设。Mann-Whitney U 是非参数检验,不要求正态分布,只要求两组样本独立,对这类分组数据更稳妥。输出结果里 p 值小于 0.05 的指标就是两组差异显著的关键候选。

根据我跑这个数据集的经验,alcohol、volatile acidity、sulphates 这几项的 p 值通常会非常小,说明好酒和差酒在这些指标上的差异是统计上站得住的。这个结果表可以直接放进报告里,比单纯画几个箱线图更有说服力,因为检验给出了显著性水平。

3.3 相关性矩阵:把线性相关和因果关系分开看

相关性热力图是红酒数据集分析里出镜率最高的一张图,也是最容易被过度解读的一张图。

corr = df.corr(numeric_only=True) plt.figure(figsize=(10, 8)) sns.heatmap(corr, annot=True, fmt=".2f", cmap="RdBu_r", vmin=-1, vmax=1, center=0) plt.show()

df.corr() 默认算 Pearson 相关系数,只捕捉线性关系。fmt=".2f" 控制热力图格子里的数字只显示两位小数,不然整张图密密麻麻全是长数字。vmin 和 vmax 固定色标范围,center=0 让正负相关用颜色深浅对称区分。

看这张图时有两个纪律:第一,quality 与任何指标的相关系数绝对值大概率在 0.2 到 0.5 之间,个别能到 0.6,不要期待看到 0.9 这种数字,那在真实数据里几乎不存在,看到反而要怀疑数据有问题;第二,相关系数小不等于没关系,它只说明“线性关系弱”。quality 和 alcohol 的相关系数大概在 0.47 左右,虽然不算高,但它依然是所有指标里与 quality 线性关系最强的之一。相关性分析的任务是“排序和筛选”,不是“下结论”。哪些指标值得进模型,要交给下一章的建模来判断。

4. 建模找关键变量:从回归系数到特征重要性

4.1 线性回归:用系数和显著性解释指标方向

探索性分析找出了差异显著的指标,接下来要量化“影响有多大”。线性回归是这一步最直接的工具,它的优势在于系数可以直接解释:某个指标每增加一个单位,酒质量评分平均变化多少分。

import statsmodels.formula.api as smf formula = 'quality ~ Q("alcohol") + Q("volatile acidity") + Q("sulphates") + Q("citric acid") + Q("pH") + Q("density")' model = smf.ols(formula, data=df).fit() print(model.summary())

statsmodels 的公式接口用的是 patsy 语法,字段名里带空格时必须用 Q("字段名") 包起来,否则解析会报错。这是用 statsmodels 分析这个数据集最常见的报错点,比模型本身还容易卡住人。summary() 输出的表格里重点看三列:coef 是系数,P>|t| 是显著性,以及 R-squared 整体拟合度。

红酒数据集上这个回归的 R-squared 一般在 0.3 到 0.4 之间,这是正常水平,别指望它能解释 80% 的方差,品酒师评分里有大量主观因素,不是纯理化指标能完全预测的。系数方面,alcohol 的系数通常是正的且显著,volatile acidity 的系数是负的且显著,这符合酿酒常识:挥发酸过高说明酒有醋味,质量分自然低。这里要特别提醒:系数只能描述“相关性方向”,不能直接写“降低挥发酸就能提高评分”,因为这是观察性数据,没有控制所有混淆变量。

4.2 随机森林:捕捉线性模型看不到的模式

线性回归假设指标和评分之间是直线关系,但真实关系可能是非线性的,比如酒精度在 10 度以下时对评分影响不大,超过 11 度后影响变陡。随机森林这类树模型能自动捕捉这种阈值效应。

from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split X = df.drop(columns=["quality"]) y = df["quality"] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) rf = RandomForestRegressor( n_estimators=400, max_depth=6, min_samples_leaf=5, random_state=42, ) rf.fit(X_train, y_train) print("R2:", rf.score(X_test, y_test))

这里的参数都不是随便填的。n_estimators=400 是树的棵数,不是越大越好,400 棵在这个数据量下已经能稳定收敛;max_depth=6 限制每棵树深度,防止它记住单一样本;min_samples_leaf=5 要求叶子节点至少 5 个样本,进一步平滑预测。随机森林在这个数据集上的 R-squared 通常比线性回归高一些,能到 0.5 左右,高出来的部分就来自非线性关系。

树模型不做特征缩放也能工作,所以不需要对 X 做标准化。这也是为什么我在流水线里把标准化这步放在建模决策之后,而不是清洗之后:先决定用什么模型,再决定要不要标准化。

4.3 特征重要性:用排序结果解释模型

随机森林训练完成后,需要回答“哪些变量对评分的预测贡献最大”。sklearn 树模型自带 feature_importances_ 属性,但它有一个已知弱点:对高基数特征有偏向,而且只看单变量的分裂收益,不考虑变量间的交互。更稳的做法是置换重要性,它的原理是:把测试集某一列的顺序随机打乱,然后看模型误差增加多少,增加越多说明这列越重要。

from sklearn.inspection import permutation_importance result = permutation_importance(rf, X_test, y_test, n_repeats=10, random_state=42) importance_df = pd.DataFrame({ "特征": X.columns, "重要性": result.importances_mean, "标准差": result.importances_std, }).sort_values("重要性", ascending=False) print(importance_df)

n_repeats=10 表示每列打乱 10 次取平均,减少随机波动;importance_std 是 10 次结果的标准差,如果标准差比均值还大,说明这个重要性估计不稳定,解释时要谨慎。置换重要性比 feature_importances_ 更接近“真实预测贡献”,因为它是直接在模型预测误差上做实验。

根据常见结果,alcohol、volatile acidity、sulphates 通常排在前列,和前面组间差异检验的结论互相印证。到这里,分析已经从“相关”推进到了“预测贡献”,逻辑链条是完整的:先看分布、再检验差异、再建模型、最后排序重要性。这四步分别回答了“是什么、是否有差异、影响多大、哪个更重要”。

4.4 用 SHAP 看单个样本的预测贡献

置换重要性给出的是全局排序,但作业里如果能再进一步,展示单个样本的预测过程中哪些变量起了正向作用、哪些起了负向作用,分析的深度就明显不一样了。SHAP 是这一步最顺手的工具,它为每个样本的每个特征分配一个贡献值,这些贡献值加起来等于模型预测相对基线的偏移。

import shap explainer = shap.TreeExplainer(rf) shap_values = explainer.shap_values(X_test) shap.summary_plot(shap_values, X_test)

shap.summary_plot 输出的点阵图里,每个点代表一个样本,横坐标是 SHAP 值,正值表示把评分预测推高,负值表示拉低,颜色深浅表示特征值的大小。从这张图能直观看出:alcohol 的值越大,SHAP 值越往正方向走,而 volatile acidity 的值越大,SHAP 值越往负方向走。

需要强调的是,SHAP 解释的是模型行为,不是真实世界的因果。它能告诉你“在这个模型看来,某样本因为酒精度高所以预测评分高”,不能告诉你“酿酒时提高酒精度就能提高评分”。这个边界在报告里写清楚,反而显得分析严谨。如果环境里没装 shap,可以用上面的 permutation importance 替代,结论方向基本一致,只是少了单样本的粒度。

5. 红酒数据集分析避坑指南:五个最容易丢分的地方

5.1 把 quality 当连续变量:预测出 5.43 分怎么解释

现象:线性回归训练完成后,模型对测试集样本预测出 5.43、6.12 这类带小数点的评分,报告里不知道怎么写。

原因:quality 是品酒师打的整数分,在实际采集时不存在 5.43 分这个真实标签。回归模型本身不在乎这一点,它只做数值拟合,输出自然可以是小数,但这个数字无法对照真实含义。

解决:回归模型只用来解释方向和系数,不做精确预测;如果要做预测解读,把任务转成分类,比如 quality >= 7 是好酒、<= 5 是差酒,用分类模型输出好酒概率。概率比小数分好解释得多,也更贴合业务语言。

5.2 相关系数太低就认为没东西可分析

现象:热力图画出来,所有指标和 quality 的相关系数都不到 0.5,于是在报告里写“各指标与质量相关性较弱,无显著发现”。

原因:Pearson 相关系数只看线性关系,而指标对质量的影响可能是非线性的,也可能是和其他指标交互后起作用。单看线性相关,会漏掉大量信号。

解决:用随机森林的置换重要性或 SHAP 看非线性贡献,把“相关性弱”和“预测贡献小”分开写。很多时候 alcohol 的线性相关只有 0.4 出头,但在树模型里是最重要的变量,这就是非线性阈值效应造成的。

5.3 先标准化再回归,系数变得没法解释

现象:对全部特征做标准化后跑线性回归,系数变成“每增加一个标准差,评分变化多少”,写报告时发现没法向非技术读者解释。

原因:标准化改变了变量的单位,回归系数也跟着变成标准差的倍数。这对树模型没影响,但对线性回归的解释性影响很大,会让结论变得绕。

解决:回归模型用原始单位建模,系数直接写成“volatile acidity 每升高 0.1 个单位,评分平均下降约 X 分”。只在做正则化回归或某些需要统一量纲的算法时才做标准化,而且要先把系数翻译回原始单位再写结论。

5.4 忽略多重共线性,回归系数符号和常识相反

现象:线性回归输出里,某个酸度指标的系数是正的,但单看相关性它和 quality 是负相关,整个结论自相矛盾。

原因:fixed acidity、citric acid、density 这几个变量之间相关性很高,同时放进回归模型时,系数的符号和大小会被多重共线性扰乱,系数的方向不再单独代表“保持其他变量不变时该变量的作用”。

解决:建模前算一下 VIF,经验规则是 VIF 大于 10 的变量需要处理,要么删掉冗余的,要么只保留其中一个代表。这个数据集里常见的做法是保留 pH 和 volatile acidity,去掉与它们高度纠缠的 fixed acidity 和 density,模型系数会稳定很多。

5.5 把相关性直接写成因果,整段结论被主审质疑

现象:报告里写“提高酒精度可以提升红酒质量”,老师或评审问一句“这是你做了控制实验得出的结论?”

原因:观察性数据只能证明相关,酒精度高的酒可能同时来自更成熟的葡萄、更好的酿造工艺,评分高是多种因素共同作用的结果,不是单纯酒精度的功劳。

解决:全文用“与……相关”“对评分有显著预测贡献”这类表述,明确说明“本研究基于观察数据,不能推断因果”。这不丢分,反而是很多人拿不到高分的原因被堵上了。

6. 让报告深一个档次:用典型样本把结论讲透

建模和重要性排序做完,报告到这一步可以交了,但分数要想再上一档,缺的是“活生生的例子”。线性回归系数说 volatile acidity 高会拉低评分,这是抽象的;找一个具体的测试集样本,展示它的各项指标、模型预测分、以及每个特征对预测分贡献了多少,读者一下子就能理解模型在做什么。这就是 SHAP 单样本解释的用武之地。

sample_idx = 0 sample = X_test.iloc[[sample_idx]] shap_value = explainer.shap_values(sample)[0] shap.force_plot( explainer.expected_value, shap_value, sample, matplotlib=True, figsize=(20, 3), )

force_plot 输出一条瀑布式的横条,base value 是模型在测试集上的平均预测,红色块把预测往上推、蓝色块往下拉。从这张图能直接读出:这个样本被预测为 6 分,主要是 alcohol 贡献了正向作用、volatile acidity 贡献了负向作用。报告里把这张图放进去,配两句话说明“好酒和差酒的分歧点在哪些指标上”,分析的颗粒度就从“全体样本”细化到了“单个样本”。

我自己的习惯是选两个极端样本,一个预测高分、一个预测低分,并排展示,然后写一段对比分析:高分样本酒精度高、挥发酸低、硫酸盐高;低分样本正好反过来。这种写法比单列十个指标的均值更让人记得住,答辩时被提问的概率也低。

这个数据集我前前后后做了五六遍,最深的体会是:它真正的难点从来不是跑通代码,而是每一步都问一句“这个结论我拿什么验证”。数据清洗时问有没有重复行,探索分析时问差异是否统计显著,建模后问变量重要性是否在置换检验下稳定,写报告时问因果和相关的边界有没有守住。每问一句,报告就扎实一分。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询