Resumen ejecutivo

La segregación ocupacional por género es un fenómeno estructural que concentra a mujeres y hombres en diferentes ocupaciones y sectores, reproduciendo desigualdades que limitan oportunidades, perpetúan brechas salariales y restringen el desarrollo del capital humano. El Índice de Disimilitud (ID) de Nuevo León es 0.539, lo que refleja un nivel alto de segregación. Según García, Hernández y Rivera (2018), el estado se ubica entre los más segregados del país, solo detrás de Oaxaca, Veracruz e Hidalgo, y más segregado que Baja California, Ciudad de México o Tamaulipas. Los centros comunitarios son agentes clave en la capacitación laboral, pero si no se diseñan con perspectiva de género pueden reproducir la segregación. Talleres “feminizados” y “masculinizados” refuerzan estereotipos y mantienen la brecha salarial, ya que los oficios asociados a mujeres suelen tener menores ingresos y mayor informalidad. La interseccionalidad agrava el problema: ser mujer, vivir en pobreza, tener bajo nivel educativo o ciertas condiciones familiares genera múltiples barreras. En Nuevo León, 31% de las personas en pobreza tienen secundaria incompleta o menos (2022), y los CDSC son su principal o única vía de capacitación. Aunque la Ley de Desarrollo Social de Nuevo León y el Plan Estatal de Desarrollo 2022–2027 promueven la igualdad de género y el trabajo decente (ODS 5 y 8), los programas sin enfoque inclusivo pueden excluir a mujeres de oficios técnicos y reproducir desigualdades. Los resultados cuantitativos reflejan un escenario positivo y satisfactorio entre las personas usuarias de los centros comunitarios. No obstante, se identifica la necesidad de mejorar la cantidad y calidad de los materiales empleados durante las capacitaciones. En los hallazgos cualitativos, se observa un interés equilibrado entre oficios tradicionales, capacitación técnica y desarrollo personal. Los participantes buscan talleres prácticos y útiles para el autoempleo, que les permitan adquirir habilidades aplicables a la vida cotidiana o al ámbito laboral. También se destaca una creciente demanda por cursos de idiomas y tecnología. La participación en los cursos es principalmente femenina (88%), conformada por mujeres adultas, en su mayoría casadas. Predomina un nivel educativo medio, con estudios de secundaria y preparatoria, y una alta proporción de mujeres que se dedican al hogar, aunque también hay trabajadoras y estudiantes. Los talleres más cursados son los de Belleza, Zumba, Corte y Confección, Gimnasia y Piñatas, impartidos sobre todo en los centros de San Bernabé, Monte Kristal, Santa Fe y La Alianza. Estos resultados reflejan tanto el interés por oficios tradicionales como la oportunidad de diversificar la oferta formativa hacia áreas técnicas y con mayor potencial de empleabilidad.

Pregunta de investigación:

¿De qué manera el género, estado civil, nivel de escolaridad y ocupación actual de los participantes en talleres influyen en su elección del taller cursado y cómo se vincula esta elección con la segregación ocupacional por género observada en los Centros Comunitarios de Nuevo León desde 2017-2025?

Descripción técnica del trabajo realizado

Integración de bases

Realizamos la consolidación, limpieza y análisis descriptivo de bases de datos provenientes de encuestas aplicadas en centros comunitarios del estado de Nuevo León. Utilizamos diversas librerías de R (tidyverse, readxl, janitor, entre otras) para automatizar la lectura y estandarización de los archivos en formato Excel.

Cada base fue procesada para extraer el año y trimestre de su nombre, incorporando esta información en una nueva variable denominada anio_trimestre. Asimismo, se homogenizaron los nombres de las variables y se unificaron todos los archivos en un solo conjunto de datos. Posteriormente, estandarizamos las variables categóricas tipo “Sí” y “No” a formato numérico binario, filtramos los registros para conservar únicamente los centros comunitarios ubicados en Nuevo León y corregimos los formatos de texto y numéricos para garantizar la consistencia de la información.

Con la base depurada, generamos estadísticas descriptivas globales y por periodo, incluyendo conteos de registros, medias, desviaciones estándar y distribuciones de frecuencia para variables categóricas. También identificamos y exportamos las respuestas abiertas a archivos independientes, permitiendo su análisis cualitativo posterior.

Guardamos la base unificada y los distintos productos derivados en formatos CSV y RDS dentro de una carpeta de resultados. Este proceso nos permitió disponer de una base consolidada, estandarizada y lista para análisis comparativos y estudios adicionales sobre el desempeño de los centros comunitarios en el estado.

# LIBRERÍAS

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.1     ✔ stringr   1.5.2
## ✔ ggplot2   4.0.0     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
library(janitor)
## 
## Attaching package: 'janitor'
## 
## The following objects are masked from 'package:stats':
## 
##     chisq.test, fisher.test
library(stringr)
library(purrr)
library(skimr)
library(dplyr)
library(knitr)

options(readr.show_col_types = FALSE)

# CARGA DE BASES DE DATOS

path_dir <- "C:/Users/tds1301/Desktop/Ciencia2" #Para poner la dirección a la carpeta donde están las bases
# Lista todos los Excel (xlsx/xls) en la carpeta y subcarpetas
xls_files <- list.files(path_dir, pattern = "\\.xlsx$|\\.xls$", recursive = TRUE, full.names = TRUE)
if (length(xls_files) == 0) stop("No se encontraron archivos Excel en la carpeta indicada.")

# UNIFICACIÓN EN UN DATAFRAME

# Creación de columna anio_trimestre
# Este código obtiene la información del año y trimestre del nombre de las bases de datos
get_anio_trimestre <- function(file_path) {
  fname <- basename(file_path)
  # Esta parte detecta al año (4 dígitos)
  anio <- str_extract(fname, "(?<!\\d)\\d{4}(?!\\d)")
  # Esta parte detecta al trimestre (Q1|Q2|Q3|Q4 o T1|T2|T3|T4 o 1T|2T|3T|4T)
  tri_raw <- str_extract(fname, "(Q[1-4]|T[1-4]|[1-4]T)")
  if (!is.na(tri_raw)) {
    tri <- str_replace_all(tri_raw, c("^T" = "Q", "T$" = "")) # T1 -> Q1; 1T -> Q1 (simplificación)
    tri <- str_replace(tri, "([1-4])T", "Q\\1")
  } else {
    # Si no detecta el trimestre, que busque un dígito entre 1-4 cercano a 'trim' o 'tri'
    tri <- str_extract(str_to_lower(fname), "tri?m?[^0-9]*([1-4])")
    tri <- if (!is.na(tri)) paste0("Q", str_extract(tri, "[1-4]")) else NA_character_
  }
  paste0(anio %||% "NA", "_", tri %||% "Q?")
} # Para pegar el año extraído seguido de un guión y luego el trimestre para la columna anio_trimestre

# Convertir variables a numéricas o categóricas según corresponda
read_one_excel <- function(f) {
  readxl::read_excel(f, col_types = "text", guess_max = 10000) |> # Lee el excel
    janitor::clean_names() |> # Limpia los nombres para evitar espacios o caracteres especiales
    dplyr::mutate(
      anio_trimestre = get_anio_trimestre(f), # Agrega la variable anio_trimestre antes creada
      source_file    = basename(f), #Agrega una columna con el nombre original de las bases de datos
    )
}
# Para unir todos los archivos
df_all <- purrr::map_dfr(xls_files, read_one_excel)
## New names:
## New names:
## New names:
## New names:
## New names:
## • `Calidad de la información brindada por el personal del Centro Comunitario`
##   -> `Calidad de la información brindada por el personal del Centro
##   Comunitario...27`
## • `Calidad de la información brindada por el personal del Centro Comunitario`
##   -> `Calidad de la información brindada por el personal del Centro
##   Comunitario...28`
## • `Favor de justificar su respuesta` -> `Favor de justificar su respuesta...44`
## • `Favor de justificar su respuesta` -> `Favor de justificar su respuesta...46`
## • `Indique el por qué de su respuesta` -> `Indique el por qué de su
##   respuesta...49`
## • `Indique el por qué de su respuesta` -> `Indique el por qué de su
##   respuesta...51`
# Para convertir variables específicas a numéricas o categóricas dependiendo de lo que son
df_all <- df_all %>%
  mutate(privacidad = as.character(horario_de_inicio_de_taller))
