☰
R语言lavaan包SEM实战:从CFA验证到中介效应检验完整指南
2026/10/8 20:09:53 网站建设 项目流程

每次收到统计求助消息,十有八九是这样开头的:“老师,我问卷收完了,能帮我看下中介效应怎么跑吗?”然后附上一份几百行、列名是A1到A25的Excel。这类问题十有八九都会落到同一个工具上:R语言的lavaan包,跑结构方程模型(SEM)。

结构方程模型这几年在国内社科、医学、管理、教育领域的出镜率越来越高,但真正把原理和操作都捋清楚的人并不多。很多人是把SPSS的回归结果硬改成SEM画风,潜变量、测量误差、拟合指标、中介效应这些概念一团浆糊。这篇博文不搞教科书式铺陈,直接从一个能跑通的完整案例讲起,把lavaan建模、CFA验证、结构路径、中介效应、拟合评估、常见报错排查、纵向数据扩展一次性讲透。无论你是刚开始学SEM的硕士生,还是已经被审稿人逼着补中介检验的科研人员,这篇都能给你一套能直接“抄作业”的流程。

1. 先搞清楚SEM到底在解决什么问题

1.1 SEM不是回归分析的简单升级

我见过太多人把SEM理解成“可以一次跑多个回归的回归”。这个理解不算错,但丢掉了SEM最值钱的部分。SEM的核心是两层结构:测量模型和结构模型。测量模型说的是“我测到的题目能不能代表我构念中的那个潜变量”,结构模型说的是“潜变量之间到底是谁影响谁”。普通回归只能回答第二个问题,而且是在默认题目得分没有误差的前提下回答。

实际问卷里,A1到A5测“工作压力”,B1到B4测“职业倦怠”,你拿五道题的均值去回归,等于告诉模型:每道题对“工作压力”的贡献完全一样,且测量没有误差。这两条假设基本都不成立。有些题目区分度高,有的低,有的还带着社会赞许偏差。SEM在估计潜变量的时候,会根据数据自动分配题目权重,并且把测量误差从结构关系里剥离开。误差剥离之后,路径系数往往会比拿均值回归更“干净”,有时候甚至能救回一个原本不显著的效应。

再用生活类比说一句:普通回归像是拿一个不靠谱的温度计直接读室温,SEM则是同时看三支温度计,并估计出“真实室温”对它们读数的影响,再拿这个“真实室温”去预测开空调的电费。lavaan干的正是后面这件事。

1.2 为什么用lavaan而不是Mplus或AMOS

SEM的常用工具不少:商业软件里AMOS靠图形界面起家,Mplus在潜变量建模领域地位很高;R生态里除了lavaan,还有sem、piecewiseSEM,以及做贝叶斯SEM的blavaan。lavaan能成为很多人默认选择,原因挺实际:

  • 完全免费开源,不依赖图形界面,所有模型用几行文本语法描述,天然可复现、可版本管理;
  • 语法设计贴近Mplus,从Mplus转过来的人学习成本极低;
  • 支持连续、有序分类、二分类指标,支持缺失数据的FIML处理,支持多群组分析、bootstrap中介效应、测量不变性这些发论文刚需功能;
  • 背后生态完整,semTools提供辅助检验,semPlot负责画路径图,standardizedSolution直接输出标准化系数。

下表是我在实际使用中的体感对比,帮还没入门的读者做决策参考。

工具成本学习曲线建模范围可复现性适合人群
AMOS高平缓,拖拽即可常规SEM足够差,操作不可记录偏好图形界面的入门者
Mplus高陡峭极广,复杂潜变量模型强中,代码可保存进阶研究者、复杂模型需求者
lavaan免费中等常规及进阶SEM均可强,文本全流程记录用R的科研工作者、喜欢代码驱动的人
piecewiseSEM免费中等适用于局部估计、小样本复杂网络强生态学、处理复杂因果网络的人

一句话选型建议:如果你只要跑一次SEM交差,AMOS也行;如果你以后还要做多群组、纵向、bootstrap、潜变量交互,直接学lavaan一步到位。反正R已经在你的工作流里了。

