AFC en R con lavaan: Tutorial Paso a Paso (con Código)

El Análisis Factorial Confirmatorio (AFC, o CFA en inglés) es una de las herramientas más potentes de la psicometría moderna. Si el análisis factorial exploratorio (AFE) permite descubrir la estructura latente de un conjunto de ítems, el AFC permite poner a prueba si esa estructura se sostiene empíricamente. En este tutorial vas a aprender a realizar un AFC completo en R con el paquete lavaan, desde la especificación del modelo hasta la interpretación y el reporte de resultados en formato APA 7.

Ansiedad (factor latente) Item 1 "Me siento tenso/a" Item 2 "Me preocupo por todo" Item 3 "Pulso acelerado" Item 4 "Me cuesta relajarme" .78 .82 .75 .69 ε₁ Modelo CFA · 1 factor, 4 indicadores · lavaan λ = cargas estandarizadas · ε = varianza residual de cada ítem Fit: χ² (2) = 1.84, p = .398 · CFI = 0.999 · RMSEA = 0.012 [.000, .078] · SRMR = 0.014
Modelo CFA unidimensional para ansiedad. Las cargas λ entre .69 y .82 son saturaciones aceptables. Los errores ε capturan la varianza específica de cada ítem.

1. ¿Qué es el Análisis Factorial Confirmatorio?

El AFC es una técnica de modelado de ecuaciones estructurales (SEM) en la que el investigador especifica a priori cuántos factores latentes existen y qué ítems miden cada factor. A diferencia del AFE, donde el software descubre la estructura, en el AFC eres tú quien impone la estructura teórica y luego evalúas si los datos la confirman.

AFE vs. AFC:
  • AFE: exploratorio, genera hipótesis sobre la estructura factorial. No se fijan restricciones.
  • AFC: confirmatorio, contrasta una hipótesis específica. Cada ítem carga en un solo factor predefinido; las cargas cruzadas se fijan en cero.

Usa el AFC cuando ya tienes una teoría sobre la estructura del instrumento, por ejemplo después de un AFE previo en otra muestra, o cuando adaptas un cuestionario ya validado a una nueva población.

2. Requisitos previos

Para seguir este tutorial necesitas:

  • R (versión 4.0 o superior) y RStudio instalados.
  • Los paquetes lavaan y semTools.
  • Un conjunto de datos con al menos 200 observaciones (idealmente N ≥ 300).
  • Variables continuas o ítems Likert con al menos 5 categorías de respuesta.

Instala los paquetes necesarios si aún no los tienes:

# Instalar paquetes (solo la primera vez)
install.packages("lavaan")
install.packages("semTools")

# Cargar librerías
library(lavaan)
library(semTools)
Sobre el tamaño muestral:

Aunque N ≥ 200 es un mínimo frecuentemente citado, la regla depende de la complejidad del modelo. Para modelos con 3 factores y 12 ítems, un N entre 250 y 400 es recomendable. Con muestras pequeñas (< 150), los errores estándar se inflan y los índices de ajuste se vuelven inestables. Si tus ítems son ordinales con pocas categorías (4 o menos), debes usar un estimador específico como WLSMV en lugar del clásico ML.

3. Ejemplo práctico: el dataset

Vamos a trabajar con un ejemplo realista: la validación del Cuestionario de Ansiedad Multidimensional (CAM-12), un instrumento ficticio de 12 ítems con respuesta Likert de 5 puntos (1 = Nunca, 5 = Siempre). El cuestionario mide tres dimensiones de la ansiedad con 4 ítems cada una.

Ítem Factor Contenido
C1CognitivoNo puedo dejar de preocuparme
C2CognitivoTengo pensamientos catastróficos
C3CognitivoMe cuesta concentrarme por la preocupación
C4CognitivoAnticipo lo peor en cada situación
S1SomáticoSiento tensión muscular frecuente
S2SomáticoTengo palpitaciones sin causa aparente
S3SomáticoSudo excesivamente en situaciones cotidianas
S4SomáticoSiento opresión en el pecho
E1EmocionalMe siento nervioso/a sin motivo claro
E2EmocionalTengo una sensación constante de miedo
E3EmocionalMe irrito con facilidad
E4EmocionalMe siento emocionalmente desbordado/a

