packs <- c("psych", "dplyr", "tidyr", "ggplot2", "lavaan", "lavaanPlot")
to_install <- packs[!packs %in% installed.packages()[,1]]
if(length(to_install)) install.packages(to_install)
lapply(packs, library, character.only = TRUE)
## [[1]]
## [1] "psych" "stats" "graphics" "grDevices" "utils" "datasets"
## [7] "methods" "base"
##
## [[2]]
## [1] "dplyr" "psych" "stats" "graphics" "grDevices" "utils"
## [7] "datasets" "methods" "base"
##
## [[3]]
## [1] "tidyr" "dplyr" "psych" "stats" "graphics" "grDevices"
## [7] "utils" "datasets" "methods" "base"
##
## [[4]]
## [1] "ggplot2" "tidyr" "dplyr" "psych" "stats" "graphics"
## [7] "grDevices" "utils" "datasets" "methods" "base"
##
## [[5]]
## [1] "lavaan" "ggplot2" "tidyr" "dplyr" "psych" "stats"
## [7] "graphics" "grDevices" "utils" "datasets" "methods" "base"
##
## [[6]]
## [1] "lavaanPlot" "lavaan" "ggplot2" "tidyr" "dplyr"
## [6] "psych" "stats" "graphics" "grDevices" "utils"
## [11] "datasets" "methods" "base"
diccionario_bfi <- data.frame(
variable = c(paste0("A", 1:5), paste0("C", 1:5), paste0("E", 1:5),
paste0("N", 1:5), paste0("O", 1:5), "gender", "education", "age"),
descripcion = c(
"Amabilidad: es considerado y amable con casi todo el mundo",
"Amabilidad: tiene una naturaleza de confianza",
"Amabilidad: es cooperativo",
"Amabilidad: está dispuesto a perdonar",
"Amabilidad: es generoso con los demás",
"Conciencia: hace las tareas de forma eficiente",
"Conciencia: cumple sus compromisos",
"Conciencia: es ordenado",
"Conciencia: es meticuloso",
"Conciencia: es fiable",
"Extraversión: es comunicativo",
"Extraversión: es sociable",
"Extraversión: está lleno de energía",
"Extraversión: le gusta estar con gente",
"Extraversión: tiene entusiasmo",
"Neuroticismo: se siente inseguro",
"Neuroticismo: se preocupa con facilidad",
"Neuroticismo: se siente nervioso",
"Neuroticismo: se deprime fácilmente",
"Neuroticismo: se siente triste con frecuencia",
"Apertura: tiene imaginación activa",
"Apertura: valora el arte",
"Apertura: tiene muchas ideas",
"Apertura: es original",
"Apertura: aprecia la belleza",
"Género del encuestado (1 = Hombre, 2 = Mujer)",
"Nivel educativo (1 = menos educación, 6 = más educación)",
"Edad del encuestado en años"
),
grupo = c(rep("Amabilidad", 5), rep("Conciencia", 5),
rep("Extraversión", 5), rep("Neuroticismo", 5),
rep("Apertura", 5), "Demográfico", "Demográfico", "Demográfico"),
stringsAsFactors = FALSE
)
View(diccionario_bfi)
data(bfi, package = "psych")
invertidos <- c("A4", "C4", "C5", "E3", "E4", "E5", "N1", "N2", "N4", "N5")
bfi_clean <- bfi |>
mutate(across(all_of(invertidos), ~ 7 - .x),
A1 = NULL, O2 = NULL, O4 = NULL, O5 = NULL) |>
drop_na()
La base de datos utilizada en este análisis es bfi
,
incluida en el paquete psych
de R. Esta base fue
desarrollada con fines educativos y de demostración psicométrica por el
Dr. William Revelle (Northwestern University), y contiene respuestas
auto-reportadas sobre 25 ítems diseñados para evaluar los cinco grandes
factores de personalidad: Amabilidad (A),
Conciencia (C), Extraversión (E),
Neuroticismo (N) y Apertura a la experiencia
(O). Cada ítem está codificado en una escala Likert de 1 a 6.
Además, la base incluye tres variables sociodemográficas:
gender
(1 = Hombre, 2 = Mujer), education
(escala de 1 a 6) y age
(en años).
Diversos estudios y ejercicios de clase han empleado esta base para ilustrar análisis factoriales exploratorios (EFA), confirmatorios (CFA), evaluaciones de fiabilidad y modelos de ecuaciones estructurales (SEM). Sin embargo, la mayoría de estos análisis usan la base completa sin realizar ajustes finos sobre la selección de ítems ni incorporar predictores sociodemográficos de forma estructurada.
En el presente taller se realiza un enfoque diferenciado que incluye:
age
y education
centradas,
permitiendo evaluar sus efectos sobre los rasgos de personalidad.En conjunto, este análisis no solo valida empíricamente la estructura Big Five, sino que también aporta una mirada más integrada y pedagógica al uso de SEM en psicometría aplicada.
A continuación, se resumen los valores medios, desviaciones estándar, mínimos y máximos de los ítems. Se observan altos puntajes en Amabilidad y Conciencia, mientras que Extraversión muestra puntajes bajos. Los ítems presentan adecuada dispersión.
tabla_resumen <- bfi_clean |>
summarise(across(everything(),
list(media = mean, sd = sd, min = min, max = max),
.names = "{.col}_{.fn}")) |>
pivot_longer(everything(),
names_to = c("variable",".value"),
names_sep = "_")
print(tabla_resumen)
## # A tibble: 24 × 5
## variable media sd min max
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 A2 4.83 1.16 1 6
## 2 A3 4.63 1.29 1 6
## 3 A4 2.26 1.45 1 6
## 4 A5 4.59 1.25 1 6
## 5 C1 4.56 1.22 1 6
## 6 C2 4.40 1.31 1 6
## 7 C3 4.32 1.29 1 6
## 8 C4 4.50 1.36 1 6
## 9 C5 3.74 1.63 1 6
## 10 E1 2.96 1.62 1 6
## # ℹ 14 more rows
Los histogramas muestran una tendencia alta en Amabilidad y Conciencia, baja en Extraversión, y dispersión adecuada en el resto.
plot_items <- function(prefijo, titulo) {
cols <- grep(paste0("^", prefijo), names(bfi_clean), value = TRUE)
ggplot(pivot_longer(bfi_clean, all_of(cols), names_to = "item"),
aes(value)) +
geom_histogram(binwidth = 1, fill = "steelblue", colour = "white") +
facet_wrap(~item, ncol = 2) +
labs(title = titulo,
x = "Valor (escala 1-6)", y = "Frecuencia") +
theme_minimal(base_size = 10)
}
prefijos <- c(A = "Amabilidad", C = "Conciencia", E = "Extraversión",
N = "Neuroticismo", O = "Apertura")
for(pf in names(prefijos)) {
g <- plot_items(pf, paste("Distribución de ítems –", prefijos[pf]))
print(g)
}
Las mujeres tienden a puntuar más alto en Amabilidad, Conciencia y Apertura. Los hombres puntúan levemente más en Extraversión y Neuroticismo.
bfi_segmentado <- bfi_clean |>
mutate(
Amabilidad = rowMeans(across(starts_with("A")), na.rm = TRUE),
Conciencia = rowMeans(across(starts_with("C")), na.rm = TRUE),
Extraversion = rowMeans(across(starts_with("E")), na.rm = TRUE),
Neuroticismo = rowMeans(across(starts_with("N")), na.rm = TRUE),
Apertura = rowMeans(across(starts_with("O")), na.rm = TRUE),
genero = factor(gender, levels = c(1,2), labels = c("Hombre","Mujer"))
)
bfi_largo <- pivot_longer(bfi_segmentado,
Amabilidad:Apertura,
names_to = "factor",
values_to = "puntaje")
g_genero <- ggplot(bfi_largo,
aes(genero, puntaje, fill = genero)) +
geom_boxplot(alpha = .70, outlier.size = 1.2) +
facet_wrap(~factor, scales = "free", nrow = 2) +
scale_fill_manual(values = c("#F8766D","#00BFC4")) +
labs(title = "Comparación de factores de personalidad por género",
x = "Género", y = "Puntaje promedio por factor") +
theme_minimal(base_size = 10) +
theme(legend.position = "none")
print(g_genero)
Se evalúa la consistencia interna mediante alfa de Cronbach (α). Cuatro factores muestran valores aceptables (> .70), excepto Apertura (α = .56), lo que sugiere revisión de ítems.
factores <- list(
Amabilidad = grep("^A", names(bfi_clean), value = TRUE),
Conciencia = grep("^C", names(bfi_clean), value = TRUE),
Extraversion = grep("^E", names(bfi_clean), value = TRUE),
Neuroticismo = grep("^N", names(bfi_clean), value = TRUE),
Apertura = grep("^O", names(bfi_clean), value = TRUE)
)
lapply(factores, function(v) psych::alpha(bfi_clean[, v], check.keys = TRUE)$total$raw_alpha)
## $Amabilidad
## [1] 0.721423
##
## $Conciencia
## [1] 0.7312899
##
## $Extraversion
## [1] 0.7691843
##
## $Neuroticismo
## [1] 0.8156476
##
## $Apertura
## [1] 0.5634645
El modelo CFA muestra ajuste aceptable pero no óptimo. CFI y TLI son cercanos a .90, y RMSEA indica ajuste moderado. La estructura de cinco factores se mantiene.
model_bfi <- '
Amabilidad =~ A2 + A3 + A4 + A5
Conciencia =~ C1 + C2 + C3 + C4 + C5
Extraversion =~ E1 + E2 + E3 + E4 + E5
Neuroticismo =~ N1 + N2 + N3 + N4 + N5
Apertura =~ O1 + O3
C1 ~~ C2
N1 ~~ N2
N3 ~~ N4
'
fit_cfa <- cfa(model_bfi, data = bfi_clean,
estimator = "WLSMV",
ordered = grep('^[ACENOP]', names(bfi_clean), value = TRUE),
std.lv = TRUE)
fitMeasures(fit_cfa)[c("cfi.scaled","tli.scaled","rmsea.scaled","srmr")]
## cfi.scaled tli.scaled rmsea.scaled srmr
## 0.88177439 0.85893535 0.09046218 0.07087932
Se incluye edad y educación como covariables. La estructura factorial se conserva y los efectos son pequeños pero significativos en algunos factores.
bfi_sem <- bfi_clean |>
mutate(age_c = scale(age, TRUE, FALSE)[,1],
education_c = scale(education, TRUE, FALSE)[,1])
sem_bfi <- paste(model_bfi, "
Amabilidad ~ age_c + education_c
Conciencia ~ age_c + education_c
Extraversion ~ age_c + education_c
Neuroticismo ~ age_c + education_c
Apertura ~ age_c + education_c
")
fit_sem <- sem(sem_bfi, data = bfi_sem,
estimator = "WLSMV",
ordered = grep('^[ACENOP]', names(bfi_sem), value = TRUE),
std.lv = TRUE)
round(fitMeasures(fit_sem)[c("cfi.scaled","tli.scaled","rmsea.scaled","srmr")], 3)
## cfi.scaled tli.scaled rmsea.scaled srmr
## 0.876 0.875 0.085 0.071
parameterEstimates(fit_sem, standardized = TRUE) |>
filter(op == "~") |>
select(Factor = lhs, Predictor = rhs, Beta = std.all, p = pvalue)
lavInspect(fit_sem, "rsquare")
## A2 A3 A4 A5 C1 C2
## 0.409 0.580 0.293 0.620 0.257 0.290
## C3 C4 C5 E1 E2 E3
## 0.308 0.565 0.547 0.290 0.529 0.493
## E4 E5 N1 N2 N3 N4
## 0.589 0.386 0.428 0.390 0.729 0.694
## N5 O1 O3 Amabilidad Conciencia Extraversion
## 0.338 0.371 0.557 0.019 0.015 0.005
## Neuroticismo Apertura
## 0.015 0.012
El diagrama ilustra cargas factoriales y efectos de edad y educación en los cinco factores.
lavaanPlot(model = fit_sem, stand = TRUE, coef = TRUE,
graph_options = list(rankdir = "LR", layout = "dot", splines = "polyline"),
node_options = list(shape = "box", fontsize = 12, width = 1.2, height = 0.6, fixedsize = FALSE),
edge_options = list(fontsize = 10, minlen = 2, arrowsize = 0.6))