2. 动手建模前必须想清楚的三件事

2.1 理论框架先进脑子,模型语法才进R

SEM最容易被忽视的一步不是写代码,而是画理论路径图。我自己的习惯是先在纸上把方框、椭圆和箭头画出来:显变量(问卷题目)用方框、潜变量用椭圆、因果箭头用实线、允许相关的误差项用双向虚线。不要小看这张手绘图,它至少能在动手之前逼你把三个问题想清楚:

第一,你的研究到底是“探索影响因素”还是“检验理论机制”。前者用回归或者SEM的直接效应就够,后者才需要认真讨论中介和调节。第二,你的潜变量有几个、每个潜变量由哪些题目构成,这个在问卷设计阶段就该定下来,而不是跑完CFA再倒推。第三,方向性假设必须来自文献和理论,不是让模型帮你找因果。

有一个非常典型的反例:有人把问卷里所有正向题目和反向题目放进同一个潜变量,CFA结果惨不忍睹,于是开始疯狂删题。其实问题出在反向计分题没有提前转置,不是题目质量不好。这种错误只要在纸上画图、逐题核对方向就能完全避免。

2.2 潜变量指标的“三有原则”

潜变量的指标设计有几条硬经验,我管它叫“三有原则”:

  • 有数量:一个潜变量至少3个显变量,有4到6个更稳。两个指标理论上能识别,但自由度紧、容易出非正定问题,新手尽量避开。
  • 有方向:所有指标要与潜变量方向一致,反向题必须统一转置成“高分=构念更强”,否则载荷为负还影响拟合。
  • 有区分:同一个因变量维度上的题目不要同时丢进两个潜变量,否则交叉载荷会让你在修正指数里疲于奔命。

还要提一下单指标潜变量的情况。如果你某个核心变量只有一个题,或者直接用总分代表,lavaan也允许把它作为显变量放进模型,语法上无需特殊处理,回归路径照写。但这时候它就没有测量误差剥离能力了,本质上跟回归模型里的变量没区别。

2.3 样本量:别等跑完才发现不够

SEM的样本量问题网上说法很多,我直接给一套实用的参考框架。经验法则层面,样本量与自由参数的比值(N:q)至少要达到5:1,理想是10:1以上。一个中等复杂度模型大概有30到50个自由参数,按10:1算至少需要300到500人。只做简单模型、潜变量又少,200人可以凑合;模型复杂还硬上100人,跑出来的结果千万别当科研结论用。

为什么有这个要求?SEM本质是拿样本协方差矩阵去拟合模型隐含的协方差矩阵。样本量太小,协方差矩阵的波动性太大,参数估计不稳,拟合指标也飘忽不定。最常见的表现就是:今天跑拟合不错,明天换个missing值处理方式,RMSEA从0.04跳到0.09,这种模型基本不可信。

更硬核的做法是蒙特卡洛模拟:在给定模型结构、预期效应量、样本量下,模拟生成几百上千份数据,跑通完整分析流程,统计检验功效。lavaan自带的simulateData配合sem就能完成这个工作,我们下个章节的演示案例就是先模拟数据再拟合,一举两得。

3. 完整实操:一个问卷数据是怎么跑通的

3.1 构造一个可以复现的演示数据

为了把流程讲清楚,这里我用lavaan的simulateData构造了一份模拟数据,模拟一个经典的三变量中介模型:“工作压力(stress) → 职业倦怠(burnout) → 离职意向(turnover)”。这份数据有400人,每个潜变量配了若干显变量,载荷设定在0.7左右,潜变量之间的路径系数设定为中等效应。模拟数据的好处是结果“完美”,方便你看清模型的正确玩法;真实数据不理想才是常态,那是下一节讨论的事。

# 加载lavaan,请先安装:install.packages("lavaan") library(lavaan) # 设定生成数据的模型结构 pop_model <- ' stress =~ st1 + st2 + st3 + st4 + st5 burnout =~ bu1 + bu2 + bu3 + bu4 turnover =~ to1 + to2 + to3 # 中间变量结构 burnout ~ 0.5*stress turnover ~ 0.4*burnout + 0.2*stress ' set.seed(2024) dat <- simulateData(pop_model, sample.nobs = 400, standardized = TRUE) head(dat)

