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.
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: 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
lavaanysemTools. - 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:
install.packages("lavaan")
install.packages("semTools")
# Cargar librerías
library(lavaan)
library(semTools)
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 |
|---|---|---|
| C1 | Cognitivo | No puedo dejar de preocuparme |
| C2 | Cognitivo | Tengo pensamientos catastróficos |
| C3 | Cognitivo | Me cuesta concentrarme por la preocupación |
| C4 | Cognitivo | Anticipo lo peor en cada situación |
| S1 | Somático | Siento tensión muscular frecuente |
| S2 | Somático | Tengo palpitaciones sin causa aparente |
| S3 | Somático | Sudo excesivamente en situaciones cotidianas |
| S4 | Somático | Siento opresión en el pecho |
| E1 | Emocional | Me siento nervioso/a sin motivo claro |
| E2 | Emocional | Tengo una sensación constante de miedo |
| E3 | Emocional | Me irrito con facilidad |
| E4 | Emocional | Me siento emocionalmente desbordado/a |
Simularemos los datos con una estructura factorial conocida para que puedas reproducir el ejemplo completo:
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.
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:
fit <- cfa(modelo, data = dat, estimator = "MLR")
- 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 = TRUEo indicando qué variables son ordinales.
Para datos ordinales, la llamada sería:
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.
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í:
standardizedSolution(fit) |>
subset(op == "=~")
Los resultados de nuestro ejemplo simulado:
| Ítem | Factor | λ (carga est.) | SE | p |
|---|---|---|---|---|
| C1 | Cognitivo | .74 | .03 | < .001 |
| C2 | Cognitivo | .79 | .03 | < .001 |
| C3 | Cognitivo | .69 | .04 | < .001 |
| C4 | Cognitivo | .64 | .04 | < .001 |
| S1 | Somático | .71 | .04 | < .001 |
| S2 | Somático | .67 | .04 | < .001 |
| S3 | Somático | .59 | .05 | < .001 |
| S4 | Somático | .73 | .04 | < .001 |
| E1 | Emocional | .77 | .03 | < .001 |
| E2 | Emocional | .81 | .03 | < .001 |
| E3 | Emocional | .54 | .05 | < .001 |
| E4 | Emocional | .70 | .04 | < .001 |
- λ ≥ .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:
library(semTools)
compRelSEM(fit)
| Factor | ω (omega) | Interpretación |
|---|---|---|
| Cognitivo | .81 | Buena fiabilidad |
| Somático | .77 | Aceptable |
| Emocional | .79 | Aceptable-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:
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.
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_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
"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:
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.