☰
R语言回归分析可视化全流程:从探索、诊断到展示的图形实战指南
2026/10/4 18:09:12 网站建设 项目流程

做回归分析,很多朋友上来就套一个lm()函数,然后盯着summary()输出里的星号数量兴奋半天。真正到了汇报或者写结论的时候才发现,想说服别人你的模型靠谱,光靠那几行系数表根本不够。不管是R语言新手,还是长期在业务一线写分析脚本的人,回归分析的最后一道工序其实是图形——图形能一眼暴露数据里藏着的问题,也能把模型结论翻译成人人都能看懂的语言。《R语言实战》第八章整章都在讲回归,其中图形相关的部分,恰恰是我这些年用得最多、也最容易被初学者忽略的内容。这一篇就把回归分析图形的完整链路拆开,从探索、诊断到展示、延伸模型,一步一步讲清楚。

这篇内容适合两类人:一类是正在啃《R语言实战》第八章,想弄明白那些图形到底是干什么用的读者;另一类是已经会用lm()做回归,但总觉得自己的分析缺了点什么,想往更专业方向走的数据分析从业者。我会用R语言自带的数据集做演示,代码直接复制就能跑,每个图形背后是什么原理、怎么看、有哪些坑,都会详细说明。

1. 先理清楚:回归分析里的图形到底在解决什么问题

1.1 图形在回归建模全流程中的角色

很多教程把回归分析讲成一条直线:收集数据、拟合模型、看P值、写结论。但在实际项目中,回归分析是一个反复迭代的过程,图形在每一步都有不可替代的作用。

建模之前,你需要用图形了解数据形态。男女身高、体重和年龄的关系,如果上来就直接建线性模型,你根本不知道变量之间是不是线性关系、有没有明显的离群点、是否存在聚类结构。这些信息靠str()、summary()这种描述统计是看不全的,一张散点图往往比十个统计量更能说明问题。

建模过程中,图形是诊断工具。线性回归有一系列假设:残差独立、方差齐性、正态性、线性关系等。plot(lm对象)四张图能快速判断这些假设是否被违反,这是《R语言实战》第八章的核心内容之一。模型拟合完不是终点,而是诊断的开始。

建模之后,图形承担展示和沟通的职责。业务方不关心你的残差是否正态,他们想看的是:变量X每增加一个单位,Y大概会怎么变;不同分组的预测值差异有多大。一张带置信区间的拟合图,比一张回归系数表有效得多。

1.2 我常用的三类图形划分

根据用途,我把回归分析中的图形分成三类:

  • 探索型图形:散点图、散点图矩阵、箱线图、密度图、相关性热图。作用是建模前摸清数据结构,识别变量间的潜在关系,预判可能的问题。
  • 诊断型图形:残差图、Q-Q图、Scale-Location图、Cook距离图、成分残差图。作用是检查模型假设是否成立,找出影响点和高杠杆点,为模型修正提供方向。
  • 展示型图形:拟合线图、置信区间带图、森林图、生存曲线、分组对比图。作用是把分析结果以最直观的方式呈现给读者,辅助结论落地。

打个比方,探索型图形是"先看看食材新不新鲜",诊断型图形是"做菜过程中试个味看看熟没熟",展示型图形是"摆盘上桌给客人看"。三类图形的目的完全不同,很多人只做了展示型,跳过了前两类,结果就是模型本身有问题却不自知。

2. 建模前必做的探索性图形:让数据先开口说话

2.1 散点图:最小但最关键的可视化单元

散点图是回归分析最底层的图形,也是必须先做的第一步。拿R语言自带的mtcars数据集来说,研究车的重量(wt)和油耗(mpg)的关系:

plot(mtcars$wt, mtcars$mpg, xlab = "Weight (1000 lbs)", ylab = "Miles per gallon", main = "Weight vs MPG", pch = 16, col = "#4878CF")

这段代码会画出一张最基础的散点图。pch = 16把点变成实心圆点,col设置颜色。看这张图时,你重点要看四件事:方向(正相关还是负相关)、强度(点是否紧贴一条虚拟的线)、离群点(有没有点明显偏离整体模式)、形态(是线性还是弯曲)。

如果把mtcars里mpg最大的那辆车标记出来,可以这样:

idx <- which(mtcars$mpg == max(mtcars$mpg)) text(mtcars$wt[idx], mtcars$mpg[idx], rownames(mtcars)[idx], pos = 3, cex = 0.9)

text()函数可以在指定坐标处添加文本,pos = 3表示文字出现在点的上方。这个技巧在探索阶段非常实用,你发现异常点后第一时间能知道是哪个样本。

