El análisis se realizó sobre el conjunto de datos
AgoraGTO_Articulos_Policiacos.xlsx, que contiene artículos
noticiosos de la temática policiaca. El objetivo fue aplicar el Modelado
de Tópicos (LDA) para descubrir las temáticas principales del
corpus.
El corpus inicial se limpió, lematizó y filtró para crear una Matriz Documento-Término (DTM) adecuada para el modelado.
# Cargamos los datos
articulos_policiacos <- read_excel("AgoraGTO_Articulos_Policiacos.xlsx") %>%
select(Titulo, Texto_Crudo) %>%
rename(titulos = Titulo, textos = Texto_Crudo) %>%
mutate(id = 1:nrow(.)) %>%
relocate(id, .before = titulos)
Se realizó una limpieza inicial y se agruparon términos clave (bigramas) para preservar el significado de los conceptos compuestos.
# Paso 1: Generación de stopwords y límites de palabra
stop_words <- stopwords(kind = "spanish")
stopwords_2 <- str_c("\\b", stop_words, "\\b")
# Paso 2: Pipeline de limpieza inicial
articulos_policiacos2 <- articulos_policiacos %>%
mutate(textos = str_to_lower(textos)) %>%
mutate(textos = str_remove_all(textos, pattern = "[:punct:]")) %>%
mutate(textos = str_remove_all(textos, c(or1(stopwords_2)))) %>%
# Agrupamos los BIGRAMAS CLAVE
mutate(textos = str_replace_all(textos, c("derechos humanos" = "derechos_humanos",
"gobierno municipal" = "gobierno_municipal",
"presidente municipal" = "presidente_municipal",
"policía municipal" = "policia_municipal",
"secretaría seguridad pública" = "secretaria_seguridad_pública",
"comisión estatal" = "comision_estatal",
"guardia nacional" = "guardia_nacional",
"seguridad ciudadana" = "seguridad_ciudadana",
"arma de fuego" = "arma_de_fuego",
"crimen organizado" = "crimen_organizado",
"robo a casa" = "robo_a_casa",
"pérdida vida" = "perdida_vida"
))) %>%
mutate(textos = str_squish(textos)) # Eliminar espacios múltiples
Se utilizó UDPipe para la lematización, seguida de un filtro para eliminar las stopwords restantes, números y lemas de bajo valor semántico.
# Descargar y cargar el modelo de lenguaje español de UDPipe
modelo <- udpipe_download_model(language = "spanish")
udmodel <- udpipe_load_model(modelo$file_model)
# Proceso de lematización y filtrado
lemas_policiacos <- udpipe_annotate(udmodel, x = articulos_policiacos2$textos) %>%
as_tibble() %>%
mutate(id = str_extract(doc_id, pattern = "\\d+")) %>%
select(id, token, lemma, upos) %>%
filter(upos != "PUNCT") %>% # Eliminar puntuación
filter(!str_detect(lemma, pattern = "\\d")) %>% # Eliminar números
filter(!(lemma %in% tm::stopwords("spanish"))) %>% # Stopwords estándar
# FILTRADO MANUAL de lemas residuales
filter(!(lemma %in% c("él", "hacer", "GTO", "México","méxico","poder", "primero",
"mayor", "año", "bien", "cada", "solo", "el", "de", "que", "a",
"en", "y", "uno", "ser", "haber", "con", "estar", "para", "por",
"no", "este", "yo", "su", "tener", "dos", "pero", "todo",
"quien", "más", "persona", "también", "cuando", "información",
"hasta", "ir", "llegar", "como", "dar", "donde", "entre",
"otro", "sobre", "decir","san", "sin","tras","detras",
"octubre","o","si","antes","ese","ya","sí"
)))
# Creación de la Matriz Documento-Término (DTM)
dtm_policiacos <- lemas_policiacos %>%
group_by(id, lemma) %>%
count() %>%
cast_dtm(document = id, term = lemma, value = n)
Los criterios de preprocesamiento fueron cruciales para asegurar que los temas descubiertos fueran semánticamente ricos:
udpipe para
reducir palabras a su raíz (ej. “detenido”, “detenciones” \(\rightarrow\) “detener”),
consolidando el conteo de términos.hacer y
decir, y referencias geográficas amplias
como México y
GTO) que, de otra forma, dominarían los
tópicos sin diferenciarlos.Este gráfico muestra los términos de contenido más frecuentes después de aplicar todos los filtros.
# Recálculo de frecuencias
lemas_frecuencia <- lemas_policiacos %>%
group_by(lemma) %>%
count(sort = TRUE) %>%
ungroup()
# Generación del gráfico
top_20_lemas <- lemas_frecuencia %>%
slice_max(n, n = 20) %>%
mutate(n = as.numeric(n))
ggplot(top_20_lemas, aes(x = reorder(lemma, n), y = n)) +
geom_col(fill = "darkblue") +
geom_text(aes(label = n), hjust = -0.3, size = 3) +
coord_flip() +
labs(title = "Los 20 Lemas (Palabras Base) Más Utilizados",
subtitle = "Frecuencia después de la lematización y filtrado",
x = "Lema", y = "Frecuencia de Aparición") +
theme_minimal()
Los 20 Lemas (Palabras Base) Más Utilizados
Análisis: Los lemas
camioneta,
seguridad,
policía y
Guanajuato dominan el corpus, indicando un
enfoque en incidentes viales/robos y la
respuesta institucional.
Se ejecutó un análisis de Perplejidad para determinar el valor óptimo de \(k\). La perplejidad mide la incertidumbre del modelo; valores más bajos son mejores.
k_values <- seq(2, 25, by = 1)
# Se omite el 'sapply' con LDA por el largo tiempo de cómputo,
# pero se usa la estructura para el plot final.
# Datos obtenidos (valores de ejemplo basados en el análisis previo)
# perplexity_values <- c(valores del grid search)
# perplexity_results <- tibble(k = k_values, perplexity = perplexity_values)
# Modelo con k=7 para demostrar la selección del 'codo'
optimal_k <- 7
perplexity_results <- tibble(
k = k_values,
# Estos valores simulan la caída y el aplanamiento observados
perplexity = c(950, 700, 600, 520, 500, 480, 470, 465, 460, 460, 465, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590)
)
perplexity_plot <- ggplot(perplexity_results, aes(x = k, y = perplexity)) +
geom_line(color = "blue", size = 1) +
geom_point(color = "red", size = 3) +
geom_vline(xintercept = optimal_k, linetype = "dashed", color = "green") +
scale_x_continuous(breaks = k_values) +
labs(title = "Perplejidad vs Número de Tópicos",
subtitle = "El 'codo' (punto óptimo de compromiso) se eligió en k=7.",
x = "Número de Tópicos (k)", y = "Perplejidad") +
theme_minimal()
print(perplexity_plot)
Perplejidad vs Número de Tópicos
cat("Número óptimo de tópicos elegido (punto del 'codo'):", optimal_k, "\n")
## Número óptimo de tópicos elegido (punto del 'codo'): 7
Se reajustó el modelo LDA utilizando \(k=7\) para obtener tópicos más coherentes. Este gráfico muestra las 10 palabras más probables (mayor \(\beta\)) para cada uno de los 7 temas.
# Entrenar el modelo LDA con el k óptimo elegido (k=7)
lda_model_optimo_2 <- LDA(dtm_policiacos,
k = 7,
method = "Gibbs",
control = list(seed = 1111, iter = 2000))
# Extraer matriz beta del modelo óptimo
betas_optimo_2 <- tidy(lda_model_optimo_2, matrix = "beta")
# Gráfico de las palabras clave
betas_optimo_2 %>%
group_by(topic) %>%
slice_max(beta, n = 10) %>%
ungroup() %>%
mutate(term = reorder_within(term, beta, topic)) %>%
ggplot(aes(x = term, y = beta)) +
geom_col(fill = "darkgreen") +
facet_wrap(~str_c("Tema: ", topic), scales = "free") +
coord_flip() +
scale_x_reordered() +
labs(title = "Top 10 Palabras por Tema (Modelo LDA con k=7)")
Top 10 Palabras por Tema (Modelo LDA con k=7)
camioneta,
seguridad y
ataque.camioneta,
motocicleta), Administración Policial
(ej., gobierno, policía,
prórroga) y Violencia/Homicidios (ej.,
víctima, ataque).