查看原文
其他

R 语言独家|用公式指定 plspm 路径就像 lavaan 一样|PLS 结构方程模型

ecologyR ecologyR 2024-03-25
  • 引言

  • 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)的构造案例

⑤ R 语言 SEM 笔记:使用 ggplot2 衍生包画 Nature 结构方程模型图

⑥ R 语言 SEM:结构方程模型合集

⑦ R 语言 SEM 笔记:结构方程模型的多组分析(Multigroup Analysis)

继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存