lavaanModalidad: Individual o en parejas
Entrega: Script .R comentado + informe en
.Rmd o .pdf
En esta práctica trabajaremos con dos bases de datos clásicas en psicometría y ciencias del comportamiento, ambas disponibles directamente en R sin necesidad de importar archivos externos:
| Dataset | Paquete | Constructo principal |
|---|---|---|
HolzingerSwineford1939 |
lavaan |
Habilidades cognitivas (inteligencia) |
bfi |
psych |
Personalidad: Big Five (OCEAN) |
Además, como base de práctica autónoma, se introduce un tercer dataset:
| Dataset | Paquete | Constructo principal |
|---|---|---|
affect |
psych |
Afecto positivo/negativo (estado emocional) |
La práctica se divide en dos bloques:
HolzingerSwineford1939bfi / affectHolzingerSwineford1939Este dataset clásico (Holzinger & Swineford, 1939) contiene puntuaciones de 301 estudiantes de séptimo y octavo grado en 26 tests de habilidades cognitivas. Se utilizan habitualmente 9 indicadores que representan tres factores latentes:
| Factor latente | Indicadores |
|---|---|
Visual (vis) |
x1, x2, x3 |
Textual (tex) |
x4, x5, x6 |
Velocidad (spe) |
x7, x8, x9 |
# Instalar paquetes si es necesario
# install.packages(c("lavaan", "semTools", "semPlot", "tidyverse"))
library(lavaan)
library(semTools)
library(semPlot)
# Cargar datos
data("HolzingerSwineford1939")
datos_hs <- HolzingerSwineford1939
# Exploración inicial
str(datos_hs[, paste0("x", 1:9)])
psych::describe(datos_hs[, paste0("x", 1:9)])En lavaan, el operador =~ define la
relación “es medido por” (factor → indicadores).
modelo_cfa <- '
# Definición de factores latentes
visual =~ x1 + x2 + x3
textual =~ x4 + x5 + x6
speed =~ x7 + x8 + x9
'Nota de identificación: Por defecto,
lavaanfija la carga del primer indicador de cada factor a 1.0 para identificar la escala del factor latente. Alternativamente, se puede estandarizar la varianza del factor (std.lv = TRUE).
ajuste_cfa <- cfa(
model = modelo_cfa,
data = datos_hs,
estimator = "MLR" # ML Robusto: adecuado ante desviaciones de normalidad
)
summary(ajuste_cfa,
fit.measures = TRUE, # Índices de ajuste
standardized = TRUE, # Cargas estandarizadas
rsquare = TRUE) # Varianza explicada de cada indicadorLos principales índices de ajuste que debéis reportar e interpretar:
| Índice | Criterio de buen ajuste | Referencia |
|---|---|---|
| χ² / df | < 3.0 (aceptable < 5.0) | Kline (2023) |
| CFI | ≥ .95 (aceptable ≥ .90) | Hu & Bentler (1999) |
| TLI (NNFI) | ≥ .95 (aceptable ≥ .90) | Tucker & Lewis (1973) |
| RMSEA | ≤ .06 (aceptable ≤ .08) | Browne & Cudeck (1993) |
| SRMR | ≤ .08 | Hu & Bentler (1999) |
| AIC / BIC | Menor = mejor (comparativo) | — |
# Extraer índices de ajuste de forma ordenada
fitMeasures(ajuste_cfa, c("chisq", "df", "pvalue",
"cfi", "tli", "rmsea",
"rmsea.ci.lower", "rmsea.ci.upper",
"srmr", "aic", "bic"))# Cargas estandarizadas
standardizedSolution(ajuste_cfa) |>
subset(op == "=~") |>
print()
# Fiabilidad compuesta (omega) — función actualizada
semTools::compRelSEM(ajuste_cfa)
# Varianza Media Extraída (AVE) — validez convergente
semTools::AVE(ajuste_cfa)Criterios de validez convergente y discriminante (Fornell & Larcker, 1981):
- AVE ≥ .50 → el factor explica más varianza que el error (validez convergente)
- √AVE > correlación entre factores → validez discriminante
Los índices de modificación (MI) estiman cuánto mejoraría el χ² si se liberara un parámetro actualmente restringido. Deben interpretarse con criterio teórico, no mecánicamente.
modindices(ajuste_cfa,
sort. = TRUE,
maximum.number = 10,
op = c("=~", "~~")) # Cargas cruzadas y covarianzas de error⚠️ Importante: Un MI elevado no justifica por sí solo modificar el modelo. Cualquier respecificación debe tener justificación teórica. El uso puramente empírico de los MI es una fuente habitual de capitalización sobre el azar.
semPlot::semPaths(
object = ajuste_cfa,
what = "std",
layout = "tree",
edge.label.cex = 0.8,
residuals = TRUE,
intercepts = FALSE,
rotation = 2,
title = TRUE,
title.label = "CFA: Habilidades Cognitivas (Holzinger & Swineford, 1939)"
)# Modelo 1: Tres factores correlacionados (modelo teórico)
# → ajuste_cfa (ya estimado)
# Modelo 2: Un único factor general (hipótesis nula de unidimensionalidad)
modelo_unifactorial <- '
g =~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
'
ajuste_uni <- cfa(modelo_unifactorial, data = datos_hs, estimator = "MLR")
# Modelo 3: Tres factores ortogonales (sin correlación entre factores)
ajuste_ort <- cfa(modelo_cfa, data = datos_hs,
estimator = "MLR",
orthogonal = TRUE)
# Tabla comparativa
comparar_modelos <- compareFit(ajuste_uni, ajuste_ort, ajuste_cfa)
summary(comparar_modelos)
# Prueba de diferencia chi-cuadrado (modelos anidados)
lavTestLRT(ajuste_uni, ajuste_cfa)Una vez validada la estructura factorial, podemos especificar relaciones entre factores latentes. En este ejemplo, hipotetizamos que la habilidad Visual y Textual predicen la Velocidad de procesamiento, con un posible efecto indirecto (mediación).
Visual ──────────────────────────→ Speed
Visual ──→ Textual ──────────────→ Speed
(mediación parcial)
En lavaan, la parte estructural utiliza el operador
~ (“es regresado sobre”).
Para la mediación, etiquetamos las rutas con nombres arbitrarios
seguidos de *:
modelo_sem <- '
# ── PARTE DE MEDICIÓN (CFA) ──────────────────────────────
visual =~ x1 + x2 + x3
textual =~ x4 + x5 + x6
speed =~ x7 + x8 + x9
# ── PARTE ESTRUCTURAL ────────────────────────────────────
# Efecto directo de visual sobre speed (cd = "c direct")
speed ~ cd*visual
# Ruta de mediación: visual → textual → speed
textual ~ a*visual
speed ~ b*textual
# ── EFECTOS INDIRECTOS Y TOTALES ─────────────────────────
indirecto := a * b
total := cd + (a * b)
'set.seed(123) # Reproducibilidad
ajuste_sem <- sem(
model = modelo_sem,
data = datos_hs,
estimator = "ML",
se = "bootstrap",
bootstrap = 1000
)
summary(ajuste_sem,
fit.measures = TRUE,
standardized = TRUE)# Intervalos de confianza Bootstrap BCa
parameterEstimates(
ajuste_sem,
boot.ci.type = "bca.simple",
level = 0.95,
ci = TRUE
) |>
subset(label %in% c("a", "b", "cd", "indirecto", "total"))Interpretación: Si el IC al 95% del efecto indirecto (
a × b) no incluye el cero, podemos concluir que existe mediación estadísticamente significativa.
# Modelo alternativo: mediación completa (sin efecto directo visual → speed)
modelo_sem_med_completa <- '
visual =~ x1 + x2 + x3
textual =~ x4 + x5 + x6
speed =~ x7 + x8 + x9
textual ~ a*visual
speed ~ b*textual
indirecto := a * b
'
ajuste_med_completa <- sem(modelo_sem_med_completa,
data = datos_hs, estimator = "ML")
# Prueba de diferencia chi-cuadrado (modelos anidados)
lavTestLRT(ajuste_med_completa, ajuste_sem)
# Criterios de información
fitMeasures(ajuste_sem, c("aic", "bic", "cfi", "rmsea"))
fitMeasures(ajuste_med_completa, c("aic", "bic", "cfi", "rmsea"))Tenéis acceso a dos datasets con constructos psicológicos distintos. Cada estudiante (o pareja) debe elegir uno como dataset principal, aunque se valorará positivamente la comparación entre ambos.
bfi (paquete psych)Descripción: 2.800 participantes, 25 ítems de personalidad (Big Five) + variables sociodemográficas. Cada factor tiene 5 indicadores:
| Factor | Ítems | Descripción |
|---|---|---|
| A (Agreeableness) | A1–A5 | Amabilidad |
| C (Conscientiousness) | C1–C5 | Responsabilidad |
| E (Extraversion) | E1–E5 | Extroversión |
| N (Neuroticism) | N1–N5 | Neuroticismo |
| O (Openness) | O1–O5 | Apertura a la experiencia |
⚠️ Algunos ítems están formulados en sentido inverso (A1, C4, C5, E1, E2, O2, O5). Deberéis decidir si recodificarlos o especificar la dirección en el modelo.
bfi_limpio <- bfi[, 1:25] # Solo los 25 ítems
bfi_limpio <- na.omit(bfi_limpio) # Eliminar NA (o usar FIML en lavaan)affect (paquete psych)Descripción: 330 participantes en un estudio de estados emocionales. Contiene medidas de afecto positivo y negativo (PANAS), así como variables de bienestar y activación:
| Bloque | Variables | Descripción |
|---|---|---|
| Afecto positivo | PA1, PA2, PA3,
PA4 |
Entusiasmo, interés, determinación, atención |
| Afecto negativo | NA1, NA2, NA3,
NA4 |
Angustia, molestia, culpa, nerviosismo |
| Bienestar | TA, EA |
Activación total y emocional |
| Condición | Film |
Tipo de película vista (experimental) |
data("affect")
affect_limpio <- na.omit(affect[, c("PA1","PA2","PA3","PA4",
"NA1","NA2","NA3","NA4",
"TA","EA")])1.1. Especificación del modelo
Especifica en lavaan el modelo CFA correspondiente a la
estructura teórica del dataset elegido:
bfi: modelo de 5 factores (Big
Five) con 5 indicadores cada unoaffect: modelo de 2 factores (PA
y NA) con 4 indicadores cada unoJustifica brevemente: ¿Por qué esta estructura y no otra? ¿Existe apoyo teórico o empírico previo?
1.2. Estimación y decisiones metodológicas
Estima el modelo con cfa(). Debes justificar:
La elección del estimador (ML, MLR, WLSMV…).
Pista: examina la distribución de los ítems con
psych::describe() o psych::mardia().
El tratamiento de los datos perdidos:
eliminación por lista (na.omit) o estimación FIML
(missing = "FIML" en lavaan). ¿Cuál es más
apropiado y por qué?
La estrategia de identificación: ¿fijas la
primera carga o estandarizas la varianza del factor
(std.lv = TRUE)? ¿Qué consecuencias tiene cada
opción?
1.3. Evaluación del ajuste
Reporta e interpreta los siguientes índices:
χ²(df) = p =
CFI = TLI =
RMSEA = IC 90% [ , ]
SRMR =
AIC = BIC =
¿El ajuste es satisfactorio? Si no lo es, ¿qué tipo de misfit detectas (cargas bajas, covarianzas de error, cargas cruzadas)?
1.4. Validez de constructo
Calcula e interpreta:
semTools::compRelSEM(ajuste_cfa) # Fiabilidad compuesta (omega)
semTools::AVE(ajuste_cfa) # Varianza Media Extraída
inspect(ajuste_cfa, "cor.lv") # Correlaciones entre factores latentes1.5. Índices de modificación y respecificación (opcional con justificación)
¿Alguno tiene sentido teórico? Si decides respecificar el modelo, explica el razonamiento y compara el modelo original con el respecificado.
1.6. Comparación de modelos
Contrasta tu modelo teórico con al menos dos alternativas:
| Modelo | Descripción |
|---|---|
| M0 | Un factor general (unidimensional) |
| M1 | Tu modelo teórico (multidimensional) |
| M2 | Modelo respecificado (si lo hay) |
Usa lavTestLRT() para modelos anidados y compara AIC/BIC
para modelos no anidados.
2.1. Planteamiento de hipótesis
Formula explícitamente:
Sugerencias según dataset:
- bfi: El Neuroticismo predice negativamente el Bienestar (operacionalizado como ausencia de rasgos negativos); la Responsabilidad media parcialmente esa relación.
- affect: El Afecto Positivo (PA) predice positivamente la Activación Total (TA); el Afecto Negativo (NA) actúa como mediador o moderador.
2.2. Especificación del modelo SEM en lavaan
Construye el modelo completo con:
:=2.3. Estimación con bootstrapping
set.seed(_____) # Introduce tu número de semilla y justifica
ajuste_sem <- sem(
model = modelo_sem,
data = ___________,
estimator = "ML",
se = "bootstrap",
bootstrap = 1000
)2.4. Interpretación de la mediación
parameterEstimates(ajuste_sem,
boot.ci.type = "bca.simple",
level = 0.95) |>
subset(label %in% c("a", "b", "cd", "indirecto", "total"))Responde:
2.5. Índices de modificación en el SEM
¿Hay rutas estructurales no hipotetizadas que merezcan consideración teórica?
2.6. Comparación de modelos estructurales alternativos
| Modelo | Descripción |
|---|---|
| SEM_A | Mediación parcial (efecto directo + indirecto) |
| SEM_B | Mediación completa (solo efecto indirecto) |
| SEM_C | Modelo saturado / alternativo teórico |
Usa lavTestLRT() y criterios AIC/BIC. Justifica la
elección del modelo final.
Redacta un informe de resultados siguiendo las normas APA 7ª edición para modelos SEM (ver Kline, 2023). El informe debe incluir:
semPlot)El informe no debe superar 2.500 palabras (sin contar tablas ni figuras).
| Criterio | Peso | Descripción |
|---|---|---|
| Especificación correcta del modelo CFA | 20% | Sintaxis lavaan, identificación, estimador |
| Evaluación e interpretación del ajuste | 20% | Índices, MI, respecificación justificada |
| Validez de constructo | 10% | ω, AVE, validez discriminante |
| Planteamiento teórico del SEM | 15% | Hipótesis motivadas, diagrama previo |
| Análisis de mediación y bootstrapping | 20% | IC, interpretación, tamaño del efecto |
| Comparación de modelos | 10% | Modelos alternativos, criterios de selección |
| Calidad del informe y presentación | 5% | APA, claridad, reproducibilidad del código |
# Documentación interactiva de lavaan
vignette("lavaan")
browseVignettes("lavaan")
# Web oficial con tutoriales
# https://lavaan.ugent.be
# Cheatsheet de semTools
# https://github.com/simsem/semTools/wikiDocumento preparado para el Máster en Análisis de Datos en
Ciencias Sociales.
Curso académico 2025–2026.