Simularemos los datos con una estructura factorial conocida para que puedas reproducir el ejemplo completo:

# Simular datos para el ejemplo (N = 350)
set.seed(2026)
n <- 350

# Generar 3 factores latentes correlacionados
Sigma_f <- matrix(c(1.0, 0.5, 0.4,
                   0.5, 1.0, 0.3,
                   0.4, 0.3, 1.0), nrow = 3)
factores <- MASS::mvrnorm(n, mu = c(0, 0, 0), Sigma = Sigma_f)

# Cargas factoriales poblacionales
lambdas <- c(.75, .80, .70, .65, # Cognitivo
             .72, .68, .60, .74, # Somático
             .78, .82, .55, .71) # Emocional

# Generar ítems: Y = lambda * F + error
items <- matrix(NA, n, 12)
factor_idx <- rep(1:3, each = 4)
for (j in 1:12) {
  error_var <- 1 - lambdas[j]^2
  items[, j] <- lambdas[j] * factores[, factor_idx[j]] +
               rnorm(n, 0, sqrt(error_var))
}

# Convertir a escala Likert 1-5
items <- round(pnorm(items) * 4 + 1)
items[items < 1] <- 1; items[items > 5] <- 5

# Crear data frame
dat <- as.data.frame(items)
colnames(dat) <- c(paste0("C", 1:4),
                   paste0("S", 1:4),
                   paste0("E", 1:4))
head(dat)

4. Paso 1: Especificar el modelo

En lavaan, la estructura factorial se define con una sintaxis compacta. El operador =~ (se lee "es medido por") asigna ítems a cada factor latente. Cada línea define un factor y sus indicadores.

# Definir el modelo de 3 factores
modelo <- '
  # Factor 1: Ansiedad Cognitiva
  cognitivo =~ C1 + C2 + C3 + C4

  # Factor 2: Ansiedad Somática
  somatico =~ S1 + S2 + S3 + S4

  # Factor 3: Ansiedad Emocional
  emocional =~ E1 + E2 + E3 + E4
'

Observa que no especificamos cargas cruzadas: cada ítem aparece en un solo factor. Tampoco especificamos correlaciones entre factores porque lavaan las estima por defecto en el modelo CFA. Internamente, lavaan fija la carga del primer indicador de cada factor a 1 para fijar la escala del factor latente (esta es la configuración por defecto; también puedes usar std.lv = TRUE para fijar la varianza del factor a 1 en lugar de fijar una carga).

5. Paso 2: Estimar el modelo

Usamos la función cfa() para ajustar el modelo a los datos:

# Estimar el modelo
fit <- cfa(modelo, data = dat, estimator = "MLR")
¿Qué estimador elegir?
  • ML (Maximum Likelihood): el clásico. Asume normalidad multivariante. Adecuado para datos continuos normales.
  • MLR (Robust ML): como ML pero con errores estándar robustos y estadístico chi-cuadrado escalado (Satorra-Bentler). Recomendado cuando hay ligera no-normalidad. Es la opción más versátil para datos continuos o Likert con 5+ categorías.
  • WLSMV (Weighted Least Squares Mean and Variance adjusted): el estándar para datos ordinales con pocas categorías (Likert de 4 puntos o menos). Usa correlaciones policóricas internamente. Se especifica con ordered = TRUE o indicando qué variables son ordinales.

Para datos ordinales, la llamada sería:

# Alternativa para datos ordinales
fit_ord <- cfa(modelo, data = dat, estimator = "WLSMV", ordered = TRUE)

6. Paso 3: Evaluar el ajuste del modelo

El ajuste global del modelo se evalúa con múltiples índices. Nunca bases tu decisión en uno solo.