df_all <- df_all %>%
  mutate(
    # Convertir a numérica (1 = "Sí", 0 = "No")
    esta_de_acuerdo_con_el_aviso_de_privacidad = case_when(
      esta_de_acuerdo_con_el_aviso_de_privacidad == "Sí" ~ 1,
      esta_de_acuerdo_con_el_aviso_de_privacidad == "No" ~ 0,
      TRUE ~ NA_real_  # Si hay otros valores, los convertimos a NA
    ),
    x1_el_tramite_del_curso_al_que_se_inscribio_fue_gratuito = case_when(
      x1_el_tramite_del_curso_al_que_se_inscribio_fue_gratuito == "Sí" ~ 1,
      x1_el_tramite_del_curso_al_que_se_inscribio_fue_gratuito == "No" ~ 0,
      TRUE ~ NA_real_ 
    ),
    ha_visitado_el_centro_comunitario_durante_los_ultimos_dos_meses = case_when(
      ha_visitado_el_centro_comunitario_durante_los_ultimos_dos_meses == "Sí" ~ 1,
      ha_visitado_el_centro_comunitario_durante_los_ultimos_dos_meses == "No" ~ 0,
      TRUE ~ NA_real_ 
    ),
    x4_considera_que_las_instalaciones_del_centro_comunitario_son_accesibles_para_personas_con_discapacidad = case_when(
      x4_considera_que_las_instalaciones_del_centro_comunitario_son_accesibles_para_personas_con_discapacidad == "Sí" ~ 1,
      x4_considera_que_las_instalaciones_del_centro_comunitario_son_accesibles_para_personas_con_discapacidad == "No" ~ 0,
      TRUE ~ NA_real_ 
    ),
    x5_considera_que_lo_aprendido_en_el_curso_taller_le_sera_de_utilidad = case_when(
      x5_considera_que_lo_aprendido_en_el_curso_taller_le_sera_de_utilidad == "Sí" ~ 1,
      x5_considera_que_lo_aprendido_en_el_curso_taller_le_sera_de_utilidad == "No" ~ 0,
      TRUE ~ NA_real_ 
    ),
    x7_recomendaria_a_otras_personas_este_curso_o_taller = case_when(
      x7_recomendaria_a_otras_personas_este_curso_o_taller == "Sí" ~ 1,
      x7_recomendaria_a_otras_personas_este_curso_o_taller == "No" ~ 0,
      TRUE ~ NA_real_ 
    ),
    x8_estaria_interesado_a_en_tomar_otro_curso_taller_en_este_centro_comunitario = case_when(
      x8_estaria_interesado_a_en_tomar_otro_curso_taller_en_este_centro_comunitario == "Sí" ~ 1,
      x8_estaria_interesado_a_en_tomar_otro_curso_taller_en_este_centro_comunitario == "No" ~ 0,
      TRUE ~ NA_real_ 
    ))


# Unir bases de datos para Nuevo León
# Lista de centros comunitarios de Nuevo León
centros_nuevo_leon <- c(
  "ALLENDE", "CERRALVO", "PRADOS DE SANTA ROSA", "SANTA FE", "CHINA", "EL CARMEN",
  "BUSTAMANTE", "ALIANZA REAL", "EULALIO VILLARREAL", "FERNANDO AMILPA", "CADEREYTA",
  "GALEANA", "TIERRA PROPIA", "VALLE SOLEADO", "GENERAL TERAN", "ZUAZUA", "HIGUERAS",
  "ARBOLEDAS DE LOS NARANJOS", "LAS SABINAS", "UNIDAD PILOTO", "LOS ENCINOS", 
  "MONTE KRISTAL", "EL MIRADOR", "HECTOR CABALLERO", "ALIANZA SECTOR Q", "LINARES", 
  "LINARES LIBERTAD", "LA ALIANZA", "LOMA CHIQUITA", "RENE ALVAREZ", "SAN BERNABE", 
  "MONTEMORELOS", "TOPO CHICO", "PESQUERIA", "VALLE DE LA ESPERANZA", 
  "ALFONSO MARTINEZ DOMINGUEZ", "SABINAS HIDALGO", "INDEPENDENCIA", "LA ESTANZUELA", 
  "REVOLUCION PROLETARIA", "SIERRA VENTANA", "SALINAS VICTORIA"
)
# Convierte a mayúsculas los nombres de los centros comunitarios por si vienen en minúsculas o mixto
df_all <- df_all |>
  mutate(centro_comunitario = str_to_upper(centro_comunitario)) |>
  filter(centro_comunitario %in% centros_nuevo_leon) #Deja sólo los datos para Nuevo León
# Limpia vacíos y cambia decimal_mark si usas coma
df_all <- df_all |>
  # Cambia vacíos a NA solo en las columnas de texto
  dplyr::mutate(across(where(is.character), ~na_if(.x, ""))) |> 
  # Convierte correctamente los tipos de datos, pero sin alterar las columnas numéricas
  readr::type_convert(locale = readr::locale(decimal_mark = "."))  # Esto se usa para las columnas numéricas y de fecha
## 
## ── Column specification ────────────────────────────────────────────────────────
## cols(
##   .default = col_character(),
##   marca_temporal = col_double(),
##   fecha_en_que_se_aplica_la_encuesta = col_logical(),
##   x2_cuantos_anos_cumplidos_tiene = col_double(),
##   horario_de_inicio_de_taller = col_double(),
##   x1_1_en_caso_de_haber_respondido_no_en_la_pregunta_anterior_favor_de_responder_el_costo_del_tramite_de_inscripcion = col_double(),
##   el_contenido_o_temas_del_curso_taller = col_double(),
##   la_cantidad_y_calidad_de_materiales_y_equipo_utilizados_en_el_curso_taller = col_double(),
##   los_horarios_del_curso_taller = col_double(),
##   el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller = col_double(),
##   la_forma_de_ensenanza_utilizada_en_las_clases = col_double(),
##   la_motivacion_a_la_participacion_de_parte_del_tallerista_o_instructor = col_double(),
##   el_cumplimiento_del_tallerista_o_instructor_con_los_horarios_establecidos = col_double(),
##   la_resolucion_de_preguntas_dudas_y_el_enriquecimiento_de_comentarios = col_double(),
##   trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario = col_double(),
##   calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario = col_double(),
##   salones_o_aulas = col_double(),
##   salas_de_computo = col_double(),
##   banos = col_double(),
##   gimnasio_canchas = col_double(),
##   polivalente = col_double()
##   # ... with 27 more columns
## )
## ℹ Use `spec()` for the full column specifications.
# REVISIÓN DE ESTADÍSTICOS GENERALES

# Conteo de registros total y por periodo
n_total <- nrow(df_all) # Cuenta todas las filas para saber las cantidades de datos
registros_por_periodo <- df_all |> # Cuenta los registros por periodo (es decir, por base de datos)
  count(anio_trimestre, name = "n_registros") |>
  arrange(anio_trimestre)

# Medias y desviaciones estándar de las variables numéricas
# Detecta las columnas numéricas
num_cols <- df_all |>
  select(where(is.numeric)) |>
  colnames()
# Calcula la mediva y la desviación estándar para todas las variables
estad_global_num <- df_all |>
  summarise(across(all_of(num_cols), #Da un resumen en un dataframe
                   list(media = ~mean(.x, na.rm = TRUE),
                        sd    = ~sd(.x,   na.rm = TRUE)),
                   .names = "{.col}__{.fn}"))
# En formato largo para facilitar lectura
estad_global_num_long <- estad_global_num |>
  pivot_longer(everything(), #Para que sea en formato ancho
               names_to = c("variable","estadistico"), #Separa la columna en el nombre de la variable original y si fue mean o sd lo que se usó
               names_sep = "__",
               values_to = "valor") |>
  arrange(variable, estadistico) # Ordena los resultados
