Aplicar al análisis de clases latentes

Nada más

Autor/a

Andrés González Santa Cruz

Fecha de publicación

8 de abr, 2025

1 Configurar

Código
#eliminar archivos previos y limpiar la memoria del entorno
rm(list=ls());gc()
          used (Mb) gc trigger (Mb) max used (Mb)
Ncells  600569 32.1    1372488 73.3   686845 36.7
Vcells 1105201  8.5    8388608 64.0  1879001 14.4
Código
#cargar datos
load("_data/palestine.RData")

#Definir el repositorio sobre el que instalar los paquetes desde Chile
options(repos=structure(c(CRAN="https://cran.dcc.uchile.cl/"))) 

options(install.packages.check.source = "yes") # Chequea la fuente de los paquetes


if(!require(pacman)){install.packages("pacman");require(pacman)}

pacman::p_unlock(lib.loc = .libPaths()) #para no tener problemas reinstalando paquetes

#obliga a desplegarlo en la versión de R 4.4.0
if(Sys.info()["sysname"]=="Windows"){
if (getRversion() != "4.4.1") { stop("Requiere versión de R 4.4.1. Actual: ", getRversion()) }
}

# Cargar o instalar paquetes necesarios
pacman::p_load(
  showtext,   # Para cambiar la fuente de las letras
  ggplot2,    # Para elaborar gráficos
  plotly,     # Para elaborar gráficos interactivos
  magick,     # Para mostrar imágenes
  knitr,      # Para hacer tablas e interactuar con informes
  kableExtra, # Tablas más bonitas
  tidyverse,  # Para manipular bases de datos
  rio,        # Para importar y exportar bases de datos en distintos formatos
  psych,      # Para explorar variables
  parallel,   # Para paralelizar los procesos en la CPU
  doParallel, # Para paralelizar los procesos en la CPU
  glca,       # Para llevar a cabo análisis de clases latentes
  DiagrammeR, # Para generar gráficos esquemáticos
  DiagrammeRsvg, # Para exportar gráficos
  rsvg,       # Para transformar gráficos en formato .svg
  htmlwidgets, # Para visualizarlos en una presentación
  janitor,    # Permite limpiar bases de datos, entre otras funciones
  tableone,
  install = TRUE # Instala los paquetes si no están presentes
)

#para comparaciones
if(!require(chisq.posthoc.test)){devtools::install_github("ebbertd/chisq.posthoc.test")}


2 Describir datos

Código
# Crear un subconjunto de las variables de interés
variables_interes <- c("QKUW40_1", "QKUW40_2", "QKUW40_3", "QKUW40_4", 
                       "QKUW40_5", "QKUW40_90", "QKUW40_97", "QKUW40_98", 
                       "QKUW40_99", "Q1002", "Q1005")


# Crear un subconjunto de las variables de interés, actualizado
variables_interes_act <- 
c("Apoyo1. Boicot a empresas que apoyan a Israel", "Apoyo2. Seguimiento continuo de noticias relacionadas con la guerra", 
"Apoyo3. Participación en actividades públicas de solidaridad", 
"Apoyo4. Difusión de mensajes de solidaridad en redes sociales", 
"Apoyo5. Donaciones monetarias a los residentes de Gaza", "Apoyo90. Otro – Por favor, especifique:", 
"Apoyo97. No he tomado ninguna de estas acciones", "Apoyo98. No sabe", 
"Apoyo99. Se niega a responder", "Sexo", "Estatus ocupacional")

# Generar la tabla descriptiva
tabla_descriptiva <- CreateTableOne(vars = variables_interes_act, 
      data = arabebarometro_selected%>%
         dplyr::mutate(across(
           starts_with("QKUW40_"), ~dplyr::case_when(
             .==0~"No", .==1~"Sí", T~NA_character_
           )))%>%
        dplyr::mutate(Q1005= dplyr::case_when(
    Q1005 == "1" ~ "Empleado",
    Q1005 == "2" ~ "Auto-empleado",
    Q1005 == "3" ~ "Retirado",
    Q1005 == "4" ~ "Dueñ@ de casa",
    Q1005 == "5" ~ "Estudiante",
    Q1005 == "6" ~ "Desempleado/buscando",
    Q1005 == "99" ~ "No responde",
    TRUE ~ NA_character_
  ))%>%
    dplyr::mutate(Q1002= dplyr::case_when(
      Q1002== "1"~ "Hombre",
      Q1002== "2"~ "Mujer",
      T~ NA_character_)) %>%
  dplyr::rename(
    "Apoyo1. Boicot a empresas que apoyan a Israel" = "QKUW40_1",
    "Apoyo2. Seguimiento continuo de noticias relacionadas con la guerra" = "QKUW40_2",
    "Apoyo3. Participación en actividades públicas de solidaridad" = "QKUW40_3",
    "Apoyo4. Difusión de mensajes de solidaridad en redes sociales" = "QKUW40_4",
    "Apoyo5. Donaciones monetarias a los residentes de Gaza" = "QKUW40_5",
    "Apoyo90. Otro – Por favor, especifique:" = "QKUW40_90",
    "Apoyo97. No he tomado ninguna de estas acciones" = "QKUW40_97",
    "Apoyo98. No sabe" = "QKUW40_98",
    "Apoyo99. Se niega a responder" = "QKUW40_99", 
    "Sexo" = "Q1002",
    "Estatus ocupacional" = "Q1005"), 
  factorVars = variables_interes_act)

# Mostrar la tabla en consola
tabla_df <- as.data.frame(print(tabla_descriptiva, showAllLevels = TRUE, missing = TRUE))
tabla_df <-cbind.data.frame(var= gsub("\\s+", " ",gsub("\\.", " ", rownames(tabla_df))), tabla_df)
rownames(tabla_df)<-NULL
# tabla_df$var <- sub("\\.\\.", ": ", tabla_df$var)
# tabla_df$var <- sub("\\.\\.\\.\\.", "", tabla_df$var)
# tabla_df$var <- sub("\\.", " ", tabla_df$var)
# tabla_df$var <- sub("\\.", " ", tabla_df$var)
# tabla_df$var <- sub("\\.", " ", tabla_df$var)
# tabla_df$var <- sub("\\.$", "", tabla_df$var)
tabla_df$var <- ifelse(grepl("^X", tabla_df$var), "", tabla_df$var)
Código
tabla_df%>% 
  knitr::kable(caption="Descriptivos", col.names=c("Variable", "Categoría", "Total", "Perdidos"))%>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"),font_size = 12) %>% 
  kableExtra::scroll_box(width = "100%", height = "575px") 
