ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

机器学习房价预测可视化系统:Flask与ECharts全链路实战

2026/10/5 15:35:44 拓冰建站 浏览量
机器学习房价预测可视化系统:Flask与ECharts全链路实战 做机器学习项目最容易陷入两个极端要么只跑通一个模型就偃旗息鼓要么花大量时间折腾前后端却忽略了数据本身。今天要拆解的这套基于大数据的房屋数据分析可视化系统恰好踩在了中间那根平衡线上——用机器学习预测房源价格用Flask搭建服务端再用可视化图表把分析结果直观呈现出来。这是一个典型的数据采集特征工程模型训练Web展示全链路项目既有算法层面的东西又有工程落地的内容非常适合作为毕业设计、求职作品集里的实战项目来打磨。这套系统的核心价值在于它不是一个孤立的模型训练脚本而是一套能跑起来、能演示、能讲清楚设计逻辑的完整系统。你从各地商品房数据出发经过清洗、特征构造、模型训练最后在网页上看到房价分布、影响因素排名、价格预测结果这些可视化内容。无论你是正在找机器学习方向的选题还是想学习如何把算法变成产品这套系统的设计思路都值得从头到尾捋一遍。1. 系统整体思路为什么是机器学习Flask可视化这套组合1.1 这个项目到底解决什么问题先说需求侧。二手房市场、商品房交易平台上积累了海量的房源数据里面藏着面积、朝向、楼层、装修、小区位置、周边配套等几十个字段。买家关心的是这套房子值不值这个价卖家关心的是我挂牌价定多少合理而数据分析要做的事情就是把这些非结构化的经验判断变成由数据驱动的、可量化的结论。这套系统围绕三条主线展开第一条线是数据分析对房源数据做多维度的统计比如不同商圈的均价差异、面积与总价的关系、楼层对单价的影响第二条线是预测算法选择一种或多种机器学习模型对房源价格进行预测输入一套房子的特征输出一个合理的价格区间第三条线是可视化把分析结果和预测结果通过图表在前端页面集成展示让业务结论一目了然。换句话说系统不是单纯告诉你北京的房价平均每平六万而是能让用户选一个商圈、输入面积和户型点一下按钮就得到预测价格同时还能看到影响价格的特征权重排序。这样一个闭环既体现了机器学习的能力又照顾到了应用的完整度。1.2 技术选型背后的几个考量技术栈选的是Python生态里的经典组合数据处理用Pandas和NumPy可视化分析用Matplotlib和Seaborn做探索性分析Web端图表用ECharts机器学习用Scikit-learn后端服务用Flask。Flask在这套系统里承担的是轻量级应用服务器的角色。很多人会问为什么不直接用Django或者说为什么不把模型封装成单独的API服务这里有一个实际场景的考量项目属于教学型、演示型系统数据量和并发量都不会很大用户群体主要是管理者和分析人员。Flask足够轻、足够灵活一个Python文件就能启动服务模板渲染和接口返回都可以兼顾不像Django那样带有完整但略显沉重的ORM和管理后台。更重要的是Flask把模型加载和接口逻辑放在同一个进程里开发调试极其方便这对于新手梳理整个项目流程是非常友好的。机器学习选型上也有些讲究。房价预测本质上是一个回归任务Scikit-learn提供了从线性回归到随机森林、梯度提升树的完整算法链。这套系统采用多模型对比的策略先用线性回归做一个基线再用决策树、随机森林、XGBoost等模型去对比效果最后把表现最好的模型序列化保存供Flask运行时加载。这样做既能在文档里展示不同模型的性能对比表又能让使用者直观感受到算法选型对结果的影响。可视化的选型也有明确理由。ECharts是纯前端的图表库图表交互流畅、配置项丰富而且对中文支持好。更重要的是它跟Flask的模板渲染天然配合——后端把处理好的JSON数据传给前端ECharts接收数据后渲染图表。比起用Python生成图片再嵌入网页的做法ECharts支持缩放、提示、下钻等交互操作演示效果专业很多。2. 数据准备与特征工程房子的价格藏在哪些字段里2.1 数据采集与初步探查做房价分析数据是最核心的资产。网上有公开的房价数据集比如一些Kaggle上的房价竞赛数据、某些爬虫获取的房产平台数据但真正启动项目前必须先搞清楚数据的字段构成和样本量。常见的房源数据字段包括小区名称、所在区域、总价、单价、面积、户型几室几厅、朝向、楼层、装修情况、建造年份、建筑类型、周边配套如地铁距离、学校距离等。拿到的原始数据往往存在各种脏的情况。这里我建议第一步先做一个全面的探查清单数据量有多少行、多少列哪些字段是数值型、哪些是类别型。每条样本是否有唯一标识是否存在重复记录。每个字段的缺失比例缺失比较严重的字段要不要保留。目标变量通常是总价或单价的分布形态是否存在极端异常值。这个阶段用df.info()、df.describe()就可以快速摸清数据的大致面貌。有一点要特别提醒不要急着删除任何字段先搞清楚每个字段的业务含义。比如楼层如果是一个字符串低楼层/中楼层/高楼层那它其实是一个有序类别特征处理方式和普通字符串完全不同再比如朝向里可能存在南北南东南东西等多种取值这些都需要在特征工程阶段仔细处理。我的习惯是先写一个字段说明文档把每个字段的类型、含义、可能的取值范围记下来后面做特征处理时能省掉大量回头查证的功夫。2.2 缺失值、异常值与房价数据的清洗细节数据清洗是整个项目里最枯燥但最影响最终效果的一环。缺失值处理要看场景对于面积、总价这样直接决定房价的关键字段如果有缺失优先选择删除记录对于朝向、装修这样取值有限的类别字段可以用众数填充对于连续型字段如建造年份可以用中位数填充。异常值在房价数据里非常常见。比如总价为1万的一套房、单价为2000元一平的所谓豪宅、面积为3000平的住宅这些明显违反常识的记录往往来自数据录入错误或者爬虫解析错误。我建议针对单价和面积两个核心字段分别用一个合理的业务边界来过滤比如单价低于5000元/平和高于15万元/平都视为异常。过滤异常值的时候一定要看分布图先画出价格分布的直方图或者箱线图观察数据的正常范围再决定滤波的阈值而不是拍脑袋定上下限。这里分享一个实际操作中踩过的坑某次清洗时我按总价50万且面积30平的条件清理了一遍结果后来建模时发现部分数据被误删了。原因是一些商住两用房源面积确实很小但单价很高单价、总价、面积三者之间的逻辑关系并没有通过交叉验证。正确的做法是分别对单价和总价做分布检测然后用面积×单价≈总价的等式关系校验这三者的一致性差异超过某个比例才判定为异常这样误删率会低很多。2.3 特征构造与编码的实操经验清洗完的数据还不能直接丢进模型。房源数据里大部分特征是类别型的比如朝向、装修、建筑类型、所在区域。机器学习模型只能处理数值因此需要做编码转换。对于没有大小关系的类别特征比如朝向南、北、东、西、南北、建筑类型板楼、塔楼、平房我用One-Hot编码也就是每一类变成一个0/1的特征列。这里要特别注意如果某一类别的取值特别多比如小区名称有成百上千个取值One-Hot之后特征维度会爆炸这时候更好的做法是换成目标编码——用小区的平均房价作为该小区的特征值把高基数类别压缩成一个数值列。对于有明显大小关系的类别特征比如楼层低、中、高、装修简装、精装、豪装用映射的方式把它们转为有序数值比如低楼层1、中楼层2、高楼层3。合理的顺序映射能帮树模型快速找到切分点这一点比单纯One-Hot的效果更好。还有一个非常有效的特征构造角度从原始数据中生成新的衍生特征。比如房龄 当前年份 - 建造年份、楼龄平方项房价与房龄往往呈非线性关系、每平米价格 总价 / 面积这个通常作为单价分析的预测量、卧室面积占比、朝向是否南北这种0/1标志位。特征工程的本质是把业务常识翻译成模型能够利用的数学表达。你越了解房产交易里什么因素影响价格你构造出来的特征就越能提升模型效果。3. 预测算法选型与模型训练从线性回归到集成学习3.1 房价预测的算法对比与选型逻辑房价预测这个任务很特殊它是一个典型的多因素强非线性回归问题。面积、位置、楼层、装修、房龄、交通配套都会影响价格而且这些因素之间还可能存在交互作用。比如同样的面积在核心商圈和远郊的价格天差地别同样是高层电梯房和楼梯房的价格完全不同。针对这种数据特征我把候选算法分成三个梯队。第一梯队是线性回归、岭回归这类基础模型它们的作用是提供基线分数——如果集成模型连线性模型的20%提升都没有那大概率是特征工程出了问题而不是模型不够好。第二梯队是决策树和随机森林它们天然能处理非线性关系而且随机森林对异常值不敏感能自动评估特征重要性非常适合作为主力模型。第三梯队是梯度提升树比如XGBoost和LightGBM它们在结构化数据上几乎是无冕之王但是训练参数多、调参复杂度高适合在项目后期作为效果冲刺。实际项目里我的选型策略是这样的先用线性回归建立基线得到一个可解释的R²分数然后用随机森林跑一版默认参数观察是否有明显提升最后用网格搜索对随机森林或XGBoost做一轮调参。整个对比过程用一张表格记录下来无论是写文档还是做答辩这张表都是很有说服力的素材。3.2 基于Scikit-learn的模型训练与参数调优用Scikit-learn做房价预测非常直接。整体流程是数据集划分、模型初始化、训练、评估。但有一些细节值得注意首先是数据划分时要用分层抽样。如果目标变量存在明显的长尾分布直接用train_test_split可能让训练集和测试集的价格分布差异较大。我用StratifiedKFold对价格分箱后的标签做分层划分确保训练集、测试集中从低价房到高价房的样本比例接近。第二个关键点是特征标准化。线性回归和岭回归对特征的尺度非常敏感面积变量是几十到几百总价变量是几十万到几百万如果不做标准化正则项会不公平地惩罚小尺度特征。使用StandardScaler把训练集特征标准化然后用同一个scaler转换测试集和后续预测时的输入数据。这里要特别注意scaler只能在训练集上fit测试集和预测数据只能用transform不能重新fit否则会造成数据泄露让模型评估结果虚高。第三个细节是模型保存与加载。训练完一个效果满意的模型后用joblib.dump保存到本地文件。Flask应用启动时通过joblib.load加载模型文件预测接口拿到用户输入的特征后先做和训练时一样的预处理编码、标准化再调用model.predict()输出结果。整个过程要注意训练时的特征顺序和预测时的特征顺序必须完全一致一个字段的顺序错了预测值就完全失真了。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import StratifiedKFold, cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline import joblib # 特征列要提前固定好顺序后续预测必须严格一致 feature_cols [area, bedrooms, hall, floor_num, age, subway_distance, is_north_south, price_per_area] X df[feature_cols] y df[total_price] # 分层划分保证训练/测试集价格分布一致 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 管道方式先标准化再建模 pipeline Pipeline([ (scaler, StandardScaler()), (model, RandomForestRegressor(n_estimators300, max_depth15, random_state42)) ]) pipeline.fit(X_train, y_train) print(Train R2:, pipeline.score(X_train, y_train)) print(Test R2:, pipeline.score(X_test, y_test)) joblib.dump(pipeline, models/house_price_model.pkl)这套代码的典型效果是线性回归R²在0.6左右随机森林R²在0.85以上如果特征工程做得好XGBoost甚至能到0.9。测试集上的R²能达到这个量级说明系统已经具备实际参考价值了。3.3 模型评估不只看R²还要看误差分布很多初学者喜欢盯着R²看认为R²越高模型越好。这个判断在房价预测里需要打个折扣。R²衡量的是模型对数据方差的解释能力但房价数据里难免有一些特殊房源比如顶级豪宅、历史建筑改造房这些样本本身偏离主流规则任何模型都很难预测准。所以我在项目里同时看三个指标R²、平均绝对误差MAE和均方根误差RMSE。MAE的单位和房价一致可以直接解释为平均预测偏差多少钱。假如测试集上的MAE是15万那就意味着系统对一套300万的房子预测误差平均在5%左右这个精度在业务上是说得过去的。另外还必须画出真实值vs预测值的散点图并叠加一条yx参考线。散点越贴近对角线说明模型预测越准。如果看到散点在高价位区域出现明显的发散说明模型对高价房源的学习能力不足这时候可以考虑对价格取对数变换让长尾数据压缩得更均匀。这个技巧对线性模型尤其有效——对数变换能把乘性误差变成加性误差让线性回归的假设更成立。4. Flask后端搭建与可视化呈现把模型和数据变成能用的系统4.1 Flask接口设计与路由实现Flask在项目里承担两个职责一是渲染页面模板二是提供数据访问的API接口。我建议把路由按功能分成几块首页总览路由、数据分析图表数据路由、房价预测接口路由、历史记录存储路由。一个常见的错误是试图把所有接口都塞进一个文件里。项目虽然不大但还是要做好代码结构。我常用的目录组织是这样的app.py作为应用入口负责路由注册和启动models/目录放训练好的模型文件和特征列配置utils/目录放数据预处理函数包括特征编码和标准化逻辑templates/放HTML模板static/放ECharts、JavaScript和CSS文件。预测接口是系统的核心它的设计思路是前端页面把用户填写的表单参数通过POST请求发给后端后端把这些原始参数转换成模型所需的特征向量调用model.predict()得到预测价格再返回JSON结果给前端展示。这个接口里最容易被忽略的是参数校验。如果用户提交的面积是字符串abc或者楼层超出合理范围后端必须返回友好的错误提示而不是让模型调用直接抛异常。写接口之前先想清楚入参校验规则这比接口本身更重要。from flask import Flask, request, jsonify, render_template import joblib import pandas as pd import numpy as np app Flask(__name__) model joblib.load(models/house_price_model.pkl) feature_cols [area, bedrooms, hall, floor_num, age, subway_distance, is_north_south, price_per_area] app.route(/) def index(): return render_template(index.html) app.route(/api/predict, methods[POST]) def predict(): data request.get_json() # 参数校验缺失、类型、范围 try: area float(data.get(area)) if area 20 or area 500: return jsonify({code: 1, msg: 面积参数超出合理范围}) bedrooms int(data.get(bedrooms)) ... # 构造特征向量注意顺序必须与训练时一致 features np.array([[area, bedrooms, hall, floor_num, age, subway_distance, is_north_south, price_per_area]]) pred model.predict(features)[0] return jsonify({code: 0, predict_price: round(float(pred), 2)}) except Exception as e: return jsonify({code: 1, msg: 参数格式错误 str(e)}) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)这段代码里的feature_cols其实就是从训练脚本里导出的同一份字段列表我在项目里把特征列定义统一放到一个config.py里训练脚本和Flask应用共用这份配置从根本上避免字段顺序不一致的问题。4.2 ECharts可视化图表的落地细节可视化部分我用ECharts重点呈现几个维度的内容。第一张图是各区域平均单价柱状图——按行政区域或商圈聚合横向对比不同区域的房价水平。第二张图是面积与总价的散点图用颜色区分不同户型的样本直观展示面积与价格的相关关系。第三张图是特征重要性排名图把随机森林的feature_importances_输出成横向条形图回答什么因素对房价影响最大这个核心问题。第四张图是预测结果对比图把模型预测价格和实际价格放在一起做对比配合表格展示误差。ECharts在使用中有几个容易出问题的地方。第一个是图表数据格式ECharts的series.data需要特定的JSON结构比如散点图的数据是一个二维数组[[x1,y1],[x2,y2]]。后端返回数据前就要组装好这个结构而不是在前端用JavaScript做二次转换这样页面渲染更高效。第二个是中文乱码问题如果HTML模板没有设置meta charsetutf-8而ECharts的数据里有中文区域名图表会显示乱码这个细节在本地开发时不容易发现部署到服务器必须检查。第三个是图表的自适应问题ECharts容器需要设置固定高度否则图表只有宽度没有高度渲染结果是空白。前端的异步加载逻辑用jQuery或原生fetch都可以。页面加载时向后端请求一次数据接口拿到JSON后初始化多个ECharts实例。这里我建议在页面加载时做一个Loading状态避免用户看到空白页面。图表加载完后可以用ECharts自带的tooltip组件显示具体数值这个交互体验比静态图片好很多。4.3 前后端联调与部署经验前后端联调是整个开发过程中最耗时间的环节。一个常见的问题是后端返回的JSON字段名和前端JavaScript里取的字段名对不上比如后端返回predict_price而前端写的是predictPrice浏览器控制台不会报错但页面上的价格显示就是undefined。解法是一开始就约定好接口文档把所有字段名写清楚前后端各留一份联调时对着文档检查。部署环节本地开发用app.run(debugTrue)方便调试但真正上线演示时建议关闭debug模式并用waitress或gunicorn作为生产服务器。Windows环境我习惯用waitress一条命令waitress-serve --port5000 app:app就能启动省去配置nginx的复杂度。如果希望外网访问可以配合一个内网穿透工具把5000端口暴露出去这样在手机上也能演示系统。还有一个经验训练好的模型文件要跟Flask应用一起打包。如果模型文件丢失应用启动时报错模型文件不存在这类问题在交接项目时最常遇到。我习惯在app.py启动时加一个模型文件的检查逻辑文件不存在就直接给出明确提示而不是等到调用接口时才崩溃。5. 完整实践流程从原始数据到可演示系统5.1 项目结构与开发顺序很多人拿到一个项目不知道从何下手。我建议按照数据 → 分析 → 模型 → 接口 → 页面的顺序逐层推进每一步都有可验证的产出。具体开发顺序拆成七个步骤获取数据完成字段探查和数据清洗产出一份clean_data.csv。做探索性数据分析画出价格分布、区域均价、面积与价格关系等图表确定核心分析维度。完成特征工程确定特征列和编码方式产出一份feature_config.py。训练模型对比多个算法选择最优模型并保存为pkl文件。搭建Flask基础框架先跑通首页渲染。实现数据接口把分析结果包装成JSON返回前端。用ECharts渲染图表实现预测表单和结果展示。这套顺序的好处是每一阶段结束都有看得见的成果不会出现做了一半发现数据质量太差、模型效果不行不得不推倒重来的情况。尤其是第1到第4步属于模型能否做好的关键路径前面的工作越扎实后面的展示效果越有说服力。5.2 核心代码实现走读回到代码实现层面我把整个系统最核心的三块代码拆开讲解。第一块是数据清洗和特征工程。这部分虽然看起来简单但决定了模型上限。核心逻辑是统一数据格式、处理缺失值、过滤异常值、转换类别变量、构造衍生特征。异常值过滤这里我对单价做了一次分位数截断——取单价的1%和99%分位数作为上下界把超出边界的样本视为录入错误并删除。这个策略比固定阈值更稳健因为不同城市、不同年份的房价水平差异很大固定阈值不普适。分位数截断的代价是可能误删真实的超高价房所以要结合房价分布图人工确认边界合理性。第二块是模型训练和评估。模型选择随机森林作为最终方案因为它既有不错的预测精度又提供特征重要性输出。训练时我用管道把标准化和模型串起来然后用5折交叉验证看稳定性。交叉验证的结果比单次划分更可信如果5折的R²标准差超过了0.05说明模型对数据划分过于敏感需要检查特征工程是否有问题。训练完成后把模型和特征配置一起保存供Flask加载。第三块是Flask接口和前端渲染。除了预测接口我还写了三个数据接口分别返回区域均价聚合结果、面积价格散点数据、特征重要性排名。这三个接口的实现逻辑都是从同一份清洗后的数据源中读取数据用Pandas做分组聚合再转换成前端需要的JSON结构。这里有个性能优化的小技巧如果数据量较大可以把聚合结果在Flask启动时预加载到内存里接口直接返回内存数据避免每次请求都重新做Pandas运算响应速度会快很多。5.3 演示效果与扩展方向系统跑起来之后的演示流程大概是打开首页看到区域均价柱状图和面积价格散点图视觉上先形成整体印象然后往下滚动看到特征重要性图表明确面积、区域、地铁距离是影响房价的主要因素接着在预测表单里输入面积、户型、楼层等信息点击预测按钮页面显示预测总价和置信区间最后还可以进入明细页查看每条房源的预测值和真实值对比。这套系统后期能扩展的地方不少。比如接入数据库MySQL把每次预测结果存储起来做历史预测记录的分析加入爬虫定期抓取新房源数据实现数据自动更新把单一模型升级为模型融合综合随机森林和XGBoost的预测结果取平均稳定性会更好。不过扩展之前我建议先把当前的系统打磨完整——数据、模型、接口、页面这四个层次都能讲清楚项目的含金量已经足够了。6. 常见问题与排查技巧实录6.1 Flask启动异常与端口占用的排查开发环境下最常遇到的问题是5000端口被占用。这是因为Flask默认端口是5000而一些开发工具如预览服务也会占用这个端口。启动时报错Address already in use解法很简单要么换端口启动app.run(port5001)要么找到占用进程关掉。Windows下用netstat -ano | findstr 5000找到PID然后在任务管理器里结束进程Linux下用lsof -i:5000找到进程号再kill。另一个常见的启动异常是模板文件找不到报错TemplateNotFound。这个问题多半是templates目录的位置不对Flask默认在当前文件所在目录下查找templates文件夹。如果入口文件app.py放在项目根目录模板文件夹就应该是根目录下的templates而不是子目录里的另一个templates。有时候路径写对了但文件名大小写不匹配Windows不区分大小写所以本地正常部署到Linux就报错了这个坑极其隐蔽。6.2 模型预测结果偏差大的原因与修正如果模型训练时R²很高但实际预测时结果离谱十有八九是训练集和预测输入的数据处理不一致。最常见的是漏做标准化——训练时对特征做了StandardScaler但预测接口直接拿原始数值喂给模型。解决方法是把训练时的预处理逻辑封装成同一个处理函数预测和训练都调用它。另一个原因是特征列顺序不一致。随机森林和线性回归都对输入的特征顺序敏感训练时第一列是面积预测时第一列是卧室数量结果自然全乱。我在项目里专门写了一个特征构造函数它接收原始表单参数返回一个固定顺序的特征数组这个函数在训练和预测两端共用从根上杜绝了顺序错位问题。还有一类问题是对数变换导致的误差。如果训练时对价格做了np.log1p预测出来的结果必须做np.expm1还原很多人忘了这一步导致预测价格直接少一位数。凡是训练中做了任何变换都要在预测时做逆向变换这个对应关系要写在代码注释里。6.3 前端图表不显示与数据显示异常的排查图表不显示的原因有三大类。第一类是数据格式不对ECharts对series.data的格式要求很严格Number类型的字段必须是数值而不是字符串。如果Pandas聚合后把数值变成了numpy.int64类型直接转JSON时会报错需要在返回前用astype(float)显式转换。第二类是容器高度问题ECharts的容器div必须有明确的height样式否则图表引擎拿不到有效的画布尺寸渲染结果为空。第三类是script加载顺序问题如果ECharts的JS文件还没加载完就执行了初始化代码也会报ECharts is not defined的错误解决办法是把初始化逻辑放在window.onload回调里或者把脚本放在页面底部。数据展示异常还有一个容易被忽略的原因中文乱码。ECharts图表的坐标轴文字和数据标签里如果有中文而HTML页面没有声明UTF-8编码或者数据接口返回时被转成了Latin-1图表上就会显示一堆问号。前端统一用UTF-8后端JSON返回时jsonify会自动处理一般不会出问题但如果自己手动拼JSON字符串就要检查是否指定了ensure_asciiFalse。最后的几点实操体会整个项目做下来我最深的感受是机器学习项目的成败八成在数据两成在算法。很多人一上来就调模型参数却忽略了数据和特征的一致性最后调参调到怀疑人生。这套房屋数据分析系统最值得学习的地方不在于它用了多么高深的算法而在于它把数据处理—模型训练—服务部署—可视化展示这整条链路做完整了。如果你打算基于这个项目做毕业设计或者面试作品我建议你在文档里重点写两件事一是特征工程的思考过程你对每个字段为什么这么处理、为什么构造衍生特征的解释比代码本身更能展示你的分析能力二是模型对比的结论为什么随机森林在这个任务上优于线性回归为什么测试集R²和训练集R²的差距控制在什么范围内算合理这些思考深度是答辩时最有分量的内容。最后分享一个小技巧演示系统时不要只展示一个预测成功的结果可以挑一个已知真实成交价的房源输入系统做预测让页面同时展示真实值和预测值。这个对照组的设计能让观众一眼看出模型的实用价值比口头说R²达到了0.88要有说服力得多。