# Por periodo
estad_por_periodo <- df_all |>
  group_by(anio_trimestre) |>
  summarise(across(all_of(num_cols),
                   list(media = ~mean(.x, na.rm = TRUE), #Obtiene la media
                        sd    = ~sd(.x,   na.rm = TRUE)), #Obtiene la desviación
                   .names = "{.col}__{.fn}"),
            .groups = "drop") |>
  pivot_longer(-anio_trimestre,
               names_to = c("variable","estadistico"),
               names_sep = "__",
               values_to = "valor") |>
  arrange(anio_trimestre, variable, estadistico) #Organiza por año, variable, estadístico

# Distribución de frecuencias para las categóricas
cat_cols <- df_all |>
  select(where(\(x) is.character(x) || is.factor(x))) |> #Obtiene todas las variables character o factor
  select(-anio_trimestre, -source_file, -matches("sheet$")) |> #Excluye las variables no necesarias que creamos
  colnames() #Obtiene los nombres de las columnas
# Frecuencias globales (top 10 por columna)
frecuencias_globales <- map(cat_cols, function(col) {
  df_all |> #Con iteraciones se ven las frecuencias para cada variable factor
    filter(!is.na(.data[[col]])) |>
    tabyl(.data[[col]]) |> #Crea una tabla
    arrange(desc(n)) |>
    slice_head(n = 10) |> #Para que te de los top 10 frecuencias por columnas
    mutate(variable = col, .before = 1)
}) |>
  list_rbind() #Te da los resultados con el nombre de la variable
## Warning: Use of .data in tidyselect expressions was deprecated in tidyselect 1.2.0.
## ℹ Please use `all_of(var)` (or `any_of(var)`) instead of `.data[[var]]`
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# Frecuencias por periodo (top 5 por variable y periodo)
frecuencias_por_periodo <- map(cat_cols, function(col) {
  df_all |> #Con iteraciones se ven las frecuencias para cada variable factor por periodo
    count(anio_trimestre, .data[[col]], name = "n") |> #Hace el conteo por periodo
    group_by(anio_trimestre) |>
    arrange(anio_trimestre, desc(n)) |> #Ordena por año y trimestre
    slice_head(n = 5) |> # Te da el top 5
    ungroup() |>
    mutate(variable = col, .before = 1)
}) |>
  list_rbind() #Te da los resultados con la variable

# EXTRACCIÓN DE RESPUESTAS TEXTUALES

# Identifica las columnas de texto
text_cols_candidates <- df_all |>
  select(where(is.character)) |>
  colnames()
# Filtra por patrones comunes de preguntas abiertas
patron_texto <- "(coment|observ|abiert|texto|otro|sugerenc|opinion)"
cols_abiertas <- text_cols_candidates[str_detect(text_cols_candidates, regex(patron_texto, ignore_case = TRUE))]
# Si no detecta por nombre, toma todas las character como abiertas:
if (length(cols_abiertas) == 0) cols_abiertas <- text_cols_candidates
# Crea una lista de dataframes, uno por columna (pregunta abierta)
abiertas_list <- map(cols_abiertas, function(col) {
  df_all |>
    select(anio_trimestre, source_file, !!sym(col)) |>
    rename(respuesta_texto = !!sym(col)) |>
    filter(!is.na(respuesta_texto), str_trim(respuesta_texto) != "")
})
names(abiertas_list) <- cols_abiertas #Crea una lista con las columnas de respuestas abiertas
# Guarda cada dataframe de respuestas abiertas a CSV
out_dir2 <- file.path(path_dir, "abiertas") #Crea una dirección dentro de la carpeta de trabajo para guardar los dataframes
if (!dir.exists(out_dir2)) dir.create(out_dir2, recursive = TRUE)
walk2(abiertas_list, names(abiertas_list), function(df_i, nombre_col) {
  fn <- file.path(out_dir2, paste0("abierta_", nombre_col, ".csv")) #Guarda los dataframes con los nombres de las columnas y los guarda en csv
  readr::write_csv(df_i, fn, na = "")
})

# GUARDANDO LOS ESTADÍSTICOS IMPORANTES EN ARCHIVOS CSV

out_dir_proc <- file.path(path_dir, "procesados")
if (!dir.exists(out_dir_proc)) dir.create(out_dir_proc, recursive = TRUE)

write_csv(df_all, file.path(out_dir_proc, "datos_unificados.csv"), na = "") #Esta es la base de datos final que vamos usar en csv
write_rds(df_all, file.path(out_dir_proc, "datos_unificados.rds")) #Esta es la base en rds

write_csv(registros_por_periodo, file.path(out_dir_proc, "registros_por_periodo.csv")) #Esta es de los registros por periodo
write_csv(estad_global_num_long, file.path( out_dir_proc, "estad_global_num.csv")) # Esta es para las estadísticas de mean y sd en general
write_csv(estad_por_periodo, file.path(out_dir_proc, "estad_por_periodo.csv")) # Esta es para las estadísticas por periodo
write_csv(frecuencias_globales, file.path(out_dir_proc, "frecuencias_globales_top10.csv")) #Esta es de las frecuencias generales 
write_csv(frecuencias_por_periodo, file.path(out_dir_proc, "frecuencias_por_periodo_top5.csv")) #Esta es de las frecuencias por periodo

Limpieza, procesamiento y análisis de texto

El análisis comenzó con la carga de librerías necesarias para el procesamiento y visualización de texto, entre ellas rvest, tm, wordcloud2, dplyr y ggplot2. Se trabajó con bases de datos en formato .csv que contienen respuestas abiertas, almacenadas en una carpeta llamada “abiertas”. Solo se seleccionaron aquellas que correspondían a preguntas relevantes para la investigación.

Antes del análisis, se revisó que los archivos no tuvieran valores vacíos ni caracteres extraños. Se observó que las tildes y las letras ñ se distorsionaban, por lo que se diseñó una función limpiar_texto que elimina acentos, protege las ñ y suprime emojis y otros símbolos no deseados. Las nuevas versiones de los archivos se guardaron en una carpeta llamada “limpios”.

Posteriormente, se cargaron los archivos limpios y se aplicó un proceso de limpieza adicional mediante la librería tm. Se unió el texto en un solo documento por archivo, se convirtieron las letras a minúsculas y se eliminaron puntuaciones, números, palabras vacías en español y términos poco informativos como “gracias” o “curso”. Cada texto limpio se transformó en un corpus y se almacenó en una lista para su análisis.

Finalmente, se generaron matrices de términos y documentos para identificar la frecuencia de palabras. Con base en ello, se elaboraron nubes de palabras y gráficos de barras con las 15 palabras más frecuentes por archivo. Las nubes se guardaron en formato HTML y los gráficos en formato PNG, cada uno en sus respectivas carpetas (“nubes_palabras” y “graficos_barras”).

# LIBRERÍAS

library(rvest)   
## 
## Attaching package: 'rvest'
## The following object is masked from 'package:readr':
## 
##     guess_encoding
library(xml2)    
library(tm)     
## Loading required package: NLP
## 
## Attaching package: 'NLP'
## The following object is masked from 'package:ggplot2':
## 
##     annotate
library(wordcloud2)
library(readxl)
library(readr)
library(stringr)
library(stringi)
library(ggplot2)
library(dplyr)
library(htmlwidgets)
library(tidytext)
library(topicmodels)

# DATAFRAMES CON PREGUNTAS ABIERTAS

# Las bases de datos (una por cada pregunta abierta) fueron generadas desde la actividad número 1 y guardadas en una carpeta que se llama abiertas
# Nombres específicos de las preguntas que son de interés para la pregunta de investigación
nombres_especificos <- c("abierta_x10_si_tiene_algun_otro_curso_o_taller_que_le_interesaria_tomar_que_no_se_ofrece_en_el_centro_comunitario_favor_de_mencionarlo",
                         "abierta_comentarios_sobre_el_curso_o_taller_que_recibio",
                         "abierta_si_tiene_algun_otro_curso_o_taller_que_le_interesaria_tomar_que_no_se_ofrece_en_el_centro_comunitario_favor_de_mencionarlo",
                         "abierta_en_caso_de_elegir_otro_menciona_el_taller_al_que_asistes")
# Filtrar los archivos en la carpeta 'abiertas' que coincidan con los nombres específicos
ruta <- "C:/Users/tds1301/Desktop/Ciencia2/abiertas"
archivos_1 <- list.files(ruta, pattern = "\\.csv$", full.names = TRUE) # Obtener todos los archivos .csv
archivos <- archivos_1[basename(archivos_1) %in% paste0(nombres_especificos, ".csv")] # Filtrar solo los archivos específicos