Descriptivos
Variable Categoría Total Perdidos
n 1210
Apoyo1 Boicot a empresas que apoyan a Israel No 200 (16.5) 0.0
1010 (83.5)
Apoyo2 Seguimiento continuo de noticias relacionadas con la guerra No 598 (49.4) 0.0
612 (50.6)
Apoyo3 Participación en actividades públicas de solidaridad No 950 (78.5) 0.0
260 (21.5)
Apoyo4 Difusión de mensajes de solidaridad en redes sociales No 768 (63.5) 0.0
442 (36.5)
Apoyo5 Donaciones monetarias a los residentes de Gaza No 453 (37.4) 0.0
757 (62.6)
Apoyo90 Otro Por favor especifique No 1198 (99.0) 0.0
12 ( 1.0)
Apoyo97 No he tomado ninguna de estas acciones No 1176 (97.2) 0.0
34 ( 2.8)
Apoyo98 No sabe No 1205 (99.6) 0.0
5 ( 0.4)
Apoyo99 Se niega a responder No 1185 (97.9) 0.0
25 ( 2.1)
Sexo Hombre 621 (51.3) 0.0
Mujer 589 (48.7)
Estatus ocupacional Auto-empleado 46 ( 3.8) 0.0
Desempleado/buscando 32 ( 2.6)
Dueñ@ de casa 52 ( 4.3)
Empleado 645 (53.3)
Estudiante 153 (12.6)
No responde 5 ( 0.4)
Retirado 277 (22.9)

3 Modelar

Código
#Definimos el modelo: ítems manifiestos son explicados por una variable latente
f_lca<- item(QKUW40_1, QKUW40_2, QKUW40_3, QKUW40_4, QKUW40_5, QKUW40_90, QKUW40_97, QKUW40_98, QKUW40_99) ~ 1
#Semilla aleatoria para reproducibilidad
seed<-2125
#Opciones de la función
verbose<- F # Seguir los resultados. TRUE= ver proceso de convergencia
init <- 1e2 # puntos iniciales sobre los cuales encontrar una solución global. sugiero 5e2 
testiter <- 5e1 # Iteraciones para encontrar convergencia sugiero 5e2
#Otros parámetros son : eps = 1e-6 (tolerancia)

old <- Sys.time()

lca02 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 2, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)
lca03 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 3, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)
lca04 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 4, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)
lca05 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 5, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)
lca06 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 6, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)
lca07 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 7, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)
lca08 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 8, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)
lca09 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 9, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)
lca10 <- glca(f_lca, data = arabebarometro_selected[,paste0("QKUW40_", c(1:5, 90, 97, 98, 99))], nclass= 10, seed= seed, verbose= verbose, n.init= init, decreasing=T, maxiter=1e4, testiter = testiter)

new_med<-(Sys.time())
duración <- as.numeric(difftime(new_med, old, units = "mins"))

paste0("El modelo tomó ",round(duración,2)," minutos hasta que todos los análisis fueron computados")
[1] "El modelo tomó 0.57 minutos hasta que todos los análisis fueron computados"
Código
##| column: page
#| fig-dpi: 600

#para obtener otros índices de ajuste
gof<-
gofglca(lca02, lca03, lca04, lca05, lca06, lca07, lca08, lca09, lca10, test = "chisq")

#número total de clases latentes postuladas
max_classes<- nrow(gof$gtable)+1

#para obtener índice BLRT= Evaluar si el modelo k presenta mejor ajuste que el k-1

#número de iteraciones para bootstrap, se recomienda 5e2

if (interactive()) {
  message("Estamos en una sesión interactiva.")
  nboot= 10 # cómo estamos haciendo el ejercicio, ejecuta menos iteraciones
} else {
  nboot= 250 # si no está interactuando, ejecuta más iteraciones
  }

bootlrt <- tryCatch(
  gofglca(lca02, lca03, lca04, lca05, lca06, lca07, lca08, lca09, lca10, test = "boot", nboot = nboot, seed = 2125),
  error = function(e) {
    message("Error con 9 modelos, se ejecuta la versión con 5 modelos (lca02 a lca06)")
    gofglca(lca02, lca03, lca04, lca05, lca06, test = "boot", nboot = nboot, seed = 2125)
  }
)
# Error en if (maxdiff < eps) {: 
#   valor ausente donde TRUE/FALSE es necesario

manualcolors <- rev(c('gray20', 'gray50', 'gray80')) # Tres colores en escala de grises
manual_linetypes <- rev(c('solid', 'dashed', 'dotted')) # Tres estilos de línea
levels <- c("logLik", "Gsq", "AIC", "CAIC", "BIC", "entropy", "Res.Df")
labels <- c('Log-Verosimilitud', 'Chi2', 'Criterio de Información\nde Akaike (AIC)', 
            'AIC Corregido', 'Criterio de Información\nBayesiano (BIC)', 'Entropía', 
            'Grados de libertad residuales')

fig_lca_fit <- cbind.data.frame(rn = 2:max_classes, gof$gtable) %>%
  data.frame() %>% 
  dplyr::mutate_if(is.character, as.numeric) %>%  # Convertir columnas de carácter a numérico
  #formatear la base de manera larga (es decir), cada fila es la combinación única de una probabilidad, pregunta y clase
  tidyr::pivot_longer(cols = -rn, names_to = "indices", values_to = "value", values_drop_na = F) %>%
  #dejar en formato factor los índices, para reconocerlos  e identificarlos
  dplyr::mutate(indices = factor(indices, levels = levels, labels = labels)) %>%
  # Filtrar solo AIC y BIC
  dplyr::filter(grepl("(AIC|BIC)", indices, ignore.case = TRUE)) %>%
  dplyr::mutate(ModelIndex = factor(rn, levels = 2:max_classes)) %>% 
  ggplot(aes(x = ModelIndex, y = value, group = indices, 
             color = indices, linetype = indices)) +
  geom_line(linewidth = 1.5) +
  # Colores y estilos de línea personalizados
  scale_color_manual(values = manualcolors) +
  scale_linetype_manual(values = manual_linetypes) +
  labs(x = "Número de clases", y = "Valor", color = "Medida", linetype = "Medida") +
  theme_bw()

fig_lca_fit

#Para exportar gráfico
ggsave("_figs/_fig2_comparison_glca.png",fig_lca_fit, dpi=600)
Índices de ajuste modelo clases latentes

