R 语言独家|用公式指定 plspm 路径就像 lavaan 一样|PLS 结构方程模型
引言
1、问题
2、一个 plspm 包建的分析案例
2.1 指定 plspm 的路径有点费脑力
2.2 拟合 plspm
3、公式指定 plspm 的路径
3.1 使用 igraph 构建 DAG
3.2 一系列公式表示 DAG
3.3 画出 DAG 看看和 innerplot 是否一致
3.4 最终从公式获得 plspm 的路径
结语
引言
偏最小二乘法路径模型(plspm,Partial Least Squares Path Modeling)是一种用于结构方程建模(SEM)的技术,它特别适用于处理具有多个因变量和潜在变量的复杂模型。而且,可以用于小型数据集。
R 语言中,有一个优秀的 plspm 包,可以实现 plspm 的拟合,但是……
1、问题
plspm 包指定路径的方式(人工手动构造矩阵),相对于其他 lavaan 和 piecewiseSEM 包来说(这两个包,使用公式 y ~ x 的形式指定路径),更不符合经常用 R 语言的 stats::lm、lme4::lmer 等等函数分析数据的直觉。
那,对于 plspm 能不能像 lavaan 和 piecewiseSEM 那样,也用一串公式,获取 plspm 需要的路径矩阵?
2、一个 plspm 包建的分析案例
library(plspm)
2.1 指定 plspm 的路径有点费脑力
但是,指定 plspm 的路径矩阵,非常痛苦
尤其是面对超过 5、6 个变量的时候
# Path matrix
IMAG <- c(0, 0, 0, 0, 0, 0)
EXPE <- c(1, 0, 0, 0, 0, 0)
QUAL <- c(0, 1, 0, 0, 0, 0)
VAL <- c(0, 1, 1, 0, 0, 0)
SAT <- c(1, 1, 1, 1, 0, 0)
LOY <- c(1, 0, 0, 0, 1, 0)
pls.path <- rbind(IMAG, EXPE, QUAL, VAL, SAT, LOY)
可以通过路径矩阵画出 plspm 的结构
innerplot(pls.path)
2.2 拟合 plspm
然后,拟合
在 plspm 中,模型被分为多个块(blocks),每个块代表模型中的一个子集
每个块是一个包含起始和结束索引的向量,这些索引对应于 satisfaction 数据集中的行。如 1:5 代表第一个块 IMAG 包含 satisfaction 数据集的第 1 到第 5 行
pls.mode 重复了 6 次字符串 A,这个向量定义了每个块的模式(mode)
在 PLS 中,模式可以是 A(reflective indicators,反映性指标)或 B(formative indicators,构成性指标)
在这个例子中,所有块都是反映性指标
# Blocks of outer model
pls.block <- list(1:5, 6:10, 11:15, 16:19, 20:23, 24:27)
# Vector of modes (reflective indicators)
pls.mode <- rep("A", 6)
# Apply plspm
data(satisfaction)
pls.fit <- plspm(
satisfaction,
pls.path,
pls.block,
modes = pls.mode,
scaled = FALSE
)
plot(pls.fit)
3、公式指定 plspm 的路径
3.1 使用 igraph 构建 DAG
这时候,考虑到 sem 其实是一个图,有向无环图(DAG)。只需请出 igraph 包,即可轻松应对
library(igraph)
3.2 一系列公式表示 DAG
定义公式列表
f.tx <- list(
LOY ~ SAT + IMAG,
SAT ~ IMAG + EXPE + QUAL + VAL,
VAL ~ EXPE + QUAL,
QUAL ~ EXPE,
EXPE ~ IMAG
)
解析公式,将公式列表转换为边列表
得到的 edge.mat 其实是一个矩阵,不过,这不重要
edge.mat <- do.call(
rbind,
lapply(
f.tx, function(i) {
from <- all.vars(i)[-1]
to <- all.vars(i)[1]
cbind(from, to)
}
)
)
创建 DAG
dag <- graph_from_edgelist(
edge.mat,
directed = TRUE
)
3.3 画出 DAG 看看和 innerplot 是否一致
par(mar = c(0, 0, 0, 0), family = "Source Sans Pro", cex = 1)
igraph::plot.igraph(
dag,
layout = layout_in_circle,
vertex.size = 20,
vertex.color = "grey100",
vertex.label.color = "green4",
vertex.label.family = "Source Sans Pro",
edge.color = "grey0",
edge.arrow.mode = 2, # 2 is for forward arrows
edge.arrow.size = 1,
edge.arrow.width = 1
)
3.4 最终从公式获得 plspm 的路径
igraph 可以从 DAG 提取出 DAG 所对应的邻接矩阵
目前为止,还未达成目标,这个邻接矩阵 adj.mat0 还需要进一步操作
adj.mat0 <- as_adjacency_matrix(
dag,
type = "lower", # 没起到想要的效果
sparse = FALSE
)
adj.mat0
## SAT LOY IMAG EXPE QUAL VAL
## SAT 0 1 0 0 0 0
## LOY 0 0 0 0 0 0
## IMAG 1 1 0 1 0 0
## EXPE 1 0 0 0 1 1
## QUAL 1 0 0 0 0 1
## VAL 1 0 0 0 0 0
一个关键知识是 DAG 的拓扑排序
DAG 的一个关键特性是可以对它作拓扑排序,即:存在一种节点排列顺序,使得对于每一条有向边 (u, v),节点 u 在排列中都位于节点 v 之前
这种排序可以用来解决依赖关系和任务调度问题
获取节点的拓扑排序
g.tp <- igraph::topo_sort(dag)$name
g.tp
## [1] "IMAG" "EXPE" "QUAL" "VAL" "SAT" "LOY"
重塑矩阵
重新排列行和列
# 找位置
rd <- match(g.tp, rownames(adj.mat0))
cd <- match(g.tp, colnames(adj.mat0))
# 矩阵需要做一个转置
adj.mat1 <- t(adj.mat0[rd, cd])
adj.mat1
## IMAG EXPE QUAL VAL SAT LOY
## IMAG 0 0 0 0 0 0
## EXPE 1 0 0 0 0 0
## QUAL 0 1 0 0 0 0
## VAL 0 1 1 0 0 0
## SAT 1 1 1 1 0 0
## LOY 1 0 0 0 1 0
最后,判断出两个矩阵完全一致
adj.mat1 == pls.path
## IMAG EXPE QUAL VAL SAT LOY
## IMAG TRUE TRUE TRUE TRUE TRUE TRUE
## EXPE TRUE TRUE TRUE TRUE TRUE TRUE
## QUAL TRUE TRUE TRUE TRUE TRUE TRUE
## VAL TRUE TRUE TRUE TRUE TRUE TRUE
## SAT TRUE TRUE TRUE TRUE TRUE TRUE
## LOY TRUE TRUE TRUE TRUE TRUE TRUE
结语
完成
更多独家代码请查看本公众号其他推送:
① R 语言|全球独家|鼠标修改布局|结构方程模型可视化工具教程
② 结构方程模型|可视化升级:完全用 R 语言实现 Manuel Delgado-Baquerizo 风格的 SEM 图
③ R 语言结构方程模型:尝试用 R 画一个 PNAS 文章的 SEM 图
④ R 语言 SEM 笔记:复合变量(composite)的构造案例