# VERIFICACIÓN QUE NO CONTENGAN VALORES VACÍOS O CARACTERES EXTRAÑOS

# Nos dimos cuenta que los archivos si bien no contaban con vacíos, sí con caracteres extraños por cómo se estaban leyendo los acentos y las ñ, pero si intentabamos quitar todos los signos extraños, borraba las ñ, entonces hicimos la siguiente función:
limpiar_texto <- function(texto) {
  # para proteger la ñ y Ñ
  texto <- str_replace_all(texto, "ñ", "__ENYE__")
  texto <- str_replace_all(texto, "Ñ", "__ENYEMAY__")
  # para eliminar acentos (no elimina la letra, sólo los acentos)
  texto <- stri_trans_general(texto, "Latin-ASCII")
  # para volver a colocar las ñ, por que sino se hubieran borrado
  texto <- str_replace_all(texto, "__ENYE__", "ñ")
  texto <- str_replace_all(texto, "__ENYEMAY__", "Ñ")
  # también nos dimos cuenta que había muchas respuestas con emojis, entonces con este código los eliminamos
  texto <- str_replace_all(texto, "[^a-zA-Z0-9ñÑ .,;:!?()\"'\\-\\s]", "")
  return(texto)
}
# En esta sección, usamos la función de limpiar_texto que habíamos creado
# primero, le pedimos que cree la carpeta "limpios" si no existe
carpeta_salida <- file.path(ruta, "limpios")
if (!dir.exists(carpeta_salida)) {
  dir.create(carpeta_salida)
}
# Este código extrea el nombre de los archivos y lo guarda como nombre para cada uno
for (archivo in archivos) {
  nombre <- tools::file_path_sans_ext(basename(archivo))
  # esta parte es para leer .csv con UTF-8 para evitar errores con tildes y ñ
  df <- read_csv(archivo, show_col_types = FALSE, locale = locale(encoding = "UTF-8"))
  # nuestros dataframes tienen 3 columnas, una que es anio_trimestre, otra que es la base de donde salió el dato y luego la tercera columna que es respuesta_texto que es la que analizamos para todos los dataframes
  if (ncol(df) < 3) { #este código se encarga de que revise las bases de datos y cheque que todas tengan tres columnas y si esto no es así, imprime el nombre del archivo que no tiene las tres columnas
    cat("\n--------------------------------------------\n")
    cat("Archivo:", nombre, "\n")
    cat("No tiene al menos 3 columnas.\n")
    next
  }
  # Debido a que sólo usamos la tercer columna, en esta parte se obtiene, y se aplica la función de limpiar_texto que se había creado
  texto_no_limpio <- df[[3]]
  texto_limpio <- limpiar_texto(texto_no_limpio) # Se guarda como texto_limpio cuando se le aplica la función
  df[[3]] <- texto_limpio # se asigna a la columna 3 nuevamente
  # Nos dimos cuenta que habían líneas que consistían únicamente de emojis, entonces con seste código eliminamos los vacíos de la columna 3
  df <- df[!(is.na(df[[3]]) | str_trim(df[[3]]) == ""), ]
  #Aquí guardamos las nuevas bases como estaban nombradas más el nombre "limpio_" en una carpeta nueva que habíamos creado que se llama limpios
write_csv(df, file.path(carpeta_salida, paste0("limpio_", nombre, ".csv")))
}

# LIMPIEZA Y PREPARACIÓN DE TEXTO

# ahora cargamos todos los csv que se encuentran en la carpeta limpios
ruta <- "C:/Users/tds1301/Desktop/Ciencia2/abiertas/limpios"
limpios <- list.files(ruta, pattern = "\\.csv$", full.names = TRUE) #se guardan en una lista llamada "limpios"
# aquí creamos una lista para guardar los corpus por archivo para poder hacer los gráficos despúes
corpus_lista <- list()
# se crea la función limpiar_con_tm que es para unir todo el texto de los archivos en uno solo
limpiar_con_tm <- function(texto_uno) {
  # Unir todo el texto del archivo en uno solo
  texto_unido <- paste(texto_uno, collapse = " ")
  # creamos corpus con un solo documento con todos los vectores unidos
  corpus <- Corpus(VectorSource(texto_unido))
  # aplicamos la limpieza con corpus
  corpus <- tm_map(corpus, content_transformer(tolower)) # para que todo esté en minúsculas
  corpus <- tm_map(corpus, removePunctuation) # para quitar los signos de puntuación
  corpus <- tm_map(corpus, removeNumbers) # para quitar los números
  corpus <- tm_map(corpus, removeWords, stopwords("es")) # para quitar las palabras comunes en español
  corpus <- tm_map(corpus, stripWhitespace) # para quitar los espacios en blacno
  # palabras específicas a eliminar: estas se fueron ajustando según vimos los resultados de las gráficas
  palabras_eliminar <- c("mas", "gracias", "si", "no", "curso", "clase", "personas", "siempre", "persona", "gusta", "clases", "solo", "areas", "hace", "estan", "club", "fina", "poner", "tener", "linea", "comunitario", "gustaria", "muchas", "asi", "momento", "pues", "muchas", "mucha", "mejor")
  corpus <- tm_map(corpus, removeWords, palabras_eliminar)
  return(corpus) # regresa el corpus limpio
}
# Esta parte es un loop porque lo hace para todos los archivos
for (archivo in limpios) {
  nombre <- tools::file_path_sans_ext(basename(archivo))  # primero extra el nombre sin extensión
  df <- read_csv(archivo, show_col_types = FALSE) # lee los archivos
  if (ncol(df) < 3) {
    cat("El archivo", nombre, "no tiene al menos 3 columnas. Saltando...\n")
    next
  } # nuevamente, revisa que los archivos tengan las 3 columnas que deberían tener
  # extrae a la columna 3, que es la que usamos
  textos <- df[[3]]
  # guarda como corpus_limpio a los textos limpiados con la función de corpus
  corpus_limpio <- limpiar_con_tm(textos)
  # guarda todo en corpus_lista con el nombre original de los archivos
  corpus_lista[[nombre]] <- corpus_limpio
}
#Nota: le pusimos warning=FALSE porque mandaba warning de que se estaban eliminando líneas de texto por la limpieza, pero esto es esperado dado que hay datos que no tenían contenido suficiente
limpiar_con_tm <- function(texto_uno) {
  # Unir todo el texto del archivo en uno solo
  texto_unido <- paste(texto_uno, collapse = " ")
  # Verificar el contenido del texto unido
  cat("Contenido de texto_unido:\n", substr(texto_unido, 1, 200), "\n")
  # Crear corpus con un solo documento
  corpus <- Corpus(VectorSource(texto_unido))
  # Aplicar limpieza básica
  corpus <- tm_map(corpus, content_transformer(tolower))  # Todo en minúsculas
  corpus <- tm_map(corpus, removePunctuation)  # Eliminar puntuación
  corpus <- tm_map(corpus, removeNumbers)  # Eliminar números
  corpus <- tm_map(corpus, stripWhitespace)  # Eliminar espacios extra
  return(corpus)  # Regresa el corpus limpio
}

# ANÁLISIS DE FRECUENCIA Y VISUALIZACIÓN