这里有几个细节值得说明。simulateData的standardized = TRUE表示生成的数据全部标准化,潜变量方差为1,方便后续解读。真正的研究里你不会用simulateData生成数据,而是read.csv或readxl读入问卷数据,列名是st1到to3这样的题目编号。我的建议是:拿到真实数据后先做两步预处理——反向计分题目转置,然后用psych::describe或lavaan::lavCor检查是否有缺失值、是否有题目方差为0的列。方差为0的题目在模型里会造成协方差矩阵不可逆,症状是lavaan报“system is computationally singular”之类的错,排查起来很头疼。

3.2 第一步:先跑CFA确认测量模型

结构模型的结论是否可信,取决于测量模型是否成立。所以我的流程永远是:先拟合CFA,确认潜变量与其指标的关系靠谱,再进入完整SEM。很多教程直接跳进全模型,一旦拟合不佳,你分不清是测量问题还是路径问题,调试成本翻倍。

cfa_model <- ' stress =~ st1 + st2 + st3 + st4 + st5 burnout =~ bu1 + bu2 + bu3 + bu4 turnover =~ to1 + to2 + to3 ' fit_cfa <- cfa(cfa_model, data = dat, std.lv = TRUE) summary(fit_cfa, fit.measures = TRUE, standardized = TRUE)

看到输出时,重点看这几个地方:

  • 潜变量与指标之间的Estimate列是否全部为正,且都显著——载荷显著是测量模型的最低要求;
  • Std.all列(完全标准化载荷)最好都高于0.5,理想是0.7以上。如果某道题载荷只有0.3,说明它跟整个潜变量共享信息很少,可以考虑删除,但删除前要结合理论,别纯靠统计洁癖;
  • 拟合指标:CFI、TLI、RMSEA、SRMR,CFA阶段就不好看,后面全模型只会更难伺候。

模拟数据的CFA通常非常漂亮,载荷基本都在0.85以上,CFI接近1,RMSEA低于0.05。你看到这么完美的结果别激动,这是“人造数据”的必然结果。真实问卷能跑到CFI大于0.92、RMSEA小于0.08已经算可以接受,别拿模拟数据当现实标杆。

3.3 第二步:拟合完整结构模型并检验中介效应

CFA能接受,就可以把结构路径加进去。这里用a、b、c三个标签给路径命名,然后用:=定义中介效应和总效应。这是lavaan最实用、也最容易被新手漏掉的语法。

sem_model <- ' # 测量模型 stress =~ st1 + st2 + st3 + st4 + st5 burnout =~ bu1 + bu2 + bu3 + bu4 turnover =~ to1 + to2 + to3 # 结构模型 burnout ~ a*stress turnover ~ b*burnout + c*stress # 间接效应与总效应 indirect := a*b total := c + a*b ' fit_sem <- sem(sem_model, data = dat) summary(fit_sem, fit.measures = TRUE, standardized = TRUE)

在输出里找到Latent Variables、Regressions、Defined Parameters三张表。Regressions里看a和b两条路径:a是stress对 burnout的效应,b是burnout对turnover的净效应,c是控制burnout后stress对turnover的直接效应。Defined Parameters表会给你indirect和total的点估计、标准误、z值和p值。如果indirect的p值小于0.05,说明存在统计学显著的中介效应。

这段结果解读,我多说一句经验:中介效应的点估计等于a乘b,但它的置信区间并不关于点估计对称,尤其是样本量不大或效应量偏低时,依赖正态分布假设的p值容易失真。所以下一步必须做bootstrap,用经验分布替代正态假设。

3.4 第三步:用bootstrap给中介效应一个可信区间

在lavaan里跑bootstrap中介非常简单,加上se = "bootstrap"和bootstrap = 1000两个参数就行。bootstrap的原理是:从原始样本里有放回地反复抽取同样大小的样本,每抽一次重新估计一次模型,得到上千个indirect估计值,然后看这些值的2.5%和97.5%分位数。如果这个百分位置信区间不包含0,中介效应就成立。

