Procesamiento de Lenguaje Natural (Natural Language Processing, NLP) o Lingüística Computacional (Computational Linguistics) se refiere al conjunto de técnicas que permiten a los computadores analizar, estructurar y extraer información de textos en lenguaje humano.
El objetivo central del NLP no es que las máquinas “entiendan” el lenguaje como lo hacen los humanos, sino que puedan identificar patrones, estructuras y relaciones lingüísticas mediante procedimientos algorítmicos eficientes.
Mientras que los humanos comprenden intuitivamente el significado, los computadores destacan por su capacidad para procesar grandes volúmenes de texto de forma sistemática.
A pesar del auge de la IA, el análisis de texto mediante minería y redes sigue siendo fundamental por su transparencia, control y rigor cuantitativo.
A diferencia de los modelos de lenguaje, estos métodos permiten trazar cada paso del análisis, adaptar criterios lingüísticos específicos y obtener evidencia empírica interpretable. Además, ofrecen herramientas complementarias para validar o enriquecer resultados generados por modelos más complejos.
Su estudio fortalece la comprensión del lenguaje desde una perspectiva estructurada, crítica y replicable.
Finalmente, no requiere grandes capacidades de cómputo ni infraestructura especializada, lo que lo hace accesible y eficiente para investigadores y estudiantes.
Detectar y jerarquizar términos relevantes dentro de un corpus textual.
Identificar y cuantificar relaciones semánticas entre palabras mediante técnicas estadísticas y de redes.
Los discursos del presidente Gustavo Petro (2022) y del expresidente Iván Duque (2021), pronunciados ante la Asamblea General de las Naciones Unidas, constituyen las fuentes primarias del análisis. Ambos textos fueron procesados en su versión oficial publicada en medios institucionales y reproducidos sin modificaciones sustanciales. A partir de estos discursos se construyen representaciones cuantitativas del lenguaje mediante técnicas de minería de texto y análisis de redes lingüísticas, con el objetivo de identificar patrones semánticos, relaciones entre conceptos clave y estructuras narrativas predominantes.
##### Importar librerías ----
suppressMessages(suppressWarnings(library(readr)))
suppressMessages(suppressWarnings(library(tidyverse)))
# Notas:
# - Los warnings se suprimen para evitar alertas por caracteres no UTF-8 o
# líneas vacías.
# - UTF-8 (8-bit Unicode Transformation Format) es un formato de codificación de
# caracteres que permite representar todos los puntos de código válidos en
# Unicode.
##### Importar discurso de Gustavo Petro ----
text_petro <- read_lines("discurso_onu_petro.txt") # Leer líneas del archivo
text_petro <- unlist(c(text_petro)) # Convertir a vector de caracteres
names(text_petro) <- NULL # Eliminar nombres de los elementos
head(text_petro, n = 3) # Vista previa de las primeras líneas
## [1] "Vengo de uno de los tres países más bellos de la Tierra."
## [2] "Allí hay una explosión de vida. Miles de especies multicolores en los mares, en los cielos, en las tierras... vengo de la tierra de las mariposas amarillas y de la magia. Allí en las montañas y valles de todos los verdes, no solo bajan las aguas abundantes, bajan también los torrentes de la sangre. Vengo de un país de belleza ensangrentada."
## [3] "Mi país no solo es bello, es también violento."
##### Importar discurso de Iván Duque ----
text_duque <- read_lines("discurso_onu_duque.txt")
text_duque <- unlist(c(text_duque)) # Convertir a vector de caracteres
names(text_duque) <- NULL
head(text_duque, n = 3)
## [1] "Nos encontramos de nuevo en este foro global que ha sido históricamente un espacio vital para el desarrollo del multilateralismo, la construcción global de la paz y de soluciones a las amenazas de nuestra casa común. Lo hacemos aun en medio de una pandemia cruel que golpea nuestros sistemas de salud, nuestras economías, nuestras conquistas de equidad y el avance de la Agenda 2030."
## [2] "Por encima de cualquier consideración, nos encontramos una vez más en este histórico hemiciclo recordando la fragilidad del ser humano y, al mismo tiempo, reconociendo la grandeza y la condición de una raza humana que sabe sobreponerse a los grandes desafíos."
## [3] "El covid-19 irrumpió de manera abrupta en nuestras vidas. Cambió nuestra cotidianidad, nuestra interacción y nos arrebató seres queridos. Este virus letal ha puesto a prueba nuestras emociones para entender, HOY MÁS QUE NUNCA, cuánto vale el abrazo de un padre y el de una madre, cuánto significa compartir en familia, cuánto nos llena un encuentro amigable inesperado."
##### Convertir a data frame en formato tidy ----
# Discurso de Gustavo Petro
text_petro <- tibble(
line = seq_along(text_petro), # Crear índice de línea
text = text_petro # Texto original, línea por línea
)
# Inspección preliminar
class(text_petro)
## [1] "tbl_df" "tbl" "data.frame"
## [1] 43 2
## # A tibble: 3 × 2
## line text
## <int> <chr>
## 1 1 Vengo de uno de los tres países más bellos de la Tierra.
## 2 2 Allí hay una explosión de vida. Miles de especies multicolores en los m…
## 3 3 Mi país no solo es bello, es también violento.
El primer paso consiste en almacenar el texto en un formato estructurado que permita su análisis. En este contexto, un token representa la unidad mínima de análisis, generalmente una palabra.
La tokenización básica implica dividir el texto de modo que cada palabra (token) ocupe una línea. Por defecto, este proceso elimina la puntuación y convierte el texto a minúsculas, aunque no remueve las tildes ni otros signos diacríticos.
##### Importar librerías para tokenización ----
suppressMessages(suppressWarnings(library(tidytext)))
suppressMessages(suppressWarnings(library(magrittr)))
##### Tokenización en formato tidy ----
# ---------- Discurso de Gustavo Petro ----------
text_petro %<>%
unnest_tokens(input = text, output = word) %>% # Convertir a un token por fila
filter(!is.na(word)) # Eliminar palabras vacías o NA
class(text_petro)
## [1] "tbl_df" "tbl" "data.frame"
## [1] 2166 2
## # A tibble: 10 × 2
## line word
## <int> <chr>
## 1 1 vengo
## 2 1 de
## 3 1 uno
## 4 1 de
## 5 1 los
## 6 1 tres
## 7 1 países
## 8 1 más
## 9 1 bellos
## 10 1 de
# ---------- Discurso de Iván Duque ----------
text_duque %<>%
unnest_tokens(input = text, output = word) %>%
filter(!is.na(word))
dim(text_duque)
## [1] 2685 2
## # A tibble: 10 × 2
## line word
## <int> <chr>
## 1 1 nos
## 2 1 encontramos
## 3 1 de
## 4 1 nuevo
## 5 1 en
## 6 1 este
## 7 1 foro
## 8 1 global
## 9 1 que
## 10 1 ha
Limpieza del texto: se recomienda aplicar los siguientes pasos de normalización y depuración lingüística:
#
,
%
, $
).##### Verificar presencia de números en el texto ----
# ---------- Discurso de Gustavo Petro ----------
text_petro %>%
filter(grepl(pattern = "[0-9]", x = word)) %>% # Filtrar palabras que contienen números
count(word, sort = TRUE) # Contar frecuencia de cada token numérico
## # A tibble: 4 × 2
## word n
## <chr> <int>
## 1 co2 3
## 2 40 2
## 3 1933 1
## 4 2.800.000 1
# ---------- Discurso de Iván Duque ----------
text_duque %>%
filter(grepl(pattern = "[0-9]", x = word)) %>%
count(word, sort = TRUE)
## # A tibble: 14 × 2
## word n
## <chr> <int>
## 1 2030 5
## 2 19 2
## 3 20 2
## 4 2021 2
## 5 25 2
## 6 0,6 1
## 7 1,8 1
## 8 2016 1
## 9 2018 1
## 10 2022 1
## 11 2050 1
## 12 51 1
## 13 co2 1
## 14 cop26 1
##### Remover tokens que contienen números ----
# ---------- Discurso de Gustavo Petro ----------
text_petro %<>%
filter(!grepl(pattern = "[0-9]", x = word)) # Eliminar palabras que contienen dígitos
dim(text_petro) # Verificar dimensiones después del filtrado
## [1] 2159 2
# ---------- Discurso de Iván Duque ----------
text_duque %<>%
filter(!grepl(pattern = "[0-9]", x = word))
dim(text_duque)
## [1] 2663 2
##### Cargar stop words incorporadas en tidytext ----
# Tres diccionarios en inglés disponibles por defecto: onix, SMART y snowball
data(stop_words) # Cargar conjunto de stopwords
class(stop_words) # Clase del objeto
## [1] "tbl_df" "tbl" "data.frame"
## [1] 1149 2
## # A tibble: 10 × 2
## word lexicon
## <chr> <chr>
## 1 a SMART
## 2 a's SMART
## 3 able SMART
## 4 about SMART
## 5 above SMART
## 6 according SMART
## 7 accordingly SMART
## 8 across SMART
## 9 actually SMART
## 10 after SMART
##
## onix SMART snowball
## 404 571 174
###### Stop words en español ----
# No hay diccionarios en español disponibles por defecto en tidytext.
# Diccionario en español (con acentos) de COUNTWORDSFREE:
# http://countwordsfree.com/stopwords/spanish
# Otras alternativas:
# - https://github.com/stopwords-iso/stopwords-es
# - Librería tm: stopwords("spanish")
# El diccionario se conserva en el mismo formato que los de tidytext
stop_words_es <- tibble(
word = unlist(c(read.table("stop_words_spanish.txt", quote = "\"", comment.char = ""))),
lexicon = "custom"
)
dim(stop_words_es)
## [1] 444 2
## # A tibble: 10 × 2
## word lexicon
## <chr> <chr>
## 1 algún custom
## 2 alguna custom
## 3 algunas custom
## 4 alguno custom
## 5 algunos custom
## 6 ambos custom
## 7 ampleamos custom
## 8 ante custom
## 9 antes custom
## 10 aquel custom
##### Remover stop words en español ----
# ---------- Discurso de Gustavo Petro ----------
text_petro %<>%
anti_join(x = ., y = stop_words_es) # Eliminar palabras vacías (stopwords)
## Joining with `by = join_by(word)`
## [1] 902 2
## # A tibble: 10 × 2
## line word
## <int> <chr>
## 1 1 vengo
## 2 1 países
## 3 1 bellos
## 4 1 tierra
## 5 2 allí
## 6 2 explosión
## 7 2 vida
## 8 2 miles
## 9 2 especies
## 10 2 multicolores
## Joining with `by = join_by(word)`
## [1] 1183 2
## # A tibble: 10 × 2
## line word
## <int> <chr>
## 1 1 encontramos
## 2 1 foro
## 3 1 global
## 4 1 históricamente
## 5 1 espacio
## 6 1 vital
## 7 1 desarrollo
## 8 1 multilateralismo
## 9 1 construcción
## 10 1 global
##### Remover acentos del texto ----
# Crear lista de reemplazo para vocales acentuadas
replacement_list <- list(
'á' = 'a',
'é' = 'e',
'í' = 'i',
'ó' = 'o',
'ú' = 'u'
)
# ---------- Discurso de Gustavo Petro ----------
text_petro %<>%
mutate(word = chartr(
old = names(replacement_list) %>% str_c(collapse = ""),
new = replacement_list %>% str_c(collapse = ""),
x = word
))
dim(text_petro)
## [1] 902 2
## # A tibble: 10 × 2
## line word
## <int> <chr>
## 1 1 vengo
## 2 1 paises
## 3 1 bellos
## 4 1 tierra
## 5 2 alli
## 6 2 explosion
## 7 2 vida
## 8 2 miles
## 9 2 especies
## 10 2 multicolores
# ---------- Discurso de Iván Duque ----------
text_duque %<>%
mutate(word = chartr(
old = names(replacement_list) %>% str_c(collapse = ""),
new = replacement_list %>% str_c(collapse = ""),
x = word
))
dim(text_duque)
## [1] 1183 2
## # A tibble: 10 × 2
## line word
## <int> <chr>
## 1 1 encontramos
## 2 1 foro
## 3 1 global
## 4 1 historicamente
## 5 1 espacio
## 6 1 vital
## 7 1 desarrollo
## 8 1 multilateralismo
## 9 1 construccion
## 10 1 global
Una vez limpiado y normalizado el texto, se identifican las palabras con mayor frecuencia de aparición. Este análisis permite detectar los términos más relevantes en cada discurso y explorar patrones léxicos dominantes.
##### Top 10 de tokens más frecuentes ----
# ---------- Discurso de Gustavo Petro ----------
text_petro %>%
count(word, sort = TRUE) %>% # Contar ocurrencias y ordenar de mayor a menor
head(n = 10) # Seleccionar las 10 más frecuentes
## # A tibble: 10 × 2
## word n
## <chr> <int>
## 1 selva 23
## 2 guerra 14
## 3 drogas 13
## 4 vida 11
## 5 adiccion 10
## 6 humanidad 10
## 7 petroleo 10
## 8 planta 9
## 9 salvar 9
## 10 paz 8
# ---------- Discurso de Iván Duque ----------
text_duque %>%
count(word, sort = TRUE) %>%
head(n = 10)
## # A tibble: 10 × 2
## word n
## <chr> <int>
## 1 colombia 27
## 2 accion 11
## 3 pandemia 11
## 4 agenda 9
## 5 desarrollo 9
## 6 fiscal 9
## 7 frente 9
## 8 paz 9
## 9 social 8
## 10 año 7
##### Visualización de tokens más frecuentes ----
suppressMessages(suppressWarnings(library(gridExtra)))
# ---------- Discurso de Gustavo Petro ----------
text_petro %>%
count(word, sort = TRUE) %>%
filter(n > 7) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(x = word, y = n)) +
geom_col(fill = "darkolivegreen4", alpha = 0.8) +
theme_light() +
coord_flip() +
xlab(NULL) +
ylab("Frecuencia") +
ggtitle("Petro: Conteo de palabras") -> p1
# ---------- Discurso de Iván Duque ----------
text_duque %>%
count(word, sort = TRUE) %>%
filter(n > 7) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(x = word, y = n)) +
geom_col(fill = "blue4", alpha = 0.8) +
theme_light() +
coord_flip() +
xlab(NULL) +
ylab("Frecuencia") +
ggtitle("Duque: Conteo de palabras") -> p2
# Desplegar gráficos en paralelo
grid.arrange(p1, p2, ncol = 2)
##### Visualización con nubes de palabras ----
suppressMessages(suppressWarnings(library(wordcloud)))
# Configuración del área de gráficos
par(mfrow = c(1, 2), mar = c(1, 1, 2, 1), mgp = c(1, 1, 1))
# ---------- Discurso de Gustavo Petro ----------
set.seed(123)
text_petro %>%
count(word, sort = TRUE) %>%
with(wordcloud(
words = word,
freq = n,
max.words = 20,
colors = "darkolivegreen4"
))
title(main = "Petro")
# ---------- Discurso de Iván Duque ----------
set.seed(123)
text_duque %>%
count(word, sort = TRUE) %>%
with(wordcloud(
words = word,
freq = n,
max.words = 20,
colors = "blue4"
))
title(main = "Duque")
Los resultados muestran diferencias claras en el enfoque temático de cada discurso:
Petro enfatiza temas ambientales y humanitarios. Palabras como selva, vida, humanidad, planta, salvar y petroleo sugieren una narrativa centrada en la protección de la naturaleza y en críticas al modelo extractivista. Términos como guerra, drogas, adicción y paz indican una relación directa con el conflicto armado y sus causas estructurales.
Duque, en contraste, utiliza un lenguaje más orientado a gestión política y desarrollo institucional. Palabras como colombia, acción, agenda, fiscal, frente, desarrollo y social reflejan un enfoque en políticas públicas, recuperación económica y gobernanza. La presencia de pandemia también ubica su discurso en un contexto inmediato de crisis sanitaria.
Ambos comparten el término paz, aunque con contextos posiblemente distintos: Petro como objetivo estructural vinculado a causas profundas, y Duque en un marco más institucional o de logros de gobierno.
##### Frecuencias relativas de las palabras por autor ----
bind_rows(
mutate(.data = text_petro, author = "petro"),
mutate(.data = text_duque, author = "duque")
) %>%
count(author, word) %>%
group_by(author) %>%
mutate(proportion = n / sum(n)) %>% # Calcular proporción relativa por autor
select(-n) %>%
spread(author, proportion, fill = 0) -> frec # Convertir a formato ancho
# Reordenar columnas
frec %<>%
select(word, petro, duque)
# Inspección básica
dim(frec)
## [1] 1241 3
## # A tibble: 10 × 3
## word petro duque
## <chr> <dbl> <dbl>
## 1 abierta 0 0.000845
## 2 abordamos 0 0.000845
## 3 abrazo 0 0.000845
## 4 abrupta 0 0.000845
## 5 absorbe 0.00443 0
## 6 abundantes 0.00111 0
## 7 acabar 0.00333 0
## 8 acabara 0.00111 0
## 9 acaparamiento 0 0.000845
## 10 acaso 0.00111 0
##### Top 10 palabras en común entre ambos discursos ----
# Ordenar por frecuencia descendente en Petro, luego en Duque
frec %>%
filter(petro != 0, duque != 0) %>%
arrange(desc(petro), desc(duque)) -> frec_comun
# Inspección de dimensiones y primeras palabras
dim(frec_comun)
## [1] 82 3
## # A tibble: 10 × 3
## word petro duque
## <chr> <dbl> <dbl>
## 1 selva 0.0255 0.000845
## 2 vida 0.0122 0.000845
## 3 humanidad 0.0111 0.00254
## 4 paz 0.00887 0.00761
## 5 millones 0.00776 0.00338
## 6 america 0.00776 0.000845
## 7 pais 0.00665 0.00592
## 8 climatico 0.00665 0.00254
## 9 ustedes 0.00665 0.000845
## 10 desastre 0.00554 0.000845
Las palabras en común más frecuentes muestran que, aunque ambos discursos comparten ciertos términos, su peso relativo es significativamente mayor en el de Petro:
Términos como selva, vida, humanidad, desastre, américa y climático tienen una presencia destacada en Petro y apenas marginal en Duque, lo que refuerza el enfoque ambiental y humanista de su intervención.
Paz y país son las únicas palabras con proporciones similares, lo que sugiere un punto temático compartido, aunque posiblemente abordado desde perspectivas distintas.
El uso de millones y ustedes indica referencias comunes a la audiencia y magnitud de los problemas, pero de nuevo con mayor intensidad en Petro.
###### Proporción de palabras en común ----
# Cálculo de la proporción de palabras compartidas entre ambos discursos
prop_palabras_comunes <- dim(frec_comun)[1] / dim(frec)[1]
prop_palabras_comunes
## [1] 0.06607575
El resultado indica que solo el 6.6% de las palabras utilizadas se repiten en ambos discursos. Es decir, más del 93% del vocabulario empleado es exclusivo de cada presidente.
Esto sugiere una baja coincidencia léxica entre los discursos, lo cual refuerza la idea de que tienen enfoques temáticos y narrativas muy distintas.
A pesar de tratarse de discursos oficiales ante el mismo foro (la ONU), cada intervención refleja prioridades, estilos y marcos discursivos propios. Esta diferencia puede ser atribuida a diferencias ideológicas, contextos políticos y objetivos comunicativos divergentes.
##### Correlación de las frecuencias ----
# Nota: la prueba de correlación asume linealidad y normalidad.
# En caso de duda, se recomienda usar métodos no paramétricos o bootstrap.
# Correlación sobre todo el vocabulario (incluye palabras exclusivas)
cor.test(x = frec$duque, y = frec$petro)
##
## Pearson's product-moment correlation
##
## data: frec$duque and frec$petro
## t = -5.1651, df = 1239, p-value = 2.798e-07
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.19921982 -0.09026528
## sample estimates:
## cor
## -0.1451827
##
## Pearson's product-moment correlation
##
## data: frec_comun$duque and frec_comun$petro
## t = 0.83248, df = 80, p-value = 0.4076
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.1268859 0.3035750
## sample estimates:
## cor
## 0.09267302
La correlación entre las frecuencias relativas de todas las palabras utilizadas en ambos discursos es débilmente negativa (\(r = -0.145\)) y estadísticamente significativa (\(p < 0.001\)). Esto indica una ligera tendencia inversa: las palabras más frecuentes en un discurso tienden a ser menos frecuentes en el otro. Este hallazgo respalda la conclusión de que los discursos están construidos sobre vocabularios en gran medida distintos, con énfasis temáticos divergentes.
Cuando se analiza únicamente el subconjunto de palabras utilizadas por ambos oradores, la correlación es muy baja y no significativa (\(r = 0.093\), \(p = 0.41\)). Esto sugiere que, incluso entre las palabras compartidas, no existe un patrón consistente en su nivel de uso relativo.
La correlación de Pearson es una medida sensible a la suposición de relación lineal entre variables, la distribución normal de los datos, y la presencia de valores extremos o ceros.
En este contexto, las frecuencias relativas de palabras no cumplen claramente estos supuestos, por lo que esta prueba debe interpretarse con cautela. Además, la alta proporción de palabras exclusivas (∼93%) distorsiona el análisis global.
Como alternativa más robusta y adecuada al contexto lingüístico se recomienda usar Spearman o técnicas de re-muestreo (bootstrap). También puede considerarse una distancia de similitud léxica o análisis de componentes principales (PCA) para comparar perfiles de frecuencia.
##### Bootstrap para la correlación entre frecuencias relativas ----
suppressMessages(suppressWarnings(library(boot)))
# ---------- Definir función de correlación ----------
cor_func <- function(data, indices) {
d <- data[indices, ]
return(cor(d$duque, d$petro))
}
# ---------- Preparar datos ----------
data_cor <- frec %>%
select(duque, petro)
# ---------- Ejecutar bootstrap ----------
set.seed(123)
boot_cor <- boot(data = data_cor, statistic = cor_func, R = 2000)
# ---------- Intervalo de confianza bootstrap ----------
boot.ci(boot_cor, type = "perc")
## BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
## Based on 2000 bootstrap replicates
##
## CALL :
## boot.ci(boot.out = boot_cor, type = "perc")
##
## Intervals :
## Level Percentile
## 95% (-0.2438, -0.0738 )
## Calculations and Intervals on Original Scale
# ---------- Visualización opcional ----------
hist(boot_cor$t,
main = "Distribución bootstrap de la correlación",
xlab = "r",
col = "steelblue",
border = "white")
##### Bootstrap para la correlación sobre palabras en común ----
suppressMessages(suppressWarnings(library(boot)))
# ---------- Definir función de correlación ----------
cor_func_comun <- function(data, indices) {
d <- data[indices, ]
return(cor(d$duque, d$petro))
}
# ---------- Preparar datos ----------
data_cor_comun <- frec_comun %>%
select(duque, petro)
# ---------- Ejecutar bootstrap ----------
set.seed(123)
boot_cor_comun <- boot(data = data_cor_comun, statistic = cor_func_comun, R = 2000)
# ---------- Intervalo de confianza bootstrap ----------
boot.ci(boot_cor_comun, type = "perc")
## BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
## Based on 2000 bootstrap replicates
##
## CALL :
## boot.ci(boot.out = boot_cor_comun, type = "perc")
##
## Intervals :
## Level Percentile
## 95% (-0.1063, 0.4207 )
## Calculations and Intervals on Original Scale
# ---------- Visualización opcional ----------
hist(boot_cor_comun$t,
main = "Distribución bootstrap (palabras en común)",
xlab = "r",
col = "dodgerblue4",
border = "white")
El análisis bootstrap de la correlación entre frecuencias relativas confirma diferencias importantes entre los discursos. Al considerar todo el vocabulario, el intervalo de confianza del 95% para la correlación de Pearson fue negativo y significativo (−0.2438, −0.0738), lo que indica una tendencia inversa: las palabras más frecuentes en un discurso tienden a ser poco frecuentes en el otro. Esto respalda la idea de narrativas divergentes.
En contraste, al analizar solo las palabras en común, el intervalo bootstrap (−0.1063, 0.4207) incluye el cero, lo que sugiere que no hay una relación consistente en su frecuencia relativa. Es decir, incluso entre términos compartidos, el uso y la importancia discursiva varían entre ambos presidentes.
Frente a la prueba de Pearson tradicional, el enfoque bootstrap ofrece una estimación más robusta al no depender de supuestos estrictos. En conjunto, los resultados confirman que los discursos de Petro y Duque responden a prioridades y estilos claramente diferenciados.
A cada palabra (token simple o unigrama) se le asigna un puntaje de sentimiento, que puede representar una escala cuantitativa, una polaridad (positiva/negativa) o una categoría emocional específica.
El sentimiento del texto se define como la suma del puntaje de sus palabras individuales, según un diccionario predefinido.
##### Diccionarios de sentimiento ----
# Tidytext incluye tres diccionarios en inglés:
# - AFINN: escala de −5 a +5. [http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6010]
# - Bing: clasificación binaria (+ / −). [https://www.cs.uic.edu/~liub/FBS/sentiment-analysis.html]
# - NRC: clasificación binaria y categorías emocionales. [http://saifmohammad.com/WebPages/NRC-Emotion-Lexicon.htm]
# Diccionarios en español (no incluidos por defecto en tidytext)
# Fuente alternativa: https://www.kaggle.com/datasets/rtatman/sentiment-lexicons-for-81-languages
# ---------- Cargar diccionarios en español ----------
positive_words <- read_csv("positive_words_es.txt", col_names = "word", show_col_types = FALSE) %>%
mutate(sentiment = "Positivo")
negative_words <- read_csv("negative_words_es.txt", col_names = "word", show_col_types = FALSE) %>%
mutate(sentiment = "Negativo")
# Unir ambos diccionarios
sentiment_words <- bind_rows(positive_words, negative_words)
# ---------- Comparar tamaños de los diccionarios ----------
get_sentiments("bing") %>%
count(sentiment)
## # A tibble: 2 × 2
## sentiment n
## <chr> <int>
## 1 negative 4781
## 2 positive 2005
## # A tibble: 2 × 2
## sentiment n
## <chr> <int>
## 1 Negativo 2720
## 2 Positivo 1555
###### Visualización de palabras con carga emocional ----
suppressMessages(suppressWarnings(library(RColorBrewer)))
# ---------- Discurso de Gustavo Petro ----------
text_petro %>%
inner_join(sentiment_words) %>%
count(word, sentiment, sort = TRUE) %>%
filter(n > 2) %>%
mutate(n = ifelse(sentiment == "Negativo", -n, n)) %>% # Valores negativos para palabras negativas
mutate(word = reorder(word, n)) %>%
ggplot(aes(x = word, y = n, fill = sentiment)) +
geom_col() +
scale_fill_manual(values = brewer.pal(8, "Dark2")[c(2, 5)]) +
coord_flip(ylim = c(-7, 7)) +
labs(
title = "Petro: Conteo por sentimiento",
y = "Frecuencia",
x = NULL
) +
theme_minimal() -> p1
## Joining with `by = join_by(word)`
# ---------- Discurso de Iván Duque ----------
text_duque %>%
inner_join(sentiment_words) %>%
count(word, sentiment, sort = TRUE) %>%
filter(n > 2) %>%
mutate(n = ifelse(sentiment == "Negativo", -n, n)) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(x = word, y = n, fill = sentiment)) +
geom_col() +
scale_fill_manual(values = brewer.pal(8, "Dark2")[c(2, 5)]) +
coord_flip(ylim = c(-7, 7)) +
labs(
title = "Duque: Conteo por sentimiento",
y = "Frecuencia",
x = NULL
) +
theme_minimal() -> p2
## Joining with `by = join_by(word)`
##### Visualización comparativa de sentimientos con nubes de palabras ----
suppressMessages(suppressWarnings(library(reshape2))) # para acast()
# Configuración del área de gráficos
par(mfrow = c(1, 2), mar = c(1, 1, 2, 1), mgp = c(1, 1, 1))
# ---------- Discurso de Gustavo Petro ----------
set.seed(123)
text_petro %>%
inner_join(sentiment_words) %>%
count(word, sentiment, sort = TRUE) %>%
acast(word ~ sentiment, value.var = "n", fill = 0) %>%
comparison.cloud(
colors = brewer.pal(8, "Dark2")[c(2, 5)],
max.words = 50,
title.size = 1.5
)
## Joining with `by = join_by(word)`
title(main = "Petro")
# ---------- Discurso de Iván Duque ----------
set.seed(123)
text_duque %>%
inner_join(sentiment_words) %>%
count(word, sentiment, sort = TRUE) %>%
acast(word ~ sentiment, value.var = "n", fill = 0) %>%
comparison.cloud(
colors = brewer.pal(8, "Dark2")[c(2, 5)],
max.words = 50,
title.size = 1.5
)
## Joining with `by = join_by(word)`
La visualización de sentimiento revela diferencias claras en el tono emocional de los discursos de Petro y Duque. En el caso de Petro, predominan las palabras con carga negativa, como “culpable”, “desastre”, “irracional”, “destruir” y “muerte”, lo que sugiere un enfoque crítico y de denuncia. Estas palabras reflejan un discurso orientado a problematizar la situación actual, señalar causas estructurales y enfatizar las consecuencias humanas y ambientales de políticas pasadas. Aunque también se identifican palabras positivas, como “paz” y “belleza”, su frecuencia es limitada en comparación con los términos negativos, lo que refuerza un marco narrativo de urgencia y advertencia.
En contraste, el discurso de Duque está dominado por palabras con carga positiva, como “paz”, “compromiso”, “reforma” y “logros”. Esto indica un tono más institucional y propositivo, enfocado en resaltar avances, metas alcanzadas y acciones de gobierno. Las pocas palabras negativas que aparecen, como “crisis” y “puesto”, tienen una frecuencia menor y no estructuran el discurso de manera central. En conjunto, el lenguaje de Duque transmite una visión orientada a resultados, con énfasis en la gestión y el progreso.
Ambos discursos coinciden en destacar la palabra “paz” como un elemento positivo clave, aunque el contexto semántico y narrativo en el que se inserta difiere. Mientras que Petro la articula dentro de un llamado a transformar las causas profundas del conflicto, Duque la presenta como parte de los logros institucionales. La diferencia en la carga emocional refleja no solo estilos comunicativos distintos, sino también posicionamientos políticos y estratégicos diferenciados frente al auditorio internacional.
Hasta ahora se ha utilizado unnest_tokens
para tokenizar
por palabras individuales. A continuación, se aplica la tokenización por
secuencias de palabras (bigramas), con el objetivo de
identificar patrones de co-ocurrencia.
Este enfoque permite responder preguntas como: ¿Qué palabras tienden a aparecer juntas? ¿Qué términos suelen seguir a otros en el discurso?
##### Bigramas: ejemplo de juguete ----
# Texto de ejemplo
text <- c(
"Durante 155 años le hemos cumplido a Colombia",
"a los jóvenes de nuestro país",
"al futuro",
"a la educación",
"el conocimiento y a la construcción colectiva de conocimiento en todas las regiones."
)
# Convertir a data frame
text_df <- tibble(
line = seq_along(text),
text = text
)
# Tokenizar en bigramas
text_df %>%
unnest_tokens(input = text, output = bigram, token = "ngrams", n = 2) %>%
head(n = 10)
## # A tibble: 10 × 2
## line bigram
## <int> <chr>
## 1 1 durante 155
## 2 1 155 años
## 3 1 años le
## 4 1 le hemos
## 5 1 hemos cumplido
## 6 1 cumplido a
## 7 1 a colombia
## 8 2 a los
## 9 2 los jóvenes
## 10 2 jóvenes de
##### Importar datos ----
suppressWarnings(
text_petro <- read_csv("discurso_onu_petro.txt",
col_names = FALSE,
show_col_types = FALSE) %>%
unlist(use.names = FALSE) # Convertir a vector simple sin nombres
)
names(text_petro) <- NULL # Eliminar nombres del vector (precaución adicional)
text_petro <- tibble(
line = 1:length(text_petro), # Crear índice de línea
text = text_petro # Asignar texto original
)
##### Tokenizar en bigramas ----
# En este caso, cada token corresponde a una secuencia de dos palabras (bigrama)
text_petro %>%
unnest_tokens(input = text, output = bigram, token = "ngrams", n = 2) %>%
filter(!is.na(bigram)) -> text_petro_bi # Importante: eliminar posibles NAs
# Inspección básica
dim(text_petro_bi)
## [1] 2119 2
## # A tibble: 10 × 2
## line bigram
## <int> <chr>
## 1 1 vengo de
## 2 1 de uno
## 3 1 uno de
## 4 1 de los
## 5 1 los tres
## 6 1 tres países
## 7 1 países más
## 8 1 más bellos
## 9 1 bellos de
## 10 1 de la
###### Top 10 de bigramas más frecuentes ----
# Muchos bigramas no son informativos (e.g., "de la", "en el")
# Esto motiva aplicar nuevamente filtros con stop words
text_petro_bi %>%
count(bigram, sort = TRUE) %>%
head(n = 10)
## # A tibble: 10 × 2
## bigram n
## <chr> <int>
## 1 de la 43
## 2 la selva 21
## 3 a la 17
## 4 de los 11
## 5 la guerra 11
## 6 la humanidad 10
## 7 en la 9
## 8 las drogas 9
## 9 a las 8
## 10 de las 8
##### Omitir stop words y limpiar bigramas ----
text_petro_bi %>%
separate(bigram, c("word1", "word2"), sep = " ") %>%
# Eliminar bigramas con números
filter(!grepl(pattern = '[0-9]', x = word1)) %>%
filter(!grepl(pattern = '[0-9]', x = word2)) %>%
# Eliminar stop words en ambas posiciones
filter(!word1 %in% stop_words_es$word) %>%
filter(!word2 %in% stop_words_es$word) %>%
# Normalizar acentos
mutate(word1 = chartr(
old = names(replacement_list) %>% str_c(collapse = ''),
new = replacement_list %>% str_c(collapse = ''),
x = word1)) %>%
mutate(word2 = chartr(
old = names(replacement_list) %>% str_c(collapse = ''),
new = replacement_list %>% str_c(collapse = ''),
x = word2)) %>%
# Eliminar posibles NAs
filter(!is.na(word1)) %>%
filter(!is.na(word2)) %>%
# Contar combinaciones más frecuentes
count(word1, word2, sort = TRUE) %>%
rename(weight = n) -> text_petro_bi_counts # importante para la conformación de la red
# Inspección de resultados
dim(text_petro_bi_counts)
## [1] 152 3
## # A tibble: 10 × 3
## word1 word2 weight
## <chr> <chr> <int>
## 1 desastre climatico 5
## 2 america latina 4
## 3 selva amazonica 4
## 4 acumulacion ampliada 3
## 5 especie humana 3
## 6 crisis climatica 2
## 7 justicia social 2
## 8 veran morir 2
## 9 abundantes bajan 1
## 10 afectos competitivas 1
##### Definir una red a partir de la frecuencia (weight) de los bigramas ----
# Red binaria, no dirigida, ponderada y simple
# Recomendación: variar el umbral de filtro y explorar bigramas no consecutivos
# para redes más informativas
suppressMessages(suppressWarnings(library(igraph)))
# Crear objeto de red a partir de bigramas con peso mayor a 2
g <- text_petro_bi_counts %>%
filter(weight > 2) %>%
graph_from_data_frame(directed = FALSE)
# Visualización de la red
set.seed(123)
plot(
g,
layout = layout_with_fr,
vertex.color = 1,
vertex.frame.color = 1,
vertex.size = 3,
vertex.label.color = "black",
vertex.label.cex = 1,
vertex.label.dist = 1,
main = "Umbral = 3"
)
##### Red con un umbral diferente ----
# Red binaria, no dirigida, ponderada, con un umbral ajustado
# Este umbral es menor para incluir más relaciones
# Crear la red con un umbral de peso mayor a 0
g <- text_petro_bi_counts %>%
filter(weight > 0) %>%
graph_from_data_frame(directed = FALSE)
# Visualización de la red con layout Kamada-Kawai
set.seed(123)
plot(
g,
layout = layout_with_kk, # Layout Kamada-Kawai para una disposición más atractiva
vertex.color = 1,
vertex.frame.color = 1,
vertex.size = 3,
vertex.label = NA, # No mostrar etiquetas de los vértices
main = "Umbral = 1" # Título con el umbral aplicado
)
##### Componente conexa más grande de la red ----
# Crear la red con un umbral de peso mayor a 0
g <- text_petro_bi_counts %>%
filter(weight > 0) %>%
graph_from_data_frame(directed = FALSE)
# Asignar componentes conexas a los vértices usando components()
V(g)$cluster <- components(graph = g)$membership
# Extraer la componente conexa más grande
gcc <- induced_subgraph(
graph = g,
vids = which(V(g)$cluster == which.max(components(graph = g)$csize))
)
# Configuración de gráficos para mostrar dos visualizaciones
par(mfrow = c(1, 2), mar = c(1, 1, 2, 1), mgp = c(1, 1, 1))
# Visualización 1: Componente conexa sin peso ajustado
set.seed(123)
plot(
gcc,
layout = layout_with_kk,
vertex.color = 1,
vertex.frame.color = 1,
vertex.size = 3,
vertex.label.color = 'black',
vertex.label.cex = 0.9,
vertex.label.dist = 1
)
# Visualización 2: Componente conexa con tamaño de vértice por fuerza y ancho de borde por peso
set.seed(123)
plot(
gcc,
layout = layout_with_kk,
vertex.color = adjustcolor('darkolivegreen4', 0.1),
vertex.frame.color = 'darkolivegreen4',
vertex.size = 2 * strength(gcc),
vertex.label.color = 'black',
vertex.label.cex = 0.9,
vertex.label.dist = 1,
edge.width = 3 * E(g)$weight / max(E(g)$weight)
)
# Título para la visualización completa
title(main = "Componente conexa", outer = TRUE, line = -1)
La red de la componente conexa extraída del discurso de Petro destaca temas clave, como la crisis ecológica y la Amazonía. Conexiones como “selva” y “amazonica” reflejan la centralidad de la Amazonía en su discurso sobre el cambio climático, tratándola como un elemento vital para la salvación del planeta.
La red también subraya la relación entre la “coca” y “planta”, señalando la problemática de la producción de cocaína y sus impactos en la región. Además, “arrojan” y “venenos”, junto con “glifosato”, reflejan la crítica a las políticas que usan sustancias nocivas en la erradicación de cultivos ilícitos.
Petro critica las políticas actuales, evidenciado en conexiones como “prefieren” y “sirve” o “arrojarle” y “venenos”, donde destaca la ineficacia y los efectos dañinos de estas decisiones. Además, la red resalta la visión de “planta” y “selvatica”, vinculando la naturaleza a un concepto sagrado que debe ser protegido de políticas extractivas.
La red muestra que el discurso de Petro se enfoca en la crisis ambiental, la lucha contra las drogas y la crítica a las políticas destructivas, llamando a la acción para preservar los ecosistemas y promover una política más justa y transformadora.
En esta sección, se analiza el uso de skipgramas (técnica que identifica relaciones semánticas entre palabras no contiguas), una metodología que captura conexiones entre palabras separadas por términos intermedios. A diferencia de los bigramas, los skipgramas revelan patrones más complejos en el discurso. Este enfoque permite entender mejor las relaciones lingüísticas y cómo se articulan conceptos clave a lo largo del texto.
##### skipgrama: ejemplo de juguete ----
# Texto de ejemplo
text <- c(
"Durante 155 años le hemos cumplido a Colombia",
"a los jóvenes de nuestro país",
"al futuro",
"a la educación",
"el conocimiento y a la construcción colectiva de conocimiento en todas las regiones."
)
# Convertir el texto a un data frame
text_df <- tibble(line = 1:length(text), text = text)
# Tokenizar en skipgramaas (relaciones entre palabras no contiguas)
text_df %>%
unnest_tokens(input = text, output = skipgram, token = "skip_ngrams", n = 2) %>%
head(n = 10) # Mostrar los primeros 10 resultados
## # A tibble: 10 × 2
## line skipgram
## <int> <chr>
## 1 1 durante
## 2 1 durante 155
## 3 1 durante años
## 4 1 155
## 5 1 155 años
## 6 1 155 le
## 7 1 años
## 8 1 años le
## 9 1 años hemos
## 10 1 le
##### Importar datos ----
# Leer el archivo CSV y convertirlo a un vector sin nombres
suppressWarnings({
text_petro <- read_csv("discurso_onu_petro.txt", col_names = FALSE, show_col_types = FALSE) %>%
unlist(use.names = FALSE)
})
# Eliminar nombres de las columnas en el vector
names(text_petro) <- NULL
# Convertir el vector a un tibble con índices de línea
text_petro <- tibble(line = 1:length(text_petro), text = text_petro)
##### Tokenizar en skipgrama ----
# En este caso, cada token es un unigrama, bigrama regular o bigrama con espaciamiento
text_petro %>%
unnest_tokens(input = text, output = skipgram, token = "skip_ngrams", n = 2) %>%
filter(!is.na(skipgram)) -> text_petro_skip # Filtrar NA y almacenar en text_petro_skip
# Ver dimensiones del resultado
dim(text_petro_skip)
## [1] 6357 2
## # A tibble: 10 × 2
## line skipgram
## <int> <chr>
## 1 1 vengo
## 2 1 vengo de
## 3 1 vengo uno
## 4 1 de
## 5 1 de uno
## 6 1 de de
## 7 1 uno
## 8 1 uno de
## 9 1 uno los
## 10 1 de
##### Remover unigramas ----
suppressMessages(suppressWarnings(library(ngram)))
# Contar palabras en cada skipgrama
text_petro_skip$num_words <- text_petro_skip$skipgram %>%
map_int(.f = ~ wordcount(.x))
# Ver los primeros 10 resultados
head(text_petro_skip, n = 10)
## # A tibble: 10 × 3
## line skipgram num_words
## <int> <chr> <int>
## 1 1 vengo 1
## 2 1 vengo de 2
## 3 1 vengo uno 2
## 4 1 de 1
## 5 1 de uno 2
## 6 1 de de 2
## 7 1 uno 1
## 8 1 uno de 2
## 9 1 uno los 2
## 10 1 de 1
# Filtrar y remover unigramas (solo mantener bigramas)
text_petro_skip %<>%
filter(num_words == 2) %>% # Mantener solo bigramas
select(-num_words) # Eliminar la columna de conteo de palabras
# Ver las dimensiones y los primeros 10 resultados después del filtrado
dim(text_petro_skip)
## [1] 4195 2
## # A tibble: 10 × 2
## line skipgram
## <int> <chr>
## 1 1 vengo de
## 2 1 vengo uno
## 3 1 de uno
## 4 1 de de
## 5 1 uno de
## 6 1 uno los
## 7 1 de los
## 8 1 de tres
## 9 1 los tres
## 10 1 los países
##### Omitir stop words ----
text_petro_skip %>%
# Separar los bigramas en dos columnas (word1 y word2)
separate(skipgram, c("word1", "word2"), sep = " ") %>%
# Filtrar palabras con números
filter(!grepl(pattern = '[0-9]', x = word1)) %>%
filter(!grepl(pattern = '[0-9]', x = word2)) %>%
# Filtrar stop words (eliminando palabras comunes en español)
filter(!word1 %in% stop_words_es$word) %>%
filter(!word2 %in% stop_words_es$word) %>%
# Remover acentos de las palabras
mutate(word1 = chartr(
old = names(replacement_list) %>% str_c(collapse = ''),
new = replacement_list %>% str_c(collapse = ''),
x = word1
)) %>%
mutate(word2 = chartr(
old = names(replacement_list) %>% str_c(collapse = ''),
new = replacement_list %>% str_c(collapse = ''),
x = word2
)) %>%
# Eliminar valores NA en word1 y word2
filter(!is.na(word1)) %>%
filter(!is.na(word2)) %>%
# Contar frecuencia de cada par de palabras (bigrama)
count(word1, word2, sort = TRUE) %>%
# Renombrar la columna de conteo a "weight"
rename(weight = n) -> text_petro_skip_counts
# Ver dimensiones y primeros 10 resultados
dim(text_petro_skip_counts)
## [1] 505 3
## # A tibble: 10 × 3
## word1 word2 weight
## <chr> <chr> <int>
## 1 desastre climatico 5
## 2 america latina 4
## 3 selva amazonica 4
## 4 acumulacion ampliada 3
## 5 adiccion dinero 3
## 6 especie humana 3
## 7 salvar selva 3
## 8 acabar guerra 2
## 9 arrojarle venenos 2
## 10 crisis climatica 2
##### Definir una red a partir de la frecuencia (weight) de los bigramas ----
# Filtrar bigramas con peso mayor a 0 y crear un grafo no dirigido
g <- text_petro_skip_counts %>%
filter(weight > 0) %>%
graph_from_data_frame(directed = FALSE)
# Simplificar el grafo (eliminar bucles y aristas redundantes)
g <- igraph::simplify(g) # Importante para limpiar el grafo
# Crear una componente conexa (subgrafo más grande)
V(g)$cluster <- clusters(graph = g)$membership
## Warning: `clusters()` was deprecated in igraph 2.0.0.
## ℹ Please use `components()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
gcc <- induced_subgraph(graph = g, vids = which(V(g)$cluster == which.max(clusters(graph = g)$csize)))
# Configurar la visualización en dos paneles
par(mfrow = c(1, 2), mar = c(1, 1, 2, 1), mgp = c(1, 1, 1))
# Viz 1: Grafo básico
set.seed(123)
plot(gcc, layout = layout_with_fr,
vertex.color = 1,
vertex.frame.color = 1,
vertex.size = 3,
vertex.label = NA)
# Viz 2: Grafo con tamaño de vértices ajustado por fuerza
set.seed(123)
plot(gcc, layout = layout_with_fr,
vertex.color = adjustcolor('darkolivegreen4', 0.1),
vertex.frame.color = 'darkolivegreen4',
vertex.size = 2 * strength(gcc),
vertex.label = NA)
# Añadir título
title(main = "Componente conexa", outer = TRUE, line = -1)
Los resultados de los skipgramas muestran pares de palabras (bigrama) que aparecen frecuentemente juntas en el discurso de Petro, con un énfasis particular en temas como el desastre climático, la selva amazónica, y las crisis ambientales. Las palabras con alta frecuencia de co-ocurrencia, como “desastre climático”, “selva amazónica” y “justicia social”, reflejan los temas centrales del discurso, enfocados en la urgencia ambiental, la justicia social, y las acciones necesarias para salvar el planeta.
Además, hay una notable presencia de palabras relacionadas con el conflicto (e.g., “guerra”, “venenos”, “coca”), lo que subraya la crítica a las políticas previas, especialmente aquellas vinculadas al narcotráfico y sus impactos negativos. Otros pares de palabras, como “salvar selva” o “planta amazonica”, refuerzan la importancia de proteger el medio ambiente.
Los resultados revelan un discurso que articula de manera continua los problemas ambientales, conflictos sociales y la crisis global, con un fuerte llamado a la acción para abordar estos desafíos.
En este análisis, se comparan los discursos pronunciados por los presidentes Gustavo Petro e Iván Duque en la Asamblea General de la ONU. Utilizando la técnica de skipgramas, se exploran las relaciones semánticas entre las palabras en los discursos, lo que permite identificar patrones de co-ocurrencia más complejos. Para representar estos patrones, se construyó una red basada en los skipgramas, utilizando un umbral de frecuencia de 1 para obtener la componente conexa del grafo, lo que nos proporciona una visión detallada de las conexiones lingüísticas y temáticas en ambos discursos.
Los resultados de los skipgramas para los discursos de Petro y Duque muestran diferentes enfoques en sus discursos. En el caso de Petro, las palabras más frecuentes tienden a resaltar temas como crisis climática, desastres ambientales, y la necesidad de salvación de la naturaleza y la humanidad. Por ejemplo, las combinaciones “desastre climático”, “selva amazónica”, y “salvar selva” reflejan una fuerte preocupación por los efectos negativos del cambio climático y la deforestación. También se destacan términos relacionados con la lucha contra la adición y los problemas sociales, como “adicción dinero” y “crisis climática”, lo que sugiere una crítica hacia las causas estructurales de los problemas ambientales y sociales.
En contraste, el discurso de Duque está más enfocado en acciones políticas, reformas sociales y fuerzas de seguridad. Los skipgramas más comunes incluyen combinaciones como “acción climática”, “fuerza pública”, “paz legalidad”, lo que refleja un enfoque más institucional y orientado a la gobernanza, con un énfasis en la seguridad y el desarrollo económico. También se observa un énfasis en la agenda social y en el desarrollo sostenible, con términos como “derechos humanos” y “desarrollo sostenible”, lo que indica un discurso centrado en la cooperación internacional y las políticas de bienestar.
Los resultados muestran una diferencia clave en la orientación de ambos discursos. Petro utiliza un lenguaje que se centra en los problemas globales y ambientales, con una crítica directa a las consecuencias de las políticas actuales, mientras que Duque presenta un enfoque más institucional, centrado en reformas y desarrollo dentro de un marco de gobernanza. Ambas visiones, aunque diferentes, resaltan la importancia de temas como la paz y la sostenibilidad, pero desde perspectivas y enfoques estratégicos muy distintos.
## Warning: `clique.number()` was deprecated in igraph 2.0.0.
## ℹ Please use `clique_num()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Petro Duque
## Dist. media 7.7718 7.6044
## Grado media 2.4094 2.4541
## Grado desviación 1.8780 2.0729
## Número clan 3.0000 3.0000
## Densidad 0.0071 0.0059
## Transitividad 0.0863 0.1107
## Asortatividad -0.0474 -0.0693
Los resultados de las métricas para las redes de skipgramas de los discursos de Petro y Duque muestran varias diferencias clave:
Distancia media: La distancia media de las redes de ambos presidentes es similar, con Petro (7.77) y Duque (7.60). Esto indica que, en promedio, los nodos (palabras) en ambas redes están igualmente distantes entre sí.
Grado medio: El grado medio es ligeramente más alto en la red de Duque (2.45) en comparación con la de Petro (2.41), lo que sugiere que, en promedio, las palabras en el discurso de Duque están más conectadas (es decir, tienen más co-ocurrencias) que las de Petro.
Desviación estándar del grado: La desviación estándar del grado en la red de Duque (2.07) es mayor que en la de Petro (1.88), lo que indica que la red de Duque tiene una mayor variabilidad en la cantidad de conexiones entre las palabras, mientras que en la red de Petro las conexiones son más homogéneas.
Número de clanes: Ambos discursos presentan 3 clanes, lo que significa que las redes de Petro y Duque se dividen de manera similar en 3 grupos de palabras más conectadas entre sí.
Densidad: La densidad de la red de Petro (0.0071) es ligeramente mayor que la de Duque (0.0059), lo que sugiere que la red de Petro tiene más conexiones en relación con el número total de posibles conexiones, aunque ambas redes tienen una densidad baja, lo que indica redes con pocas conexiones entre todas las palabras posibles.
Transitividad: La transitividad en la red de Duque (0.1107) es mayor que en la de Petro (0.0863), lo que implica que en la red de Duque es más probable que las palabras conectadas a una misma palabra también estén conectadas entre sí, indicando una estructura algo más cohesiva.
Asortatividad: Ambas redes muestran valores negativos en la asortatividad (-0.0474 para Petro y -0.0693 para Duque), lo que indica que las palabras con un alto número de conexiones tienden a conectarse con palabras con un bajo número de conexiones, aunque la red de Duque muestra una asortatividad más negativa, lo que sugiere una desconexión más marcada entre palabras altamente conectadas y menos conectadas.
Las redes de ambos presidentes muestran estructuras de co-ocurrencias similares en cuanto a la división en clanes y la distancia media entre palabras, pero la red de Duque tiene una mayor variabilidad en las conexiones, una mayor transitividad y una asortatividad más negativa.
Las palabras más importantes en una red de texto se pueden identificar mediante la centralidad de eigenvector, un indicador clave en la teoría de grafos que mide la influencia de un nodo dentro de una red. En el contexto de un análisis de texto, cada palabra se trata como un nodo en una red, y las conexiones entre ellas (en este caso, los skipgramas) son representadas como enlaces. La centralidad de eigenvector evalúa no solo la cantidad de conexiones directas que tiene una palabra, sino también la importancia de las palabras a las que está conectada. De este modo, las palabras más importantes son aquellas que están conectadas a otras palabras igualmente influyentes dentro de la red, lo que les otorga una centralidad alta. Este enfoque permite identificar términos clave que tienen un impacto significativo en el discurso o contexto analizado.
Petro
## # A tibble: 10 × 2
## word eigen
## <chr> <dbl>
## 1 selva 1
## 2 amazonica 0.779
## 3 salvar 0.752
## 4 planta 0.299
## 5 integralmente 0.237
## 6 vida 0.231
## 7 humanidad 0.212
## 8 destruir 0.180
## 9 mundo 0.169
## 10 morir 0.166
Duque
## # A tibble: 10 × 2
## word eigen
## <chr> <dbl>
## 1 accion 1
## 2 colombia 0.948
## 3 climatica 0.921
## 4 avanza 0.900
## 5 frente 0.379
## 6 compromiso 0.334
## 7 estructural 0.280
## 8 historia 0.245
## 9 internacional 0.225
## 10 crisis 0.219
Los resultados de la centralidad de eigenvector para los discursos de Petro y Duque muestran las palabras más influyentes dentro de sus respectivas redes de skipgramas.
Para Petro, las palabras con mayor centralidad son “selva”, “amazonica” y “salvar”, lo que indica que estos términos tienen un alto impacto dentro del discurso, siendo centrales en la red. Estas palabras están fuertemente conectadas con otras palabras influyentes como “planta” y “humanidad”, reflejando la importancia de la naturaleza y la salvación en el contexto del mensaje.
En el caso de Duque, las palabras clave con mayor centralidad son “accion”, “colombia” y “climatica”, lo que señala que el discurso gira alrededor de la acción política, el país y el cambio climático. Estas palabras clave están relacionadas con temas de compromiso y crisis, lo que resalta los enfoques de reforma y desafíos globales dentro del mensaje.
Petro se enfoca más en temas ambientales y de salvación, mientras que Duque enfatiza la acción política y los desafíos sociales y climáticos.
A continuación se presenta el análisis de agrupamiento aplicado a los discursos de los presidentes Petro y Duque, con el objetivo de identificar los principales temas que emergen de sus intervenciones. Utilizando técnicas de análisis de redes y la asignación de palabras a clusters, se agrupan los términos más relevantes de cada discurso según su co-ocurrencia en los skipgramas. Este enfoque permite observar de manera clara cómo se estructuran los discursos de ambos presidentes y cuáles son los conceptos clave en los que centran su atención. A través del clustering, se puede explorar la distribución de las palabras en torno a diferentes temas, proporcionando una visión más profunda de las prioridades discursivas de cada mandatario y su estrategia comunicativa en contextos nacionales e internacionales.
En el análisis de los resultados del clustering de palabras en los discursos de los presidentes Petro y Duque, se observa una clara distinción en los temas centrales que abordan en sus intervenciones. Para Petro, los términos más relevantes se agrupan principalmente en torno a temas de protección ambiental y justicia social. Las palabras como selva, amazonica, salvar, vida y humanidad están agrupadas en el cluster 1, lo que indica que Petro focaliza su discurso en la defensa del medio ambiente y la protección de la humanidad frente a las crisis ecológicas. Este grupo temático refleja su enfoque en la sostenibilidad y en la urgencia de salvar la naturaleza. Por otro lado, palabras como acumulacion, planta y actuar pertenecen al cluster 18, sugiriendo que Petro también aborda la economía y los procesos sociales, aunque estos temas no son tan prominentes como los ecológicos. Además, términos como destruir y mundo están distribuidos en clusters más dispersos, lo que podría reflejar la amplitud de los temas sobre los cuales trata de generar conciencia, abarcando tanto cuestiones globales como locales.
En el caso de Duque, el clustering de palabras muestra una organización centrada en temas de acción política, paz y desarrollo económico. Palabras como accion, cambio, crisis y colombia están agrupadas en el cluster 7, destacando su énfasis en la gestión de crisis y la acción política para transformar la realidad del país. Este cluster refleja el foco de Duque en la gestión de la pandemia y otros desafíos sociales. Un segundo conjunto de palabras como desarrollo, paz, objetivos y construccion se encuentra en el cluster 15, lo que indica que el presidente también pone un fuerte énfasis en temas de desarrollo social y paz. Por otro lado, palabras como derechos, economico, fondo y comunidad pertenecen al cluster 12, reflejando su enfoque en la economía y las políticas públicas orientadas a mejorar la situación social y económica del país. Finalmente, algunas palabras como migrantes, pandemia y emociones están agrupadas en clusters que evidencian su interés en los desafíos sociales y humanitarios que atraviesa la nación.
En resumen, los discursos de Petro y Duque reflejan diferentes prioridades y enfoques temáticos. Petro se centra en la protección del medio ambiente y temas humanitarios, mientras que Duque pone énfasis en la acción política, desarrollo económico y la gestión de crisis. Los clusters identificados permiten una comprensión más clara de cómo ambos presidentes estructuran sus mensajes y abordan los principales problemas nacionales.