简介:波士顿房价分析及matlab实现.zip是一份以波士顿房价数据集为对象、使用MATLAB完成回归与分类建模的教学型实战资源,适合具备基础统计知识并希望在MATLAB环境中动手实践机器学习流程的学习者。该数据集包含506个样本与13个社会经济特征,是经典的统计学习案例。压缩包共10个文件,包括8个.m源码脚本、1个PDF报告和1个txt数据文件,分别覆盖数据导入、LDA/QDA分类、线性回归、回归树等关键实现,脚本注释清晰,便于对照学习和二次修改。目前已有4230人浏览学习。资源围绕数据预处理、模型构建与评估展开,除完整MATLAB源码外,还附带详细说明文档,可帮助读者理解回归树、线性回归等模型的构建原理、参数选择以及MSE、R²等评价指标的计算与解读,也可作为课程设计或入门机器学习项目的参考资料。 在MATLAB里跑通波士顿房价分析,是我觉得用来熟悉数据科学流程最合适的一个入门项目。这个数据集不大,506条样本、13个特征加一个目标变量,用MATLAB来复现时不会像Python那样有一堆现成的库直接调用,反而逼着你去把数据清洗、特征相关性、回归建模、残差诊断这一整套逻辑逐个捋清楚。这篇东西主要面向两类人:一是刚接触MATLAB数据分析、想找一个完整案例练手的学生,二是用惯了sklearn但想看看MATLAB在统计建模上能做到什么程度的工程师。我会直接从数据导入讲到模型评估,把我实际跑过的代码和踩过的坑都贴出来。
在开始之前,先对齐一下版本信息。我这边用的是R2022b,但这套代码在R2019a之后的版本上都能跑,唯一需要注意的是部分绘图函数的样式参数在不同版本里略有差异。如果你用的是更早的版本,遇到报错重点检查readtable、heatmap这几个函数的调用方式。
1. 项目核心思路与数据底子
1.1 波士顿房价数据到底能练什么
这个数据集最初来自UCI机器学习库,反映的是1970年代美国波士顿地区不同城镇的房价中位数(MEDV,单位千美元),严格来说它是做回归任务的标准数据。很多人第一次接触它是在Python的sklearn里,但用MATLAB复现的价值在于:你没法直接用一行train_test_split解决问题,必须手动处理数据划分、特征缩放和交叉验证,这个过程恰恰是理解回归本质的关键。
数据里的13个特征覆盖了四种信息类型,我先用表格做个梳理,方便后面对号入座。
| 特征名 | 含义 | 类型 |
|---|---|---|
| CRIM | 城镇人均犯罪率 | 社会经济指标 |
| ZN | 占地超过25000平方英尺的住宅用地比例 | 区域规划指标 |
| INDUS | 非零售商业用地比例 | 区域规划指标 |
| CHAS | 是否临近查尔斯河(1是,0否) | 区位属性 |
| NOX | 一氧化氮浓度(每千万分之一) | 环境指标 |
| RM | 平均每套住宅的房间数 | 房屋属性 |
| AGE | 建于1940年前的自住房比例 | 房屋属性 |
| DIS | 到五个就业中心的加权距离 | 区位属性 |
| RAD | 到高速公路的便捷指数 | 区位属性 |
| TAX | 每万美元房产税率 | 经济政策指标 |
| PTRATIO | 城镇师生比 | 社会指标 |
| B | 黑人比例的转换指标(1000(Bk-0.63)^2) | 社会指标(分析时通常谨慎对待) |
| LSTAT | 低收入人群比例 | 社会经济指标 |
| MEDV | 自住房屋价格中位数(千美元) | 目标变量 |
1.2 为什么用MATLAB做这个项目
很多人会问,既然Python生态那么成熟,为什么还要用MATLAB。我的看法是,MATLAB在数据可视化和交互式探查上确实有独特优势。你导入数据后,工作区直接能看到每个变量的统计量,画图时用ginput可以临时取点查看异常值,这种操作手感比Python里反复调用plt.show要流畅很多。再加上MATLAB的统计工具箱里fitlm、stepwiselm这些函数封装得很成熟,输出结果自带系数显著性检验和方差分析表,做学术报告时很方便直接引用。
从复现角度说,用MATLAB做一遍等于把机器学习的流程拆开重装了一次。没有现成的transform管道,你得自己写标准化函数;没有train_test_split,你得自己用randperm做划分。这些"笨功夫"做完之后,再看Python里那些封装好的接口,会明显更有底气。
2. 数据导入与预处理实操
2.1 从CSV文件安全导入数据
你在网上下到的数据集通常是csv格式。我用的是带表头的版本,第一行是特征名,从第二行开始是数据。这里的核心命令是readtable,它最大的好处是自动把第一行识别为变量名,同时保留数据的原始类型。
% 读取波士顿房价数据 data = readtable('BostonHousing.csv'); % 快速查看数据结构 head(data) % 查看前8行 summary(data) % 查看每列统计摘要这里有几个关键的细节。readtable在读取时如果发现某一列有字符串,会默认把它当categorical类型处理,但我们的数据集全是数值,所以不会有这个问题。不过你从不同渠道下载的数据集可能带有引号或特殊字符,稳妥起见我建议在读取时显式指定参数:
data = readtable('BostonHousing.csv', 'PreserveVariableNames', true);PreserveVariableNames设置为true可以防止MATLAB把不规范的变量名自动改成合法标识符(比如把带空格的列名改成下划线),这对后续用data.CRIM这种方式取列很重要。如果你发现列名变成了Var1、Var2这种形式,不用慌,用下面这行代码改回来:
% 如果列名丢失,手动指定 data.Properties.VariableNames = {'CRIM','ZN','INDUS','CHAS','NOX','RM','AGE','DIS','RAD','TAX','PTRATIO','B','LSTAT','MEDV'};2.2 缺失值与异常值处理策略
检查缺失值我用的是标准做法:
% 统计每列缺失值数量 missingCount = sum(ismissing(data)); disp(missingCount);波士顿房价数据整体质量很高,一般不存在缺失值,但有个别版本会混入0值。这里要注意区分:CRIM、ZN、INDUS、CHAS这几个变量取0都是有实际意义的(比如CHAS为0表示不临河),不能武断当作异常值处理。真正需要警惕的是NOX、RM、MEDV这些变量是否出现极端偏离的值。
处理异常值时,我习惯用箱线图加标准差联合判断,而不是盲目删除。实战代码如下:
% 检测MEDV中的异常值(超过3倍标准差) mu = mean(data.MEDV); sigma = std(data.MEDV); outlierIdx = abs(data.MEDV - mu) > 3 * sigma; fprintf('检测到 %d 个极端异常值\n', sum(outlierIdx));这里有个经验:波士顿房价数据中MEDV存在一个明显的截断问题,部分高房价被合并记录为50.0,这在建模时需要特别注意。如果你发现模型在高房价区域拟合效果很差,别急着加复杂模型,先检查是不是数据本身存在天花板效应。
3. 可视化探索与特征相关性分析
3.1 用热力图快速锁定关键特征
做回归建模之前,我习惯先把相关矩阵画出来。MATLAB里用corrcoef或者corr都可以,我推荐直接操作table:
% 提取数值矩阵 X = data{:, 1:13}; y = data.MEDV; % 计算相关系数矩阵 R = corrcoef([X, y]); % 画热力图 figure; heatmap([data.Properties.VariableNames, {'MEDV'}], ... [data.Properties.VariableNames, {'MEDV'}], R); colormap(parula); title('特征与房价相关系数热力图');heatmap函数在R2020a之后变得非常方便,之前还需要用imagesc自己画。从相关系数矩阵能看到两个最重要的信息:LSTAT与MEDV的负相关最显著(约-0.74),RM与MEDV正相关最明显(约0.70),这两个特征基本决定了房价模型的下限。而ZN和DIS之间相关性也很高(约0.63),这就是多重共线性的隐患,后面用逐步回归时能直观看到它被剔除。
3.2 散点图矩阵与非线性线索
相关系数只能反映线性关系,当变量与目标之间是非线性关联时,散点图矩阵是更好的选择。MATLAB里我一般用plotmatrix:
% 选择重点特征画散点矩阵 selectedVars = [data.LSTAT, data.RM, data.PTRATIO, data.MEDV]; figure; plotmatrix(selectedVars);从散点图矩阵里最值得注意的模式是LSTAT与MEDV之间存在明显的曲线关系——LSTAT较低时房价随着低收入比例上升下降得很快,之后趋于平缓。这提示我们可以在模型中引入LSTAT的平方项做多项式特征,我用这种方式把线性回归的R²从0.74提到了0.82以上。
另外,如果你使用的是较老版本的MATLAB,plotmatrix不支持直接传入table,需要先转为array。一个小技巧是在figure窗口直接用"工具>基本拟合"菜单,选定散点图后可以快速试验多项式拟合阶数,实时查看拟合效果,这是MATLAB在探索阶段特别顺手的一个功能。
4. 回归建模与模型诊断
4.1 多元线性回归和逐步回归怎么选
预处理完之后,基准模型用fitlm就能完成:
% 全特征线性回归 mdl = fitlm(data, 'MEDV ~ CRIM + ZN + INDUS + CHAS + NOX + RM + AGE + DIS + RAD + TAX + PTRATIO + B + LSTAT'); % 查看模型摘要 disp(mdl);fitlm的输出里有几个关键模块需要关注。首先是R²和调整后R²,全特征模型的R²通常在0.73左右;然后是Coefficients表里的pValue,大于0.05的变量意味着单独来看对房价解释能力不显著。如果你发现很多变量p值不达标——这在全特征模型里很常见——解决办法是改用stepwiselm做逐步回归:
% 逐步回归:基于AIC准则自动增删变量 mdlStep = stepwiselm(data, 'MEDV ~ 1', 'upper', 'MEDV ~ CRIM + ZN + INDUS + CHAS + NOX + RM + AGE + DIS + RAD + TAX + PTRATIO + B + LSTAT', 'Criterion', 'aic');这里我用的'MEDV ~ 1'表示从空模型开始,'upper'指定最大模型,MATLAB会自动基于AIC做双向搜索。实测下来它会把CHAS、AGE、INDUS等变量依次剔除,最终保留RM、LSTAT、DIS、NOX这些核心特征。这比手动一个个试要高效得多,也避免了人工选择偏差。
4.2 训练集验证集划分与损失计算
任何回归模型都需要评估泛化能力,我的划分方式是7:3随机划分,同时固定随机种子保证可复现:
rng(42); % 固定随机种子 cv = cvpartition(height(data), 'HoldOut', 0.3); trainIdx = training(cv); testIdx = test(cv); dataTrain = data(trainIdx, :); dataTest = data(testIdx, :); % 用训练集拟合模型 mdlFinal = fitlm(dataTrain, 'MEDV ~ RM + LSTAT + DIS + NOX + PTRATIO'); % 在测试集上预测 yPred = predict(mdlFinal, dataTest); yTrue = dataTest.MEDV; % 计算RMSE和R² rmse = sqrt(mean((yPred - yTrue).^2)); ssRes = sum((yTrue - yPred).^2); ssTot = sum((yTrue - mean(yTrue)).^2); r2 = 1 - ssRes / ssTot; fprintf('测试集 RMSE: %.4f\n', rmse); fprintf('测试集 R²: %.4f\n', r2);我在多次试验中,只保留RM和LSTAT两个特征的简单模型在测试集上的R²也能到0.73以上,加上DIS和NOX后能到0.80左右,这个表现已经相当可观。这里我最想提醒的是,不要在划分数据之前做任何基于全数据集的特征选择,否则会引入数据泄漏,让测试集结果虚高。
4.3 残差分析与回归假设验证
MATLAB的plotDiagnostics和plotResiduals可以快速做模型诊断。我每次建模最关注的这几张图:
figure; plotResiduals(mdlFinal, 'fitted'); % 残差与拟合值散点图 figure; plotResiduals(mdlFinal, 'probability'); % 残差正态概率图残差与拟合值图如果出现明显的喇叭形分布(残差方差随着拟合值增大而增大),说明存在异方差性。波士顿房价数据在高价位区域确实会出现这种趋势,我处理的办法是对MEDV取对数,建立log(MEDV)与特征之间的回归,然后再把预测结果指数化还原。经过这个变换后,残差分布会更接近正态,拟合精度通常也会上升。
另外一个容易被忽略的点是多重共线性,可以用方差膨胀因子(VIF)来量化。MATLAB里没有现成函数,但可以一次性写出来:
% 计算VIF X = data{:, {'RM','LSTAT','DIS','NOX','PTRATIO'}}; X = [ones(size(X,1),1), X]; vars = [data.Properties.VariableNames(contains(...))]; % 按需处理 vif = diag(inv(corrcoef(X(:,2:end)))); disp(vif);经验上的标准是VIF超过10说明共线性问题严重,如果模型里有RAD和TAX同时出现,它们的VIF大概率会超过15,这正是逐步回归会把其中一个剔除的原因。在你完全掌握统计工具箱之前,用fitlm输出的coefficient标准误大小也能侧面判断共线性,标误异常偏大的变量通常嫌疑最大。
5. 进阶优化:正则化方法与工具链扩展
5.1 岭回归和LASSO的MATLAB实现
全特征线性模型虽然可用,但容易过拟合。正则化的思路是在损失函数中加入系数惩罚项,MATLAB的ridge和lasso函数让这部分实操变得很直接:
% 数据标准化(lasso要求特征同量纲) X_std = zscore(X_train); y_center = y_train - mean(y_train); % LASSO回归:alpha=1 [B_lasso, FitInfo] = lasso(X_std, y_center, 'CV', 10); % 查看最优lambda对应的系数 lassoPlot(B_lasso, FitInfo, 'PlotType', 'CV'); bestIdx = FitInfo.Index1SE; coef_lasso = B_lasso(:, bestIdx);lassoPlot画出的图非常直观,横轴是log(lambda),上方显示当前非零系数个数,下方CV误差曲线会标出最优lambda位置。我推荐用'Index1SE'而不是'IndexMinMSE',因为1SE准则选择的是在最小误差一个标准差范围内的最简模型,泛化能力更好,系数也更稀疏。
岭回归用ridge实现,它的优势是所有特征都保留,只压缩系数幅度,适合特征间相关性较强且你不想丢弃特征的情况。两者对比的话,在波士顿房价这个数据集上LASSO通常表现更好,因为它能把部分不相关特征直接压缩到0,解释性更强。
5.2 用实时脚本和App Designer搭建分析面板
如果你不只是跑一次实验,而是想把分析过程沉淀成工具,MATLAB的实时脚本(.mlx)是很好的载体。你可以把数据导入、特征可视化、回归建模分节管理,还能在代码中间插入文本说明,生成的HTML报告直接给导师或同事看,比一堆零散.m文件专业很多。
更进一步,MATLAB App Designer可以做成一个小型数据分析面板:左侧放文件导入按钮,中间放散点图和热力图坐标区,右侧放模型指标和预测结果表。我在完成基础分析后就用App Designer封装了一个简易版本,核心就是把建模代码包进回调函数。这个过程对本科毕设和课程大作业非常加分,运行界面整体耗时控制在半小时内就能搭好。
如果你需要在论文里输出高质量图片,我还会做一步:在figure里设置好渲染器后,用exportgraphics导出为PDF矢量图,这是MATLAB在排版质量上比截图高出一大截的地方,字体清晰无锯齿,投稿时不会被要求重新截图。
6. 常见问题速查与避坑经验
6.1 数据集读取和类型问题排查
我自己在给学生或同事调试代码时,遇到频率最高的是下面几类问题,整理成一个速查表会更快定位:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| readtable后列名变成Var1 | 文件第一行被识别为数据 | 检查csv是否带表头,用ReadVariableNames指定 |
| predict时报错特征数量不匹配 | 训练与测试数据列数不一致 | 用mdl.CoefficientNames确认模型使用的特征名 |
| heatmap中文坐标乱码 | 系统字体不支持中文显示 | 设置set(groot,'DefaultAxesFontName','宋体')或改用英文标签 |
| lasso结果不稳定 | 未事先标准化数据 | 用zscore先做标准化,再传入lasso |
| 装载.mat文件时报错变量不存在 | 保存时变量被覆盖 | 使用save('filename.mat','data','yPred')显式指定变量 |
| fitlm提示列名重复 | 原csv有重复表头 | 读取后调用data.Properties.VariableNames手动去重 |
6.2 数据泄漏与划分顺序问题
这是最隐蔽的坑。有些人在做特征选择之前先把整个数据集读进来,用全量数据的均值和方差做标准化,然后再划分训练测试集。这种做法会让测试集的信息在训练阶段就"泄露"给模型,导致测试集指标虚高,模型上线后实际表现远低于预期。正确的做法是先划分,再在训练集上计算均值和标准差,用同一组参数去转换测试集。MATLAB里没有Pipeline概念,所以只能手工保证顺序,这也是用MATLAB学机器学习的一个好处——被迫理解每一步在做什么。
6.3 绘图导出与内存清理建议
如果你在R2020b之前的版本里用heatmap,图表导出到EPS格式时可能出现颜色块丢失的问题,这是旧版MATLAB对OpenGL渲染器的兼容问题。建议统一用exportgraphics(gcf,'output.png','Resolution',300),或者直接导出PDF格式,PDF格式实测最稳定。另外循环里如果反复创建figure而且没关,内存占用会显著上升,在循环末尾写一句close(gcf)或者用drawnow limitrate可以避免卡顿。
7. 从复现到扩展:项目还能怎么玩
波士顿房价分析的价值完全取决于你怎么在基线基础上往前走。多数人停在全特征线性回归这一步,实际上还有几个很容易落地的扩展方向,全都在MATLAB里可以直接动手做。第一个方向是特征工程,比如构造RM与LSTAT的交互项、把AGE按照中位数分为两个等级做分组回归,或者对LSTAT做平方变换。第二个方向是换模型,尝试fitrgp做高斯过程回归,或者用fitrtree加交叉验证做随机森林,然后对比不同模型的RMSE。第三个方向是把这套流程沉淀成自己的模板,以后面对任何表格类回归问题,直接套用划分、建模、诊断、导出这套骨架,效率会高很多。
我个人在实际操作中最受用的一个习惯是,每跑完一个模型就用save保存工作区关键变量,命名里带上时间和模型名称。波士顿房价这个项目虽然小,但从它延伸出来的建模思路可以被复用到很多现实预测任务里——从二手房价评估到设备剩余寿命预测,本质都是一回事:找到特征和目标的映射关系,并给出一个可信的误差边界。如果你能把这篇里的每一步都亲手跑一遍,再用自己的思路改进哪怕一个环节,你就已经超过了大多数只写过demo脚本的人。
本文还有配套的精品资源,点击获取