# Ver resumen con índices de ajuste y cargas estandarizadas
summary(fit, standardized = TRUE, fit.measures = TRUE)

# Extraer solo los índices de ajuste
fitMeasures(fit, c("chisq.scaled", "df", "pvalue.scaled",
                  "cfi.scaled", "tli.scaled",
                  "rmsea.scaled", "srmr"))

A continuación se muestra una tabla con los principales índices de ajuste, los valores obtenidos en nuestro ejemplo simulado y los criterios de interpretación:

Índice Valor ejemplo Buen ajuste Ajuste aceptable Interpretación
χ² (chi-cuadrado) 68.42 p > .05 χ²/df < 3 Sensible al N; con muestras grandes casi siempre es significativo
df 51 -- -- Grados de libertad del modelo
CFI .976 ≥ .95 ≥ .90 Comparative Fit Index. Compara el modelo con el modelo nulo
TLI .969 ≥ .95 ≥ .90 Tucker-Lewis Index. Similar al CFI pero penaliza modelos complejos
RMSEA .031 ≤ .06 ≤ .08 Root Mean Square Error of Approximation. Mide el error de aproximación por grado de libertad
SRMR .038 ≤ .08 ≤ .10 Standardized Root Mean Square Residual. Media estandarizada de los residuos

En nuestro ejemplo, todos los índices indican un buen ajuste: CFI y TLI superan .95, el RMSEA es inferior a .06 y el SRMR está por debajo de .08. Esto sugiere que el modelo de tres factores se ajusta adecuadamente a los datos.

7. Paso 4: Interpretar las cargas factoriales

Las cargas factoriales estandarizadas (λ) indican la fuerza de la relación entre cada ítem y su factor latente. Extraemos la solución estandarizada así:

# Cargas factoriales estandarizadas
standardizedSolution(fit) |>
  subset(op == "=~")

Los resultados de nuestro ejemplo simulado:

Ítem Factor λ (carga est.) SE p
C1Cognitivo.74.03< .001
C2Cognitivo.79.03< .001
C3Cognitivo.69.04< .001
C4Cognitivo.64.04< .001
S1Somático.71.04< .001
S2Somático.67.04< .001
S3Somático.59.05< .001
S4Somático.73.04< .001
E1Emocional.77.03< .001
E2Emocional.81.03< .001
E3Emocional.54.05< .001
E4Emocional.70.04< .001
Criterios para evaluar las cargas:
  • λ ≥ .70: Excelente. El ítem es un buen indicador del factor.
  • λ entre .40 y .70: Aceptable. El ítem contribuye al factor pero comparte varianza con otros constructos.
  • λ < .40: Problemático. Considerar eliminar el ítem o revisar su redacción.

En nuestro ejemplo, todas las cargas son superiores a .50, lo que indica que todos los ítems son indicadores razonables de sus factores correspondientes. El ítem E3 (λ = .54) es el más débil y podría revisarse en futuras versiones del instrumento.

8. Paso 5: Fiabilidad por factor

La fiabilidad compuesta (omega de McDonald) es preferible al alfa de Cronbach en el contexto del AFC porque no asume que todas las cargas factoriales son iguales (tau-equivalencia). El paquete semTools permite calcularla directamente desde el objeto ajustado:

# Fiabilidad compuesta (omega) por factor
library(semTools)
compRelSEM(fit)
Factor ω (omega) Interpretación
Cognitivo.81Buena fiabilidad
Somático.77Aceptable
Emocional.79Aceptable-buena

Valores de omega superiores a .70 se consideran aceptables, y superiores a .80 indican buena fiabilidad. Los tres factores del CAM-12 presentan fiabilidad adecuada.

9. Paso 6: Modificación del modelo (si es necesario)

Cuando el ajuste del modelo no es adecuado, los índices de modificación (MI) pueden sugerir qué restricciones relajar para mejorar el ajuste:

# Índices de modificación, ordenados de mayor a menor
modificationIndices(fit, sort. = TRUE, minimum.value = 10)