为什么一定要先做这一步?我见过有人直接跑lm(mpg ~ wt),看系数显著就开始写报告,结果数据里其实存在明显的非线性趋势,油老虎车型和小排量车混在一起,斜率的解释意义很弱。图形是统计推断的第一道防线,散点图几秒钟就能画完,但能帮你避开很多后期麻烦。

2.2 散点图矩阵与相关性热图:多变量视角的快速扫描

当变量不止两个时,两两画散点图太麻烦,这时用散点图矩阵。基础R的pairs()函数可以快速完成:

pairs(~ mpg + hp + wt + disp, data = mtcars, main = "Scatterplot Matrix")

对角线位置是变量名,非对角线是两两变量的散点图。这个函数虽然简单,但变量一多就会很难读,尤其是点重叠严重的时候。我推荐用car包里的scatterplotMatrix(),它会自动在散点周围添加平滑拟合曲线,对角线位置还可以换成箱线图或核密度曲线:

library(car) scatterplotMatrix(~ mpg + hp + wt + disp, data = mtcars, diagonal = "boxplot", smooth = FALSE)

car包是《R语言实战》作者非常推崇的扩展包,后面很多诊断图形都会用到。smooth = FALSE表示不绘制非对角线上的平滑曲线,如果想看曲线趋势,把它改成TRUE就好。

光看散点图矩阵还不够,数值型的相关性热图能帮你快速定位高度相关的变量对。用corrplot包:

library(corrplot) M <- cor(mtcars[, c("mpg", "hp", "wt", "disp", "qsec")]) corrplot(M, method = "ellipse", type = "upper", addCoef.col = "black")

method = "ellipse"用椭圆方向和扁平程度表示相关强度,type = "upper"只显示上三角避免重复,addCoef.col = "black"在每个格子里同时写上相关系数。这张图最大的价值是预警多重共线性——如果两个自变量之间的相关系数超过0.8,放进同一个回归模型里就要谨慎了。比如mtcars里的disp和wt相关系数很高,建模时就得考虑是否同时保留。

2.3 条件关系与分面小图:别忽略第三个变量的干扰

散点图矩阵只能看两两关系,但变量之间的关系往往是"有条件"的。举个例子,重量和油耗的关系在不同气缸数下可能完全不同。用条件图可以快速观察:

coplot(mpg ~ wt | cyl, data = mtcars, panel = panel.smooth)

coplot()是R基础包里的条件绘图函数,竖轴表示以cyl为条件分组。如果要更精细的控制,用ggplot2的分面更直观:

library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + geom_smooth(method = "lm", se = FALSE) + facet_wrap(~ cyl)

分面图的优势是每一组都有独立的拟合线,你能一眼看出4缸、6缸、8缸的车里,重量对油耗的影响趋势是否一致。如果不同组的斜率差异很大,说明cyl和wt之间存在交互效应,后续建模就不能只做简单的加法。

3. 回归诊断图形的正确打开方式

3.1 基础四图:plot(fit)到底在画什么

先拟合一个多元回归模型:

fit <- lm(mpg ~ wt + hp + disp, data = mtcars)

然后直接:

opar <- par(mfrow = c(2, 2)) plot(fit) par(opar)

par(mfrow = c(2, 2))把画布分成2行2列,四张图并排显示。这是《R语言实战》第八章专门讲过的内容,也是每个做回归分析的人必须能读懂的图形组合。

第一张是残差-拟合值图(Residuals vs Fitted)。横轴是模型预测值,纵轴是残差。我主要看两点:残差点是否在y=0水平线附近随机分布,有没有明显的弯曲或喇叭形。如果存在弯曲,说明模型可能漏掉了非线性项;如果出现喇叭形扩散,说明方差不齐。

第二张是正态Q-Q图。散点应大致落在45度参考线上。两端有轻微偏离在样本量小的数据集里很常见,但如果呈明显的S形或者大幅偏离,说明残差正态性假设可能有问题。

第三张是位置-尺度图(Scale-Location)。横轴还是拟合值,纵轴是标准化残差平方根,用于判断方差齐性。点应随机分布在水平线附近,不要呈现明显的上升或下降趋势。

第四张是残差-杠杆图(Residuals vs Leverage)。它能同时反映残差大小和杠杆值,虚线是Cook距离等高线,落在等高线外侧的点就是强影响力点。

这四张图的作用不是"看起来好就万事大吉",而是引导你回答三个问题:模型形式对不对、方差稳不稳、有没有极端值在里面捣乱。