Índices de ajuste modelo clases latentes

Vemos un resumen de la salida del modelo con mejores índices de ajuste BIC.

Código
#combinamos los índices de ajuste con el índice BLRT
#algunas veces podemos tener problemas de convergencia por tanto no llegar a un cómputo adecuado
#es por eso que utilizamos esta sintaxis para sortear ese problema
merge(2:max_classes, bootlrt$gtable[,"Boot p-value"], by="row.names", all=TRUE)%>%
  {
    colnames(.)<- c("row number", "rn", "blrt")
    .
  }%>%
  select(-`row number`)%>%
  cbind.data.frame(gof$gtable)%>%
    dplyr::mutate(blrt= ifelse(as.numeric(blrt)<.001, "<0.001",sprintf("%1.3f",blrt))) %>% 
  dplyr::select(rn, everything()) %>% 
  dplyr::rename("N de clases"="rn")%>%
  dplyr::mutate(across(c("logLik","AIC","CAIC", "BIC", "entropy", "Gsq"),~sprintf("%1.2f",.)))%>%
    knitr::kable(format="markdown", caption="Índices de ajuste modelos") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"),font_size = 12) %>% 
  kableExtra::scroll_box(width = "100%", height = "375px")  
Índices de ajuste modelos
N de clases blrt logLik AIC CAIC BIC entropy Res.Df Gsq
2 <0.001 -3768.29 7574.58 7690.45 7671.45 0.51 492 521.21
3 <0.001 -3623.14 7304.28 7481.13 7452.13 0.83 482 230.91
4 <0.001 -3598.00 7274.00 7511.83 7472.83 0.86 472 180.63
5 <0.001 -3581.13 7260.25 7559.07 7510.07 0.88 462 146.88
6 <0.001 -3568.67 7255.34 7615.14 7556.14 0.83 452 121.97
7 NA -3557.42 7252.84 7673.63 7604.63 0.86 442 99.47
8 NA -3551.82 7261.65 7743.42 7664.42 0.85 432 88.28
9 NA -3540.60 7259.21 7801.96 7712.96 0.89 422 65.84
10 NA -3534.30 7266.60 7870.34 7771.34 0.87 412 53.23
Código
mejor_modelo<-
as.numeric(cbind.data.frame(rn=2:max_classes,gof$gtable) %>% dplyr::summarise(which.min(BIC)+1))

Si vemos índices BLRT con valores NA es porque tuvieron problemas de convergencia.

Código
summary(
  eval(parse(text = paste0("lca",sprintf("%02.0f",mejor_modelo))))
        ) #

Call:
glca(formula = f_lca, data = arabebarometro_selected[, paste0("QKUW40_", 
    c(1:5, 90, 97, 98, 99))], nclass = 3, n.init = init, decreasing = T, 
    testiter = testiter, maxiter = 10000, seed = seed, verbose = verbose)

Manifest items : QKUW40_1 QKUW40_2 QKUW40_3 QKUW40_4 QKUW40_5 QKUW40_90 QKUW40_97 QKUW40_98 QKUW40_99 

Categories for manifest items :
          Y = 1 Y = 2
QKUW40_1      0     1
QKUW40_2      0     1
QKUW40_3      0     1
QKUW40_4      0     1
QKUW40_5      0     1
QKUW40_90     0     1
QKUW40_97     0     1
QKUW40_98     0     1
QKUW40_99     0     1

Model : Latent class analysis 

Number of latent classes : 3 
Number of observations : 1210 
Number of parameters : 29 

log-likelihood : -3623.139 
     G-squared : 230.9075 
           AIC : 7304.277 
           BIC : 7452.13 

Marginal prevalences for latent classes :
Class 1 Class 2 Class 3 
0.05343 0.74220 0.20437 

Class prevalences by group :
    Class 1 Class 2 Class 3
ALL 0.05343  0.7422 0.20437

Item-response probabilities (Y = 1) :
        QKUW40_1 QKUW40_2 QKUW40_3 QKUW40_4 QKUW40_5 QKUW40_90 QKUW40_97
Class 1   1.0000   1.0000   1.0000   1.0000   0.9710    0.8953    0.5050
Class 2   0.1395   0.5671   0.9095   0.7506   0.4046    0.9964    1.0000
Class 3   0.0406   0.0972   0.2773   0.1183   0.1086    0.9920    0.9919
        QKUW40_98 QKUW40_99
Class 1    0.9381    0.6752
Class 2    1.0000    1.0000
Class 3    0.9960    0.9838

Item-response probabilities (Y = 2) :
        QKUW40_1 QKUW40_2 QKUW40_3 QKUW40_4 QKUW40_5 QKUW40_90 QKUW40_97
Class 1   0.0000   0.0000   0.0000   0.0000   0.0290    0.1047    0.4950
Class 2   0.8605   0.4329   0.0905   0.2494   0.5954    0.0036    0.0000
Class 3   0.9594   0.9028   0.7227   0.8817   0.8914    0.0080    0.0081
        QKUW40_98 QKUW40_99
Class 1    0.0619    0.3248
Class 2    0.0000    0.0000
Class 3    0.0040    0.0162
Código
glca_mejor_ajuste<-eval(parse(text = paste0("lca",sprintf("%02.0f",mejor_modelo))))


4 Analisis

Código
##| column: page
#| fig-dpi: 600

#https://rdrr.io/cran/glca/src/R/plot.glca.R
plot(
     glca_mejor_ajuste
     , ask=F)
Modelo seleccionado

Modelo seleccionado

Modelo seleccionado

Modelo seleccionado

Vemos la entropía usando glca_mejor_ajuste$gof$entropy 0.83. Bastante buena.

Ahora veremos los perfiles de las probabilidades de respuesta a los ítems condicionales a la pertenencia a una determinada clase.

Código
##| column: page
#| fig-dpi: 600
# Configuración inicial y preparación de datos
# Extracción de parámetros rho del modelo GLCA
rho_glca <- do.call("bind_rows", glca_mejor_ajuste$param$rho$ALL) %>%
  t() %>%
  round(2) %>%
  data.table::data.table(keep.rownames = TRUE) %>%
  magrittr::set_colnames(c("variables", paste0("Class", 1:glca_mejor_ajuste$model$C))) %>%
  tidyr::separate(variables, into = c("var", "prob"), sep = ".Y =")

# Transformación de datos del modelo
lcmodel_glca <- rho_glca %>%
  tidyr::pivot_longer(
    cols = 3:5,
    names_to = "class",
    values_to = "value"
  ) 