set.seed(2024) fit_boot <- sem(sem_model, data = dat, se = "bootstrap", bootstrap = 1000) parameterEstimates(fit_boot, ci = TRUE, level = 0.95)

看输出里的indirect行,重点看ci.lower和ci.upper两列。只要上下界没有包裹0,就可以在论文里理直气壮地写“bootstrap 95%置信区间为[xx, xx],间接效应显著”。这条是审稿人非常认的证据链。

对于只想快速检验显变量中介效应的人,有一个更轻量的做法:直接用mediation包,先拟合lm模型再调用mediate()函数。但如果你已经搭好了完整SEM,就留在lavaan里一口气跑完bootstrap,不要再拆流程。一致的模型框架、一致的缺失值处理、一致的估计方法,比小巧但割裂的流程更重要。

3.5 结果汇报怎么组织

论文里SEM结果的呈现有固定套路。文字部分先报告测量模型验证,再报告描述统计与相关,最后报告结构模型路径、中介效应、置信区间。表格部分可以仿照下面的形式:

路径/效应非标准化估计标准误标准化估计p值
stress → burnout (a)0.520.060.58<0.001
burnout → turnover (b)0.440.050.47<0.001
stress → turnover (c)0.150.060.170.013
间接效应 a*b0.230.040.27<0.001
总效应 a*b+c0.380.050.44<0.001

注意非标准化和标准化的数值需要区分清楚,汇报时通常报告非标准化估计和标准误,标准化估计也要一并给出。如果期刊有自己的格式要求,照期刊来。

4. 拟合指标:别只会看p值

4.1 五件套指标速查

SEM拟合评价不是看某一个指标的p值,而是看一组指标的“整体口碑”。最常被审稿人要求报告的指标是卡方、自由度、CFI、TLI、RMSEA、SRMR。速查口径如下表。

指标优秀标准可接受标准说明
卡方检验 p值>0.05大样本下允许显著样本量大时几乎必显著,参考价值有限
CFI>0.95>0.90比较拟合指数,受样本量影响小
TLI>0.95>0.90对模型复杂度有惩罚,越复杂越吃亏
RMSEA<0.05<0.08近似误差均方根,越小越好
SRMR<0.05<0.08基于残差的指标,对错误设定敏感

如果CFI达标但RMSEA偏大,常见原因是模型忽略了一些中小型残差相关,最典型的像同一套问卷里主题相近的两道题。如果RMSEA很漂亮但CFI不足,常见原因是模型整体方向对、但某个潜变量的测量模型较弱。这个判断下个部分细说。

4.2 指标打架的时候该信谁

拟合指标一致性越高越可信,但真实数据经常出现“CFI挺好、RMSEA不行”的分裂局面。我的处理原则是:在CFI和TLI都超过0.9的前提下,优先看RMSEA和SRMR,因为这两个指标对模型错误设定的敏感度更高。RMSEA超过0.1基本宣告模型不可接受,这时候不要抠CFI的0.92。

另一个反直觉的点是卡方。结构方程模型教科书里说卡方不显著为好,但研究者的样本量一旦上了500,卡方几乎注定显著。所以现在主流做法是报告卡方和自由度,再强调其他指标的作用,而不是死磕“p>0.05才算好模型”。作为审稿人,我看到那些只报卡方、不报CFI/AI等指标的稿件,第一反应就是你别的指标不过关才藏起来。

4.3 修正指数要用,但别用疯

模型拟合不佳时,lavaan的modificationIndices()会给出修正指数,指“如果加入这条路径,卡方能下降多少”。不少初学者拿着修正指数一路改:先加变量相关,再加交叉载荷,最后模型面目全非,换一批数据立刻崩盘。这就是过度拟合。

我给自己定了几条修正纪律:一是修正只能由理论引导,不能由数据单独驱动。如果MI显示A题目残差和B题目残差相关,你先问自己这两道题是不是因为措辞相近、施测时间相近才相关,如果是,那这个残差相关在理论上讲得通,才值得加。二是每加一个参数,模型就少一个自由度,拟合是好了,但模型的简洁性和可推广能力都在下降,需要权衡。三是一次只加一条,加了再看指标变化,不要一次性把MI排名前五的全补上。四是最重要的:任何修正都要在论文里如实报告,你不要偷偷摸摸地修了一堆“数据友好”路径却只字不提。