# Decidimos hacer un loop porque son muchas bases de datos
# También, nos dimos cuenta que workcloud2 no puede imprimir todas las nubes a la vez, entonces, en su lugar decidimos que todas las gráficas se guardaran en una carpeta a parte en html y las gráficas en otra carpeta como png
# para crear carpetas para guardar resultados
if (!dir.exists("nubes_palabras")) dir.create("nubes_palabras") #aquí se guardan las nubes
if (!dir.exists("graficos_barras")) dir.create("graficos_barras") #aquí se guardan las gráficas de barras
for (nombre in names(corpus_lista)) { # aquí comienza el loop para cada uno de los archivos en la lista limpia
  corpus <- corpus_lista[[nombre]] 
  # Crear una matriz término-documento
  dtm <- DocumentTermMatrix(corpus)
  # Sumar las columnas para obtener el conteo total de cada término
  matriz <- as.matrix(dtm)
  conteo <- colSums(matriz)
  # para ordenar de mayor a menor frecuencia
  conteo_ordenado <- sort(conteo, decreasing = TRUE)
  # Se crea el data frame para graficar las nubes y barras, según la frecuencia de las palabras
  df_palabras <- data.frame(
    palabra = names(conteo_ordenado),
    frecuencia = as.numeric(conteo_ordenado),
    stringsAsFactors = FALSE
  )
  # Para crear la nube de palabras 
  nube <- wordcloud2(df_palabras, size = 0.5, color = "random-light", backgroundColor = "black")
  # para guardar la nube como archivo html en la carpeta que habíamos creado
  saveWidget(nube, file = paste0("nubes_palabras/nube_", nombre, ".html"), selfcontained = TRUE)
  # Ahora, para los gráficos de barras, esta parte saca el top15 de mayores frecuencias
  top15 <- head(df_palabras, 15)
  # para ordenar para que la barra con mayor frecuencia quede arriba
  top15$palabra <- factor(top15$palabra, levels = rev(top15$palabra))
  # para crear el gráfico
  grafico_barras <- ggplot(top15, aes(x = palabra, y = frecuencia)) + # determina los ejes
    geom_col(fill = "steelblue") + # determina el color de la gráfica
    coord_flip() +
    labs(title = paste("15 palabras más frecuentes:", nombre), # determina el título más el nombre de la base de datos
         x = "Palabra",
         y = "Frecuencia") +
    theme_minimal() # dice el tema de diseño para las gráficas
  # para guardar los gráficos de barras como png en la carpeta creada
  ggsave(filename = paste0("graficos_barras/barras_", nombre, ".png"), plot = grafico_barras, width = 8, height = 6)
}

# ANÁLISIS DE TÓPICOS POR PREGUNTA ABIERTA

# para crear una lista para guardar las DTMs
dtm_lista <- list()
# para crear la DTM para cada archivo en corpus_lista
for (nombre in names(corpus_lista)) {
  corpus <- corpus_lista[[nombre]]
  dtm <- DocumentTermMatrix(corpus)
  dtm_lista[[nombre]] <- dtm
}
# Definir el número de tópicos
num_topics <- 5
# Crear una lista para guardar los modelos LDA
lda_models <- list()
# Ajustar el modelo LDA para cada DTM
for (nombre in names(dtm_lista)) {
  dtm <- dtm_lista[[nombre]]
  lda_model <- LDA(dtm, k = num_topics, control = list(seed = 123))
  lda_models[[nombre]] <- lda_model
}
# Extraer las palabras clave por tópico
terms_lda <- list()
for (nombre in names(lda_models)) {
  lda_model <- lda_models[[nombre]]
  terms_lda[[nombre]] <- terms(lda_model, 10)
}

# ANÁLISIS DE TÓPICOS PARA EL TEXTO COMPLETO

# Para el texto de todos los archivos en un único corpus
corpus_combinado <- Corpus(VectorSource(NULL))  # crear un corpus vacío
# Para definir las stopwords en español
stopwords_es <- stopwords("es")
palabras_eliminar <- c("mas", "gracias", "si", "no", "curso", "clase", "personas", "siempre", "persona", "gusta", "clases", "solo", "areas", "hace", "estan", "club", "fina", "poner", "tener", "linea", "comunitario", "gustaria", "muchas", "asi", "momento", "pues", "muchas", "mucha", "mejor")
# Para cargar y combinar los textos de cada archivo limpio en un solo corpus
for (archivo in limpios) {
  df <- read_csv(archivo, show_col_types = FALSE) 
  textos <- df[[3]]  # para la tercera columna que contiene los textos
  # para limpiar el texto de cada archivo con la función anterior
  textos_limpios <- limpiar_con_tm(textos)
  # para eliminar las stopwords del texto limpio
  textos_sin_extras <- tm_map(textos_limpios, removeWords, palabras_eliminar)
  textos_sin_stopwords <- tm_map(textos_sin_extras, removeWords, stopwords_es)
  # para unir el texto sin stopwords al corpus combinado
  corpus_combinado <- append(corpus_combinado, textos_sin_stopwords)
}
## Contenido de texto_unido:
##  Son muy buenos los cursos ya que nos ayudan aprender cosas nuevas y desde casa es lo mejor. Excelente taller Buenos comentarios Bien Excelente instructor, Prof. ERIK leal, es muy paciente Excelente Ex
## Contenido de texto_unido:
##  idiomas extranjeros Aerobics kick boxing karate karate karate karate karate karate karate karate karate karate karate karate karate karate karate karate karate karate karate karate karate . . . . . NI
## Contenido de texto_unido:
##  Gimnasio 0 Ninguno Belleza 0 0 Ninguno Joyeria Futbol  para adolescentes 0 Panaderia 0 0 Belleza 0 0 Reposteria Taller para poner uñas Barber Inteligencia artificial 0 Corte y confeccion Cocina.para n
## Contenido de texto_unido:
##  No Artes marciales mixtas Ninguno Pintura Confeccion de mochilas y bolsas, reciclaje de ropa Hasta ahora todo bien Serigrafia Bordado No Dibujo Scrapbook , Bullet Journal Pasta flexible Reposteria Gui
# matriz
dtm_combinado <- DocumentTermMatrix(corpus_combinado)
# LDA
num_topics <- 5  # Número de tópicos
lda_model_ALL <- LDA(dtm_combinado, k = num_topics, control = list(seed = 123))
# palabras más representativas por tópico
terms_lda_ALL <- terms(lda_model_ALL, 10)

Estructura de las variables analizadas y tratamiento de datos faltantes.

cat("Registros totales:", n_total, "\n") # Escribe el conteo
## Registros totales: 31524
print(registros_por_periodo) # Da una tabla con los conteos por periodo
## # A tibble: 11 × 2
##    anio_trimestre n_registros
##    <chr>                <int>
##  1 2017_4                 410
##  2 2018_4                 376
##  3 2019_4                 498
##  4 2020_4                 848
##  5 2021_4                3702
##  6 2022_1                2003
##  7 2024_2                4607
##  8 2024_3                4592
##  9 2024_4                4191
## 10 2025_1                5262
## 11 2025_2                5035

Descripción general del conjunto de datos

El dataset contiene un total de 11 periodos (anio_trimestre) con 11 observaciones agregadas, que representan la cantidad de registros de encuestas por trimestre entre 2017 y 2025.En total se dispone de más de 5,000 observaciones individuales en los años recientes,lo que permite analizar tendencias temporales y de participación.

Variables relevantes seleccionadas

Para el análisis cuantitativo: - Calidad de la información brindada por el personal del centro - Conocimiento del tallerista o instructor - Contenido y materiales del curso/taller - Trato recibido por el personal del centro

Para el análisis cualitativo: - Curso o taller que le interesaría tomar (si no se ofrece en el centro) - Comentarios sobre el curso recibido - Elección de otro taller al que asiste - Género, estado civil, ocupación, escolaridad - Curso o taller que cursa o terminó

Estas variables son las base para examinar la participación, satisfacción y preferencias de los usuarios de los centros comunitarios, además de explorar patrones de género y posibles relaciones entre perfil sociodemográfico y tipo de taller.

Resultados cuantitativos

Tablas o gráficos con los estadísticos descriptivos más relevantes.

# Define las variables de interés para la pregunta de investigación
numericas_pregunta <- c(
  "x2_cuantos_anos_cumplidos_tiene", "el_contenido_o_temas_del_curso_taller", "la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller", "el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller", "trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario", "calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario"
)
# Imprime una tabla
estad_global_num_long %>%
  filter(variable %in% numericas_pregunta) %>%
  kable()
variable estadistico valor
calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario media 9.7084373
calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario sd 0.8729843
el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller media 9.7358505
el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller sd 0.8246910
el_contenido_o_temas_del_curso_taller media 9.6712339
el_contenido_o_temas_del_curso_taller sd 0.9773256
la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller media 9.4403025
la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller sd 1.3809906
trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario media 9.6219541
trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario sd 0.9524982
x2_cuantos_anos_cumplidos_tiene media 37.6505242
x2_cuantos_anos_cumplidos_tiene sd 15.6558161
# Define las variables de interés para la pregunta de investigación
numericas_pregunta <- c(
  "x2_cuantos_anos_cumplidos_tiene", "el_contenido_o_temas_del_curso_taller", "la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller", "el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller", "trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario", "calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario"
)
# Imprime una tabla
estad_global_num_long %>%
  filter(variable %in% numericas_pregunta) %>%
  kable()