3.2car包增强诊断图:比基础四图更细一层

基础四图够用,但某些时候需要更深度的诊断。car包里有一批很实用的函数。

用qqPlot()替换基础Q-Q图,它会自动添加95%置信带,并尝试标出异常点:

library(car) qqPlot(fit, labels = row.names(mtcars), id.method = "identify")

id.method = "identify"会进入交互模式,你在图上点击点,终端会输出对应样本名,按Esc退出。如果不想要交互,直接把labels参数加上,图上就会自动标出最可疑的点。

crPlots()是成分残差图,也叫偏残差图:

crPlots(fit)

每一幅小图对应一个预测变量,展示的是剔除了其他变量影响后,这个变量与因变量的偏关系。这张图能直观看出某个变量是否需要做变换,比如出现明显的曲线形状,说明可以尝试加入平方项。

方差齐性的检验可以结合数值方法和图形:

ncvTest(fit) spreadLevelPlot(fit)

ncvTest()输出的是一个检验结果,spreadLevelPlot()则是画图,如果拟合线是水平的,说明方差稳定;如果有明显斜率,说明方差不齐,且它会给出建议的变换幂次。这是很多教程不会细讲的点,实际项目里非常有用。

还要检查残差是否自相关,尤其对时间序列型数据:

durbinWatsonTest(fit)

这个检验的P值如果很小,说明残差存在自相关,标准误估计可能偏小,结论的可靠性就要打折扣。

3.3 影响分析与异常值识别:Cook距离和高杠杆点

回归诊断里还有一个经典组合,就是找"影响点"。一个点是否影响模型拟合,不仅看它残差大小,还要看它的杠杆值高不高。plot(fit)第四张图已经给了初步判断,但更量化的是Cook距离:

cooks <- cooks.distance(fit) # 经验阈值 threshold <- 4 / nrow(mtcars) barplot(cooks, main = "Cook's Distance", ylab = "Cook's distance", col = "steelblue") abline(h = threshold, col = "red", lty = 2)

经验上,Cook距离超过4/n的点值得关注,n是样本量。barplot直接用柱状图展示每个样本的Cook距离,红色虚线以上就是要重点审查的对象。

还可以用outlierTest()做学生化残差检验:

outlierTest(fit)

它会给出最像异常值的样本及其Bonferroni校正后的P值。注意:异常值不等于错误的观测值,删不删要看业务背景。比如在医学数据里,某个极端患者可能就是真实存在的重要信息,删了反而丢失关键信号。我的习惯是先把可疑样本列出来,结合原始数据查一遍,再决定是否剔除或单独建模。

avPlots()(增变量图)也值得一用:

avPlots(fit)

它能展示每个变量在控制了其他变量后的边际关系,适合建模诊断的最后阶段反复查看。

4. 从线性到广义:回归族模型的图形化呈现

4.1 多元线性回归的拟合效果图

线性回归只有一个预测变量时,画拟合线很简单。如果是多元模型,二维空间没办法直接画,但有两个替代方案。

第一个方案是画预测值 vs 实际值,所有模型通用:

plot(fitted(fit), mtcars$mpg, xlab = "Fitted values", ylab = "Actual values", main = "Predicted vs Actual") abline(0, 1, lty = 2, col = "red")

如果点都聚集在45度线附近,说明模型预测能力不错;如果系统性偏离,说明模型存在偏差。

第二个方案是选一个关键变量,画带置信区间的拟合线。比如展示wt对mpg的影响,可以直接用ggplot2:

library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point(size = 3, alpha = 0.7) + geom_smooth(method = "lm", formula = y ~ x, se = TRUE, level = 0.95, color = "#D55E00", fill = "#E69F00")

se = TRUE会画置信区间带,level = 0.95是置信水平。但要注意,geom_smooth用的是单变量拟合,不是多元模型的边际效应。想展示多元模型的预测区间,更严谨的做法是用predict()生成网格预测值再画:

newdata <- data.frame( wt = seq(min(mtcars$wt), max(mtcars$wt), length.out = 100), hp = mean(mtcars$hp), disp = mean(mtcars$disp) ) pred <- predict(fit, newdata, interval = "confidence") plot_data <- cbind(newdata, pred) plot_data$wt <- plot_data$wt ggplot(plot_data, aes(wt, fit)) + geom_ribbon(aes(ymin = lwr, ymax = upr), alpha = 0.2, fill = "#E69F00") + geom_line(color = "#D55E00", linewidth = 1.2) + labs(x = "Weight (1000 lbs)", y = "Predicted MPG")