# Unión de etiquetas con probabilidades del modelo
lcmodel_glca <- lcmodel_glca %>%
  dplyr::mutate(pr = as.numeric(gsub("[^0-9.]+", "", prob))-1) %>% 
  dplyr::mutate(etiqueta= dplyr::case_when(
    grepl("_1$", var) ~ "01.Boicot\nempresas", 
    grepl("_2$", var) ~ "02.Seguir\nnoticias", 
    grepl("_3$", var) ~ "03.Actividades\nsolidaridad",
    grepl("_4$", var) ~ "04.Difusión\nsolidaridad",
    grepl("_5$", var) ~ "05.Donaciones\nmonetarias",
    grepl("_90$", var) ~ "90.Otro\n(Especificar)",
    grepl("_97$", var) ~ "97.No he tomado\nninguna acción",
    grepl("_98$", var) ~ "98.No sabe",
    grepl("_99$", var) ~ "99.Se niega\nresponder",
    TRUE ~ NA_character_  # Para manejar valores no coincidentes
  ))

# Generación de etiquetas para gráficos
lcmodel_glca$text_label <- paste0(
  "Etiqueta:", lcmodel_glca$etiqueta,
  "<br>Categoría:", lcmodel_glca$pr,
  "<br>%: ", scales::percent(lcmodel_glca$value)
)
#Ordenamos las opciones por orden de aparición
lcmodel_glca$var <- factor(lcmodel_glca$var, levels = paste0("QKUW40_", c(1:5, 90, 97, 98, 99)))

# Cálculo de porcentajes de clase, >.5 PROBABILIDAD DE PERTENENCIA
glca_pop_perc_05 <- glca_mejor_ajuste$posterior$ALL %>%
  dplyr::mutate_all(~ ifelse(. > .5, 1, 0)) %>%
  dplyr::mutate(final_05 = dplyr::case_when(
    `Class 1` == 1 ~ 1, `Class 2` == 1 ~ 2, `Class 3` == 1 ~ 3
  )) %>%
  janitor::tabyl(final_05) %>%
  dplyr::mutate(
    percent = scales::percent(percent, accuracy = .1),
    class = paste0("Clase", final_05),
    print = paste0(class, "\n(n=", n, ";", percent, ")")
  )
# Cálculo de porcentajes de clase, >.7 PROBABILIDAD DE PERTENENCIA
glca_pop_perc_07 <- glca_mejor_ajuste$posterior$ALL %>%
  dplyr::mutate_all(~ ifelse(. > .7, 1, 0)) %>%
  dplyr::mutate(final_07 = dplyr::case_when(
    `Class 1` == 1 ~ 1, `Class 2` == 1 ~ 2, `Class 3` == 1 ~ 3
  )) %>%
  janitor::tabyl(final_07) %>%
  dplyr::mutate(
    percent = scales::percent(percent, accuracy = .1),
    class = paste0("Clase", final_07),
    print_07 = paste0(class, "[>.7 prob]","\n(n=", n, ";", percent, ")")
  ) %>% 
  #sacamos sujetos no clasificados
  dplyr::filter(!is.na(final_07)) %>% 
  #seleccionamos columnas de interés para unir y no generar redundancia
  dplyr::select(final_07, class, print_07)

# Unión con datos transformados
lcmodel_glca <- lcmodel_glca %>%
  dplyr::mutate(class=gsub("Class","Clase",class)) %>% 
  left_join(glca_pop_perc_05, by = c("class")) %>%
  left_join(glca_pop_perc_07, by = c("class")) %>%
  dplyr::select(-class) %>%
  dplyr::rename("class" = "print") 

# Gráfico principal
zp <- ggplot(dplyr::mutate(lcmodel_glca, class=gsub("\n","",class)), aes(x = var, y = value, fill = factor(pr), label = text_label)) +
  geom_bar(stat = "identity", position = "stack") +
  facet_grid(class ~ .) +
  scale_fill_brewer(type = "seq", palette = "Greys", na.value = "white") +
  theme_bw() +
  labs(
    y = "Porcentaje de probabilidad de respuesta",
    x = "",
    fill = "Categorías de\nRespuesta"
  ) +
  theme(
    axis.text.y = element_blank(),
    axis.ticks.y = element_blank(),
    panel.grid.major.y = element_blank(),
    axis.text.x = element_text(angle = 30, hjust = 1)
  ) +
  guides(fill = guide_legend(reverse = TRUE))

# Exportación del gráfico y datos
# ggplotly(zp, tooltip = c("text_label")) %>%
#   layout(xaxis = list(showticklabels = TRUE), height = 600, width = 1000)
ggplotly(zp, tooltip = c("text_label")) %>%
  layout(
    legend = list(
      orientation = "h", # Leyenda horizontal
      x = 0.5,           # Centra horizontalmente
      y = -0.2           # Mueve la leyenda debajo del gráfico
    ),
    xaxis = list(showticklabels = TRUE),
    height = 700, # Ajusta la altura
    width = 700, # Ajusta el ancho
    margin = list(l = 100, r = 50, t = 50, b = 150) # Márgenes para etiquetas y títulos
  )

#EXPORTAMOS EL GRÁFICO NO-INTERACTIVO
ggsave("_figs/_fig4_LCA_patrones_respuesta.png", zp, dpi = 600)

Probabilidades de respuesta esperada por cada categoría en cada ítem según Clase estimada

Código
#Exportar las tablas obtenidas
lcmodel_glca %>% 
  #excluimos columnas de procesos intermedios que pueden confundirnos
  dplyr::select(-any_of(c("text_label", "prob", "n", "percent", "final_05", "final_07"))) %>%
  #sacamos información redundante
  #utilizamos doble escape (\\) para sacar símbolos que pueden ser interpretados literalmente
  dplyr::mutate(print_07 = gsub("\\[>\\.7 prob\\]\\n", " ", print_07))%>%
  dplyr::mutate((across(c("etiqueta", "class"),~gsub("\n", " ",.))))%>%
  #renombramos columnas para mayor claridad
  dplyr::rename("Prob. de pertencer a clase [P>.5]"="class", "Prob. de pertencer a clase [P>.7]"="print_07") %>% 
  {
  rio::export(.,"_output/variables_probabilidades_respuesta.xlsx")
  rio::export(.,"_output/variables_probabilidades_respuesta.csv")
  knitr::kable(., caption= "Probabilidades de respuesta de cada clase y clasificaciones por según distintos criterios de clasificación por probabilidad posterior de pertenencia", escape=F)
  }