variable estadistico valor
calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario media 9.7084373
calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario sd 0.8729843
el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller media 9.7358505
el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller sd 0.8246910
el_contenido_o_temas_del_curso_taller media 9.6712339
el_contenido_o_temas_del_curso_taller sd 0.9773256
la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller media 9.4403025
la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller sd 1.3809906
trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario media 9.6219541
trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario sd 0.9524982
x2_cuantos_anos_cumplidos_tiene media 37.6505242
x2_cuantos_anos_cumplidos_tiene sd 15.6558161
estad_resumen <- tribble(
  ~variable, ~estadistico, ~valor,
  "calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario", "media", 9.7084373,
  "calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario", "sd", 0.8729843,
  "el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller", "media", 9.7358505,
  "el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller", "sd", 0.8246910,
  "el_contenido_o_temas_del_curso_taller", "media", 9.6712339,
  "el_contenido_o_temas_del_curso_taller", "sd", 0.9773256,
  "la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller", "media", 9.4403025,
  "la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller", "sd", 1.3809906,
  "trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario", "media", 9.6219541,
  "trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario", "sd", 0.9524982,
  "x2_cuantos_anos_cumplidos_tiene", "media", 37.6505242,
  "x2_cuantos_anos_cumplidos_tiene", "sd", 15.6558161
)

# Gráfico de barras para las medias de satisfacción
estad_resumen %>%
  filter(estadistico == "media", !grepl("x2_", variable)) %>%
  mutate(variable = recode(variable,
    "calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario" = "Calidad de la información",
    "el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller" = "Conocimiento del tallerista",
    "el_contenido_o_temas_del_curso_taller" = "Contenido del curso",
    "la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller" = "Calidad de materiales",
    "trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario" = "Trato del personal"
  )) %>%
  ggplot(aes(x = valor, y = reorder(variable, valor))) +
  geom_vline(xintercept = mean(c(9.44, 9.74, 9.71, 9.67, 9.62)),
             linetype = "dashed", color = "gray60") +
  geom_point(color = "#4B9CD3", size = 5) +
  geom_text(aes(label = round(valor, 2)), hjust = -0.3, size = 4) +
  scale_x_continuous(limits = c(8.5, 10)) +
  labs(
    title = "Promedio de satisfacción por dimensión evaluada",
    x = "Media de satisfacción",
    y = NULL,
    caption = "Línea discontinua = promedio general de satisfacción"
  ) +
  theme_minimal(base_size = 13) +
  theme(
plot.title = element_text(face = "bold", hjust = 0.5),
    plot.caption = element_text(size = 9, hjust = 0.5, color = "gray40"),
    axis.text.y = element_text(size = 11)
  )

estad_media_sd <- estad_resumen %>%
  pivot_wider(names_from = estadistico, values_from = valor) %>%
  filter(!grepl("x2_", variable)) %>%
  mutate(variable = recode(variable,
    "calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario" = "Calidad de la información",
    "el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller" = "Conocimiento del tallerista",
    "el_contenido_o_temas_del_curso_taller" = "Contenido del curso",
    "la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller" = "Calidad de materiales",
    "trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario" = "Trato del personal"
  ))

# Graficar 
estad_media_sd <- estad_resumen %>%
  pivot_wider(names_from = estadistico, values_from = valor) %>%
  filter(!grepl("x2_", variable)) %>%
  mutate(variable = recode(variable,
    "calidad_de_la_informacion_brindada_por_el_personal_del_centro_comunitario" = "Calidad de la información",
    "el_conocimiento_del_tallerista_o_instructor_sobre_el_tema_del_curso_taller" = "Conocimiento del tallerista",
    "el_contenido_o_temas_del_curso_taller" = "Contenido del curso",
    "la_calidad_y_cantidad_de_materiales_y_equipo_utilizados_en_el_curso_taller" = "Calidad de materiales",
    "trato_recibido_por_el_personal_que_labora_en_el_centro_comunitario" = "Trato del personal"
  ))

# Calcular promedios globales
media_prom <- mean(estad_media_sd$media)
sd_prom <- mean(estad_media_sd$sd)

# Graficar con estilo mejorado
ggplot(estad_media_sd, aes(x = sd, y = media, label = variable)) +
  # Líneas de referencia (cuadrantes)
  geom_hline(yintercept = media_prom, linetype = "dashed", color = "gray60") +
  geom_vline(xintercept = sd_prom, linetype = "dashed", color = "gray60") +

  # Puntos coloreados por variable
  geom_point(aes(color = variable), size = 5, alpha = 0.9, show.legend = FALSE) +
  geom_text(nudge_y = 0.045, size = 4) +

  # Escalas ajustadas para centrar los datos
  scale_y_continuous(limits = c(9.4, 9.8)) +
  scale_x_continuous(limits = c(0.75, 1.45)) +

  # Colores suaves y profesionales
  scale_color_manual(values = c(
    "Conocimiento del tallerista" = "#1f78b4",
    "Calidad de la información" = "#33a02c",
    "Contenido del curso" = "#ff7f00",
    "Trato del personal" = "#6a3d9a",
    "Calidad de materiales" = "#e31a1c"
  )) +

  # Etiquetas y diseño general
  labs(
    title = "Nivel y consistencia de satisfacción por dimensión",
    subtitle = "Línea horizontal: media general | Línea vertical: desviación estándar promedio",
    x = "Desviación estándar (variabilidad de respuestas)",
    y = "Media (nivel de satisfacción)"
 ) +

  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5, size = 10, color = "gray40"),
    axis.text = element_text(size = 11),
    panel.grid.minor = element_blank()
)

Interpretación breve de los patrones identificados.

Gráfica /Tabla #1 Reflejan un escenario positivo y satisfactorio entre los usuarios. Se percibe un trato profesional y amable por parte del personal del centro comunitario y los talleristas, así como una buena calidad tanto en el contenido de los talleres como en los materiales. Mejorar la cantidad y la calidad de los materiales usados en los talleres.

Grafíca #2 La gráfica muestra el promedio de satisfacción por dimensión, con niveles muy altos en todos los aspectos.Destacan el conocimiento del tallerista (9.74) y la calidad de la información (9.71), mientras que la calidad de materiales (9.44) es la menor, aunque sigue siendo elevada.Las diferencias son mínimas, lo que refleja un desempeño equilibrado y una satisfacción general sostenida entre los usuarios.

Gráfica #3 La gráfica muestra la relación entre el nivel de satisfacción y la consistencia de las respuestas.Destacan conocimiento del tallerista y calidad de la información por su alta satisfacción y baja variabilidad. En cambio, la calidad de materiales presenta una mayor dispersión y una media ligeramente menor, reflejando opiniones más diversas. En general, se mantiene una tendencia positiva y estable, con áreas de mejora en los recursos materiales

Resultados cualitativos

Principales palabras clave

# Define las variables de interés para la pregunta de investigación
variables_pregunta <- c(
  "x1_cual_es_su_genero",
  "x3_actualmente_cual_es_su_estado_civil",
  "x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy",
  "x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones",
  "curso_o_taller_que_cursa_o_termino",
  "centro_comunitario",
  "x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones"
)
# Imprime una tabla
frecuencias_globales %>%
  filter(variable %in% variables_pregunta) %>%
  select(variable, n, percent, x1_cual_es_su_genero, x3_actualmente_cual_es_su_estado_civil, x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy, x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones, curso_o_taller_que_cursa_o_termino, centro_comunitario, x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones
) %>%
  kable()
