ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python葡萄酒质量分析项目:数据挖掘作业从数据读入到建模评估全流程

Python葡萄酒质量分析项目:数据挖掘作业从数据读入到建模评估全流程 简介这份资源是面向计算机相关专业学生与数据挖掘学习者的葡萄酒质量分析实战项目适用于课程设计、期末大作业以及需要完整案例练手的场景。包内共16个文件以10个csv数据文件为核心配套3个py源码脚本与3个txt说明文档压缩包约595KB体积轻便、结构清晰下载后无需过多调试即可运行。项目围绕葡萄酒的酒精含量、酸度、密度等变量与质量评定展开源码覆盖数据读取、清洗、特征处理、建模分析与可视化等关键环节可用来练习分类或回归任务帮助读者系统走通从数据预处理到结果评估的完整流程。目前已有83人学习关注适合作为高分课程作业范本与数据挖掘入门到进阶的实战参考。1. 葡萄酒质量分析项目从一份数据到一套能交差的数据挖掘作业拿到“Python实现的葡萄酒质量分析项目源码及全部数据”这个标题多数人第一反应是找一份能跑的代码改改学号就交。但真正做过数据挖掘大作业的人知道翻车点从来不在模型多花哨而在数据读进来那一刻——分隔符是分号不是逗号、类别标签是字符串不是数字、样本极度不平衡导致准确率虚高到 0.95 但召回惨不忍睹。这个项目要解决的就是把一份理化指标数据走完“读入—清洗—探索—建模—评估”的完整链路产出一份能写进报告、能答辩、能复现的结果。它适合正在做数据挖掘课程作业的学生也适合想用一个小数据集练手 Python 数据分析与数据挖掘实战的入门者。下面我按自己带学生做这个项目的顺序把每一步的参数、坑和判断标准讲清楚。2. 数据读入与字段理解先搞清楚每一列在说什么2.1 红白葡萄酒两个文件的结构差异常见的葡萄酒质量数据集分两个文件红葡萄酒大约 1599 条白葡萄酒大约 4898 条字段是固定的 11 个理化指标加 1 个质量评分。理化指标包括固定酸度、挥发性酸度、柠檬酸、残糖、氯化物、游离二氧化硫、总二氧化硫、密度、pH、硫酸盐、酒精含量。质量评分是 0 到 10 的整数但实际分布集中在 5、6、7 三档这是后面建模必须处理的核心问题。读入时最容易踩的坑是分隔符。这个数据集原始文件用的是分号;而不是逗号直接pd.read_csv会把整行读成一列。另一个坑是列名里带空格比如fixed acidity后续用点号访问属性会报错必须统一改成下划线。我一般会在读入阶段就把列名规范化避免后面反复改。import pandas as pd import numpy as np # 红白葡萄酒分开读分隔符是分号这点和普通 csv 不同 red pd.read_csv(winequality-red.csv, sep;) white pd.read_csv(winequality-white.csv, sep;) # 列名带空格统一替换成下划线方便后续 df.fixed_acidity 这种写法 red.columns [c.replace( , _) for c in red.columns] white.columns [c.replace( , _) for c in white.columns] # 加一列标记颜色来源后面可以合并分析也可以做二分类 red[wine_type] red white[wine_type] white df pd.concat([red, white], ignore_indexTrue) print(df.shape) # 预期 (6497, 13) print(df[quality].value_counts().sort_index())这段代码的逻辑是先分别读入再合并而不是直接读一个文件。参数上sep;是关键ignore_indexTrue保证合并后索引连续。value_counts那行是必须看的你会看到 5、6、7 占了绝大多数3、4、8、9 极少这就是后面要处理的不平衡信号。如果这里不看直接上模型准确率会骗你。2.2 缺失值与异常值的判断标准这个数据集通常没有显式缺失值但不代表干净。理化指标里有几个字段存在物理上不合理的取值比如残糖为 0 但密度却偏高或者游离二氧化硫大于总二氧化硫。这类问题用isnull().sum()查不出来得靠业务逻辑判断。我一般会做三件事先查缺失再查描述性统计的极值最后用箱线图的四分位距标记离群点。注意离群点不等于错误值葡萄酒的硫酸盐、残糖本身就有长尾分布直接删会丢信息。我的做法是保留但在报告里说明并在建模时用对异常值不敏感的树模型。# 缺失值检查 print(df.isnull().sum().sum()) # 预期 0 # 业务逻辑异常游离二氧化硫不应大于总二氧化硫 mask df[free_sulfur_dioxide] df[total_sulfur_dioxide] print(逻辑异常条数:, mask.sum()) # 用 IQR 标记离群点只统计不删除 Q1 df[residual_sugar].quantile(0.25) Q3 df[residual_sugar].quantile(0.75) IQR Q3 - Q1 outliers df[(df[residual_sugar] Q1 - 1.5*IQR) | (df[residual_sugar] Q3 1.5*IQR)] print(残糖离群条数:, len(outliers))参数上 1.5 倍 IQR 是常规阈值想更宽松可以调到 3。这里不删数据的原因是样本量本来就不大删多了模型更不稳。逻辑异常那几行如果存在建议直接删因为物理上不可能属于录入错误。3. 探索性分析与特征工程把评分问题变成可建模问题3.1 用相关性热力图锁定关键特征探索阶段最值得做的一件事是看哪些理化指标和质量评分相关。常见结论是酒精含量正相关、挥发性酸度负相关这两个几乎在所有版本的数据里都成立。但光看相关系数不够因为相关系数只反映线性关系而酒精和评分的关系可能是非线性的。我一般会先算皮尔逊相关系数再画热力图重点看和quality那一列的排序。注意不要用热力图去解释因果它只告诉你谁和谁一起动。真正写报告时我会把相关系数绝对值排前五的字段单独拎出来做分组箱线图看不同质量档位下这些字段的分布差异。import matplotlib.pyplot as plt import seaborn as sns corr df.corr(numeric_onlyTrue)[quality].sort_values(ascendingFalse) print(corr) plt.figure(figsize(10, 8)) sns.heatmap(df.corr(numeric_onlyTrue), annotFalse, cmapcoolwarm) plt.title(Wine Quality Correlation) plt.tight_layout() plt.savefig(corr.png, dpi150)numeric_onlyTrue是为了跳过wine_type这种字符串列不加会报错。sort_values让最相关的排最上面方便快速判断。保存图片时dpi150是报告里够用的清晰度再高文件会大。3.2 质量评分二值化与类别不平衡处理原始评分是 3 到 9 的多分类但 3、4、8、9 样本极少直接做多分类会导致这些类完全学不到。常见做法是二值化把评分大于等于 7 的当作“好酒”其余当作“普通酒”。这样正负样本比例大约在 1:3 到 1:4 之间仍然不平衡但比多分类可控。二值化之后必须处理不平衡。我一般用两种手段一是class_weightbalanced让模型自动给少数类更高权重二是 SMOTE 过采样但 SMOTE 要在训练集上做绝不能在全量数据上做否则测试集里混入合成样本评估结果全是假的。这是血泪经验见过太多人在这里翻车。from sklearn.model_selection import train_test_split from imblearn.over_sampling import SMOTE # 二值化7 为好酒 df[is_good] (df[quality] 7).astype(int) print(df[is_good].value_counts(normalizeTrue)) X df.drop(columns[quality, is_good, wine_type]) y df[is_good] # 先切分再在训练集上过采样 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy) smote SMOTE(random_state42) X_train_res, y_train_res smote.fit_resample(X_train, y_train) print(过采样后:, np.bincount(y_train_res))stratifyy保证切分后训练集和测试集的正负比例一致不加的话可能测试集里好酒特别少。random_state固定是为了结果可复现答辩时老师让你再跑一遍结果得一样。SMOTE 只作用在X_train上测试集保持原始分布这样评估才反映真实泛化能力。4. 建模与评估为什么准确率高不代表模型好4.1 三个基线模型的对比与参数设置我一般会跑三个模型做对比逻辑回归、随机森林、梯度提升。逻辑回归作为线性基线随机森林看非线性能力梯度提升看能不能再往上顶一点。三个都跑报告里才有对比只跑一个模型说服力不够。参数上逻辑回归用max_iter1000防止不收敛随机森林用n_estimators200、max_depth不限制先看效果梯度提升用n_estimators200、learning_rate0.05。注意随机森林和梯度提升都要设class_weight或依赖过采样后的数据这里因为已经 SMOTE 过就不再重复加权。from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier from sklearn.preprocessing import StandardScaler from sklearn.metrics import classification_report, roc_auc_score # 逻辑回归对量纲敏感先标准化 scaler StandardScaler() X_train_s scaler.fit_transform(X_train_res) X_test_s scaler.transform(X_test) models { LR: LogisticRegression(max_iter1000, random_state42), RF: RandomForestClassifier(n_estimators200, random_state42), GB: GradientBoostingClassifier(n_estimators200, learning_rate0.05, random_state42) } for name, model in models.items(): if name LR: model.fit(X_train_s, y_train_res) pred model.predict(X_test_s) prob model.predict_proba(X_test_s)[:, 1] else: model.fit(X_train_res, y_train_res) pred model.predict(X_test) prob model.predict_proba(X_test)[:, 1] print(name, AUC:, round(roc_auc_score(y_test, prob), 4)) print(classification_report(y_test, pred))标准化只在逻辑回归上用树模型不需要因为树按阈值分裂量纲不影响。predict_proba取第二列是因为正类是 1。AUC 比准确率更值得看因为它不受阈值影响能反映模型排序能力。4.2 评估指标的选择别只盯 accuracy不平衡数据下 accuracy 是最容易骗人的指标。假设好酒只占 20%模型全预测普通酒accuracy 也有 80%但召回为 0等于没学到东西。所以报告里必须同时给 precision、recall、f1 和 AUC。我一般重点看两个好酒这一类的 recall以及整体 AUC。recall 高说明模型能把好酒找出来AUC 高说明排序能力稳。如果 recall 低但 precision 高说明模型太保守可以调低判定阈值反过来则说明模型太激进。阈值调整是答辩时容易被问的点提前想清楚。from sklearn.metrics import precision_recall_curve # 以随机森林为例看阈值对 recall 的影响 rf RandomForestClassifier(n_estimators200, random_state42) rf.fit(X_train_res, y_train_res) prob rf.predict_proba(X_test)[:, 1] prec, rec, thr precision_recall_curve(y_test, prob) # 找 recall 不低于 0.7 时对应的阈值 idx np.where(rec[:-1] 0.7)[0] if len(idx): print(建议阈值:, round(thr[idx[0]], 3), precision:, round(prec[idx[0]], 3))precision_recall_curve返回的阈值比 precision 和 recall 少一个所以用thr[idx[0]]时要注意索引对齐。这段代码的意义是让你在报告里能说清楚“我为什么选 0.5 或者为什么调成别的”而不是默认阈值一把梭。5. 避坑与常见问题那些让作业从高分变挂科的细节5.1 数据泄漏标准化和过采样的顺序错了现象是测试集 AUC 高得离谱0.99 以上但换个随机种子就崩。原因是在切分之前就做了标准化或 SMOTE测试集的信息泄漏进了训练过程。解决是严格先train_test_split再在训练集上fit标准化器测试集只transformSMOTE 同理只在训练集上做。这个顺序错了整份报告的数据都不可信。5.2 把 quality 当连续值做回归现象是模型输出 5.7、6.3 这种小数不知道怎么对应到质量等级。原因是直接拿 quality 做回归但 quality 本质是序数类别不是连续量。解决是二值化做分类或者用序数回归。大作业里二值化最稳报告也好写。如果非要做多分类至少把 3、4 合并、8、9 合并减少极端类。5.3 忽略 wine_type 导致红白混在一起现象是模型效果一般且酒精含量的重要性异常高。原因是红白葡萄酒的理化分布差异很大混在一起后wine_type成了隐含的强特征模型可能在学颜色而不是学质量。解决是要么分开建模要么把wine_type做独热编码显式喂进去并在报告里说明。我一般建议分开做红白各一套结果对比着写更有内容。5.4 随机种子不固定导致结果无法复现现象是答辩时老师让你再跑一遍结果和报告里不一样。原因是train_test_split、模型初始化、SMOTE 都没设random_state。解决是所有涉及随机的地方统一设random_state42并在代码开头集中定义。这是最不该丢的分但每年都有人栽。5.5 特征重要性直接当因果结论写现象是报告里写“酒精含量越高酒越好”被老师追问因果依据。原因是把随机森林的 feature_importances_ 当成了因果证据。解决是措辞改成“酒精含量与质量评分呈正相关且在模型中重要性靠前”只讲关联不讲因果。数据挖掘作业考的是流程和判断不是让你下科学结论。6. 进阶技巧用交叉验证和特征重要性把报告写出层次想让这份作业从“能跑”变成“高分”我一般会加两件事分层交叉验证和基于特征重要性的特征筛选。分层交叉验证比单次切分稳能给出均值和方差报告里写“5 折交叉验证 AUC 0.81±0.02”比单次 0.82 有说服力得多。特征重要性则能帮你回答“哪些指标真正在起作用”但要注意前面说的只讲关联。from sklearn.model_selection import StratifiedKFold, cross_val_score cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) rf RandomForestClassifier(n_estimators200, random_state42) # 注意交叉验证内部也要处理不平衡这里用 pipeline 更严谨 from imblearn.pipeline import Pipeline as ImbPipeline pipe ImbPipeline([ (smote, SMOTE(random_state42)), (clf, rf) ]) scores cross_val_score(pipe, X, y, cvcv, scoringroc_auc) print(AUC:, scores.mean().round(4), ±, scores.std().round(4))这里用imblearn的 Pipeline 而不是 sklearn 的是因为 SMOTE 只能在训练折上做普通 Pipeline 做不到这一点。scoringroc_auc直接指定评估指标省得自己算。跑完你会得到一个均值和标准差标准差小说明模型稳大说明数据或参数有问题值得在报告里讨论。特征重要性可以这样看rf.fit(X_train_res, y_train_res) imp pd.Series(rf.feature_importances_, indexX.columns) print(imp.sort_values(ascendingFalse).head(8))排前面的通常是酒精含量、挥发性酸度、密度、硫酸盐这几个。但别急着下结论可以再做一步把重要性最低的几个特征删掉重跑交叉验证看 AUC 有没有明显下降。如果没降说明这些特征确实冗余报告里就能写“经特征筛选模型在减少 X 个特征后性能保持稳定”这是加分项。最后说个我自己的习惯所有中间结果都存成 csv 或图片不要只留在 notebook 的输出里。答辩前把corr.png、classification_report的文本、交叉验证的分数整理到一个结果文件夹老师问什么都能立刻翻出来。这个项目本身不难难的是把每一步的判断依据讲清楚而判断依据就藏在这些你随手存下来的中间产物里。希望帮到你。本文还有配套的精品资源点击获取
返回列表