简介:面向数据挖掘课程设计及期末大作业场景,这份基于Python的葡萄酒质量分析项目提供了一套可完整运行的实战案例,覆盖数据读取、清洗、分析、可视化与建模流程。数据集包含酒精含量、酸度、密度等多项指标及对应质量评分,可用于分类或回归模型训练,适合计算机相关专业学生及希望上手数据挖掘的初学者参考。资源包共16个文件,以10个csv数据文件为核心,另有3个Python源码脚本和3个说明文档,压缩包整体约595KB,经过调试后下载即可直接运行。目前已有84人学习,通过该项目可系统练习数据处理、特征工程、模型训练与结果评估等关键环节,也可作为课程设计或期末大作业的整体范本。
1. 数据挖掘大作业怎么拿高分:先拆一遍葡萄酒质量分析源码的完整链路
课程设计答辩时,老师问得最多的不是「你准确率多少」,而是「你这个流程为什么这么设计」。代码能跑但分数上不去,多半是流程缺了环节。这份 Python 实现的葡萄酒质量分析项目,正好是标准的加分结构:红白葡萄酒两套数据的清洗、相关性分析、特征工程、三种分类模型对比,以及分类报告和混淆矩阵可视化,链路完整,不是只丢给你一个训练脚本。它适合两类人:一是数据挖掘期末大作业还没定题目的,可以照这个结构直接改造成自己的题目;二是想弄清楚课程设计怎么才不像应付差事的,照着从头复现一遍比看十篇教程都管用。源码和全部数据都在一个包里,先花十分钟把骨架摸透,再动手改,效率高得多。
2. 摸清骨架再跑通主程序:数据目录、环境安装与第一次运行输出
这个资源不是把几十个文件堆在一个文件夹里让你自己猜,而是按课程设计答辩逻辑拆好的工程目录。我拿到之后的第一件事是列文件清单,搞清楚谁负责数据、谁负责建模、谁负责出图,然后装环境、跑一次主程序,确认输出数值和预期一致。这三步做完,你才有资格去改代码,否则后面所有改动都是盲改。
2.1 源码包里有什么:两个 CSV 数据文件与脚本分工
| 文件/目录 | 作用 | 说明 |
|---|---|---|
| data/winequality-red.csv | 红葡萄酒数据集 | 1599 条记录,11 个理化特征加 1 个质量评分 |
| data/winequality-white.csv | 白葡萄酒数据集 | 4898 条记录,字段与红酒完全一致 |
| data_preprocess.py | 数据清洗与特征工程 | 缺失值检查、相关性热力图、标签二值化 |
| train_model.py | 模型训练与评估 | 逻辑回归 / 决策树 / 随机森林三组对比 |
| visualize.py | 可视化输出 | 混淆矩阵、ROC 曲线、特征重要性柱状图 |
| main.py | 主入口脚本 | 串联全流程,支持命令行参数切换 |
| requirements.txt | 依赖清单 | pandas / numpy / matplotlib / seaborn / scikit-learn |
按阶段拆文件的好处,答辩时能直接当讲稿用。老师问「每个文件干什么」,你顺着列表把职责说清楚,印象分先拿到一半。数据预处理单独一个文件也很关键,因为清洗逻辑最具体、最好讲,是最容易展示工作量的部分。这个项目的数据规模约 6500 条,对课程设计来说非常合适——跑起来快,又能展示出特征分布和类别不均衡这些真实问题,不用自己造数据。
2.2 环境准备:Python 3.8 以上加五个库,一条命令装完
依赖没有整花活,就是数据挖掘课上最常见的五个库。我用虚拟环境装的原因是课程设计中途一定会改模型、试新库,虚拟环境能把这份项目的依赖和全局 Python 隔离,项目删了也不影响其他作业。命令行操作如下:
cd wine_quality_project python -m venv venv source venv/bin/activate # Linux/macOS;Windows 下用 venv\Scripts\activate pip install -r requirements.txtrequirements.txt 里的版本要注意两个边界:pandas 2.x 起要求 Python 3.9 以上,如果本机是 3.8,要么升 Python 要么手动指定 pandas==1.5.3;scikit-learn 认准 1.0 以上即可,1.x 的 API 基本稳定。装完后用一条命令验证环境没装错:
python -c "import pandas, sklearn, matplotlib; print(pandas.__version__, sklearn.__version__, matplotlib.__version__)"能打印出版本号,说明依赖全部就位。这一步别跳过,我见过太多人pip install结束就以为装好了,实际 scikit-learn 装了一半,后面 import 直接报错。
2.3 先跑为敬:主入口的参数设计与第一次输出的读法
main.py 把整个流程串成了一条命令,我用红葡萄酒加随机森林试跑:
python main.py --type red --model rf --target binary参数含义拆开说:--type选数据集,red 和 white 二选一;--model选模型,lr 是逻辑回归,dt 是决策树,rf 是随机森林;--target选任务,binary 表示二分类(quality 大于等于 7 算好酒),multi 表示按 3~8 分做多分类。
第一次跑通之前,我的建议是不要改任何代码。先看输出里的四个关键数字:数据集形状是否为 1599 行 12 列、缺失值是否为 0、好酒和普通酒的样本数比例、交叉验证平均分和测试集分类报告。这几个数字就是后续所有修改的基准线。每次改完代码,拿新输出和基准线对比,出问题立刻能定位到是哪一步引入的。
提示:第一次跑完先别急着看模型分数,先确认数据行数。red 应该是 1599,white 应该是 4898。这个数对不上,说明你手里的数据文件被改过,或者 pandas 读取方式不对,后面所有分析都建立在错误基础上。
3. 数据预处理才是分水岭:12 列葡萄酒数据的清洗、相关性与标签策略
很多作业分数低,低在预处理部分只写一句「数据没有缺失值,所以不用处理」。这个数据集确实没有缺失值,但它有偏态分布、有特征共线性、有标签不均衡,这些才是预处理要展示的功夫。把这一章做厚,答辩时你就有东西可讲,而不是干巴巴地贴一张 describe 的表格。
3.1 数据集结构盘点:红白两份数据能不能合在一起用
先用 pandas 把数据读进来,这里有一个必须注意的细节——分隔符不是逗号:
import pandas as pd red = pd.read_csv("data/winequality-red.csv", sep=";") white = pd.read_csv("data/winequality-white.csv", sep=";") print(red.shape, white.shape) print(red.dtypes)sep=";"是这份数据的第一个坑。UCI 的葡萄酒质量数据集用分号做分隔符,不带这个参数,pandas 会把整行读成单列,shape 直接变成 1599 行 1 列,后面所有代码都会翻车。dtypes 输出显示全部是数值型,没有类别变量需要编码,这让预处理省了很多事。红白两份数据的字段完全一致,但分布差异明显——白葡萄酒的残糖和二氧化硫整体偏高,密度分布也不同。项目里默认分开建模,不建议直接 concat 成一份,答辩时老师问「为什么不合并」,你可以理直气壮地说两类酒的理化指标分布不同,合并会模糊决策边界。
3.2 缺失值与异常值:数据没缺失不代表不用处理
缺失值检查跑一遍,结果全是 0,但这只是第一步。真正值得展示的是异常值分析:
for col in red.columns: q1, q3 = red[col].quantile(0.25), red[col].quantile(0.75) iqr = q3 - q1 outlier_count = ((red[col] < q1 - 1.5 * iqr) | (red[col] > q3 + 1.5 * iqr)).sum() print(col, outlier_count)这段用经典的 IQR 方法统计每列异常值数量。残糖和总二氧化硫的异常值最多,这符合葡萄酒工艺——个别甜型酒的残糖远高于干型酒。处理策略不是无脑删除:对随机森林这类树模型,异常值通常保留,因为树模型按阈值切分,不依赖尺度;但逻辑回归对异常值敏感,训练前要做 RobustScaler 稳健缩放,避免个别极端值把系数拉偏。我一般的处理原则是只删「多列同时异常」的极端样本,比如总二氧化硫超过 300 同时密度也异常的行,而不是把所有 IQR 超限的样本全删掉,那样会损失有效信息。
3.3 相关性分析:热力图要画,但别只看热力图
相关性热力图是课程设计的标配图,也是答辩最容易被追问的地方:
import seaborn as sns import matplotlib.pyplot as plt corr = red.corr() plt.figure(figsize=(10, 8)) sns.heatmap(corr, annot=True, fmt=".2f", cmap="coolwarm", linewidths=0.5) plt.title("Wine Quality Feature Correlation Matrix") plt.show()annot=True把相关系数值标在格子里,字体大小在字段多时可以调小。从热力图能看到两个关键结论:酒精与质量正相关最高,约 0.48;挥发性酸度与质量负相关约 -0.39。这两个结论答辩时能讲出来,说明你真的看过数据。但热力图有个陷阱:密度和酒精高度负相关(约 -0.50),残糖和密度相关高达 0.84,这些特征之间存在共线性。逻辑回归遇到共线性,回归系数的解释会不稳定,这属于数据挖掘课程里「多重共线性」的知识点。我的做法是建模时不完全依赖手工剔除,而是用随机森林的特征重要性去验证;如果做逻辑回归,可以对高度相关的特征组做个 VIF 检查,把 VIF 大于 10 的特征挑出来说明处理思路。
3.4 标签策略:二分类还是多分类,答辩时怎么说
数据里的 quality 是 3 到 8 的整数评分,原始多分类任务并不好做。常见的处理是转成二分类,阈值取 7:
import numpy as np red["label"] = np.where(red["quality"] >= 7, 1, 0) print(red["label"].value_counts())np.where把质量大于等于 7 的样本标成 1(好酒),其余标成 0。输出会显示正负样本大约 1:6 的比例,这个不均衡比例在后面评估模型时非常关键。如果直接把 quality 当多分类的 y,6 个类别里有几个类只有十几条样本,模型很难学出规律,答辩时还容易被追问「你处理的是不是有序分类问题」。我的课程设计建议:主模型用二分类,把多分类作为拓展实验放一页,效果不理想也能解释成样本太少、类别有序,至少展示了思考深度。
注意:阈值取 7 还是 6,直接改变样本分布。改成 6,正例比例升到约 45%,模型准确率会好看很多,但「质量 6 分算好酒」这个业务定义很难站住脚。高分作业的要点是先写清楚「为什么取 7」,再谈模型结果。先定义问题,再做实验,顺序不能反。
4. 建模评估别只看准确率:逻辑回归、随机森林与交叉验证的正确打开方式
准确率是课程设计里最容易虚高的指标。这个数据集上,无脑把全部样本预测成「普通酒」,准确率也有 87%。所以模型对比要讲「相对多数类基线提升了多少」,而不是盯着绝对准确率自嗨。这一章按「选型理由 → 数据划分 → 评估指标 → 可视化输出」的顺序展开,每一步都是答辩时可以展开讲的知识点。
4.1 模型选型:为什么是这三个模型而不是一个跑到底
项目里默认的三组模型:逻辑回归、决策树、随机森林。选型理由在答辩时要能一句话讲清:逻辑回归是线性模型的代表,可解释性强,每个特征的系数直接对应正向还是负向影响;决策树是单棵树的代表,能画出树形图,直观展示分裂依据;随机森林是 Bagging 集成的代表,通过多棵树投票降低方差,效果通常最好。三者在复杂度上形成梯度,正好对应课程里「线性模型 → 单树模型 → 集成模型」的教学顺序。课程设计不需要上 XGBoost 或深度学习,一是数据量只有几千条,二是这几个模型足够你把知识点串起来,答辩时每一层都有话讲。
4.2 训练集划分:stratify 和 random_state 的坑
数据划分的代码有几个参数不能乱填:
from sklearn.model_selection import train_test_split X = red.drop(columns=["quality", "label"]) y = red["label"] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y, random_state=42 )stratify=y的作用是在切分时保持好酒和普通酒的比例和原始数据一致。正负样本 1:6 的数据如果不加这个参数,随机切分可能把好酒样本集中到训练集或测试集,导致测试结果失真。random_state=42的作用是让结果可复现。这里有一个实操层面很要命的细节:不设random_state,每次跑出来的准确率都不一样,答辩时老师让你现场再跑一次,数字对不上,体验极差。之后换模型时,交叉验证也用分层策略:
from sklearn.model_selection import StratifiedKFold, cross_val_score cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(model, X_train, y_train, cv=cv, scoring="accuracy") print(f"CV mean: {scores.mean():.4f} (+/- {scores.std():.4f})")交叉验证的均值比单次划分的结果更稳定,报告里写这个数字比写单次测试集准确率更有说服力。n_splits=5是课程设计常用的折数,数据量不大,5 折在稳定性和计算成本之间比较平衡。
4.3 评估指标组合:分类报告、混淆矩阵、ROC 曲线
训练完成后,先用classification_report看完整指标:
from sklearn.metrics import classification_report, confusion_matrix, roc_curve, auc y_pred = model.predict(X_test) print(classification_report(y_test, y_pred, target_names=["normal", "good"]))分类报告里的recall列比准确率更重要——它反映模型对少数类的识别能力。在这个数据集上,好酒类别的召回率通常偏低,你要重点比较随机森林是否比逻辑回归把好酒召回率提高了一截,这个提升幅度才是报告的核心结论。混淆矩阵可视化是论文必备图:
cm = confusion_matrix(y_test, y_pred) sns.heatmap(cm, annot=True, fmt="d", cmap="Blues") plt.xlabel("Predicted Label") plt.ylabel("True Label") plt.show()ROC 曲线的代码也很短,主要是把 predict_proba 输出的概率拿来做曲线:
fpr, tpr, _ = roc_curve(y_test, model.predict_proba(X_test)[:, 1]) print(f"AUC: {auc(fpr, tpr):.4f}")AUC 对类别不均衡不敏感,这个数据集上逻辑回归 AUC 通常在 0.85 上下,随机森林能到 0.90 左右。答辩时把准确率、召回率、AUC 三个数一起摆出来,说明你看过不止一个指标,比单贴一个准确率高级得多。
4.4 可视化输出:特征重要性图与保存规范
特征重要性图是随机森林特有的输出,也是连接数据分析与模型解释的桥梁:
import pandas as pd importances = pd.Series(model.feature_importances_, index=X.columns).sort_values() importances.plot(kind="barh", figsize=(8, 6)) plt.xlabel("Importance") plt.tight_layout() plt.savefig("figures/feature_importance.png", dpi=150)sort_values()让最重要的特征显示在顶部,barh 是水平柱状图,标签不容易截断。savefig要指定dpi=150,否则图片放进论文打印出来是糊的。特征重要性的结论通常与相关性分析一致:酒精、挥发性酸度、密度排前三。这张图答辩时比模型分数更值得讲,因为它把「数据理解」和「模型解释」接上了——你已经知道哪些特征和好酒有相关性,现在模型又确认了一遍,两条证据链互相印证。
5. 避坑手册:课程设计翻车现场的五类高频问题与排查思路
这一章写的是复现这类课程设计项目时最常见的翻车现场,每一条都是实际踩过的。现象、原因、解决三步写清楚,你遇到类似报错可以直接按图索骥。
5.1 CSV 读取出来只有一列
现象:pd.read_csv("winequality-red.csv")之后df.shape显示 1599 行 1 列,所有字段挤在一个单元格里。
原因:这个数据集的分隔符是分号,而 pandas 默认按逗号分隔。加了sep=";"就正常。这是 UCI 这套数据的经典陷阱。
解决:
df = pd.read_csv("data/winequality-red.csv", sep=";", encoding="utf-8")如果还乱码,说明文件被 Excel 以其他编码另存过,把encoding换成"gbk"或"latin1"逐个试。我习惯在代码开头加一个断言:assert df.shape[1] == 12,列数不对直接报错,避免数据读歪了还往下跑。
5.2 准确率 0.87 但模型其实没用
现象:逻辑回归跑完了,测试集准确率 0.87,看起来很漂亮,但看了混淆矩阵发现模型几乎把所有样本都判成普通酒。
原因:标签不均衡,好酒只占约 13%。模型只要学会「全部说普通」,准确率就有 87%,这对模型来说是最省事的策略。
解决:别只盯着准确率。看分类报告里好酒类别的召回率,或者直接算 AUC。报告里这样表述:「多数类基线准确率为 87%,随机森林在保持整体准确率 91% 的同时,把好酒召回率从逻辑回归的 42% 提升到 65%。」用「相对基线提升多少」来陈述,答辩老师一听就知道你理解这个数据的特点。
5.3 图里中文全部变成方块
现象:plt.title("特征重要性")显示成一片方框。
原因:matplotlib 默认字体不包含中文字符。
解决:在绘图脚本开头加两行,把字体改成系统自带的中文字体:
plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False第二行是修正负号显示异常的。另一个更省事的做法是图里全部用英文标签,中文只出现在报告正文。课程设计论文本来就是要中英文混排的,图注用英文反而看着规范。
5.4 本机跑通换电脑就报错:scikit-learn 版本差异
现象:在你自己电脑上训练好的模型,换到实验室电脑重新跑剧本,预测时报AttributeError或者大量 warning。
原因:scikit-learn 版本差异导致部分 API 行为不同。比如RandomForestClassifier的n_estimators默认值在 0.22 版本之前是 10,之后是 100,版本不对模型表现完全不同。跨版本加载保存的模型文件兼容性也差。
解决:课程设计不要保存训练好的模型文件,只保存预测结果和图表。分析代码跟着走,到哪台机器跑都是重新训练,省掉序列化这一层麻烦。在 requirements.txt 里固定scikit-learn>=1.0,换机器先pip install -r requirements.txt统一版本。
5.5 随机森林训练慢,跑一次四五分钟
现象:n_estimators=500,max_depth=None,跑完一次交差验证小课间都不够。
原因:默认设置对这个只有 6500 条的数据集是高配。每棵树都长到完全生长,500 棵树就是 500 次完整划分,大部分收益是边际递减的。
解决:把n_estimators调到 100,max_depth限制到 10,效果几乎不变但速度快几倍。被老师问为什么这么调,就答:「数据量只有几千条,树太多会导致训练时间线性增长,而分类效果的提升在 100 棵树之后已经看不出来了。」这个回答本身就展示了对模型复杂度与数据规模关系的理解。
6. 换个数据集照样用:把葡萄酒分析改造成你的专属课程设计
如果你不想交一份和别人撞车的作业,最好的办法是保留这套代码框架,换一个数据集。UCI 上常见的成人收入预测、心脏病分析、小麦种子分类,字段结构都和葡萄酒数据兼容。改造的核心只有两件事:字段映射和标签定义。
假设换成成人收入预测,代码里只需要改数据读取和字段名:
mapping = { "wine": { "features": ["alcohol", "volatile acidity", "density"], "label": "quality >= 7" }, "adult": { "features": ["age", "education-num", "hours-per-week"], "label": "income > 50K" } } df = pd.read_csv("data/adult.data", header=None) df.columns = ["age", "workclass", "fnlwgt", "education", "education-num", "marital-status", "occupation", "relationship", "race", "sex", "capital-gain", "capital-loss", "hours-per-week", "native-country", "income"]换成新数据集后,需要连带确认四件事:分隔符还是不是逗号,有没有类别字段需要做编码,缺失值是空字符串还是 NaN,标签列要不要把>50K和<=50K映射成 1 和 0。类别字段用pd.get_dummies做独热编码,规模不大可以直接处理。新数据的相关性热力图和特征重要性图要重新跑一遍,写报告时以新数据的结果为准,葡萄酒的结论一律不能复用,这是原则问题。
从那以后,我拿到任何新课程设计题目,都会先花十五分钟把字段含义、分隔符、标签分布写在一张纸上,再打开编辑器。这个习惯就是跑这份葡萄酒项目时养成的——当时我跳过了描述统计直接建模,后来所有分析都建立在对数据结构的错误理解上,返工了一整天。希望这篇拆解能帮你把同样的弯路省掉,一次跑通。
本文还有配套的精品资源,点击获取