Memuat
Memuat
Covariance-Based SEM (lavaan) for Education and Learning
0 views
Testing the path from academic self-efficacy to learning motivation to academic achievement with covariance-based SEM and model fit indices (the analysis is illustrated with synthetic data). Covariance-based SEM (CB-SEM) with lavaan estimates the measurement and structural models together and judges how well the model reproduces the data covariance matrix. It suits theses that test a theory (for example TAM or TPB) with Likert questionnaires and reasonably large samples.

Testing the path from academic self-efficacy to learning motivation to academic achievement with covariance-based SEM and model fit indices (the analysis is illustrated with synthetic data).
| Role | Variable | Unit |
|---|---|---|
| Main | Academic achievement | Likert 1-5 |
| Supporting 1 | Academic self-efficacy | Likert 1-5 |
| Supporting 2 | Learning motivation | Likert 1-5 |
The model fits the data well (CFI = 0.972, TLI = 0.963, RMSEA = 0.052, SRMR = 0.046). Academic self-efficacy has a significant direct effect on Academic achievement (beta = 0.200, p = 0.008), and the indirect effect through Learning motivation is 0.203 (p < 0.001). The model explains 31.0% of the variance in Academic achievement.
Computed from illustrative synthetic data, not from a real study.
Covariance-based SEM (CB-SEM) with lavaan estimates the measurement and structural models together and judges how well the model reproduces the data covariance matrix. It suits theses that test a theory (for example TAM or TPB) with Likert questionnaires and reasonably large samples.
The model fits when CFI and TLI exceed 0.90 (preferably 0.95) and RMSEA and SRMR are below 0.08. Then report standardised loadings, standardised path coefficients with p-values, R2 of the endogenous constructs and the indirect effect a*b to conclude on the mediator.
A short starting script with this project's variable names. The delivered version is adapted to your data and fully commented.
# Contoh kode R: Covariance-Based SEM (lavaan)
suppressPackageStartupMessages({
library(ggplot2)
library(patchwork)
library(lavaan)
})
set.seed(42)
# ---- Gaya grafik ----
pal <- c("#1f6feb", "#e8590c", "#2f9e44", "#ae3ec9", "#f59f00")
warna <- list(bg = "#f4f6fa", panel = "#ffffff", teks = "#1f2933", redup = "#5f6b78", grid = "#e3e8ee", bar = "#1d3557")
tata <- 1
theme_set(theme_minimal(base_size = 11) + theme(legend.position = "bottom"))
# ---- Data sintetis: 12 butir Likert 1-5 dari tiga konstruk laten ----
n <- 200
a <- runif(1, 0.4, 0.7) # jalur X1 -> X2 (mediator)
b <- runif(1, 0.3, 0.55) # jalur X2 -> Y
cp <- runif(1, 0.1, 0.35) # jalur langsung X1 -> Y
e_x1 <- rnorm(n)
e_x2 <- a * e_x1 + rnorm(n, 0, sqrt(1 - a^2))
e_y <- as.numeric(scale(cp * e_x1 + b * e_x2 + rnorm(n, 0, runif(1, 0.55, 0.75))))
# butir Likert: muatan acak 0.65-0.9, ambang diacak agar rata-rata butir sekitar 3.4-3.8
likert <- function(eta, k) {
sapply(seq_len(k), function(j) {
l <- runif(1, 0.65, 0.9)
s <- l * eta + rnorm(length(eta), 0, sqrt(1 - l^2))
findInterval(s, sort(c(-2, -1.1, -0.2, 0.9) + runif(1, -0.3, 0.2))) + 1
})
}
# butir Y ke-4 sedikit ikut dipengaruhi X1 (muatan silang kecil) agar kecocokan model realistis, tidak sempurna
d <- data.frame(likert(e_x1, 4), likert(e_x2, 4), likert(e_y, 3),
likert(as.numeric(scale(e_y + runif(1, 0.25, 0.5) * e_x1)), 1))
names(d) <- c(paste0("academic_self_efficacy", "_", 1:4), paste0("learning_motivation", "_", 1:4), paste0("academic_achievement", "_", 1:4))
write.csv(d, "data_contoh.csv", row.names = FALSE)
# ---- Spesifikasi model: pengukuran (=~), struktural (~), efek tidak langsung (:=) ----
model <- paste0(
"academic_self_efficacy =~ ", paste(names(d)[1:4], collapse = " + "), "\n",
"learning_motivation =~ ", paste(names(d)[5:8], collapse = " + "), "\n",
"academic_achievement =~ ", paste(names(d)[9:12], collapse = " + "), "\n",
"learning_motivation ~ a*academic_self_efficacy\n",
"academic_achievement ~ b*learning_motivation + c*academic_self_efficacy\n",
"tidak_langsung := a*b\n",
"total := c + a*b\n")
cat(model)
fit <- sem(model, data = d, estimator = "MLR") # ML robust (data Likert diperlakukan kontinu)
print(summary(fit, fit.measures = TRUE, standardized = TRUE, rsquare = TRUE))
# ---- Indeks kecocokan model (versi robust, cadangan versi biasa) ----
fm <- fitMeasures(fit, c("cfi.robust", "tli.robust", "rmsea.robust", "srmr", "cfi", "tli", "rmsea"))
pilih <- function(r, s) if (is.finite(fm[[r]])) fm[[r]] else fm[[s]]
idx <- c(CFI = pilih("cfi.robust", "cfi"), TLI = pilih("tli.robust", "tli"),
RMSEA = pilih("rmsea.robust", "rmsea"), SRMR = fm[["srmr"]])
print(round(idx, 3))
# ---- Koefisien terstandar ----
ss <- standardizedSolution(fit)
jalur <- ss[ss$op == "~", ]
ambil <- function(lhs, rhs) jalur[jalur$lhs == lhs & jalur$rhs == rhs, ]
j_a <- ambil("learning_motivation", "academic_self_efficacy"); j_b <- ambil("academic_achievement", "learning_motivation"); j_c <- ambil("academic_achievement", "academic_self_efficacy")
tl <- ss[ss$op == ":=" & ss$lhs == "tidak_langsung", ]
muat <- ss[ss$op == "=~", ]
r2 <- lavInspect(fit, "rsquare")
print(jalur[, c("lhs", "rhs", "est.std", "se", "pvalue")]); print(tl)
# ---- Grafik ----
singkat <- function(s, w = 18) { # nama konstruk pendek untuk grafik
k <- strsplit(s, " ")[[1]]
if (nchar(s) > w) k <- k[!tolower(k) %in% c("of", "to", "the", "and", "for", "in", "on", "with")]
i <- length(k)
while (nchar(paste(k, collapse = " ")) > w && i >= 1) {
if (nchar(k[i]) > 5) k[i] <- paste0(substr(k[i], 1, 4), ".")
i <- i - 1
}
substr(paste(k, collapse = " "), 1, w)
}
nm_k <- make.unique(unname(sapply(c("academic_self_efficacy", "learning_motivation", "academic_achievement"), singkat)), sep = " ")
bintang <- function(p) ifelse(p < 0.001, "***", ifelse(p < 0.01, "**", ifelse(p < 0.05, "*", " ns")))
elips <- function(x0, y0, rx = 0.74, ry = 0.3, id = 1) {
t_ <- seq(0, 2 * pi, length.out = 60)
data.frame(x = x0 + rx * cos(t_), y = y0 + ry * sin(t_), id = id)
}
pos <- data.frame(x = c(0, 1.6, 3.2), y = c(0, 1.25, 0), lab = nm_k)
de <- do.call(rbind, lapply(1:3, function(i) elips(pos$x[i], pos$y[i], id = i)))
# kotak indikator: X1 di kiri bawah, X2 di atas, Y di kanan bawah
ind <- data.frame(x = c(-0.9, -0.3, 0.3, 0.9, 0.7, 1.3, 1.9, 2.5, 2.3, 2.9, 3.5, 4.1),
y = rep(c(-0.95, 2.35, -0.95), each = 4), grup = rep(1:3, each = 4),
lab = paste0(rep(c("X1.", "M.", "Y."), each = 4), rep(1:4, 3)))
ind$xl <- pos$x[ind$grup]; ind$yl <- pos$y[ind$grup] + ifelse(ind$grup == 2, 0.3, -0.3)
st <- data.frame(x = c(0.45, 2.15, 0.78), y = c(0.3, 0.95, 0), xend = c(1.05, 2.75, 2.42), yend = c(1.0, 0.3, 0),
lab = sprintf("%.2f%s", c(j_a$est.std, j_b$est.std, j_c$est.std), bintang(c(j_a$pvalue, j_b$pvalue, j_c$pvalue))),
lx = c(0.55, 2.65, 1.6), ly = c(0.78, 0.78, -0.14))
p1 <- ggplot() +
geom_segment(data = ind, aes(xl, yl, xend = x, yend = y + ifelse(grup == 2, -0.15, 0.15)), colour = warna$redup,
linewidth = 0.4, arrow = arrow(length = unit(0.07, "inches"), type = "closed")) +
geom_tile(data = ind, aes(x, y), width = 0.5, height = 0.3, fill = warna$panel, colour = warna$redup, linewidth = 0.4) +
geom_text(data = ind, aes(x, y, label = lab), colour = warna$teks, size = 2.6) +
geom_segment(data = st, aes(x, y, xend = xend, yend = yend), colour = warna$teks, linewidth = 0.9,
arrow = arrow(length = unit(0.13, "inches"), type = "closed")) +
geom_text(data = st, aes(lx, ly, label = lab), colour = warna$teks, fontface = "bold", size = 3.3) +
geom_polygon(data = de, aes(x, y, group = id, fill = factor(id)), show.legend = FALSE) +
scale_fill_manual(values = pal[1:3]) +
geom_text(data = pos, aes(x, y, label = lab), colour = warna$panel, fontface = "bold", size = 3) +
annotate("text", x = 1.6, y = -1.38, size = 3, colour = warna$teks,
label = sprintf("Std. indirect = %.3f%s R2 = %.2f", tl$est.std, bintang(tl$pvalue), r2[["academic_achievement"]])) +
coord_cartesian(xlim = c(-1.25, 4.45), ylim = c(-1.5, 2.6)) +
theme_void() + theme(plot.background = element_rect(fill = warna$panel, colour = NA),
plot.title = element_text(face = "bold", colour = warna$teks, margin = margin(4, 0, 4, 6))) +
labs(title = "Standardised structural model (MLR)")
df <- data.frame(indeks = factor(names(idx), levels = names(idx)), nilai = unname(idx),
ambang = c(0.9, 0.9, 0.08, 0.08), arah = c("Higher is better", "Higher is better", "Lower is better", "Lower is better"))
p2 <- ggplot(df, aes(indeks, nilai, fill = arah)) +
geom_col(width = 0.6) +
geom_errorbar(aes(ymin = ambang, ymax = ambang), width = 0.8, linetype = "dashed", colour = warna$teks, linewidth = 0.6) +
geom_text(aes(label = sprintf("%.3f", nilai)), vjust = -0.4, colour = warna$teks, size = 3) +
facet_wrap(~arah, scales = "free") +
scale_fill_manual(values = pal[4:5], guide = "none") +
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
labs(title = "Fit indices vs cut-offs (dashed)", x = NULL, y = "Value")
muat$konstruk <- factor(nm_k[match(muat$lhs, c("academic_self_efficacy", "learning_motivation", "academic_achievement"))], levels = nm_k)
muat$butir <- factor(ind$lab, levels = rev(ind$lab))
p3 <- ggplot(muat, aes(est.std, butir, colour = konstruk)) +
geom_vline(xintercept = 0.7, linetype = "dashed", colour = warna$redup) +
geom_segment(aes(x = 0, xend = est.std, yend = butir), linewidth = 0.9) +
geom_point(size = 2.6) +
geom_text(aes(label = sprintf("%.2f", est.std)), hjust = -0.35, size = 2.6, colour = warna$teks) +
scale_colour_manual(values = pal[1:3], name = NULL) +
scale_x_continuous(limits = c(0, 1.1), breaks = seq(0, 1, 0.25)) +
labs(title = "Standardised loadings", x = "Loading", y = NULL)
p_hasil <- if (tata == 1) p1 / (p2 | p3) + plot_layout(heights = c(1.15, 1)) else p1 | (p2 / p3)
print(p_hasil)
The original project file is available. It is not just the picture above: you get the working R project. It can be downloaded after payment, and we send it by WhatsApp or email.
ZIP: analisis.R + data_contoh.csv + BACA-DULU.txt
Choose how much help you need. Each higher package includes everything in the lower ones. Prices exclude 11% VAT.
Package 1
US$28
excl. 11% VAT
Duration: 7 working days
Output: R files + result figures
Deliverables
Package 2 · most chosen
US$81
excl. 11% VAT
Duration: 13 working days
Output: Code + analysis + interpretation notes
Includes all of Package 1, plus
Package 3
US$178
excl. 11% VAT
Duration: 18-21 working days
Output: Code + analysis + guidance until ready to present
Includes all of Packages 1 & 2, plus
Yes. The original project file is a ZIP containing the complete analisis.R script, a data_contoh.csv sample dataset and a BACA-DULU.txt guide. It can be downloaded after payment, and we send it by WhatsApp or email. For your own data (Package 2 and 3) the script is adapted to your dataset.
Yes. Covariance-Based SEM (lavaan) is commonly used at this level in Education and Learning. We adapt the analysis to your research question, your own data and your supervisor's requirements.
No. The sheet is computed from illustrative synthetic data so you can see the kind of output R Studio gives. For your order we run the same method on your own data.
That is the point of the packages. Every script is commented, and Package 3 includes two coaching sessions plus practice questions so you can explain the method and the results at your seminar and defence.
Each project has three packages: Package 1 (code and explanation) US$28, Package 2 (your data analysis and interpretation) US$81 and Package 3 (full guidance) US$178, excluding 11% VAT. Larger datasets, extra methods or tight deadlines adjust the quote after a short consultation.
The result sheet uses illustrative synthetic data. R is a programming language of the R Foundation project; RStudio and Posit are trademarks of Posit PBC.
Back to R Studio portfolio