Probabilidades de respuesta de cada clase y clasificaciones por según distintos criterios de clasificación por probabilidad posterior de pertenencia
var value pr etiqueta Prob. de pertencer a clase [P>.5] Prob. de pertencer a clase [P>.7]
QKUW40_1 1.00 0 01.Boicot empresas Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_1 0.14 0 01.Boicot empresas Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_1 0.04 0 01.Boicot empresas Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_1 0.00 1 01.Boicot empresas Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_1 0.86 1 01.Boicot empresas Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_1 0.96 1 01.Boicot empresas Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_2 1.00 0 02.Seguir noticias Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_2 0.57 0 02.Seguir noticias Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_2 0.10 0 02.Seguir noticias Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_2 0.00 1 02.Seguir noticias Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_2 0.43 1 02.Seguir noticias Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_2 0.90 1 02.Seguir noticias Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_3 1.00 0 03.Actividades solidaridad Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_3 0.91 0 03.Actividades solidaridad Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_3 0.28 0 03.Actividades solidaridad Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_3 0.00 1 03.Actividades solidaridad Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_3 0.09 1 03.Actividades solidaridad Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_3 0.72 1 03.Actividades solidaridad Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_4 1.00 0 04.Difusión solidaridad Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_4 0.75 0 04.Difusión solidaridad Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_4 0.12 0 04.Difusión solidaridad Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_4 0.00 1 04.Difusión solidaridad Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_4 0.25 1 04.Difusión solidaridad Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_4 0.88 1 04.Difusión solidaridad Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_5 0.97 0 05.Donaciones monetarias Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_5 0.40 0 05.Donaciones monetarias Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_5 0.11 0 05.Donaciones monetarias Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_5 0.03 1 05.Donaciones monetarias Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_5 0.60 1 05.Donaciones monetarias Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_5 0.89 1 05.Donaciones monetarias Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_90 0.90 0 90.Otro (Especificar) Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_90 1.00 0 90.Otro (Especificar) Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_90 0.99 0 90.Otro (Especificar) Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_90 0.10 1 90.Otro (Especificar) Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_90 0.00 1 90.Otro (Especificar) Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_90 0.01 1 90.Otro (Especificar) Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_97 0.51 0 97.No he tomado ninguna acción Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_97 1.00 0 97.No he tomado ninguna acción Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_97 0.99 0 97.No he tomado ninguna acción Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_97 0.49 1 97.No he tomado ninguna acción Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_97 0.00 1 97.No he tomado ninguna acción Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_97 0.01 1 97.No he tomado ninguna acción Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_98 0.94 0 98.No sabe Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_98 1.00 0 98.No sabe Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_98 1.00 0 98.No sabe Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_98 0.06 1 98.No sabe Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_98 0.00 1 98.No sabe Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_98 0.00 1 98.No sabe Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_99 0.68 0 99.Se niega responder Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_99 1.00 0 99.Se niega responder Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_99 0.98 0 99.Se niega responder Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)
QKUW40_99 0.32 1 99.Se niega responder Clase1 (n=64;5.3%) Clase1 (n=64;5.3%)
QKUW40_99 0.00 1 99.Se niega responder Clase2 (n=946;78.2%) Clase2 (n=856;70.7%)
QKUW40_99 0.02 1 99.Se niega responder Clase3 (n=200;16.5%) Clase3 (n=152;12.6%)


A partir del gráfico anterior y la tabla, podemos interpretar las clases:

Clase 1= Describe a personas que no han tomado ninguna acción concreta, no saben o no responden.

Clase 2= Describe a personas que han actuado de forma individual e internalizante.

Clase 3= Describe a personas que han actuado de muchas formas, incluidas acciones colectivas, mobilizaciones y difusión.

De ahí uno podría hacer interpretaciones en contraste con teorías de movimientos sociales, advocacy groups, etc. Por ejemplo, grupos que reflejan actitudes de desmovilización o desconexión, grupos de acciones individuales, simbólicas o de bajo costo (“participación selectiva” / free-riding), y un grupo cuya movilización tiende a ser colectiva, incorporando recursos y redes con mayor nivel de compromiso y participación activa.


Código
posterior_glca_05<-
glca_mejor_ajuste$posterior$ALL %>% 
    dplyr::mutate_all(~ifelse(.>.5,1,0)) %>% 
  dplyr::mutate(final_05=dplyr::case_when(`Class 1`==1~1,`Class 2`==1~2, `Class 3`==1~3))

posterior_glca_07<-
glca_mejor_ajuste$posterior$ALL %>% 
    dplyr::mutate_all(~ifelse(.>.7,1,0)) %>% 
  dplyr::mutate(final_07=dplyr::case_when(`Class 1`==1~1,`Class 2`==1~2, `Class 3`==1~3))


glca_mejor_ajuste$posterior$ALL %>%
  dplyr::mutate_all(~ ifelse(. > .5, 1, 0)) %>%
  dplyr::mutate(final_05 = dplyr::case_when(
    `Class 1` == 1 ~ 1, `Class 2` == 1 ~ 2, `Class 3` == 1 ~ 3
  )) %>% 
  dplyr::mutate(final_07= posterior_glca_07$final_07) %>% 
  janitor::tabyl(final_05, final_07) %>% 
  dplyr::rename("No clasificado" = "NA_") %>% 
  knitr::kable(
    col.names = c("Clases", "Clase 1", "Clase 2", "Clase 3", "No clasificado"),
    caption = "Clasificación por valores superiores a 0,7 en probabilidad vs. 0,5"
  )
Clasificación por valores superiores a 0,7 en probabilidad vs. 0,5
Clases Clase 1 Clase 2 Clase 3 No clasificado
1 64 0 0 0
2 0 856 0 90
3 0 0 152 48
Código
posterior_glca_07 %>% 
    rowwise() %>%
  dplyr::mutate(count_ones = sum(c_across(starts_with("Class")) == 1)) %>%
  ungroup() %>% 
  janitor::tabyl(final_07,count_ones) %>% 
  knitr::kable(col.names= c("Clasificación por valores superiores a 0,7 en probabilidad", "0", "1"), caption="Pruebas por probabilidades de clasificación posterior")
Pruebas por probabilidades de clasificación posterior
Clasificación por valores superiores a 0,7 en probabilidad 0 1
1 0 64
2 0 856
3 0 152
NA 138 0

Vemos que hay algunas observaciones que si clasificáramos por un criterio más estricto de pertenencia (al menos 49% de la variabilidad es explicada por la clase) no alcanzan a pertenecer a una determinada clase.