El output muestra pares de parámetros que, de liberarse, reducirían el χ² en al menos el valor indicado (MI). Las modificaciones más frecuentes son añadir covarianzas entre errores de ítems del mismo factor.

ADVERTENCIA IMPORTANTE:

Nunca añadas modificaciones de forma puramente empírica. Cada cambio debe tener una justificación teórica. Las covarianzas residuales entre ítems del mismo factor pueden justificarse si los ítems comparten método (misma formulación inversa, formato de respuesta similar) o contenido similar más allá del constructo. Añadir cargas cruzadas o covarianzas sin justificación convierte tu AFC en un ejercicio exploratorio disfrazado.

Si, por ejemplo, los ítems S1 y S4 comparten varianza residual (quizás porque ambos se refieren a tensión física localizada), podrías re-especificar el modelo así:

# Modelo re-especificado: añadir covarianza residual S1 ~~ S4
modelo_v2 <- '
  cognitivo =~ C1 + C2 + C3 + C4
  somatico =~ S1 + S2 + S3 + S4
  emocional =~ E1 + E2 + E3 + E4

  # Covarianza residual con justificación teórica
  S1 ~~ S4
'


fit_v2 <- cfa(modelo_v2, data = dat, estimator = "MLR")

# Comparar modelos
anova(fit, fit_v2)

10. Cómo reportar los resultados en APA 7

Plantilla de reporte APA 7:

"Se realizó un análisis factorial confirmatorio para evaluar la estructura de tres factores del CAM-12 utilizando el paquete lavaan (Rosseel, 2012) en R. Se empleó el estimador de máxima verosimilitud robusto (MLR). Los resultados indicaron un buen ajuste del modelo a los datos: χ²(51) = 68.42, p = .051, CFI = .976, TLI = .969, RMSEA = .031 [IC 90% .000, .049], SRMR = .038. Las cargas factoriales estandarizadas oscilaron entre .54 y .81, y todas fueron estadísticamente significativas (p < .001). La fiabilidad compuesta (omega de McDonald) fue adecuada para los tres factores: ansiedad cognitiva (ω = .81), ansiedad somática (ω = .77) y ansiedad emocional (ω = .79). En conjunto, los resultados respaldan la estructura tridimensional del cuestionario."

Adapta los valores a tus datos reales. Incluye siempre: estimador utilizado, χ² con df y p, CFI, TLI, RMSEA con intervalo de confianza, SRMR, rango de cargas factoriales y fiabilidad.

11. Errores frecuentes en AFC

Estos son los errores más comunes que se encuentran al revisar estudios que aplican AFC. Evitarlos mejorará sustancialmente la calidad de tu análisis:

  1. Usar la misma muestra del AFE para el AFC. El AFC es un análisis confirmatorio: debe realizarse sobre datos independientes. Si exploras y confirmas con los mismos datos, estás capitalizando sobre el azar. Divide tu muestra o recoge datos nuevos.
  2. Modificar el modelo solo por los MI sin justificación teórica. Los índices de modificación son guías estadísticas, no mandatos. Añadir covarianzas residuales o cargas cruzadas sin una razón sustantiva transforma tu AFC en un análisis exploratorio encubierto y compromete la validez del proceso confirmatorio.
  3. Muestra demasiado pequeña. Con N < 150, los parámetros se estiman con error excesivo, los intervalos de confianza son amplísimos y los índices de ajuste se vuelven erráticos. Para modelos con 10-15 ítems y 3 factores, un N mínimo de 250 es prudente.
  4. Ignorar la naturaleza ordinal de los ítems Likert. Si tus ítems tienen 4 o menos categorías de respuesta, usar ML o MLR (que asumen continuidad) puede distorsionar las estimaciones. En estos casos, WLSMV con correlaciones policóricas es más apropiado.
  5. Reportar solo el χ². El χ² es extremadamente sensible al tamaño muestral: con N > 300, casi cualquier modelo será rechazado. Siempre reporta un conjunto completo de índices (CFI, TLI, RMSEA con IC, SRMR) para dar una imagen equilibrada del ajuste.
  6. No verificar la normalidad multivariante. Si usas ML estándar, la no-normalidad infla el χ² y sesga los errores estándar. Como mínimo, revisa la curtosis multivariante (test de Mardia) y usa MLR si hay desviaciones. Puedes comprobarlo con mardia(dat) del paquete psych.