5. 常见问题排查与进阶场景

5.1 不收敛、负方差、非正定矩阵的源头排查

lavaan跑不出结果时,常见报错包括模型不收敛、某些方差估计为负、赫赛矩阵非正定。新手经常慌,资深使用者其实有一套固定的排查顺序。

第一步看样本量。200以下跑复杂模型,各种妖蛾子都正常。第二步看模型设定:是否有潜变量只用两个指标、是否有指标同时属于两个潜变量、是否有路径方向恰好等于“中介变量同时又是调节变量”一类复杂设定。第三步看数据:变量之间是否存在近似完全线性相关,某道题是否几乎无变异,缺失值比例是否过高。第四步看语法:有没有把同一个变量名写进两个潜变量、有没有把方差这个关键词写错。

一个常见但隐蔽的坑是潜变量尺度设定。lavaan默认以第一个指标的载荷固定为1来定义潜变量尺度,如果第一道题本身变异很小,整个潜变量尺度就会不稳。我会直接用std.lv = TRUE让潜变量方差固定为1,这样潜变量尺度更标准,迭代也更稳定。代价是非标准化回归系数的解释会变成“潜变量每变动一个标准差”,不同模型之间横向比时小心措辞。

5.2 横断面与纵向:中介模型的取证边界

最近咨询里常听到一个说法:“我想横断面看影响因素,再做个纵向SEM验证中介效应。”这个思路很对,但要知道两者的证据强度完全不同。横断面数据里做中介检验,本质上是在同一时间点上“同时”看到X、M、Y,我们只能证明X和M之间的关联、M和Y之间的关联同时存在,因果先后顺序完全是理论假设撑起来的。审稿人一旦较真,横断面中介的证据链很容易被质疑。

纵向SEM能把因果顺序问题缓解很多。设计上要求至少三个时间点:T1测量自变量X、T2测量中介M、T3测量因变量Y,路径上让T1的X影响T2的M,再让T2的M影响T3的Y,同时控制各变量自回归。这才是“纵向中介”比较硬的标准做法。

如果只有两次测量,能做的是T1的X和M预测T2的Y,但T1和T2之间的M到底是“基线水平”还是“中间变化量”,解释空间有限。那种非要说“T2的M是T1到T2期间发生变化导致的结果”的研究,统计上其实站不住脚。

5.3 三波纵向交叉滞后模型的lavaan写法

要让纵向模型落地,最有代表性的基础结构是交叉滞后面板模型。下面代码用模拟数据做一个简化演示:三个时间点的同一对变量(x和y),同时估计自回归路径和交叉滞后路径,检验“T1的x是否预测T2的y、T2的x是否预测T3的y”。

set.seed(2024) n <- 300 x1 <- rnorm(n, 50, 10); y1 <- rnorm(n, 50, 10) x2 <- 0.5*x1 + 0.2*y1 + rnorm(n, 0, 8) y2 <- 0.5*y1 + 0.2*x1 + rnorm(n, 0, 8) x3 <- 0.5*x2 + 0.2*y2 + rnorm(n, 0, 8) y3 <- 0.5*y2 + 0.2*x2 + rnorm(n, 0, 8) dat_long <- data.frame(x1, y1, x2, y2, x3, y3) clpm_model <- ' x2 ~ a1*x1 + d1*y1 y2 ~ c1*y1 + b1*x1 x3 ~ a2*x2 + d2*y2 y3 ~ c2*y2 + b2*x2 # 同期相关 x1 ~~ y1 x2 ~~ y2 x3 ~~ y3 # 纵向间接效应:x1 -> y2 -> x3 ind1 := b1*d2 ' fit_clpm <- sem(clpm_model, data = dat_long) summary(fit_clpm, standardized = TRUE)