5 Caracterización/ Comparación con otras variables

5.1 Clasificar-analizar

Código
invisible("Ocupación (Q1005)")
cbind.data.frame(ocupacion=arabebarometro_selected$Q1005, clasif_05= posterior_glca_05$final_05) %>%
    dplyr::mutate(ocupacion= dplyr::case_when(ocupacion%in%c("1","2")~"activa", ocupacion=="5"~"estudiante", T~"inactiva"))%>%
    dplyr::group_by(ocupacion, clasif_05)%>% 
    dplyr::count() %>% 
    dplyr::mutate(clasif_05= dplyr::case_when(clasif_05=="1"~"Clase1",clasif_05=="2"~"Clase2", clasif_05=="3"~"Clase3", T~NA_character_))%>%
    tidyr::pivot_wider(names_from = clasif_05, values_from= n)%>%
  dplyr::mutate(total= Clase1+Clase2+Clase3)%>%
  dplyr::mutate(Clase1= glue::glue("{Clase1}({scales::percent(Clase1/total)})"))%>%
  dplyr::mutate(Clase2= glue::glue("{Clase2}({scales::percent(Clase2/total)})"))%>%
  dplyr::mutate(Clase3= glue::glue("{Clase3}({scales::percent(Clase3/total)})"))

cbind.data.frame(ocupacion= arabebarometro_selected$Q1005, clasif_05= posterior_glca_05$final_05) %>%
    dplyr::mutate(ocupacion= dplyr::case_when(ocupacion%in%c("1","2")~"activa", ocupacion=="5"~"estudiante", T~"inactiva"))%>%
    janitor::tabyl(ocupacion, clasif_05)%>%
    janitor::chisq.test()

#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_
#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_#_

invisible("Sexo (Q1002)")
cbind.data.frame(sexo=arabebarometro_selected$Q1002, clasif_05= posterior_glca_05$final_05) %>%
  dplyr::group_by(sexo, clasif_05)%>% 
  dplyr::count() %>% 
  dplyr::mutate(sexo= ifelse(sexo=="1","hombre", "mujer"))%>%
    dplyr::mutate(clasif_05= dplyr::case_when(clasif_05=="1"~"Clase1",clasif_05=="2"~"Clase2", clasif_05=="3"~"Clase3", T~NA_character_))%>%
  tidyr::pivot_wider(names_from = clasif_05, values_from= n)%>%
  dplyr::mutate(total= Clase1+Clase2+Clase3)%>%
  dplyr::mutate(Clase1= glue::glue("{Clase1}({scales::percent(Clase1/total)})"))%>%
  dplyr::mutate(Clase2= glue::glue("{Clase2}({scales::percent(Clase2/total)})"))%>%
  dplyr::mutate(Clase3= glue::glue("{Clase3}({scales::percent(Clase3/total)})"))

chisq.test(arabebarometro_selected$Q1002, posterior_glca_05$final_05) 

chisq.posthoc.test(table(arabebarometro_selected$Q1002,posterior_glca_05$final_05))

cbind.data.frame(sexo=arabebarometro_selected$Q1002, clasif_07= posterior_glca_07$final_07) %>%
  dplyr::group_by(sexo, clasif_07) %>%
  dplyr::count() %>% 
  dplyr::mutate(sexo= ifelse(sexo=="1","hombre", "mujer"))%>%
  dplyr::mutate(clasif_07= dplyr::case_when(clasif_07=="1"~"Clase1",clasif_07=="2"~"Clase2", clasif_07=="3"~"Clase3", T~NA_character_))%>%
  tidyr::pivot_wider(names_from = clasif_07, values_from= n)%>%
  dplyr::mutate(total= Clase1+Clase2+Clase3)%>%
  dplyr::mutate(Clase1= glue::glue("{Clase1}({scales::percent(Clase1/total)})"))%>%
  dplyr::mutate(Clase2= glue::glue("{Clase2}({scales::percent(Clase2/total)})"))%>%
  dplyr::mutate(Clase3= glue::glue("{Clase3}({scales::percent(Clase3/total)})"))

#excluimos los casos perdidos
chisq.test(table(arabebarometro_selected$Q1002[!is.na(posterior_glca_07$final_07)],posterior_glca_07$final_07[!is.na(posterior_glca_07$final_07)]))

#obtenemos los resultados
chisq.posthoc.test(table(arabebarometro_selected$Q1002[!is.na(posterior_glca_07$final_07)],posterior_glca_07$final_07[!is.na(posterior_glca_07$final_07)]))
# A tibble: 3 × 5
# Groups:   ocupacion [3]
  ocupacion  Clase1 Clase2   Clase3   total
  <chr>      <glue> <glue>   <glue>   <int>
1 activa     30(4%) 543(79%) 118(17%)   691
2 estudiante 9(6%)  104(68%) 40(26%)    153
3 inactiva   25(7%) 299(82%) 42(11%)    366

    Pearson's Chi-squared test

data:  .
X-squared = 19.887, df = 4, p-value = 0.0005256

# A tibble: 2 × 5
# Groups:   sexo [2]
  sexo   Clase1 Clase2   Clase3   total
  <chr>  <glue> <glue>   <glue>   <int>
1 hombre 27(4%) 488(79%) 106(17%)   621
2 mujer  37(6%) 458(78%) 94(16%)    589

    Pearson's Chi-squared test

data:  arabebarometro_selected$Q1002 and posterior_glca_05$final_05
X-squared = 2.3893, df = 2, p-value = 0.3028

  Dimension     Value         1          2          3
1         1 Residuals -1.502353  0.3468845  0.5195646
2         1  p values  0.798036  1.0000000  1.0000000
3         2 Residuals  1.502353 -0.3468845 -0.5195646
4         2  p values  0.798036  1.0000000  1.0000000
# A tibble: 2 × 6
# Groups:   sexo [2]
  sexo   Clase1 Clase2   Clase3   `NA` total
  <chr>  <glue> <glue>   <glue>  <int> <int>
1 hombre 27(5%) 438(80%) 83(15%)    73   548
2 mujer  37(7%) 418(80%) 69(13%)    65   524

    Pearson's Chi-squared test

data:  table(arabebarometro_selected$Q1002[!is.na(posterior_glca_07$final_07)],     posterior_glca_07$final_07[!is.na(posterior_glca_07$final_07)])
X-squared = 2.7833, df = 2, p-value = 0.2487

  Dimension     Value         1          2          3
