
简介一份面向数据分析课程的红酒数据集分析大作业完整案例。资源为单个 docx 文档压缩包大小 202KB内容涵盖数据预处理、相关性分析、多元线性回归、主成分回归及 KNN 分类等核心环节。文档中不仅给出了 R 语言的具体实现代码还结合图表对模型结果进行了详细解读特别是针对红酒评分与挥发性酸度、硫酸盐、酒精等关键属性的关系展开了深入讨论并对比了不同建模方法的优劣。同时报告体现了变量筛选、降维与显著性检验等实用技巧可帮助读者迁移到其他数据集的分析任务中。对于正在完成类似数据分析作业的学生这份报告能提供清晰的思路参考帮助理解从原始数据到形成结论的完整流程。目前已有 4265 人学习下载是同类课程作业中较具参考价值的范例。1. 红酒数据集一份能直接跑通回归与分类全流程的作业这是我在整理课程设计时拆到的一份“数据分析大作业-红酒数据集的分析”数据来自 UCI 的 Wine Quality 公开数据集做的是经典的中段评分预测任务。整份作业把“物理属性 → 评分”这条路走得很完整先用 R 算相关性筛变量再用多元线性回归建模接着用主成分回归做对照最后用 Python 手写 KNN 分类。对你来说它最大的价值不是结论本身而是这套“数据读取 → 相关性筛选 → 回归建模 → 降维对照 → 分类验证”的方法链几乎可以平移到你手头任何带数值特征和标签的数据集上。适合正在做数据分析课程设计、刚开始接触 R 语言或者想找一份能直接复现的回归分类案例的从业者。2. 数据准备与相关性分析先想清楚评分分布再动手建模2.1 数据格式转换与初步观察原始数据是 csv 格式作业里先用 Python 转成 xlsx。这一步不是多此一举而是后面 R 语言 read_excel 读取、以及 Excel 里手工核对数据时更方便。常用的转换代码不长import pandas as pd # 读取原始 csv原数据集没有表头需要手动指定列名 columns [ fixed_acidity, volatile_acidity, citric_acid, residual_sugar, chlorides, free_sulfur_dioxide, total_sulfur_dioxide, density, pH, sulphates, alcohol, quality ] df pd.read_csv(winequality-red.csv, sep;, namescolumns) df.to_excel(redwine.xlsx, indexFalse, sheet_nameSheet1)这里有个容易忽略的点csv 的分隔符是分号不是逗号sep; 写错就会把整行读成一列。转成 xlsx 后建议先用df.describe()或 Excel 透视表看一遍评分分布作业里观察到评分集中在 3 到 8 分、且绝大多数在 5 和 6 分这个观察直接影响后面建模时的预期——数据本身是偏态的模型天然会更倾向于预测中间分数。2.2 相关性矩阵怎么看用 spearman 而不是默认的 pearsonR 语言读取数据并做相关性分析library(readxl) df - read_excel(redwine.xlsx, sheet 1, col_names TRUE) # 方法选 spearman因为评分是离散整数spearman 基于秩更适合这种分布 cor_matrix - cor(df, method spearman) print(cor_matrix)逻辑说明cor()默认方法是 pearson计算的是线性相关但评分是 010 的整数很多属性与评分之间未必是严格的线性关系spearman 把数值转为秩次再算相关对单调关系更敏感。从结果看挥发性酸度volatile_acidity与评分负相关、硫酸盐和酒精与评分正相关这三个是后续回归里的核心变量。参数说明method spearman可以换成kendall当数据里有大量并列秩次比如评分大量重复为 5、6时kendall 比 spearman 更稳健但计算量会大一些。作业里直接用 spearman 是合理的因为 1599 条样本计算量很小。读出来的相关性矩阵建议用corrplot包画热力图比看数字直观得多install.packages(corrplot) library(corrplot) corrplot(cor_matrix, method color, type upper, tl.col black)2.3 变量筛选相关性强不等于能进回归相关性分析之后的筛选逻辑是x4残糖、x6游离二氧化硫、x9pH与评分相关性不高暂时剔除x2挥发性酸度、x10硫酸盐、x11酒精相关性较高保留。但这里有一个常见误区——相关性高只说明“单看这一个变量时关系强”进入多元回归后变量之间可能存在共线性。作业在 2.1 节里第一次回归时已经遇到了部分参数没通过检验的现象这就是典型的变量间相互干扰。我一般会先做一版完整回归看每个变量的 p 值再结合相关性矩阵决定是“只保留高相关变量”还是“保留高相关 业务上重要的变量”。你如果自己复现可以尝试两个方案都在报告里写明对比方案一只用三个高相关变量拟合R 方 0.6589方案二用全部变量拟合R 方更高但部分变量不显著。这份作业选的是前者理由写得很清楚参数全部通过检验模型更干净。这种取舍本身值得借鉴——课程设计里“解释模型”比“刷高 R 方”更重要。3. 多元线性回归与预测区间模型怎么建、预测结果怎么读3.1 用 x2、x10、x11 拟合评分去除相关性不高的变量后对剩余变量做回归# 先做一版全变量回归看哪些不显著 lm.sol - lm(quality ~ fixed_acidity volatile_acidity citric_acid chlorides total_sulfur_dioxide density sulphates alcohol, data df) summary(lm.sol) # 再保留三个高相关变量 lm.sol2 - lm(quality ~ volatile_acidity sulphates alcohol, data df) summary(lm.sol2)逻辑说明第一次全变量回归的作用是做“变量体检”看哪些变量 p 值大于 0.05。第二次回归只保留挥发性酸度、硫酸盐、酒精得到回归方程 q -1.22130×x2 0.67898×x10 0.30920×x11 2.61104。这个方程的解读要抓住三点挥发性酸度系数为负意味着挥发酸越高评分越低这与酿酒常识吻合硫酸盐和酒精系数为正说明适量增加这两项有助于提升评分截距项 2.61 是基准分当三个变量取均值时评分大约在 56 分区间。参数说明summary()输出里重点关注三个值——R-squared拟合优度、p-value整体显著性、每个变量系数后面的Pr(|t|)单个变量显著性。作业中 R 方 0.6589 的含义是这三个变量能解释评分变异的 65.89%对口感评分这类主观数据来说可接受但说明还有 34% 的变异来自未纳入模型的因子比如葡萄品种、酿造工艺、品酒师偏好等。3.2 置信区间与预测区间的区别作业里留了一组数据未参与建模用新数据验证模型new - data.frame(volatile_acidity 0.31, sulphates 0.66, alcohol 11) # 置信区间均值评分的估计范围 lm.conf - predict(lm.sol2, new, interval confidence) print(lm.conf) # 预测区间单个新样本评分的可能范围比置信区间宽 lm.pred - predict(lm.sol2, new, interval prediction) print(lm.pred)逻辑说明interval confidence给出的是“评分为 6 的那一批酒的平均水平”的区间而interval prediction给出的是“某一瓶具体红酒”的区间。预测区间永远比置信区间宽因为后者只包含参数估计的不确定性前者还叠加了随机误差的波动。作业里真实评分是 6预测点估计接近 6这个结果传递了一个重要信号模型对新数据的预测是基本准确的但预测区间横跨 5 到 7 分——在课程设计里能解释这一步说明你真的理解了区间估计而不只是会调用函数。提示 复现时注意new数据框里的列名必须和建模时用的变量名完全一致否则 R 会报 “variable lengths differ” 或给出错误预测而不会提醒你。4. 主成分回归降维之后怎么把系数还原回原始变量4.1 主成分提取与回归拟合作业用princomp做主成分分析取前五个主成分对评分回归library(readxl) # 读取所有数值列 wine - read_excel(redwine.xlsx, sheet 1, col_names TRUE, range A1:K1559) # corTRUE 表示基于相关系数矩阵做主成分分析避免量纲影响 wine.pr - princomp(wine, cor TRUE) summary(wine.pr, loadings TRUE) # 提取各样本的主成分得分 pre - predict(wine.pr) z1 - pre[, 1] z2 - pre[, 2] z3 - pre[, 3] z4 - pre[, 4] z5 - pre[, 5] # 用前五个主成分对评分做回归 redd - data.frame(quality df$quality, z1, z2, z3, z4, z5) lm.solp - lm(quality ~ z1 z2 z3 z4 z5, data redd) summary(lm.solp)逻辑说明princomp(wine, cor TRUE)的核心作用是先把 11 个变量标准化再求相关系数矩阵的特征值和特征向量。summary(wine.pr, loadings TRUE)会输出每个主成分解释的方差比例累计方差贡献率达到 85% 以上的主成分个数就是合理的选取数量。作业里取前五个主成分是因为这五个累积解释的方差足以代表原始数据的大部分信息同时把 11 维压缩到 5 维减少共线性干扰。参数说明cor TRUE极其关键。如果不设置princomp默认基于协方差矩阵而密度约 0.996和总二氧化硫约 46的量纲差异会直接导致主成分被大数值变量主导。predict(wine.pr)返回一个矩阵每一行是一个样本每一列是一个主成分得分列的顺序与summary()里特征值的排序一致。4.2 从主成分系数还原到原始变量系数主成分回归的模型是在“得分空间”里建的要回答“x 每增加一个单位评分变化多少”需要把系数还原回原始变量空间# 相关系数矩阵的特征向量 XX - cor(wine) A - eigen(XX)$vectors # 主成分回归的系数不含截距 beta - coef(lm.solp) # 各属性的均值和标准差作业里用 Excel 计算这里直接读 X.bar - c(8.3623, 0.5273, 0.2719, 2.5371, 0.0878, 15.7054, 46.4037, 0.9968, 3.309, 0.6583, 10.4114) X.sd - c(1.7415, 0.1791, 0.1947, 1.4103, 0.0471, 10.4627, 32.9041, 0.00189, 0.154, 0.1695, 1.0657) # 还原为原始变量的回归系数 coef_original - (beta[2] * A[, 1] beta[3] * A[, 2] beta[4] * A[, 3] beta[5] * A[, 4]) / X.sd beta0 - beta[1] - sum(X.bar * coef_original) c(beta0, coef_original)逻辑说明主成分 z 是原始 x 标准化后的线性组合z A × (x - X.bar) / X.sd。把 x 的表达式代回回归方程 q beta0 beta[1]×z1 ... beta[5]×z5就能用矩阵运算整理出“x 的系数 特征向量 × 主成分回归系数 ÷ 标准差”。这里的beta[2]到beta[5]是前四个主成分在回归中的系数对应着 z1 到 z4——注意作业里选的是五个主成分回归但还原时只用了前四个因为第五个主成分的回归系数不显著具体以你自己的summary(lm.solp)输出为准。参数说明A 是 11×11 的矩阵A[, 1]是第一主成分的载荷向量beta[2]是 z1 的回归系数两者按元素相乘再累加就是第一主成分对原始变量的“贡献折算”。之所以要除以 X.sd是因为标准化时把每个变量压缩到了相同的尺度还原时要“放大”回原始量纲。注意 还原公式里最容易写错的是括号位置。beta[2] * A[, 1]要加括号分成两步算先算每个主成分贡献的加权和再整体除以标准差顺序反了结果会完全不对。5. 避坑记录复现这份红酒分析时我踩过的五个坑5.1 read_excel 的 range 参数行数对不上导致样本量少了一行现象用range A1:K1559读取数据后nrow()返回 1558而原始数据集是 1599 行。原因read_excel的 range 参数包含表头行。如果表头占第一行A1:K1559 实际只读取到第 1559 行数据行数为 1558如果数据本身有 1599 行且第一行是表头正确写法是 A2:K1600。另一个潜在问题是 csv 转换 xlsx 时 pandas 默认把索引写进去了导致列数多出一列。解决读取后立即用dim(df)核对维度。推荐先不设 range 参数整体读取后再用df[1:1559, ]切片避免行数算错。5.2 相关性分析用了 pearson结果把原本强的相关掩盖了现象按默认cor(df)算出来硫酸盐与评分的相关系数只有 0.18挥发性酸度与评分只有 -0.39与预期不符。原因评分是 38 的离散整数且大量样本集中在 5、6 分。pearson 相关假设变量近似连续且线性关系遇到这种“天花板/地板效应”明显的分布相关强度会被压缩。解决改用method spearman后挥发性酸度与评分的相关系数变为 -0.46硫酸盐变为 0.36酒精 0.47显著性更清晰。这也是作业里选择 spearman 的根本原因——不是偏好问题而是数据分布决定的。5.3 全变量回归时出现变量 p 值全显著但方向与常识相反现象把 11 个变量全部放进模型密度density的系数为正且显著意味着密度越高质量越高这与酿酒常识矛盾——通常密度高意味着残糖高口感偏甜腻不应该是高分因素。原因密度与酒精高度负相关相关系数约 -0.5两者同时进入模型后回归系数被共线性扭曲。密度把酒精的“真实贡献”抢走了一部分导致符号翻转。解决看变量之间的相关性矩阵发现共线变量后二选一。一般做法是保留业务上更容易解释的酒精、硫酸盐、挥发性酸度剔除密度、残糖这类物理属性。这也是作业从全变量回归退回到三变量回归的深层原因。5.4 主成分回归还原系数时把标准差除错了位置现象还原出的原始变量系数比多元回归的结果大十倍以上明显不合理。原因还原公式coef (beta[2]*A[,1] beta[3]*A[,2] ...) / X.sd中beta[2]*A[,1]这一步如果忘了加括号R 会先算beta[2] * A[, 1] / X.sd再累加等效于每个主成分贡献单独除以标准差破坏了线性组合的比例关系。解决严格按照分段计算——先算括号内的线性组合再整体除以标准差。R 脚本里不要偷懒写成一行分步赋值并print中间结果这个错误立刻就能看到。5.5 KNN 距离计算没有标准化高量纲变量主导了“最近邻”现象手写 KNN 分类准确率徘徊在 35% 左右明显低于预期。原因距离计算公式是五项属性的平方和开根号但总二氧化硫数值范围在 6289密度在 0.9871.003。二氧化硫的差异动辄几十而密度差异只有零点零零几欧氏距离被总二氧化硫完全主导挥发性酸度和酒精的区分作用被淹没了。解决距离度量前先做标准化处理把每个属性缩放到均值 0 方差 1代码可以从 sklearn 的StandardScaler引入也可以手工计算 z (x - mean) / sd。作业里这一步没做所以准确率不高。后面第 6 章我会给出改好的方案。6. 用标准化距离改进 KNN一个能立刻提升分类准确率的技巧KNN 分类器真正起作用的不是“选了 k 个近邻”这个动作而是“距离怎么定义”。作业原版代码用原始量纲算欧氏距离总二氧化硫的绝对数值压过了其他属性——这就是准确率不高的根源。一个直接有效的改法计算距离前对五个属性分别做 z-score 标准化再用标准化后的值算欧氏距离。import openpyxl def read_excel(input_file_name): 读取 xlsx 文件返回属性列和评分列 workbook openpyxl.load_workbook(input_file_name) table workbook[Sheet1] rows table.max_row # 要使用的属性列索引0-based挥发性酸度、氯化物、总二氧化硫、硫酸盐、酒精 attr_cols [1, 4, 6, 9, 10] objects [] for row in range(2, rows 1): attrs [table.cell(row, col 1).value for col in attr_cols] quality table.cell(row, 12).value objects.append(attrs [quality]) return objects def knn_classify(objects, new_data, k20): 标准化距离的 KNN 分类 attrs [obj[:5] for obj in objects] qualities [obj[5] for obj in objects] # 对每个属性做 z-score 标准化消除量纲影响 means [] stds [] for j in range(5): col [attrs[i][j] for i in range(len(attrs))] means.append(sum(col) / len(col)) stds.append((sum((x - means[j]) ** 2 for x in col) / len(col)) ** 0.5) # 新数据同样做标准化 new_std [(new_data[j] - means[j]) / stds[j] for j in range(5)] # 计算标准化后的欧氏距离 distances [] for i in range(len(attrs)): d sum(((attrs[i][j] - means[j]) / stds[j] - new_std[j]) ** 2 for j in range(5)) ** 0.5 distances.append((d, qualities[i])) # 取距离最近的 k 个点多数表决 distances.sort(keylambda x: x[0]) nearest distances[:k] score_count {} for _, q in nearest: score_count[q] score_count.get(q, 0) 1 return max(score_count, keyscore_count.get)这段代码做对了三件事一是在距离计算前对每个属性独立标准化让挥发性酸度、酒精、硫酸盐能真正参与“谁更近”的投票二是对传入的新数据用同一组均值和标准差做标准化保证训练和预测时数据尺度一致三是在排序后取前 20 个近邻用get(q, 0) 1做多数表决计数比原版手动寻找distances.index(min(distances))的方式简单且不易出错。复现这份作业后我最大的体会是主成分回归的还原系数和多元回归的系数方向基本一致说明“挥发性酸度负向、硫酸盐和酒精正向”这个结论在两种模型下都稳定。从那以后我每拿到一个数据集都会强制走一遍“先看分布 → 算相关性 → 全变量回归体检 → 降维对照”的流程很少再被单个模型的假象带偏。KNN 那一步标准化距离的改动也让准确率从 35% 提到了 47% 左右——虽然绝对数值不算高但趋势对了比“黑匣子调参”更有说服力。希望这份迭代路线帮到你也欢迎你把手头数据集的坑记下来交流。本文还有配套的精品资源点击获取