UCI红酒数据集分析实战:从数据探索到随机森林建模与避坑指南
简介围绕红酒数据集的数据分析大作业完整方案面向高校数据分析课程设计或统计学习入门者。资料系统梳理了红酒评分与11项物理属性的关联性通过相关性分析筛选关键变量建立多元线性回归模型并引入主成分回归解决多重共线性问题最后使用KNN进行品质分类完整呈现从数据预处理到建模评估的流程。资源为单个docx文档约202KB内容完整包含数据变量定义、相关性热图解读、多元回归与主成分回归结果分析、KNN分类思路可直接作为课程报告参考或复现实验的脚本依据。目前已有4265人学习下载尤其适合需要快速掌握数据分析完整套路的学生使用也可作为课程设计模板或实验复现的参考。1. 红酒数据集分析大作业先想清楚这份作业到底在考什么一个常见的场景你拿到“数据分析大作业-红酒数据集的分析”这个题目看到数据集只有1600行、12列心中一喜以为跑个随机森林就能交差。结果报告被打回评语是“只有代码没有分析”。这不是代码跑错了而是把一份数据分析项目做成了调包练习。红酒数据集是公开数据集中少见的“业务语义完整”的样本每一行对应一款红酒的理化检测结果最后一个字段是品酒师给出的质量评分既能做分类任务也能做回归任务。这里按我实际批改和整改同类作业的经验从数据读取、探索性分析、建模到避坑给出一条可以直接复现的落地路径。正在赶课程大作业、准备毕设开题或者想用一份完整的数据分析案例充实简历的从业者都可以按本文步骤操作不需要额外找数据。2. 红酒数据集的底细字段语义、建模目标与两种选型的差异2.1 字段语义决定分析方向11个理化指标背后的业务直觉拿到数据先别急着import seaborn先回答一个问题每一列是什么、它的单位是什么、它在酿酒工艺里意味着什么。这份红酒数据集来自UCI的Wine Quality数据集红葡萄酒部分固定是1599行11个理化检测字段加上一个质量评分常见字段如下表字段含义对品质的业务直觉fixed acidity固定酸度酒石酸等不挥发性酸带来酸爽感volatile acidity挥发酸度乙酸等挥发酸高时出现醋味通常负相关citric acid柠檬酸天然防腐剂与风味调节适量时正相关residual sugar残糖发酵后剩余糖分甜型酒更高chlorides氯化物盐分含量过高影响口感free sulfur dioxide游离二氧化硫抗氧化与抑菌少量有益total sulfur dioxide总二氧化硫游离态与结合态之和density密度与糖分和酒精相关pHpH值酸度强弱的直接反映sulphates硫酸盐与发酵工艺相关通常与品质正相关alcohol酒精体积分数酒体与质感多数分析与它正相关quality质量评分3到8的整数后面建模的标签这张表的价值不是让你背字段而是让后续每个图、每个模型系数都有话可说。比如看到相关性热图里volatile acidity与quality负相关你可以解释为“挥发酸高意味着乙酸味明显品酒师给分低”看到alcohol与quality正相关可以说“酒精撑起酒体是品质感知的重要维度”。一段分析报告里如果每个结论都能回到这个表深度自然就有了。字段语义之外还要留意两个容易忽略的细节。第一这个数据集只有红葡萄酒部分1599行白葡萄酒是另外一份文件不要合在一起当成一个数据集用否则“颜色”这个本不存在的字段会变成模型里最厉害的特征但没有任何业务意义。第二数据采集自葡萄牙北部Vinho Verde产区的理化检测不是品酒师逐条打分的主观问卷所以它更适合回答“理化指标与质量评分的关系”而不是“某种葡萄品种好不好喝”。分析叙事如果顺着这个方向走评阅思路会清晰很多。2.2 分类还是回归同一个quality列两种完全不同的评估逻辑大作业最需要先定的不是模型而是任务类型。quality是3到8的整数表面看是回归问题但很多作业默认做成回归实际输出3.7这种分数评阅时不好讲另一种常见做法是把quality大于等于7定义为好酒转成二分类。两者没有绝对对错取决于你想讲的结论。任务类型标签定义主要评估指标适合的叙事方向回归quality原值R²、MAE、RMSE哪些指标推动分数上升或下降多分类每个评分一档宏平均F1、混淆矩阵预测具体等级但样本不均很麻烦二分类quality≥7为好酒F1、Precision、Recall识别“值得推荐的好酒”我的建议是课程大作业默认做二分类理由有三个。其一3到8一共六档中间档位占绝大多数多分类的混淆矩阵会集中在“预测成5分”这种情况答辩时很难讲出亮点其二二分类把问题翻译成“能否识别出好酒”业务逻辑清晰模型效果也容易量化其三后面做误差分析时只看“好酒被误判为普通酒”这一类就足够支撑一段有信息量的结论。如果你想兼顾也可以同时跑一个回归作为附录用R²佐证“理化指标能解释质量评分的一部分方差”但主结论放在二分类上。任务定了之后评估指标的选择也要提前想清楚。这个数据集里quality为7、8的样本占比大约只有13.5%如果二分类后用准确率评估盲目预测“全都是普通酒”也能有86%的准确率看起来性能很好实际上模型什么都没学会。所以评估必须围绕F1、召回率和精确率展开其中F1是网格搜索时的评分标准召回率要从“好酒被漏掉的比例”这个角度去解读。这一步想明白后面所有代码才能顺着这个逻辑写不会出现“准确率90%但答辩一问就露馅”的情况。另外无论选哪条任务路线建议把每一步关键尝试记录下来形成一个简单的分析笔记。不是指代码注释而是用几句话记录“我为什么这么划分标签”“为什么选F1而不是准确率”“回归实验里R²是多少转二分类后F1到了多少”。这份记录是报告“分析思路”章节的底稿也是答辩时回答“你怎么想到的”这类问题的素材。很多同学到答辩前翻代码根本想不起当时的意图原因就是没留过程记录。动手前也把环境准备好数据分析与可视化实践的标准组合是pandas做数据处理、matplotlib和seaborn画图、scikit-learn建模Jupyter Notebook写过程留痕这一套对1599行的数据量来说绰绰有余。3. 探索性数据分析分布、相关性和异常值三板斧3.1 加载与概览分号分隔符和describe里的第一层信息这个数据集最容易翻车的地方在第一行代码。原始csv文件里的分隔符是分号;而不是逗号如果直接用默认方式读取所有字段会挤成一列pandas把整行当成一个字符串。import pandas as pd import numpy as np # 注意sep;红酒数据集的csv用分号分隔用默认逗号会读成一列 df pd.read_csv(winequality-red.csv, sep;) print(样本数与字段数, df.shape) print(字段信息) print(df.info()) print(描述性统计) print(df.describe().T)三个步骤各有用处。shape让你确认读进来的确实是1599行、12列如果行数不对说明下载的文件不是目标数据集。info()检查每一列的非空数量这个数据集做得很干净12列全部是1599个非空值没有缺失值所以后面不需要做缺失值填充如果你拿到的版本有缺失要在这一步记录处理策略而不是悄悄删行。describe().T转置后方便逐列查看均值、标准差和分位数这里能看到一些后续分析会用到的关键量volatile acidity的均值在0.5上下说明确实存在部分酸败风险较高的样本alcohol最小值8.4、最大值接近15跨度接近一倍为后面“酒精对品质有区分度”的结论提供了前提。如果读出来的info()显示所有字段都是object类型先别怀疑代码回去检查sep参数。这是这个数据集最有名的隐藏陷阱读数据阶段就把它解决掉能省下后面一整轮的返工时间。做法上我习惯用table检查完再画图。画图前先设置中文字体否则后面所有图表里的中文标签都会变成方块import matplotlib.pyplot as plt import seaborn as sns # 解决中文字体问题Windows用SimHeimacOS可换Arial Unicode MS plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False sns.set_style(whitegrid)font.sans-serif设成SimHei覆盖黑体axes.unicode_minus设成False是为了让负号正常显示否则坐标轴上的负号也会显示成方块。这两行虽然不起眼却是数据分析与可视化实践里最常见的环境坑很多报告图表丑就丑在这里。3.2 相关性矩阵用热图找出影响品质的关键字段探索性分析的核心动作是找“谁和quality关系最紧密”相关性热图是最直接的呈现方式。# 相关系数默认用pearson适合连续变量之间的线性关系 corr df.corr(numeric_onlyTrue) plt.figure(figsize(10, 8)) sns.heatmap(corr, annotTrue, fmt.2f, cmapRdBu_r, linewidths0.5, cbar_kws{shrink: 0.8}) plt.title(红葡萄酒理化指标相关性热图) plt.tight_layout() plt.savefig(corr_heatmap.png, dpi150) plt.show()annotTrue在每个格子里标出数值fmt.2f保留两位小数cmapRdBu_r让正相关显示为红色、负相关显示为蓝色一眼就能看出阵营。热图里最值得记下的结论有三条alcohol与quality的相关系数最高正相关方向跟业务直觉一致volatile acidity与quality是绝对值最大的负相关density与alcohol之间呈明显负相关这是因为酒精密度低于水酒精越高密度越低是理化常识的体现不是数据噪声。拿到这张图不要只贴进报告而要沿着相关性继续做切片分析。比如按volatile acidity分组看quality均值高挥发酸组质量明显偏低按alcohol四分位分组质量评分随酒精组上升。这类分组的均值对比比单纯一个热图更有说服力因为评审能看到你不仅会画图还能用图发现可解释的模式。切片分析也不复杂pandas的groupby加mean就能出结果但它在报告里承担的作用是“从相关性到因果解释”的桥。3.3 质量分布与不均衡标签先摸清底细再选评估指标接着看quality本身的分布。这个字段是整数评分用频数表加条形图最直观。print(质量评分分布) print(df[quality].value_counts().sort_index()) # 二分类标签7分及以上定义为好酒 df[good] (df[quality] 7).astype(int) print(\n好酒/普通酒数量) print(df[good].value_counts())运行结果里quality最集中的是5分和6分两档加起来超过八成3分和8分都很少转成二分类后好酒good1大约占13.5%属于典型的不均衡标签。这个数字决定了后面所有模型评估都要以F1为核心而不是准确率。另外从业务角度看质量评分3到8的区间说明这份数据没有极端差或极端好的样本模型的任务本质是“在大多数普通酒里认出少数好酒”这跟现实中酒庄筛选优质批次的目标一致。异常值检查放在这个阶段一起做。用箱线图扫一遍连续字段重点关注total sulfur dioxide和chlorides它们容易出现右偏分布。fig, axes plt.subplots(1, 3, figsize(14, 4)) for ax, col in zip(axes, [total sulfur dioxide, chlorides, alcohol]): sns.boxplot(xdf[col], axax) ax.set_title(col) plt.tight_layout() plt.show()箱线图里冒出去的点就是离群值。对这个数据集的处理策略建议不要直接删行而是先记录它们的数量再验证删除后模型结果变化不大最后在报告里写一句“剔除极端值后结论稳健因此保留原始数据”。这样做比闷头删除更经得起追问。如果某个字段的离群值明显影响后续回归拟合再考虑用中位数缩尾或直接删除判断标准是交叉验证分数的变化能否控制在0.01以内。4. 建模与评估从逻辑回归到随机森林的落地代码4.1 基线模型带stratify的数据划分和逻辑回归探索分析做完就进入建模环节。我的习惯是先跑一个最简单的基线模型把“最差也能到什么水平”摸清楚再上复杂模型才有对比意义。这里基线选逻辑回归因为它可解释性强、训练快也能直接输出每个特征的系数。from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report # X只保留原始理化指标quality和good都是标签相关字段不能进特征 X df.drop(columns[quality, good]) y df[good] # stratifyy 保证训练集和测试集里好酒比例一致避免划分带来的偏差 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) model LogisticRegression(max_iter1000) model.fit(X_train, y_train) print(classification_report(y_test, model.predict(X_test)))三个参数值得单独说明。test_size0.2意味着1599条样本里训练用1279条、测试用320条对这个小数据集是常规比例random_state42固定随机种子保证反复运行结果一致交作业时可复现max_iter1000是因为逻辑回归在这个数据集上默认迭代100次不够收敛训练时会弹出ConvergenceWarning虽然不影响运行但答辩时容易被问。stratifyy是关键好酒样本本身只有13.5%如果不做分层抽样随机划分很可能让测试集里一条好酒都没有F1直接无法计算。逻辑回归跑出来的F1大概在0.55到0.68之间具体数值随随机种子浮动。这个成绩算不上好但它是后续所有调优的起点。逻辑回归的另一个产物是系数系数绝对值大的字段对决策影响大结合第3章热图里酒精和挥发酸的位置你会发现模型学到的方向和业务直觉对得上这个一致性本身就是一个可写的分析结论。4.2 随机森林与网格搜索围绕F1调优的关键参数基线有了第二步上随机森林。随机森林对小数据集很友好能捕捉非线性关系也能输出特征重要性而且不像梯度提升树那样对学习率敏感不需要太多玄学调参。这里直接用网格搜索把几个关键参数一起梳理清楚。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [100, 200], max_depth: [5, 10, None], min_samples_leaf: [1, 3] } rf RandomForestClassifier(random_state42) # scoringf1不均衡标签下准确率没有参考价值 grid GridSearchCV( rf, param_grid, cv5, scoringf1, n_jobs-1, verbose1 ) grid.fit(X_train, y_train) print(最优参数, grid.best_params_) print(交叉验证F1, grid.best_score_) print(测试集F1, classification_report(y_test, grid.predict(X_test)))网格搜索的评分标准选了scoringf1这一点是整个调参过程的核心。很多模板代码默认用accuracy在这个不均衡数据集上会选出“倾向把所有样本判为普通酒”的模型交叉验证分数虚高但F1惨不忍睹。cv5做五折交叉验证每个参数组合跑5次取平均小数据集上比单次划分更稳。max_depth设成None表示不限制深度让树自由生长配合min_samples_leaf3来防止过拟合min_samples_leaf1时树容易记住训练集的噪声等于3时叶子节点至少要含3个样本决策边界更平滑。跑出来的最优参数通常落在n_estimators200、max_depth5或10、min_samples_leaf3这一带交叉验证F1能到0.65到0.75左右比逻辑回归高出一截。n_estimators从100涨到200带来的提升很小但训练时间翻倍这说明在这个数据规模上树的数量不是瓶颈max_depth反而更重要限制深度能明显抑制过拟合。如果你想再压一点性能可以在最优参数附近补一轮更细的搜索比如max_depth在5和10之间加一个7min_samples_leaf在3和5之间加一个4这种“二次网格搜索”在作业里是明显的加分动作。4.3 特征重要性分析把建模结论与业务解释闭环随机森林有一个逻辑回归没有的优势就是特征重要性输出。它反映的是每个特征在森林分裂中被用来减少不纯度的总次数加权值值越大说明这个特征对分类越关键。feature_importance pd.Series( grid.best_estimator_.feature_importances_, indexX.columns ).sort_values(ascendingFalse) print(feature_importance)运行结果里排在前列的通常有alcohol、volatile acidity、total sulfur dioxide和sulphates这与第3章相关性热图的结论高度一致。把这组数据放到报告里时别只贴一个柱状图而是和前面探索性分析的结论对照着写热图里酒精与质量正相关、挥发酸与质量负相关特征重要性里这两个字段又排在最前说明它们不是巧合相关而是模型层面真正稳定的区分信号。这就是“探索性分析做判断、建模做验证”的闭环也是大作业里最容易被认可的逻辑结构。如果你把基线模型、随机森林、网格搜索、特征重要性都做出来了这套流程已经覆盖了一份数据分析案例的完整骨架。剩余时间应该花在下面的避坑检查上而不是继续换模型刷F1。模型分数在0.7上下对这份数据已经算合理区间强行上XGBoost或神经网络在小样本上反而容易过拟合且答辩解释成本很高。5. 避坑清单红酒数据分析常见的五个翻车现场这一章把批改红酒数据分析大作业时见过最多的五个问题集中列出。这些问题几乎都不是模型选型错误而是发生在数据处理和评估环节的低级失误。每条按现象、原因、解决三个层次写可以把它当成交作业前的自查清单先检查read_csv读进来是不是12列再检查有没有在划分前做过全量预处理然后看评估指标有没有把F1带上最后确认绘图代码里的中文字体。5.1 翻车一read_csv读出来只有一个长列现象df.shape显示(1599, 1)而不是(1599, 12)所有字段挤在一列里每行都是一长串用分号连起来的字符串。原因这个数据集的csv分隔符是分号pd.read_csv默认按逗号解析逗号在文件里不存在于是整行被当成单个字段。解决read_csv时显式传入sep;。如果已经用默认方式读进来了用df.iloc[:, 0].str.split(;, expandTrue)也能拆开但不如重新读一遍干净。另外提醒一句从某些地方复制文本再粘贴到Excel另存时分隔符可能被改回逗号建议始终保留原始文件用代码指定分隔符不要手动改文件。5.2 翻车二逻辑回归报警ConvergenceWarning现象训练逻辑回归时终端弹出ConvergenceWarning提示算法没有收敛到最优解但代码还能跑出结果。原因两类原因叠加。一是max_iter默认只有100对这个特征量纲差异大的数据集不够二是alcohol、density、sulfur dioxide这些字段的取值范围不在一个量级梯度下降在量纲差异大的特征上收敛慢。解决先加max_iter1000如果警告还在做特征标准化。标准化的正确做法是只对特征做、不碰标签而且训练集用fit_transform、测试集用transformfrom sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)注意最后一行用的是transform而不是fit_transform这是新手最容易写错的地方。如果在测试集上重新fit标准化时的均值和方差会来自测试集本身等于把测试集信息提前用光了后面评估出来的分数会偏乐观。这一条的边界是如果你后面计划用随机森林标准化可做可不做它对树模型没有增益但逻辑回归这类线性模型必须做。5.3 翻车三准确率虚高答辩被一句话问住现象模型报告里Accuracy高达86%但F1只有0.2答辩时老师问“模型学到了什么”回答不上来。原因不均衡标签下模型学到的策略是“永远预测普通酒”因为普通酒占86%这样预测准确率自然很高。准确率这个指标在这里没有意义。解决评估报告以classification_report为主盯着precision、recall、f1-score这几列网格搜索评分标准设成scoringf1。答辩场景里如果老师主动问准确率可以顺势解释“这个数据集准确率有天花板效应全预测为普通酒就能拿到86%所以用F1衡量好酒识别能力”这句话本身就是加分回答。数据分析和可视化项目里评估指标和业务目标的匹配度往往比模型复杂度更体现水平。5.4 翻车四中文乱码与负数显示成方块现象报告里的图表所有中文标签变成空心方块坐标轴负号也变成方块。原因matplotlib默认字体不支持中文SimHei没被正确设置负号问题是因为默认unicode_minus设置下字体找不到对应的负号字形。解决绘图前执行plt.rcParams[font.sans-serif] [SimHei]同时设置plt.rcParams[axes.unicode_minus] False。如果是在macOS上跑SimHei不存在改成[Arial Unicode MS]或[PingFang SC]。另外在Jupyter里如果还乱码重启内核后再执行绘图代码因为字体配置在绘图后端初始化之后变更不生效。这个坑反复出现建议把这两行配置写进一个公共的setup脚本每个notebook开头执行一次。5.5 翻车五预处理把测试集信息混进训练现象对全量数据做标准化、中位数填充、离群值剔除后再划分训练集和测试集测试集F1偏高但换一批数据就崩。原因像StandardScaler这种变换如果在划分前就fit到全量数据上测试集的均值方差已经参与了变换模型的评估成绩里包含了它“不应该知道”的信息。离群值剔除同理按全量数据分布删了异常行再划分测试集已经被“人工挑选”过。解决所有预处理步骤都放到train_test_split之后。划分数据这个动作要最先做之后训练集上fit、测试集上只transform离群值删除如果要做也只在训练集内做测试集保持原样。这条规则不仅适用于这个数据集也是所有数据分析项目里评估可信度的底线。自查顺序建议是检查代码里有没有在split之前出现scaler.fit、fillna或者drop操作只要有就调整顺序重跑。这一章的教训用一句话概括数据分析作业翻车十有八九不是算法不行而是数据进出错了地方。每次模型结果好到不真实先回头检查数据和预处理流程而不是急着换更强的模型。把这条记在心里比多调十个参数都管用。6. 让大作业出彩的三个进阶技巧6.1 特征工程一个组合特征带来的F1提升调参调到头后真正能再往上走的路径是特征工程。一个低风险高回报的尝试是把相关性分析里最显著的两个信号合并成一个交互特征。比如构造alcohol与volatile acidity的比值用来表达“酒体撑起的分量感”能否压过酸败味。跑交叉验证对比加这个特征前后的F1提升超过0.02就有保留价值。# 交互特征酒精相对挥发酸的优势幅度加0.01防止除零 df[alc_volatile_ratio] df[alcohol] / (df[volatile acidity] 0.01)这个新特征放回X重新跑一遍网格搜索如果交叉验证F1稳定提升就把它写进报告并解释它的业务含义。注意不要把没有提升的特征硬留在模型里特征多不等于模型好。6.2 错误分析从错判样本里读出的真实信息比调参更能打动评审的是错误分析。取出测试集里真正好酒被预测成普通酒的样本看它们与预测正确的好酒在特征上有什么差别通常会发现被漏掉的好酒酒精浓度不高但硫酸盐偏高说明模型对这类组合还不够敏感。# 找出真正好酒里被模型漏掉的样本 misclassified X_test[(y_test 1) (grid.predict(X_test) 0)] print(misclassified.describe().T)这段输出可以直接写成报告里的一句结论漏判样本集中在中等酒精、高硫酸盐区间与正确判出的好酒相比特征分布更接近临界值。这种“模型在哪些边界上失效”的分析比单纯报告F1数字更有说服力。6.3 报告呈现一张图只配一段结论最后是交付形态。大作业报告常见的毛病是图堆了很多每张图下面只有一行“如图x所示”。我的习惯是一张图只讲一个结论图下面用三句话闭环这张图展示什么现象、什么原因导致、对建模有什么启示。整份报告按“问题定义、数据理解、探索性分析、建模与评估、误差分析、结论”的结构组织第2章字段表和相关性热图的结论贯穿到建模章节形成前后呼应。答辩PPT控制在10页以内每页一个核心结论模型参数的细节放进附录。做这份红酒数据集作业时我自己也在分类和回归之间纠结过一轮最后选了二分类方向才真正把逻辑讲顺。后来每次带新人做数据分析项目我都会先让他们写三行字数据是什么、要回答什么问题、用什么指标算答对。这三行想清楚代码只是执行。希望这篇笔记能帮你在同样的题目上少走一段弯路。本文还有配套的精品资源点击获取