12. Código completo reproducible

A continuación, el script completo que puedes copiar y pegar en R para replicar todo el análisis de principio a fin:

# ============================================================
# AFC en R con lavaan: Script completo reproducible
# Cuestionario de Ansiedad Multidimensional (CAM-12)
# ============================================================

# 1. Cargar paquetes ----
library(lavaan)
library(semTools)

# 2. Simular datos (N = 350) ----
set.seed(2026)
n <- 350

# Matriz de correlaciones entre factores latentes
Sigma_f <- matrix(c(1.0, 0.5, 0.4,
                   0.5, 1.0, 0.3,
                   0.4, 0.3, 1.0), nrow = 3)
factores <- MASS::mvrnorm(n, mu = c(0, 0, 0), Sigma = Sigma_f)

# Cargas factoriales poblacionales
lambdas <- c(.75, .80, .70, .65, # Cognitivo
             .72, .68, .60, .74, # Somático
             .78, .82, .55, .71) # Emocional

# Generar ítems observados
items <- matrix(NA, n, 12)
factor_idx <- rep(1:3, each = 4)
for (j in 1:12) {
  error_var <- 1 - lambdas[j]^2
  items[, j] <- lambdas[j] * factores[, factor_idx[j]] +
               rnorm(n, 0, sqrt(error_var))
}

# Convertir a escala Likert 1-5
items <- round(pnorm(items) * 4 + 1)
items[items < 1] <- 1; items[items > 5] <- 5

dat <- as.data.frame(items)
colnames(dat) <- c(paste0("C", 1:4), paste0("S", 1:4), paste0("E", 1:4))

# 3. Especificar el modelo de 3 factores ----
modelo <- '
  cognitivo =~ C1 + C2 + C3 + C4
  somatico =~ S1 + S2 + S3 + S4
  emocional =~ E1 + E2 + E3 + E4
'


# 4. Estimar el modelo con MLR ----
fit <- cfa(modelo, data = dat, estimator = "MLR")

# 5. Evaluar el ajuste global ----
summary(fit, standardized = TRUE, fit.measures = TRUE)

# Índices de ajuste seleccionados
fitMeasures(fit, c("chisq.scaled", "df", "pvalue.scaled",
                  "cfi.scaled", "tli.scaled",
                  "rmsea.scaled", "srmr"))

# 6. Cargas factoriales estandarizadas ----
standardizedSolution(fit) |> subset(op == "=~")

# 7. Fiabilidad compuesta (omega) ----
compRelSEM(fit)

# 8. Índices de modificación ----
modificationIndices(fit, sort. = TRUE, minimum.value = 10)

# 9. (Opcional) Modelo re-especificado ----
modelo_v2 <- '
  cognitivo =~ C1 + C2 + C3 + C4
  somatico =~ S1 + S2 + S3 + S4
  emocional =~ E1 + E2 + E3 + E4
  S1 ~~ S4
'

fit_v2 <- cfa(modelo_v2, data = dat, estimator = "MLR")
anova(fit, fit_v2)
summary(fit_v2, standardized = TRUE, fit.measures = TRUE)

Si tienes dudas sobre algún paso o quieres profundizar en temas avanzados como la invarianza de medida, la comparación de modelos anidados o el AFC con variables categóricas, consulta los artículos relacionados de este sitio. El AFC es una herramienta fundamental para la validación de instrumentos psicológicos, y dominar su uso te permitirá evaluar la calidad de las medidas con las que trabajas en tu investigación.

Sigue leyendo

Todos los articulos del blog