
简介这份数据分析大作业资料围绕红酒数据集展开面向统计学、数据科学相关专业学生及需要完成课程项目的学习者帮助解决评分影响因素建模与分类的实操问题。压缩包内共1个docx文档约202KB以文字与代码截图形式完整呈现分析流程。内容涵盖红酒11项物理属性与评分的关联性分析、相关性降维、多元线性回归拟合、主成分回归以及KNN简单分类并给出R语言与Python的具体实现代码和结果解读。读者可据此掌握从数据格式转换、相关性检验到回归建模与预测的完整思路理解挥发性酸度、硫酸盐、酒精等变量对评分的作用同时获得主成分回归还原原变量系数的运算方法。目前已有4264人学习下载适合作为课程大作业参考或数据分析入门练手素材。1. 红酒数据集分析一份大作业里藏着多少真实业务问题红酒数据集Wine Quality几乎是每个做数据分析大作业的人绕不开的一份数据。它不复杂1599 条红葡萄酒样本、11 个理化指标、1 个质量评分看起来随便跑跑就能出图。但我带过几届学生做这个题真正拉开差距的不是谁画的图好看而是谁能把「数据分析」这四个字拆成一条完整的链路数据质量核查、特征工程、回归与分类双线建模、模型评估、结论落地。热搜里天天刷屏的 Python 数据分析、KNN、多元线性回归、XGBoost 二分类其实都能在这份数据上跑一遍而且跑出来的结果差异很大正好用来理解「为什么同一个数据集不同方法结论不一样」。这篇文章面向三类人正在做大作业但不知道从哪下手的学生、想拿这份数据练手分类与回归的转行者、以及需要一份可复现流程做教学演示的从业者。我会按真实项目顺序讲先做数据体检再分别用多元线性回归做质量预测、用 KNN 和 XGBoost 做二分类中间穿插参数怎么调、指标怎么看、哪里最容易翻车。所有代码基于 pandas scikit-learn xgboost本地 Python 3.10 环境即可跑通不需要 GPU。2. 数据体检与特征工程先搞清楚这份数据能不能直接用2.1 红酒数据集的字段含义与分布陷阱Wine Quality 红葡萄酒子集一共 12 列前 11 列是输入特征最后 quality 是 0–10 的整数评分。字段包括固定酸度、挥发性酸度、柠檬酸、残糖、氯化物、游离二氧化硫、总二氧化硫、密度、pH、硫酸盐、酒精浓度。很多人拿到数据直接df.describe()看一眼就开跑这是第一个坑。先看分布。quality 的取值集中在 5 和 6分别占大约 42% 和 40%3、4、8 这些极端值样本极少。这意味着如果你直接做多分类模型会严重偏向 5 和 6如果你做回归评分预测会被拉向均值。更关键的是这份数据的 quality 是品酒师主观打分的中位数本身带有噪声所以回归的 R² 很难做高0.3–0.4 已经算正常别指望 0.9。另一个陷阱是量纲。总二氧化硫动辄上百柠檬酸只有零点几如果不做标准化KNN 这种基于距离的算法会被大数值特征完全主导。下面这段代码做一次完整的数据体检。import pandas as pd import numpy as np df pd.read_csv(winequality-red.csv, sep;) # 1. 缺失值与重复值 print(缺失值:\n, df.isnull().sum().sum()) print(重复行数:, df.duplicated().sum()) # 2. 目标变量分布 print(df[quality].value_counts().sort_index()) # 3. 特征量纲差异 print(df.describe().loc[[min, max, mean, std]].T) # 4. 特征与质量的相关系数 corr df.corr()[quality].sort_values(ascendingFalse) print(corr)这段代码做了四件事确认没有缺失值这份数据确实干净、发现 240 条左右重复行、看清 quality 的偏态分布、用相关系数初步筛特征。逻辑上重复行要不要删取决于你的目标——如果做预测建模重复样本会导致数据泄漏建议去重如果只是做描述性分析可以保留。参数上sep;是这份 CSV 的分隔符用默认逗号会读成一整列这是新手最常见的翻车点。相关系数通常显示酒精浓度正相关最强约 0.48挥发性酸度负相关最强约 -0.39。这两个特征在后面建模里会反复出现。2.2 特征工程从 11 列里造出真正有用的变量原始 11 列直接喂模型不是不行但做特征工程能明显提升可解释性。我一般会做三件事去重、构造比值特征、处理偏态。# 去重 df df.drop_duplicates().reset_index(dropTrue) # 构造两个业务含义明确的特征 df[free_so2_ratio] df[free sulfur dioxide] / (df[total sulfur dioxide] 1e-6) df[acid_balance] df[fixed acidity] - df[volatile acidity] # 对偏态特征做对数变换 skewed [residual sugar, chlorides, sulfates] for col in skewed: df[col] np.log1p(df[col]) print(df.shape)free_so2_ratio表示游离二氧化硫占总二氧化硫的比例这个比值在酿酒里直接关系到防腐效果比单独看两个绝对值更有意义。acid_balance用固定酸度减挥发性酸度粗略刻画酸度结构。对数变换针对的是残糖、氯化物、硫酸盐这三个右偏严重的特征log1p而不是log是为了避免零值报错。做完这一步特征从 11 列变成 13 列样本从 1599 降到 1359 左右。别小看去重它直接影响后面交叉验证的可靠性。提示特征工程不是越多越好。我见过有人在这份数据上造了 40 多个特征结果模型过拟合交叉验证分数反而下降。先加有业务解释的再考虑多项式组合。3. 多元线性回归预测质量评分R² 上不去到底卡在哪3.1 回归建模的最小可跑通流程多元线性回归是这份数据上最直观的建模方式目标是把 quality 当连续值预测。完整流程包括划分数据集、标准化、训练、评估。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score X df.drop(columns[quality]) y df[quality] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) scaler StandardScaler() X_train_s scaler.fit_transform(X_train) X_test_s scaler.transform(X_test) lr LinearRegression() lr.fit(X_train_s, y_train) y_pred lr.predict(X_test_s) print(R2:, r2_score(y_test, y_pred)) print(RMSE:, np.sqrt(mean_squared_error(y_test, y_pred))) coef pd.Series(lr.coef_, indexX.columns).sort_values() print(coef)关键点有三个。第一fit_transform只在训练集上做测试集用transform否则标准化会引入测试集信息这是数据泄漏的经典形式。第二random_state42保证结果可复现大作业里不固定随机种子每次跑出来分数不一样没法写报告。第三系数要看标准化后的因为量纲已经统一系数大小直接反映特征重要性。典型结果R² 在 0.32–0.38 之间RMSE 约 0.65。酒精浓度系数最大且为正挥发性酸度系数为负和相关系数结论一致。3.2 R² 只有 0.35是模型不行还是数据本身的问题很多人跑到这里就慌了觉得 R² 太低。其实这是这份数据的固有特性不是你的代码有问题。原因有三层。第一层quality 是离散的整数评分本质上是序数变量用线性回归去拟合本身就存在模型设定误差。第二层品酒师打分有主观噪声同样的理化指标可能打出不同分数这部分噪声无法被特征解释。第三层11 个理化指标并不包含所有影响口感的因素比如单宁结构、香气物质这些没进数据集。那怎么提升我一般会做两件事。一是把 quality 二值化转成分类问题这就是下一章的内容。二是尝试多项式特征或树模型捕捉非线性关系。但要注意线性回归的价值在于可解释性系数直接告诉你「酒精浓度每高一个标准差评分平均高多少」这个结论在业务沟通里比一个高 R² 的黑箱模型更有用。注意如果 R² 出现负数说明模型比直接预测均值还差通常是标准化做错、特征里混入了目标泄漏列或者训练测试集划分有问题。先查这三项。4. KNN 与 XGBoost 二分类把评分预测变成好坏酒判别4.1 二分类标签怎么定义才合理把 quality 转成二分类最直接的做法是 quality 7 记为 1好酒否则为 0。但这样正样本只有约 13%类别严重不平衡。另一种做法是 quality 6 记为 1正样本约 55%相对均衡。我一般推荐后者原因是大作业的核心是展示完整流程而不是追求极端不平衡场景下的调参技巧。如果一定要用 7 作为阈值就必须引入class_weightbalanced或 SMOTE 过采样否则模型会全部预测为 0准确率看着有 87%但召回率为 0毫无意义。from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import classification_report, confusion_matrix # 二分类标签 df[good_wine] (df[quality] 6).astype(int) X df.drop(columns[quality, good_wine]) y df[good_wine] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) scaler StandardScaler() X_train_s scaler.fit_transform(X_train) X_test_s scaler.transform(X_test) knn KNeighborsClassifier(n_neighbors5, weightsdistance) knn.fit(X_train_s, y_train) y_pred_knn knn.predict(X_test_s) print(confusion_matrix(y_test, y_pred_knn)) print(classification_report(y_test, y_pred_knn))stratifyy保证训练集和测试集的正负样本比例一致这是分类任务里必须加的。weightsdistance让近邻的投票权重更大通常比均匀投票好一点。4.2 KNN 的 k 值怎么选交叉验证比拍脑袋靠谱KNN 最关键的参数就是 k。k1 时对噪声极度敏感k 太大又会把决策边界抹平。正确做法是用交叉验证扫一遍。from sklearn.model_selection import cross_val_score k_range range(3, 31, 2) cv_scores [] for k in k_range: knn KNeighborsClassifier(n_neighborsk, weightsdistance) scores cross_val_score(knn, X_train_s, y_train, cv5, scoringf1) cv_scores.append(scores.mean()) best_k k_range[np.argmax(cv_scores)] print(最佳 k:, best_k, F1:, max(cv_scores))这里用 F1 而不是准确率作为评分标准因为即使二分类相对均衡F1 也能同时兼顾精确率和召回率。cv5是五折交叉验证数据量 1300 多条五折足够稳定。通常最佳 k 落在 9–15 之间F1 在 0.72–0.76。KNN 的优点是简单、无需训练、可解释相似样本相似结果缺点是预测慢、对量纲敏感、高维下距离失效。这份数据 13 个特征不算高维KNN 表现尚可但如果继续加特征KNN 会迅速退化。4.3 XGBoost 二分类参数怎么设、和 KNN 差在哪XGBoost 是热搜里高频出现的二分类模型在这份数据上通常比 KNN 高 2–4 个百分点。核心参数不多但每个都影响很大。from xgboost import XGBClassifier xgb XGBClassifier( n_estimators300, max_depth4, learning_rate0.05, subsample0.8, colsample_bytree0.8, eval_metriclogloss, random_state42 ) xgb.fit(X_train, y_train) # 树模型不需要标准化 y_pred_xgb xgb.predict(X_test) print(classification_report(y_test, y_pred_xgb)) # 特征重要性 importance pd.Series(xgb.feature_importances_, indexX.columns) print(importance.sort_values(ascendingFalse).head(8))参数说明n_estimators300是树的数量配合learning_rate0.05属于慢学习率多树组合泛化更稳。max_depth4控制单棵树复杂度这份数据特征少深度超过 6 基本必过拟合。subsample和colsample_bytree都是 0.8引入随机性防过拟合。eval_metriclogloss是二分类标准评估指标。注意 XGBoost 不需要标准化因为树模型基于阈值分裂量纲不影响。这一点和 KNN、线性回归不同别把标准化流程无脑套上去。特征重要性通常显示酒精浓度、挥发性酸度、硫酸盐排前三和前面的相关系数、回归系数互相印证。这种多方法交叉验证结论一致是大作业里非常加分的部分。5. 避坑与排查这份数据上最容易翻车的五个地方5.1 读取时分隔符写错整份数据变成一列现象df.shape返回 (1599, 1)所有特征挤在一列里后续df[quality]直接报 KeyError。 原因Wine Quality 的 CSV 用分号分隔不是逗号pd.read_csv默认sep,。 解决显式写pd.read_csv(winequality-red.csv, sep;)。如果拿到的是 UCI 原始格式还要注意表头行。5.2 标准化在划分数据集之前做导致数据泄漏现象交叉验证分数高得离谱换一组测试数据后暴跌。 原因先对全量数据fit_transform测试集的均值和方差信息泄漏进了训练过程。 解决严格按「先划分、再在训练集 fit、测试集 transform」的顺序。用 Pipeline 可以强制这个顺序避免手滑。5.3 二分类阈值定太高模型全预测负类现象准确率 0.87 看着不错但混淆矩阵里正类召回率为 0classification_report的 f1-score 对正类是 0。 原因quality 7 作正类时正样本只占 13%模型学到「全猜 0 就能拿高准确率」。 解决要么把阈值降到 6要么加class_weightbalanced要么用 SMOTE。评估指标换成 F1 或 AUC别只看准确率。5.4 KNN 忘记标准化大数值特征主导距离现象KNN 准确率和随机猜差不多但换成 XGBoost 就正常。 原因总二氧化硫数值上百柠檬酸只有零点几欧氏距离几乎完全由二氧化硫决定。 解决KNN、SVM、逻辑回归这类基于距离或梯度的模型必须标准化树模型不需要。记住这个对应关系别一刀切。5.5 重复行没处理交叉验证分数虚高现象本地交叉验证 F1 有 0.85换一份同分布数据只有 0.72。 原因数据里有约 240 条完全重复的行去重前同一样本可能同时出现在训练折和验证折造成泄漏。 解决建模前先df.drop_duplicates()。如果业务上重复行有含义比如多次检测要单独加一列计数特征而不是简单保留。6. 把结论讲成业务语言从模型输出到可汇报的洞察大作业最后一步不是交代码是交结论。我见过太多人把classification_report截图一贴就完事这在实际工作里会被打回。真正有价值的做法是把模型输出翻译成业务语言并且用多种方法交叉验证同一个结论。拿这份数据来说三个模型都指向同一个事实酒精浓度是预测红酒质量最稳定的正向特征。线性回归里它的标准化系数最大XGBoost 里它的特征重要性排第一相关系数也是最高的 0.48。这种「三方法一致」的结论比任何单一模型的输出都可信。你可以这样写进报告在控制其他理化指标不变的前提下酒精浓度每提高一个标准差质量评分平均提升约 0.3 分被判定为好酒的概率提升约 15 个百分点。第二个可汇报的结论是挥发性酸度的负向作用。它在三个方法里都排进前三系数为负。酿酒工艺上挥发性酸度主要来自醋酸过高会让酒有刺鼻酸味这个结论和领域知识完全吻合。这种「数据结论 领域解释」的组合才是数据分析区别于纯跑模型的地方。第三个结论关于模型选型本身。KNN 的 F1 约 0.74XGBoost 约 0.78差距不大但 KNN 预测一条新样本要遍历整个训练集XGBoost 训练完预测是常数时间。如果做成线上服务XGBoost 更合适如果只是离线分析KNN 的可解释性更好——你可以直接找出「和这瓶酒最相似的 5 瓶它们都是好酒」这种解释在业务沟通里非常直观。最后说一个我自己的习惯。每次做完这类大作业我都会留一个reproduce.py把从读数据到出结论的所有步骤串成一条命令能跑完的脚本固定随机种子输出一份 markdown 格式的结果摘要。这样别人拿到你的仓库python reproduce.py就能复现全部数字比在报告里写「我跑出来是 0.78」有说服力得多。这个习惯我从做第一份数据分析工作保持到现在帮我省了无数次「这个数当时怎么来的」的后悔药。希望帮到你。本文还有配套的精品资源点击获取