这组代码把其他变量固定在均值,只让wt变化,得到的是多元模型在该维度上的边际预测,解释起来才站得住脚。

4.2 Logistic回归的S型曲线

分类问题里最常用的是Logistic回归,图形上最经典的输出就是S型概率曲线。用mtcars裡的am(变速箱类型,0/1)做因变量,hp做自变量:

logit_fit <- glm(am ~ hp, data = mtcars, family = binomial()) newdata <- data.frame(hp = seq(50, 350, length.out = 100)) pred_prob <- predict(logit_fit, newdata, type = "response") plot(newdata$hp, pred_prob, type = "l", xlab = "Horsepower", ylab = "Probability of Manual", main = "Logistic Regression Curve", lwd = 2, col = "#4878CF")

原始数据点是0/1值,预测值是连续概率,所以直接用ggplot2加抖动点配合平滑曲线:

ggplot(mtcars, aes(hp, am)) + geom_point(position = position_jitter(height = 0.05), size = 2.5) + geom_smooth(method = "glm", method.args = list(family = binomial()), se = TRUE, color = "#D55E00")

这里position_jitter(height = 0.05)给0/1点加一点纵向抖动,避免重叠导致的视觉误导。读这条S型曲线时有一个关键点要注意:曲线越陡,说明该变量对概率的影响越强;曲线中点对应的横坐标,通常是概率等于50%的决策边界。

4.3 Cox回归、Poisson模型的生存曲线与森林图

回归分析不止线性回归和Logistic回归,生存分析里的Cox回归是另一个高频场景。针对时间-事件数据,最常用的图形是Kaplan-Meier生存曲线和Cox模型的森林图:

library(survival) library(survminer) fit_km <- survfit(Surv(time, status) ~ sex, data = lung) ggsurvplot(fit_km, data = lung, conf.int = TRUE, risk.table = TRUE)

survminer包对生存曲线做了很好的美化,conf.int = TRUE显示置信区间带,risk.table = TRUE在下方展示各时间点的风险人数。这张图可以直接放进任何报告里。

Cox模型的系数解释比较抽象,用森林图一目了然:

library(forestmodel) cox_fit <- coxph(Surv(time, status) ~ age + sex + ph.ecog, data = lung) forest_model(cox_fit)

森林图的每行是一个变量,点估计值配合置信区间横线。如果横线跨越1.0这条参考线,说明该变量的效应在统计上不显著。这种图对业务沟通非常友好,不用解释exp(coef)是什么,对方看一眼就懂哪个变量是风险因素、哪个是保护因素。

Poisson计数模型的思路也类似,拟合完glm(count ~ x, family = poisson())后,用predict(..., type = "response")画预测均值线即可。另外,时间序列回归模型的残差可以用acf()检查是否存在自相关,这在回归诊断环节也能衔接上:

acf(residuals(fit), main = "残差自相关图")

5. ggplot2体系下回归图形的进阶玩法

5.1 用ggplot2重构散点与拟合线

如果你已经熟悉ggplot2,那所有基础绘图能做的事都可以用ggplot2做得更精致。以散点加拟合线为例,geom_smooth()是核心:

p <- ggplot(mtcars, aes(wt, mpg)) + geom_point(size = 3, alpha = 0.7) + geom_smooth(method = "lm", formula = y ~ x, se = TRUE, level = 0.95, color = "#D55E00") + labs(title = "Weight vs MPG", x = "Weight (1000 lbs)", y = "Miles per gallon") + theme_bw(base_size = 14)

注意method = "lm"是线性拟合,formula = y ~ x是必须显式声明的,否则在高版本ggplot2里会因global环境的变量解析问题报错。theme_bw()是期刊论文常用的黑白色调,比默认灰底更适合正式场合。

如果想展示多元模型在某变量上的边际效应,可以用predict()手动构造数据框再叠加,方法在4.1节已经写过了。用geom_ribbon()画置信区间带,效果比geom_smooth更可控。

5.2 分组回归与交互效应可视化

分组回归是数据分析的常见场景,ggplot2处理起来非常方便:

ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) + geom_point(size = 2.5) + geom_smooth(method = "lm", formula = y ~ x, se = FALSE) + labs(color = "Cylinders") + theme_bw()

三条不同颜色的拟合线如果斜率差异明显,就预示着分组变量与其他变量之间存在交互作用。想要正式检验交互项,可以在模型中加入乘积项,然后用facet_wrap()分面展示:

fit_inter <- lm(mpg ~ wt * factor(cyl), data = mtcars) summary(fit_inter) ggplot(mtcars, aes(wt, mpg)) + geom_point() + geom_smooth(method = "lm", formula = y ~ x, se = FALSE) + facet_wrap(~ cyl, scales = "free_x") + theme_bw()

