中介分析用于探讨自变量通过中介变量对因变量的间接影响,广泛应用于社会科学等领域。本文对比了五个R包: mediation
、lavaan
、psych
、piecewiseSEM
和brms
。通过每个包的具体操作和结果展示,分析了其各自的优缺点:mediation
包简单易用,lavaan
支持潜变量,psych
操作便捷,piecewiseSEM
提供可视化和结构方程模型支持,brms
适合复杂模型。本文总结了不同R包在中介分析中的适用性,为研究者提供了实用的选择指南。
目录
简介 数据 模型 R包介绍 mediation lavaan psych brms 更多复杂性 交互作用 广义线性模型 缺失数据 替代方法 bnlearn Python Stata 总结 R包功能比较总结
简介
A → B → C
数据
mediation
包附带的jobs
数据集。以下是描述。econ_hard:处理前的经济困难程度,取值为1到5。 sex:性别的指示变量。1 = 女性 age:年龄(单位:岁)。 educ:教育程度的五个类别的因子。 job_seek:衡量求职自我效能感水平的连续量表,取值为1到5。中介变量。 depress2:处理后的抑郁症状测量。结果变量。 treat:参与者是否被随机选中参加JOBS II培训项目的指示变量。1 = 参与。
data(jobs, package = 'mediation')
模型
中介模型: job_seek ~ treatment + econ_hard + sex + age
结果模型: depression ~ treatment + econ_hard + sex + age + job_seek
R包介绍
mediation
lavaan
psych
brms
mediation 包
mediation
包开始,因为它基本上不需要比运行R中标准回归模型更多的编程能力。该包提供了平均因果中介效应(ACME),其定义如下(来自帮助文件):平均因果中介效应(ACME)表示当中介变量取其在处理条件下实现的值而不是在控制条件下实现的值时,潜在结果的预期差异,同时处理状态本身保持不变。
mediation
包能够将不同的模型用于中介变量和结果的原因。例如,中介变量可以是二元的,需要逻辑回归模型,而结果模型可能是生存模型。mediation
包,只需运行中介变量和结果的各自模型,然后使用mediate
函数获得最终结果。library(mediation)
model_mediator <- lm(job_seek ~ treat + econ_hard + sex + age, data = jobs)
model_outcome <- lm(depress2 ~ treat + econ_hard + sex + age + job_seek, data = jobs)
# 通过准贝叶斯近似进行估计
mediation_result <- mediate(
sims = 500,
treat = "treat",
mediator = "job_seek"
)
拟合观察到的结果和中介变量的模型。 从模型参数的采样分布中模拟模型参数。 重复以下三个步骤:
模拟中介变量的潜在值, 给定中介变量的模拟值,模拟潜在结果, 计算因果中介效应。
summary(mediation_result)
plot(mediation_result)
Estimate 95% CI Lower 95% CI Upper p-value
ACME -0.016 -0.038 0.009 0.220
ADE -0.045 -0.127 0.047 0.292
Total Effect-0.061 -0.149 0.027 0.188
Prop. Med. 0.226 -3.222 1.596 0.344
使用标准的R模型和语法 对中介变量和结果的多种模型类型 同时提供多种结果 良好的文档和相关的文章可免费获取 可以进行“调节的”中介
依赖于 MASS
包对简单的随机效应模型支持有限 在一些模型复杂性方面功能可能有限 不支持潜变量
piecewiseSEM包
piecewiseSEM
包的功能与 mediation
包类似,但它支持更多的模型类型,并能生成附加输出(如标准化结果)和更多选项(如多组分析、相关残差),还可以对模型进行可视化。
library(piecewiseSEM)
model_mediator <- lm(job_seek ~ treat + econ_hard + sex + age, data = jobs)
model_outcome <- lm(depress2 ~ treat + econ_hard + sex + age + job_seek, data = jobs)
mediation_result <- psem(model_mediator, model_outcome, data = jobs)
summary(mediation_result)
运行结果如下:
Structural Equation Model of mediation_result
Call:
job_seek ~ treat + econ_hard + sex + age
depress2 ~ treat + econ_hard + sex + age + job_seek
AIC BIC
26.000 88.417
---
Tests of directed separation:
No independence claims present. Tests of directed separation not possible.
Global goodness-of-fit:
Fisher's C = 0 with P-value = 1 and on 0 degrees of freedom
---
Coefficients:
Response Predictor Estimate Std.Error DF Crit.Value P.Value Std.Estimate
job_seek treat 0.0656 0.0515 894 1.2748 0.2027 0.0425
job_seek econ_hard 0.0532 0.0246 894 2.1612 0.0309 0.0720 *
job_seek sex -0.0076 0.0487 894 -0.1567 0.8755 -0.0052
job_seek age 0.0046 0.0023 894 1.9779 0.0482 0.0658 *
depress2 treat -0.0403 0.0435 893 -0.9255 0.3550 -0.0291
depress2 econ_hard 0.1485 0.0208 893 7.1323 0.0000 0.2248 ***
depress2 sex 0.1068 0.0411 893 2.5957 0.0096 0.0818 **
depress2 age 0.0006 0.0020 893 0.3306 0.7410 0.0104
depress2 job_seek -0.2400 0.0282 893 -8.4960 0.0000 -0.2682 ***
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05
可以使用 piecewiseSEM
的绘图功能快速生成模型的可视化图:
plot(mediation_result)
该图由 Diagrammer
和 graphviz
支持,可以用于进一步的定制化处理。需要注意的是,目前尚无自动计算间接效应的功能,需要手动进行计算。此外,原有的 semEff
包以前支持 psem
对象的间接效应计算,但目前不可用。
优点
支持标准 R 模型和语法
支持多种类型的模型(如线性和广义线性模型)
可以生成结构方程模型样式的结果(如模型拟合、标准化系数、AIC)
可以快速生成模型的可视化图
局限性
不支持自动计算间接效应
不支持潜变量
lavaan 包
lavaan
包中将采用的方法,事实上,截止本文撰写时,这是我们唯一的方法。lavaan
专门用于结构方程模型,例如因子分析、增长模型和我们在此进行的中介模型,强烈建议用于此类模型。虽然它在评估中介方面仅限于标准线性模型案例,但它是我们工具中唯一能够轻松结合潜变量的工具。例如,我们可以将抑郁结果作为潜在变量,潜在于各个问卷项目之下。此外,我们还可以结合多个中介和多个结果。lavaan
中按照之前描述的方式标记a、b和c'路径。否则,lavaan
非常容易使用,对于观测变量,使用标准的R公式表示模型。除此之外,我们使用:=
操作符定义我们要计算的感兴趣的效应。我们将整个模型作为一个简单的字符字符串指定,然后使用sem
函数进行分析。library(lavaan)
sem_model = '
job_seek ~ a*treat + econ_hard + sex + age
depress2 ~ c*treat + econ_hard + sex + age + b*job_seek
# 直接效应
direct := c
# 间接效应
indirect := a*b
# 总效应
total := c + (a*b)
'
model_sem = sem(sem_model, data=jobs, se='boot', bootstrap=500)
rsq=T) # 与mediation包中的ACME比较
lavaan 0.6-6 ended normally after 25 iterations
Estimator ML
Optimization method NLMINB
Number of free parameters 11
Number of observations 899
Model Test User Model:
Test statistic 0.000
Degrees of freedom 0
Parameter Estimates:
Standard errors Bootstrap
Number of requested bootstrap draws 500
Number of successful bootstrap draws 500
Regressions:
Estimate Std.Err z-value P(>|z|)
job_seek ~
treat (a) 0.066 0.049 1.332 0.183
econ_hard 0.053 0.024 2.242 0.025
sex -0.008 0.047 -0.163 0.870
age 0.005 0.002 1.934 0.053
depress2 ~
treat (c) -0.040 0.044 -0.905 0.365
econ_hard 0.149 0.022 6.908 0.000
sex 0.107 0.038 2.831 0.005
age 0.001 0.002 0.332 0.740
job_seek (b) -0.240 0.030 -8.079 0.000
Variances:
Estimate Std.Err z-value P(>|z|)
.job_seek 0.524 0.030 17.610 0.000
.depress2 0.373 0.022 17.178 0.000
R-Square:
Estimate
job_seek 0.011
depress2 0.120
Defined Parameters:
Estimate Std.Err z-value P(>|z|)
direct -0.040 0.045 -0.904 0.366
indirect -0.016 0.012 -1.324 0.185
total -0.056 0.046 -1.224 0.221
job_seek
而言,我们可以看到,我们没有发现太多的中介作用的原因是涉及的协变量一开始就没有解释中介变量的任何变化。在这种情况下,初步调查本可以节省我们的麻烦。可以处理多个中介 可以处理多个“处理”变量 可以处理多个结果 可以使用潜变量 支持一些多层次模型 可以进行调节的中介和被中介的调节(尽管不适用于潜变量)
需要额外的编码来估计间接效应 仅支持单个随机效应 虽然模型可以结合中介/结果的二元或有序变量,但在这些设置中没有直接的方法来计算类似于 mediation
包中的间接效应
psych 包
psych
包利用了在标准线性模型情况下,可以仅根据协方差矩阵通过适当的回归模型获得结果的事实。它与lavaan
非常相似,尽管使用的是普通最小二乘法,而不是最大似然法。这里的好处是语法允许你仅关注感兴趣的效应,或者包括所有内容,如果你对经济困难、年龄和性别的间接效应感兴趣,这很不错。-
而不是+
的简化版本。这仅意味着它们被包括在模型中,但没有显示关于它们的结果。中介变量用()
标识。另一个好处是可以快速绘制结果,显示未调整和调整后的直接效应之间的差异,以及适当的自举区间。library(psych)
mediation_psych = mediate(
depress2 ~ treat + (job_seek) - econ_hard - sex - age,
data = jobs,
500 =
)
mediation_psych
Mediation/Moderation Analysis
Call: mediate(y = depress2 ~ treat + (job_seek) - econ_hard - sex -
age, data = jobs, n.iter = 500)
The DV (Y) was depress2* . The IV (X) was treat* . The mediating variable(s) = job_seek* . Variable(s) partialled out were econ_hard sex age
Total effect(c) of treat* on depress2* = -0.06 S.E. = 0.05 t = -1.24 df= 895 with p = 0.21
Direct effect (c') of treat* on depress2* removing job_seek* = -0.04 S.E. = 0.15 t = 14.91 df= 893 with p = 4.6e-45
Indirect effect (ab) of treat* on depress2* through job_seek* = -0.02
Mean bootstrapped indirect effect = -0.02 with standard error = 0.01 Lower CI = -0.04 Upper CI = 0.01
R = 1.07 R2 = 1.15 F = -3510.4 on 2 and 893 DF p-value: 1
To see the longer output, specify short = FALSE in the print statement or ask for the summarysummary(mediation_psych)
Call: mediate(y = depress2 ~ treat + (job_seek) - econ_hard - sex -
age, data = jobs, n.iter = 500)
Direct effect estimates (traditional regression) (c')
depress2* se t df Prob
Intercept 2.21 0.15 14.91 893 4.60e-45
treat -0.04 0.04 -0.93 893 3.55e-01
job_seek -0.24 0.03 -8.50 893 8.14e-17
R = 1.07 R2 = 1.15 F = -3510.4 on 2 and 893 DF p-value: 1
Total effect estimates (c)
depress2* se t df Prob
treat -0.06 0.05 -1.24 895 0.215
'a' effect estimates
job_seek se t df Prob
Intercept 3.67 0.13 29.33 894 5.65e-133
treat 0.07 0.05 1.27 894 2.03e-01
'b' effect estimates
depress2* se t df Prob
job_seek -0.24 0.03 -8.5 894 7.83e-17
'ab' effect estimates (through mediators)
depress2* boot sd lower upper
treat -0.02 -0.02 0.01 -0.04 0.01
psych
包还处理多个中介和结果,这是一个额外的好处。最简单的语法,基本上是一个行的模型 快速绘制结果 可以处理多个中介、“处理”和结果 可以进行“调节的”中介
仅限于标准线性模型(lm) 依赖于 MASS
包
brms 包
brms
。它的名称代表使用Stan
进行贝叶斯回归建模,Stan
是一个用于贝叶斯分析的强大概率编程语言。我不会详细讨论贝叶斯分析,但可以查看我的相关文档。brms
在中介分析方面没有做任何特别的事情,但它的hypothesis
函数可以让我们测试路径乘积的方法。此外,sjstats
包基本上会以与mediation
包相同的方式为我们提供结果,实际上,mediation
包基本上是使用频率方法的贝叶斯解决方案。如果我们确实对结果和中介有不同的分布,我们将相对容易地获得这些平均预测值及其差异,因为贝叶斯方法始终考虑后验预测分布。无论如何,以下是代码。library(brms)
model_mediator <- bf(job_seek ~ treat + econ_hard + sex + age)
model_outcome <- bf(depress2 ~ treat + job_seek + econ_hard + sex + age)
med_result = brm(
model_mediator + model_outcome + set_rescor(FALSE),
data = jobs
)
file = 'data/mediation_brms.RData')
load('data/mediation_brms.RData')
summary(med_result)
Family: MV(gaussian, gaussian)
Links: mu = identity; sigma = identity
mu = identity; sigma = identity
Formula: job_seek ~ treat + econ_hard + sex + age
depress2 ~ treat + job_seek + econ_hard + sex + age
Data: jobs (Number of observations: 899)
Samples: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
total post-warmup samples = 4000
Population-Level Effects:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
jobseek_Intercept 3.67 0.12 3.43 3.91 1.00 6699 3749
depress2_Intercept 2.21 0.15 1.92 2.50 1.00 6174 3091
jobseek_treat 0.07 0.05 -0.03 0.17 1.00 6322 2709
jobseek_econ_hard 0.05 0.02 0.00 0.10 1.00 6266 2656
jobseek_sex -0.01 0.05 -0.10 0.09 1.00 5741 2655
jobseek_age 0.00 0.00 0.00 0.01 1.00 6539 2846
depress2_treat -0.04 0.04 -0.12 0.04 1.00 5458 3102
depress2_job_seek -0.24 0.03 -0.30 -0.18 1.00 5950 2938
depress2_econ_hard 0.15 0.02 0.11 0.19 1.00 7543 3102
depress2_sex 0.11 0.04 0.03 0.19 1.00 5599 2699
depress2_age 0.00 0.00 -0.00 0.00 1.00 4555 2887
Family Specific Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma_jobseek 0.73 0.02 0.69 0.76 1.00 6639 3276
sigma_depress2 0.61 0.01 0.59 0.64 1.00 6145 2987
Samples were drawn using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
brms
,我们可以按以下方式计算间接效应:# 使用hypothesis函数
hypothesis(med_result, 'jobseek_treat*depress2_job_seek = 0')
# sjstats包提供了与mediation包类似的输出
# print(sjstats::mediation(med_result), digits=4)
sjstats::mediation(med_result) %>% kable_df()
jobseek_*
的都是中介模型的结果,而depress2_*
是结果模型的结果。此时,我们有相同的结果,但使用贝叶斯方法,我们可以看到更多有趣的内容。例如,我们可以看到我们实际上并没有很好地捕捉到抑郁结果的偏度。我们的预测值与观察值并不完全匹配。对于中介变量,我们要好一些,但基于我们模型的预测可能仍然有点高。pp_check(med_result, resp = 'depress2') + ggtitle('Depression Outcome')
pp_check(med_result, resp = 'jobseek') + ggtitle('Mediator')
yrep
)是后验预测抽样值。语法简洁 功能极其强大——模型主要受想象力限制 基本上完成了 mediation
包近似的工作贝叶斯推断的所有优点:诊断、后验预测检查、模型比较等
估计速度较慢 超过标准线性模型时需要“手动”计算,但从贝叶斯的角度来看,这已经是常见的方法 需要对贝叶斯方法有一定的熟悉程度 更复杂
更多复杂的场景
交互作用
lm
及其扩展,纳入交互项对于上述所有包来说都是微不足道的,但路径乘积类型的方法不再适用(a*b ≠ c')。广义线性模型
mediation
包特别自豪于此,但brms
可以做它能做的任何事情,甚至更多,尽管你可能需要做更多的工作来实际计算结果。lavaan
实际上可以对二元和有序变量进行有限的模型,但要获得适当的间接估计将需要非常繁琐的手动方法。缺失数据
lavaan
、psych
和brms
包提供了一种或多种方法来处理这种情况(例如,多重插补)。替代方法
bnlearn
bnlearn
包允许研究有向、部分有向和无向图。在DAG方面,我们可以用它来基本上复制我们一直在讨论的中介模型。不过,值得注意的是,该包将有效地测试路径是否包含,而不是假设它们,但我们仍然可以根据需要施加理论约束。不仅如此,我们还可以以贝叶斯网络和Pearl的因果图理论为基础,以原则性的方式搜索感兴趣的路径,我们还将有工具通过交叉验证进一步避免过拟合。whitelist = data.frame(
from = c('treat', 'treat', 'job_seek'),
to = c('job_seek', 'depress2', 'depress2')
)
blacklist = expand.grid(
from = colnames(mediation_result$model.y$model),
to = c('treat', 'sex', 'age', 'econ_hard')
)
# 为了简化输出,我们将处理和性别作为数值型(后面解释)
library(dplyr)
jobs_trim = jobs %>%
select(depress2, treat, econ_hard, sex, age, job_seek) %>%
mutate(
treat = as.numeric(jobs$treat),
sex = as.numeric(jobs$sex)
)
library(bnlearn)
model = gs(jobs_trim, whitelist = whitelist, blacklist = blacklist)
plot(model)
whitelist = data.frame(
from = c('treat', 'age', 'sex', 'econ_hard', 'treat', 'job_seek', 'age', 'sex', 'econ_hard'),
to = c('job_seek', 'job_seek','job_seek','job_seek', 'depress2', 'depress2', 'depress2', 'depress2', 'depress2')
)
blacklist = expand.grid(
from = colnames(mediation_result$model.y$model),
to = c('treat', 'sex', 'age', 'econ_hard')
)
model = gs(jobs_trim, whitelist = whitelist, blacklist = blacklist)
plot(model)
parameters = bn.fit(model, jobs_trim)
parameters$depress2$coefficients
(Intercept) treat econ_hard sex age job_seek
2.2076414333 -0.0402647000 0.1485433818 0.1068048699 0.0006488642 -0.2399549527
parameters$job_seek$coefficients
(Intercept) treat econ_hard sex age
3.670584908 0.065615003 0.053162413 -0.007637336 0.004586492
lavaan
的默认最大似然估计。bnlearn
将根据因子取值的不同生成不同的条件模型。换句话说,对于treatment == 'treatment'
和treatment == control
,将有一个单独的模型。在我们的例子中,这将等同于允许一切与处理交互,例如lm(job_seek ~ treat * (econ_hard + sex + age))
,抑郁模型也是如此。这可能会扩展到任何二元变量(例如,包括性别)。如果中介变量是二元变量,这可能是我们想要做的。使用 Python
statsmodels
的实现。它遵循Imai的方法,因此可以被视为mediation
包的Python版本。输出与将处理作为因子变量时得到的基本相同,在这种情况下,您会获得每个处理类别的单独结果。对于我们的演示,这是不必要的,因此您可以将“平均”结果与之前的mediation
包结果进行比较。import statsmodels.api as sm
from statsmodels.stats.mediation import Mediation
import numpy as np
import pandas as pd
outcome_model = sm.OLS.from_formula("depress2 ~ treat + econ_hard + sex + age + job_seek",
data = jobs)
mediator_model = sm.OLS.from_formula("job_seek ~ treat + econ_hard + sex + age",
data = jobs)
med = Mediation(outcome_model, mediator_model, "treat", "job_seek")
med_result = med.fit(n_rep = 500)
print(np.round(med_result.summary(), decimals = 3))
Estimate Lower CI bound Upper CI bound P-value
ACME (control) -0.016 -0.048 0.014 0.332
ACME (treated) -0.016 -0.048 0.014 0.332
ADE (control) -0.043 -0.130 0.044 0.308
ADE (treated) -0.043 -0.130 0.044 0.308
Total effect -0.059 -0.144 0.029 0.208
Prop. mediated (control) 0.241 -1.710 2.254 0.364
Prop. mediated (treated) 0.241 -1.710 2.254 0.364
ACME (average) -0.016 -0.048 0.014 0.332
ADE (average) -0.043 -0.130 0.044 0.308
Prop. mediated (average) 0.241 -1.710 2.254 0.364
使用 Stata
sem
命令的选项。Stata使得在此示例中获取间接效应变得容易,但它对每个协变量都这样做,因此输出有点冗长。对于使用Stata的人来说,他们不需要单独的SEM包来获得这些结果。use "data\jobs.dta"
sem (job_seek <- treat econ_hard sex age) (depress2 <- treat econ_hard sex age job_seek), cformat(%9.3f) pformat(%5.2f)
estat teffects, compact cformat(%9.3f) pformat(%5.2f)
总结
R包功能比较总结
功能 | mediation包 | lavaan包 | piecewiseSEM包 | psych包 | brms包 |
*
表示大致支持,带有一些注意事项。•
表示支持。