1         1 Residuals -1.474144  0.0636584  0.9280558
2         1  p values  0.842657  1.0000000  1.0000000
3         2 Residuals  1.474144 -0.0636584 -0.9280558
4         2  p values  0.842657  1.0000000  1.0000000

5.2 Modelo con covariable

Ahora predecimos la pertenencia a una clase latente basados en el valor de Sexo Q1002 y Ocupación Q1005 (ver tabla de variables), recodificando entre población activa o no activa económicamente.

Código
f_lca_adj<- item(QKUW40_1, QKUW40_2, QKUW40_3, QKUW40_4, QKUW40_5, QKUW40_90, QKUW40_97, QKUW40_98, QKUW40_99) ~ Q1002 + Q1005
lca03_adj <- glca(f_lca_adj, data = arabebarometro_selected[,c(paste0("QKUW40_", c(1:5, 90, 97, 98, 99)),"Q1002", "Q1005")]%>% dplyr::mutate(Q1005= dplyr::case_when(Q1005%in%c("1","2")~"activa", Q1005=="5"~"estudiante", T~"inactiva")), 
    nclass= 3, 
    seed= seed, 
    verbose= verbose, 
    n.init= init, 
    decreasing=T, 
    maxiter=1e4, 
    testiter = testiter)
Código
lca03_adj$coefficient$ALL
$`Class1/3`
                Odds Ratio Coefficient  Std. Error    t value     Pr(>|t|)
(Intercept)     11.3172196   2.4263254   0.5664119  4.2836768 1.988565e-05
Q1002            0.5791354  -0.5462190   0.3262480 -1.6742447 9.434888e-02
Q1005estudiante  1.1842732   0.1691293   0.4376050  0.3864884 6.992051e-01
Q1005inactiva    0.3382201  -1.0840585   0.3628500 -2.9876214 2.869980e-03

$`Class2/3`
                Odds Ratio Coefficient  Std. Error   t value     Pr(>|t|)
(Intercept)     27.7086236   3.3217437   0.5102814  6.509631 1.113777e-10
Q1002            0.7200225  -0.3284728   0.2930988 -1.120690 2.626491e-01
Q1005estudiante  0.6121956  -0.4907034   0.4123701 -1.189959 2.343032e-01
Q1005inactiva    0.7080095  -0.3452977   0.2970773 -1.162316 2.453434e-01
  • Podría decirse que hombres empleados (intercepto) tienen mayores chances de pertenecer a la clase 1 vs. la 3 (OR= 11.3), y 2 vs. 3 (OR= 27.7)
  • Encontrarse inactivo (retirado, dueñ@ de casa o desempleado y buscando trabajo vs. empleados) se asocia a menores chances de pertenecer a la clase 1 vs. la 3.
  • No hay evidencia de una asociación entre pertenecer a una clase y el sexo.

Tener en cuenta la escasez de observaciones para algunas categorías de respuesta.

Código
# Crear un data frame combinado con ocupación, clasificación y sexo
data_combined <- cbind.data.frame(
    ocupacion = arabebarometro_selected$Q1005,
    clasif_05 = posterior_glca_05$final_05,
    sexo = arabebarometro_selected$Q1002
) %>%
    # Reemplazar valores en la variable 'ocupacion'
    mutate(
        ocupacion = case_when(
            ocupacion %in% c("1", "2") ~ "activa",
            ocupacion == "5" ~ "estudiante",
            TRUE ~ "inactiva"
        )
    )

# Crear tabla de frecuencias cruzadas con xtabs
tabla_cruzada <- xtabs(~ clasif_05 + ocupacion + sexo, data = data_combined)

6 Información de la sesión

Código
#guardamos la información generada
save.image("_data/palestine2.RData")