scales = "free_x"允许每个面板的x轴范围独立,避免某些组数据范围过窄导致拟合线看起来太平坦。交互项可视化时,一定要用模型预测值来画,而不是直接用各组单独拟合的数据点,因为前者对应的是同一个模型的参数估计,能体现整体逻辑。

5.3 图形导出与出版级细节

分析完要出图,最常见的问题是导出分辨率不够或尺寸不对。用ggsave():

ggsave("regression_plot.png", p, width = 8, height = 6, dpi = 300, units = "in")

dpi = 300是出版级的最低标准,width和height用英寸配合dpi能精确控制像素尺寸。如果想导出矢量图用于论文投稿:

ggsave("regression_plot.pdf", p, width = 8, height = 6, units = "in", device = "pdf")

基础绘图的保存则用:

png("base_plot.png", width = 3000, height = 2000, res = 300) plot(fit) dev.off()

res = 300配合width = 3000、height = 2000,输出的图片在屏幕上放大也不会有锯齿。这里有个细节:在RStudio里直接执行plot(fit)时图形在右下角窗口,如果直接用鼠标右键导出,分辨率默认可能是96dpi,放到论文里一放大就糊了。所以导出图形时,老老实实用函数指定参数,别用鼠标操作。

6. 常见绘图问题与排查技巧实录

6.1 中文乱码与字体问题

R语言图形里中文乱码是个经典痛点。不同操作系统、不同图形设备,表现还不一样。在Windows上,最简单的处理是用showtext包:

library(showtext) font_add("heiti", regular = "simhei.ttf") showtext_auto()

然后就像正常画图一样,所有中文都能正确显示。simhei.ttf是Windows自带的中文字体文件,如果你的系统没有,也可以使用font_add("heiti", regular = "STHeiti Light.ttc")之类的替代。showtext_auto()会全局开启字体渲染接管,画完之后如果发现和其他图形混排有问题,可以用showtext_auto(FALSE)关闭。

如果是用ggplot2,还有一个办法是直接在主题里指定字体族:

theme(text = element_text(family = "sans"))

但这种方法的局限在于,不同系统对sans的映射不一样,跨平台复现时效果可能不同。我个人的习惯是:一旦项目里需要大量中文出图,开项目时就先配置好showtext,不要等图全部画完了再补,否则全部要重画。

6.2 图形比例与设备尺寸问题

很多人画图发现点挤成一团,或者拟合线被裁掉一部分,根源是图形设备尺寸不对。RStudio默认的绘图窗口比较小,可以用png()、pdf()等方式先打开一个指定尺寸的文件设备再画图。

另外,基础绘图里的par(pin = c(width, height))可以设置当前图形的物理尺寸,par(cex = 1.2)能整体放大文字和符号。不过对新手来说,我建议优先用ggsave()统一处理导出,图形的观感会很稳定。

6.3 诊断图形"不好看"不等于模型差

最后想聊一个心态问题。我经常看到有人跑完plot(fit),发现Q-Q图有点偏离就开始慌,或者看到残差图有点弯曲就觉得模型废了,立刻去试各种变量变换,结果越调越乱。

诊断图形是用来发现问题的,不是用来追求"好看"的。对于样本量200以下的数据,Q-Q图两端有一些偏离是非常正常的现象,关键是看是否严重到影响结论。残差图有轻微的模式也要结合业务判断,比如数据天然存在时间趋势,那加入时间变量可能是更好的选择,而不是盲目对Y取对数。

我自己在实际项目里有一个固定的图形检查顺序,分享出来供你参考:

  1. 先画散点图矩阵和相关性热图,筛选变量初印象。
  2. 拟合模型后,先看summary(),再看plot(fit)四图。
  3. 用crPlots()查看每个变量的偏关系,判断是否需要做变量变换。
  4. 用outlierTest()和Cook距离找出可疑样本,回到原始数据核实。
  5. 修正模型后,把所有关键图形重新画一遍,确认问题是否消除。
  6. 最后才做展示图,用ggplot2统一风格、导出高分辨率文件。

这个方法帮我解决过很多实际问题,尤其是在多变量同时进入模型的情况下,图形检查基本成了模型的"体检报告"。如果你刚开始学《R语言实战》第八章,别急着跳到后面的高级方法,先把这一章涉及的图形命令全部跑一遍,弄清楚每张图的横轴、纵轴和判断标准,后期建任何模型都会受益。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询