variable n percent x1_cual_es_su_genero x3_actualmente_cual_es_su_estado_civil x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones curso_o_taller_que_cursa_o_termino centro_comunitario x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones
x1_cual_es_su_genero 1765 0.8811782 Femenino NA NA NA NA NA NA
x1_cual_es_su_genero 233 0.1163255 Masculino NA NA NA NA NA NA
x1_cual_es_su_genero 5 0.0024963 Prefiero no decirlo NA NA NA NA NA NA
x3_actualmente_cual_es_su_estado_civil 1058 0.5282077 NA Casado(a) NA NA NA NA NA
x3_actualmente_cual_es_su_estado_civil 536 0.2675986 NA Soltero(a) NA NA NA NA NA
x3_actualmente_cual_es_su_estado_civil 220 0.1098352 NA Vive en unión libre NA NA NA NA NA
x3_actualmente_cual_es_su_estado_civil 71 0.0354468 NA Divorciado(a) NA NA NA NA NA
x3_actualmente_cual_es_su_estado_civil 59 0.0294558 NA Está separado(a) NA NA NA NA NA
x3_actualmente_cual_es_su_estado_civil 59 0.0294558 NA Viudo(a) NA NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 466 0.2326510 NA NA Secundaria completa NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 311 0.1552671 NA NA Preparatoria o bachillerato completo NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 255 0.1273090 NA NA Carrera técnica o comercial completa NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 235 0.1173240 NA NA Universidad completa NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 205 0.1023465 NA NA Preparatoria o bachillerato incompleto NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 86 0.0429356 NA NA Secundaria incompleta NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 85 0.0424363 NA NA Primaria incompleta NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 74 0.0369446 NA NA Universidad incompleta NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 68 0.0339491 NA NA Primaria completa NA NA NA NA
x4_cual_es_su_maximo_nivel_de_escolaridad_alcanzado_al_dia_de_hoy 67 0.0334498 NA NA Carrera técnica o comercial incompleta NA NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 884 0.4413380 NA NA NA Se dedica a los quehaceres del hogar NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 331 0.1652521 NA NA NA Está trabajando NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 278 0.1387918 NA NA NA Es estudiante NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 101 0.0504244 NA NA NA Es pensionado o jubilado NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 83 0.0414378 NA NA NA Está buscando trabajo NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 49 0.0244633 NA NA NA Está buscando trabajo, Se dedica a los quehaceres del hogar NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 33 0.0164753 NA NA NA Se dedica a los quehaceres del hogar, Es estudiante NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 31 0.0154768 NA NA NA Está trabajando, Se dedica a los quehaceres del hogar NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 26 0.0129805 NA NA NA Está trabajando, Es estudiante NA NA NA
x5_a_que_se_dedica_actualmente_puede_seleccionar_una_o_mas_opciones 12 0.0059910 NA NA NA Está trabajando, Se dedica a los quehaceres del hogar, Es estudiante NA NA NA
centro_comunitario 5713 0.1812270 NA NA NA NA NA SAN BERNABE NA
centro_comunitario 2275 0.0721672 NA NA NA NA NA MONTE KRISTAL NA
centro_comunitario 2043 0.0648078 NA NA NA NA NA SANTA FE NA
centro_comunitario 1969 0.0624603 NA NA NA NA NA LA ALIANZA NA
centro_comunitario 1562 0.0495495 NA NA NA NA NA ALIANZA REAL NA
centro_comunitario 1483 0.0470435 NA NA NA NA NA LOS ENCINOS NA
centro_comunitario 1178 0.0373684 NA NA NA NA NA EULALIO VILLARREAL NA
centro_comunitario 1139 0.0361312 NA NA NA NA NA TIERRA PROPIA NA
centro_comunitario 1022 0.0324197 NA NA NA NA NA HECTOR CABALLERO NA
centro_comunitario 921 0.0292158 NA NA NA NA NA INDEPENDENCIA NA
curso_o_taller_que_cursa_o_termino 1197 0.0384714 NA NA NA NA ZUMBA NA NA
curso_o_taller_que_cursa_o_termino 651 0.0209231 NA NA NA NA ATENCIÓN PSICOLÓGICA INDIVIDUAL NA NA
curso_o_taller_que_cursa_o_termino 523 0.0168092 NA NA NA NA BAILOTERAPIA NA NA
curso_o_taller_que_cursa_o_termino 490 0.0157485 NA NA NA NA BELLEZA I NA NA
curso_o_taller_que_cursa_o_termino 459 0.0147522 NA NA NA NA ACONDICIONAMIENTO FÍSICO NA NA
curso_o_taller_que_cursa_o_termino 431 0.0138523 NA NA NA NA PIÑATAS NA NA
curso_o_taller_que_cursa_o_termino 415 0.0133380 NA NA NA NA EDUCACIÓN INICIAL NA NA
curso_o_taller_que_cursa_o_termino 375 0.0120525 NA NA NA NA GIMNASIA NA NA
curso_o_taller_que_cursa_o_termino 372 0.0119560 NA NA NA NA CORTE Y CONFECCIÓN I NA NA
curso_o_taller_que_cursa_o_termino 365 0.0117311 NA NA NA NA TAE KWON DO PRINCIPIANTE NA NA
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 327 0.1632551 NA NA NA NA NA NA 1. Laboral / emprendimiento de un negocio
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 281 0.1402896 NA NA NA NA NA NA 1. Laboral / emprendimiento de un negocio, 3. Personal
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 230 0.1148278 NA NA NA NA NA NA 3. Personal
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 214 0.1068397 NA NA NA NA NA NA 1. Laboral / emprendimiento de un negocio, 4. Familiar
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 192 0.0958562 NA NA NA NA NA NA 3. Personal, 4. Familiar
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 163 0.0813779 NA NA NA NA NA NA 3. Personal, 5. Salud
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 97 0.0484274 NA NA NA NA NA NA 4. Familiar
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 94 0.0469296 NA NA NA NA NA NA 5. Salud
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 66 0.0329506 NA NA NA NA NA NA 2. Académico, 3. Personal
x6_lo_que_usted_aprendio_en_el_curso_taller_en_que_ambito_de_su_vida_lo_aplicaria_puede_marcar_hasta_dos_opciones 50 0.0249626 NA NA NA NA NA NA 2. Académico

Nubes de palabras

Intereses en nuevos cursos o talleres

#(Variables: “abierta_x10_” y “abierta_si_tiene_”) #Las palabras más frecuentes: repostería, cocina, carpintería, confección, belleza, inglés, computación, reflejan un equilibrio entre oficios tradicionales, capacitación técnica y desarrollo personal. Los participantes muestran interés por talleres prácticos y útiles para el autoempleo o la vida cotidiana. Además, la demanda por cursos de idiomas y tecnología evidencia una búsqueda de modernización y competitividad laboral.

#Comentarios sobre los cursos recibidos #(Variable: “abierta_comentarios_sobre_el_curso_o_taller_que_recibio”) #Términos como excelente, bueno, gusto, satisfecho revelan una valoración positiva generalizada. Los participantes destacan la enseñanza, la atención y el ambiente, reforzando la confianza en los instructores y la percepción de utilidad de los talleres.

#Elección de otros talleres #(Variable: “abierta_en_caso_de_elegir_otro_menciona_el_taller_al_que_asistes”) #Las menciones más comunes: repostería, piñatas, inglés, belleza, pintura, gimnasia, karate, muestran preferencia por actividades creativas y de bienestar personal. Este patrón sugiere una visión integral del aprendizaje, que combina expresión artística, convivencia social y desarrollo técnico.

Tópicos detectados