在这个模型里,b1代表T1的x对T2的y的交叉滞后效应,d2代表T2的y对T3的x的交叉滞后效应,两者相乘就是一条跨三个时间点的纵向链。这个演示简化了构念数量,真正的纵向中介要把X、M、Y三个变量各测三拨,模型会大不少,但语法逻辑完全相同:定义自回归路径、交叉滞后路径、同期相关,然后用:=组装间接效应。

5.4 多群组比较与测量不变性

如果你要比较男生和女生、干预组和对照组在模型路径上的差异,直接用group参数分组跑是不够严谨的,因为分组之前得先证明你的量表在两组里“量的是同一个东西”。这就是测量不变性检验。

lavaan搭配semTools可以一次跑完三个层级的检验:形态等值、载荷等值、截距等值,前一个不通过就不用看后一个。核心问题是:罐子的结构在两组里是否一样、刻度是否一样、起点是否一样。

library(semTools) fit_inv <- measurementInvariance(cfa_model, data = dat, group = "gender")

跑出来之后看不同层级模型的CFI差异和RMSEA差异。一般认为CFI变化小于等于0.01、RMSEA变化小于等于0.015时,可以接受该层级等值。这个标准比看卡方差异靠谱,因为卡方差异检验在大样本下太容易显著。

5.5 这些“R语言”热搜和SEM是什么关系

最近搜索关键词里总有“α多样性R语言”“单细胞测序组间GO富集分析”“SARIMA模型R语言”“stacking算法R语言实现”这类词,它们和SEM的关系其实没有想象中那么远。α多样性、GO富集、单细胞分析,属于组学数据处理链条;SARIMA属于时间序列预测;stacking属于机器学习集成。SEM的主场是“带潜变量的因果关系建模”,它不做预测竞赛,也不做高维特征筛选,它的价值在于解释机制。

但它们在同一个研究项目里并不冲突。比如一个生物医学研究,上游可以用单细胞分析筛选候选通路,下游用SEM整合多组学指标、临床指标与结局变量之间的关系,解释“通路激活如何经由免疫状态影响预后”。这里的SEM是机制解释环节,单细胞分析是发现环节,二者各司其职。至于stacking和SARIMA,如果研究重点是预测准确率,就交给机器学习方法;如果审稿人问“你的关键变量之间机制路径是什么”,那依然是SEM的活。

5.6 常见报错与排查速查表

我整理了lavaan实战中几个高频问题的排查路径,适合打印出来贴在屏幕边。

症状常见原因优先排查方向
模型不收敛,反复迭代警告样本量不足、初始值太差、模型设定过于复杂增大样本量、简化模型、设置std.lv = TRUE
方差估计为负潜变量尺度问题、指标共线、过拟合检查标准化解、调整尺度、检查载荷方向
协方差矩阵非正定极端缺失、完全线性相关的指标检查缺失模式、删除冗余指标
CFI很好但RMSEA超标忽略残差相关、遗漏潜变量间次要相关查看MI指数,按理论补充残差相关
bootstrap置信区间很宽样本量不足、效应量偏低考虑扩大调查、或只做探索性结论
载荷为负且显著反向计分题没有转置转置反向题后重跑

还有两条实操建议,虽然是“再基础不过”的事,但我见过太多次翻车:第一,跑模型之前确认所有题目都是数值型,字符型题目几乎必然报错;第二,不要把Excel表里带有缺失值的整片区域直接读成矩阵,用na.omit或者FIML处理缺失,不要让缺失值一路传进模型里炸掉协方差估计。

最后分享一点我自己的体会

lavaan这套流程,我前前后后跑了上百个项目。最大的感受是:SEM不是一个“跑完看结果”的工具,而是一个逼你把理论、测量、数据三者摊开看的框架。你模型拟合不好,多半不是写错了代码,而是前面的测量设计或理论推演本来就有问题。所以在正式跑主模型之前,花一个下午把CFA跑透、把指标和理论的对齐关系检查一遍,是最划算的时间投入。

另外一个小技巧:每次结果满意之后,记一下sessionInfo()里的包版本,lavaan在不同版本之间偶尔会有默认设置调整,论文返修时如果结果对不上版本,排查起来想哭。这个习惯帮我躲过至少两次审稿危机,现在也推荐给你们。

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

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

立即咨询