#vemos desde donde se producen las librerías
message(paste0("R library: ", Sys.getenv("R_LIBS_USER")))
R library: C:\Users\andre\AppData\Local/R/win-library/4.4
Código
message(paste0("Date: ",withr::with_locale(new = c('LC_TIME' = 'C'), code =Sys.time())))
Date: 2025-04-08 15:46:43.070778
Código
message(paste0("Editor context: ", getwd()))
Editor context: H:/Mi unidad/palestine/palestine
Código
#plataforma donde se desplegó el código
devtools::session_info()$platform
Warning in system2("quarto", "-V", stdout = TRUE, env = paste0("TMPDIR=", : el
comando ejecutado '"quarto"
TMPDIR=C:/Users/andre/AppData/Local/Temp/Rtmp8kiNAV/file3b9020d76836 -V' tiene
el estatus 1
 setting  value
 version  R version 4.4.1 (2024-06-14 ucrt)
 os       Windows 11 x64 (build 26100)
 system   x86_64, mingw32
 ui       RTerm
 language (EN)
 collate  Spanish_Chile.utf8
 ctype    Spanish_Chile.utf8
 tz       America/Santiago
 date     2025-04-08
 pandoc   3.2 @ C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown)
 quarto   NA @ C:\\Users\\andre\\AppData\\Local\\Programs\\Quarto\\bin\\quarto.exe
Código
sesion_info <- devtools::session_info()
Warning in system2("quarto", "-V", stdout = TRUE, env = paste0("TMPDIR=", : el
comando ejecutado '"quarto"
TMPDIR=C:/Users/andre/AppData/Local/Temp/Rtmp8kiNAV/file3b90708a5b1d -V' tiene
el estatus 1
Código
dplyr::select(
  tibble::as_tibble(sesion_info$packages),
  c(package, loadedversion, source)
) %>% 
 knitr::kable(caption = "Información paquetes de R", format = "html",
      col.names = c("Paquete", "Versión", "Fuente"),
    row.names = FALSE,
      align = c("c", "l", "r")) %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"),font_size = 12) %>% 
  kableExtra::scroll_box(width = "100%", height = "375px")  
Información paquetes de R
Paquete Versión Fuente
cachem 1.1.0 CRAN (R 4.4.1)
chisq.posthoc.test 0.1.2 CRAN (R 4.4.1)
cli 3.6.4 CRAN (R 4.4.1)
codetools 0.2-20 CRAN (R 4.4.1)
colorspace 2.1-1 CRAN (R 4.4.1)
crosstalk 1.2.1 CRAN (R 4.4.1)
curl 6.2.1 CRAN (R 4.4.1)
data.table 1.17.0 CRAN (R 4.4.1)
DBI 1.2.3 CRAN (R 4.4.1)
devtools 2.4.5 CRAN (R 4.4.1)
DiagrammeR 1.0.11 CRAN (R 4.4.1)
DiagrammeRsvg 0.1 CRAN (R 4.4.1)
digest 0.6.37 CRAN (R 4.4.0)
doParallel 1.0.17 CRAN (R 4.4.1)
dplyr 1.1.4 CRAN (R 4.4.1)
ellipsis 0.3.2 CRAN (R 4.4.1)
evaluate 1.0.3 CRAN (R 4.4.1)
farver 2.1.2 CRAN (R 4.4.1)
fastmap 1.2.0 CRAN (R 4.4.1)
forcats 1.0.0 CRAN (R 4.4.1)
foreach 1.5.2 CRAN (R 4.4.1)
fs 1.6.5 CRAN (R 4.4.2)
generics 0.1.3 CRAN (R 4.4.1)
ggplot2 3.5.1 CRAN (R 4.4.1)
glca 1.4.0 CRAN (R 4.4.1)
glue 1.8.0 CRAN (R 4.4.2)
gtable 0.3.6 CRAN (R 4.4.1)
haven 2.5.4 CRAN (R 4.4.1)
hms 1.1.3 CRAN (R 4.4.1)
htmltools 0.5.8.1 CRAN (R 4.4.1)
htmlwidgets 1.6.4 CRAN (R 4.4.1)
httpuv 1.6.15 CRAN (R 4.4.1)
httr 1.4.7 CRAN (R 4.4.1)
iterators 1.0.14 CRAN (R 4.4.1)
janitor 2.2.1 CRAN (R 4.4.1)
jsonlite 1.9.1 CRAN (R 4.4.1)
kableExtra 1.4.0 CRAN (R 4.4.1)
knitr 1.49 CRAN (R 4.4.2)
labeling 0.4.3 CRAN (R 4.4.1)
labelled 2.14.0 CRAN (R 4.4.1)
later 1.4.1 CRAN (R 4.4.2)
lattice 0.22-6 CRAN (R 4.4.1)
lazyeval 0.2.2 CRAN (R 4.4.1)
lifecycle 1.0.4 CRAN (R 4.4.1)
lubridate 1.9.4 CRAN (R 4.4.1)
magick 2.8.5 CRAN (R 4.4.1)
magrittr 2.0.3 CRAN (R 4.4.1)
MASS 7.3-60.2 CRAN (R 4.4.1)
Matrix 1.7-0 CRAN (R 4.4.1)
memoise 2.0.1 CRAN (R 4.4.1)
mime 0.12 CRAN (R 4.4.1)
miniUI 0.1.1.1 CRAN (R 4.4.1)
mitools 2.4 CRAN (R 4.4.1)
mnormt 2.1.1 CRAN (R 4.4.1)
munsell 0.5.1 CRAN (R 4.4.1)
nlme 3.1-164 CRAN (R 4.4.1)
pacman 0.5.1 CRAN (R 4.4.1)
pillar 1.10.1 CRAN (R 4.4.1)
pkgbuild 1.4.6 CRAN (R 4.4.1)
pkgconfig 2.0.3 CRAN (R 4.4.1)
pkgload 1.4.0 CRAN (R 4.4.2)
plotly 4.10.4 CRAN (R 4.4.1)
profvis 0.4.0 CRAN (R 4.4.2)
promises 1.3.2 CRAN (R 4.4.2)
psych 2.4.12 CRAN (R 4.4.1)
purrr 1.0.4 CRAN (R 4.4.1)
R.methodsS3 1.8.2 CRAN (R 4.4.1)
R.oo 1.27.0 CRAN (R 4.4.1)
R.utils 2.13.0 CRAN (R 4.4.1)
R6 2.6.1 CRAN (R 4.4.1)
ragg 1.3.3 CRAN (R 4.4.2)
RColorBrewer 1.1-3 CRAN (R 4.4.1)
Rcpp 1.0.14 CRAN (R 4.4.1)
readr 2.1.5 CRAN (R 4.4.1)
remotes 2.5.0 CRAN (R 4.4.1)
rio 1.2.3 CRAN (R 4.4.1)
rlang 1.1.5 CRAN (R 4.4.1)
rmarkdown 2.29 CRAN (R 4.4.2)
rstudioapi 0.17.1 CRAN (R 4.4.2)
rsvg 2.6.1 CRAN (R 4.4.1)
scales 1.3.0 CRAN (R 4.4.1)
sessioninfo 1.2.3 CRAN (R 4.4.1)
shiny 1.10.0 CRAN (R 4.4.1)
showtext 0.9-7 CRAN (R 4.4.1)
showtextdb 3.0 CRAN (R 4.4.1)
snakecase 0.11.1 CRAN (R 4.4.1)
stringi 1.8.4 CRAN (R 4.4.1)
stringr 1.5.1 CRAN (R 4.4.1)
survey 4.4-2 CRAN (R 4.4.1)
survival 3.6-4 CRAN (R 4.4.1)
svglite 2.1.3 CRAN (R 4.4.1)
sysfonts 0.8.9 CRAN (R 4.4.1)
systemfonts 1.2.1 CRAN (R 4.4.1)
tableone 0.13.2 CRAN (R 4.4.1)
textshaping 1.0.0 CRAN (R 4.4.1)
tibble 3.2.1 CRAN (R 4.4.1)
tidyr 1.3.1 CRAN (R 4.4.1)
tidyselect 1.2.1 CRAN (R 4.4.1)
tidyverse 2.0.0 CRAN (R 4.4.3)
timechange 0.3.0 CRAN (R 4.4.1)
tzdb 0.4.0 CRAN (R 4.4.1)
urlchecker 1.0.1 CRAN (R 4.4.1)
usethis 3.1.0 CRAN (R 4.4.2)
utf8 1.2.4 CRAN (R 4.4.1)
V8 6.0.1 CRAN (R 4.4.1)
vctrs 0.6.5 CRAN (R 4.4.1)
viridisLite 0.4.2 CRAN (R 4.4.1)
visNetwork 2.1.2 CRAN (R 4.4.1)
withr 3.0.2 CRAN (R 4.4.2)
writexl 1.5.1 CRAN (R 4.4.1)
xfun 0.51 CRAN (R 4.4.1)
xml2 1.3.7 CRAN (R 4.4.1)
xtable 1.8-4 CRAN (R 4.4.1)
yaml 2.3.10 CRAN (R 4.4.1)
zoo 1.8-13 CRAN (R 4.4.1)