# Para visualizar las palabras clave por archivo de las 4 variables más imporantes para la pregunta de investigación
print(terms_lda[[1]])
##       Topic 1     Topic 2     Topic 3     Topic 4     Topic 5    
##  [1,] "bueno"     "bueno"     "bien"      "bien"      "excelente"
##  [2,] "bien"      "excelente" "excelente" "buen"      "bueno"    
##  [3,] "taller"    "maestra"   "bueno"     "bueno"     "ninguno"  
##  [4,] "maestro"   "taller"    "maestra"   "excelente" "maestra"  
##  [5,] "buen"      "buen"      "buena"     "buena"     "taller"   
##  [6,] "exelente"  "bien"      "taller"    "niños"     "buen"     
##  [7,] "niños"     "niños"     "buen"      "espacio"   "exelente" 
##  [8,] "excelente" "maestro"   "falta"     "taller"    "buena"    
##  [9,] "espacio"   "buena"     "exelente"  "exelente"  "buenos"   
## [10,] "ninguno"   "explicado" "material"  "material"  "bien"
print(terms_lda[[2]])
##       Topic 1      Topic 2      Topic 3             Topic 4      Topic 5     
##  [1,] "reposteria" "reposteria" "taller"            "reposteria" "reposteria"
##  [2,] "piñatas"    "taller"     "ingles"            "ingles"     "piñatas"   
##  [3,] "uñas"       "pasta"      "pasta"             "gimnasia"   "flexible"  
##  [4,] "gimnasia"   "piñatas"    "fieltro"           "taller"     "panaderia" 
##  [5,] "ingles"     "uñas"       "piñatas"           "niños"      "pasta"     
##  [6,] "flexible"   "gimnasia"   "uñas"              "karate"     "pintura"   
##  [7,] "taller"     "ingles"     "panaderia"         "panaderia"  "uñas"      
##  [8,] "zumba"      "belleza"    "belleza"           "excel"      "belleza"   
##  [9,] "pasteleria" "niños"      "acondicionamiento" "kekitos"    "taller"    
## [10,] "belleza"    "dulces"     "dulces"            "piñatas"    "dulces"
print(terms_lda[[3]])
##       Topic 1      Topic 2       Topic 3       Topic 4       Topic 5      
##  [1,] "ninguno"    "ninguno"     "ninguno"     "ingles"      "ninguno"    
##  [2,] "ingles"     "bien"        "cocina"      "computacion" "ingles"     
##  [3,] "reposteria" "reposteria"  "reposteria"  "bordado"     "belleza"    
##  [4,] "natacion"   "musica"      "ingles"      "ninguno"     "reposteria" 
##  [5,] "cocina"     "carpinteria" "corte"       "box"         "repujado"   
##  [6,] "yoga"       "niños"       "uñas"        "carpinteria" "niños"      
##  [7,] "costura"    "corte"       "box"         "satisfecho"  "yoga"       
##  [8,] "bien"       "globoflexia" "carpinteria" "uñas"        "velas"      
##  [9,] "barber"     "pintura"     "belleza"     "pintura"     "pintura"    
## [10,] "box"        "confeccion"  "satisfecho"  "costura"     "globoflexia"
print(terms_lda[[4]])
##       Topic 1        Topic 2       Topic 3       Topic 4        Topic 5      
##  [1,] "cocina"       "ninguno"     "ninguno"     "ninguno"      "ninguno"    
##  [2,] "reposteria"   "cocina"      "corte"       "bien"         "bien"       
##  [3,] "tejido"       "bien"        "reposteria"  "belleza"      "reposteria" 
##  [4,] "ninguno"      "reposteria"  "cursos"      "confeccion"   "carpinteria"
##  [5,] "auxilios"     "taller"      "computacion" "dulces"       "cursos"     
##  [6,] "confeccion"   "pestañas"    "taller"      "ingles"       "computacion"
##  [7,] "belleza"      "corte"       "carpinteria" "chocolateria" "pintura"    
##  [8,] "chocolateria" "confeccion"  "talleres"    "carpinteria"  "belleza"    
##  [9,] "dulces"       "carpinteria" "ingles"      "postres"      "pestañas"   
## [10,] "postres"      "belleza"     "dulces"      "mesa"         "dulces"
# Para visualizar las palabras clave en todo el texto de los 4 archivos
print(terms_lda_ALL)
##       Topic 1      Topic 2      Topic 3     Topic 4     Topic 5      
##  [1,] "reposteria" "bien"       "excelente" "bien"      "ninguno"    
##  [2,] "piñatas"    "excelente"  "bueno"     "bueno"     "ingles"     
##  [3,] "taller"     "buenos"     "bien"      "taller"    "reposteria" 
##  [4,] "ingles"     "ninguno"    "maestra"   "ninguno"   "bien"       
##  [5,] "uñas"       "taller"     "buen"      "excelente" "cocina"     
##  [6,] "pasta"      "buen"       "buena"     "buen"      "carpinteria"
##  [7,] "belleza"    "maestra"    "ninguno"   "maestra"   "belleza"    
##  [8,] "panaderia"  "bueno"      "exelente"  "exelente"  "musica"     
##  [9,] "flexible"   "satisfecho" "taller"    "buenos"    "corte"      
## [10,] "pintura"    "buena"      "falta"     "niños"     "computacion"
# Gráficas de los 5 tópicos principales en todo el texto de los 4 archivos

lda_tidy_ALL <- tidy(lda_model_ALL)

top_terms_ALL <- lda_tidy_ALL %>%
  group_by(topic) %>%
  slice_max(beta, n = 10) %>%
  ungroup() %>%
  arrange(topic, -beta)

ggplot(top_terms_ALL, aes(x = reorder_within(term, beta, topic), y = beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free", ncol = 3) +
  scale_x_reordered() +
  theme_minimal() +
  labs(
    title = "Principales términos por tópico",
    x = "Término",
    y = "Beta (Importancia del término)"
  ) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Interpretación narrativa de los temas predominantes.

El análisis conjunto de las palabras clave, los tópicos y las variables sociodemográficas revela un patrón coherente entre el perfil de los participantes y los intereses formativos identificados. La predominancia femenina en los cursos comunitarios, acompañada de un nivel educativo medio y una ocupación centrada en el hogar, se vincula directamente con los temas más mencionados, como belleza, repostería, confección y bienestar físico. Esto indica que los talleres funcionan como espacios de capacitación práctica y empoderamiento económico, donde las mujeres encuentran oportunidades para emprender o complementar ingresos familiares.

Asimismo, la presencia de palabras y tópicos relacionados con inglés, computación, pintura o música muestra un interés por ampliar habilidades culturales y tecnológicas, reflejando una motivación de aprendizaje continuo y superación personal. La distribución territorial de los centros como San Bernabé, Monte Kristal y Santa Fe evidencia una participación activa en zonas urbanas populares, donde los talleres representan también espacios de convivencia, integración comunitaria y bienestar emocional. Finalmente, la aplicación del aprendizaje en los ámbitos personal, familiar y laboral confirma que el impacto de los cursos trasciende lo educativo, generando beneficios sociales y económicos sostenibles dentro de las comunidades.

Conclusiones parciales y próximos pasos

Los datos muestran que la participación en los centros comunitarios es principalmente femenina, con mujeres adultas, casadas y con nivel educativo medio. Los talleres más cursados (Belleza, Zumba, Corte y Confección, Piñatas) reflejan una orientación hacia actividades prácticas y de bienestar, aunque también existe interés creciente por áreas técnicas y culturales como inglés o computación.Esta tendencia sugiere que los cursos funcionan como espacios de aprendizaje, convivencia y empoderamiento económico, pero también que pueden reproducir patrones tradicionales de género.En la siguiente etapa se analizará la relación entre género, escolaridad y tipo de taller cursado, para identificar si existe segregación formativa y cómo se vincula con la estructura ocupacional de Nuevo León.

Propuesta de siguientes análisis o modelaciones a desarrollar

Análisis descriptivo del perfil de los participantes: Explorar la distribución de género, estado civil, nivel educativo y ocupación entre quienes toman los talleres. Esto permitiría ver si existen patrones demográficos marcados (por ejemplo, más mujeres casadas en talleres de oficios domésticos o más hombres solteros en talleres técnicos).

Asociación entre género y tipo de taller cursado: Identificar si existe una segregación por género en la elección del taller (por ejemplo, mujeres en repostería o manualidades, hombres en carpintería o computación).

Influencia de escolaridad y ocupación actual: Analizar si la formación previa y la posición laboral condicionan la elección del taller.

Vínculo con la segregación ocupacional en Nuevo León: Relacionar los resultados de los talleres con datos externos sobre la estructura ocupacional del estado, mostrando cómo los patrones de elección reflejan o refuerzan la segregación laboral por género en la región.

Enfoque interseccional: Integrar variables como género, estado civil y ocupación para analizar cómo la combinación de factores influye en la elección del taller. Por ejemplo, mujeres casadas con hijos podrían mostrar preferencias distintas a mujeres solteras, o hombres desempleados podrían optar por talleres distintos a hombres empleados.