En esta actividad se realiza un análisis de minería de texto sobre la obra Don Quijote de la Mancha. El propósito es examinar las relaciones entre palabras, bigramas y patrones lingüísticos que reflejan las estructuras semánticas del texto.
##Carga de librerías Se cargan las librerías necesarias para la ejecución del análisis.
library(pdftools)
library(dplyr)
library(tidytext)
library(stringr)
library(ggplot2)
library(tidyr)
library(widyr)
library(ggraph)
library(igraph)
library(stopwords)
library(wordcloud)
library(RColorBrewer)
library(tidygraph)
# Lectura de los PDF
texto01 <- pdf_text("DONQUIJOTE_PARTE1.pdf")
texto02 <- pdf_text("DONQUIJOTE_PARTE2.pdf")
# Unir las páginas dentro de cada parte
texto01 <- paste(texto01, collapse = " ")
texto02 <- paste(texto02, collapse = " ")
Se realiza una limpieza de los textos en cada documento, donde se efectuan las siguientes accioens:
Eliminación de contenido no literario
Se eliminan enlaces, nombres editoriales y frases fuera del cuerpo narrativo.
Limpieza de caracteres de control
Normalización de signos y puntuación
Ajuste de estructura textual
Formato final y espaciado
# Función de limpieza
limpiar_texto <- function(texto) {
# Se realizan la siguiente limpieza en el texto.
# Frases de origen
texto <- gsub("http://www\\.educa\\.jcyl\\.es", "", texto)
texto <- gsub("Portal Educativo EducaCYL", "", texto)
# Saltos y caracteres de control
texto <- gsub("\f", " ", texto)
texto <- gsub("\\r|\\n", " ", texto)
# Sustitución y normalización
texto <- gsub("—|–", " ", texto)
texto <- gsub("\\.{2,}", ".", texto)
texto <- gsub("\\d+", "", texto)
# Comillas, apóstrofos y espacios
texto <- gsub("[‘’´`]", "'", texto)
texto <- gsub("[“”]", "\"", texto)
texto <- gsub("\\(\\s+", "(", texto)
texto <- gsub("\\s+\\)", ")", texto)
texto <- gsub("\"\\s+", "\"", texto)
texto <- gsub("\\s+\"", "\"", texto)
# Encabezados y títulos
texto <- gsub("Miguel de Cervantes Saavedra", "", texto)
#texto <- gsub("El Ingenioso Hidalgo Don Quijote de la Mancha", "", texto)
texto <- gsub("PRIMERA PARTE|SEGUNDA PARTE", "\n", texto)
texto <- gsub("CAP[IÍ]TULO\\s*\\d+:?", "\n\nCAPÍTULO ", texto)
# Paréntesis y puntuación
texto <- gsub("\\(\\)", "", texto)
texto <- gsub("\\(.*?\\)", "", texto)
texto <- gsub("\\s+([,;:.!?])", "\\1", texto)
# Limpieza de frases editoriales
texto <- gsub("Cuenta Cide Hamete Benengeli.*?historia", "", texto)
# Espacios múltiples y formato final
texto <- gsub("\\s{2,}", " ", texto)
texto <- trimws(texto)
# Saltos de línea entre oraciones
texto <- gsub("([.!?])\\s+", "\\1\n", texto)
return(texto)
}
# Se aplica limpieza a cada parte
texto01_limpio <- limpiar_texto(texto01)
texto02_limpio <- limpiar_texto(texto02)
# Se unen las partes limpias ===
texto <- paste(texto01_limpio, texto02_limpio, collapse = "\n\n")
# Se separa el texto por oraciones y crear un DataFrame
frases <- unlist(strsplit(texto, "\\."))
df_texto <- data.frame(frase = frases, stringsAsFactors = FALSE)
# Se limpian espacios al inicio y final de cada frase
df_texto$frase <- stringr::str_trim(df_texto$frase)
# Stopwords en español
stopwords_es <- stopwords("es")
# Tokenización
df_tokens <- df_texto %>%
unnest_tokens(word, frase) %>%
filter(!word %in% stopwords_es,
str_detect(word, "[a-záéíóúñ]"))
word_counts <- df_tokens %>%
count(word, sort = TRUE)
ggplot(head(word_counts, 20), aes(x = reorder(word, n), y = n)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = n), hjust = -0.1, size = 3) + # Etiquetas
coord_flip() +
labs(
title = "Palabras más frecuentes (sin stopwords)",
x = "Palabra",
y = "Frecuencia"
) +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) + # Centrar título
expand_limits(y = max(head(word_counts$n, 20)) * 1.1) # Deja espacio para etiquetas
set.seed(123)
wordcloud(words = word_counts$word,
freq = word_counts$n,
min.freq = 20,
max.words = 200,
random.order = FALSE,
rot.per = 0.3,
colors = brewer.pal(8, "Dark2"))
bigrams <- df_texto %>%
unnest_tokens(bigram, frase, token = "ngrams", n = 2) %>%
separate(bigram, into = c("word1", "word2"), sep = " ") %>%
filter(!word1 %in% stopwords_es,
!word2 %in% stopwords_es) %>%
count(word1, word2, sort = TRUE)
head(bigrams, 15)
## word1 word2 n
## 1 don quijote 2186
## 2 dijo don 310
## 3 respondió sancho 310
## 4 sancho panza 279
## 5 respondió don 267
## 6 dijo sancho 246
## 7 vuesa merced 245
## 8 señor don 183
## 9 caballeros andantes 131
## 10 caballero andante 116
## 11 don fernando 114
## 12 sancho dijo 106
## 13 merced señor 100
## 14 señora dulcinea 96
## 15 muchas veces 81
# Filtrar bigramas más frecuentes
bigrams_grafo <- bigrams %>%
filter(n > 100) %>%
graph_from_data_frame()
ggraph(bigrams_grafo, layout = "fr") +
geom_edge_link(aes(edge_alpha = n, edge_width = n), color = "darkcyan") +
geom_node_point(size = 5, color = "lightblue") +
geom_node_text(aes(label = name), repel = TRUE) +
theme_void() +
labs(title = "Red de bigramas más frecuentes")
# Crear grupos de 10 frases para mejorar las coocurrencias
df_tokens_coocurencia_grupos <- df_texto %>%
mutate(grupo = rep(1:ceiling(nrow(df_texto)/10), each = 10, length.out = nrow(df_texto))) %>%
unnest_tokens(word, frase) %>%
filter(!word %in% stopwords_es,
str_detect(word, "[a-záéíóúñ]"))
# Se Calculan las coocurrencias por grupo
word_pairs <- df_tokens_coocurencia_grupos %>%
pairwise_count(word, grupo, sort = TRUE)
# Se muestran los pares con más de 100 coocurrencias
head(word_pairs[word_pairs$n > 100, ], 15)
## # A tibble: 15 × 3
## item1 item2 n
## <chr> <chr> <dbl>
## 1 dijo si 610
## 2 si dijo 610
## 3 quijote don 572
## 4 don quijote 572
## 5 dijo don 553
## 6 don dijo 553
## 7 si don 549
## 8 don si 549
## 9 dijo quijote 518
## 10 quijote dijo 518
## 11 si quijote 516
## 12 quijote si 516
## 13 si así 515
## 14 así si 515
## 15 dijo así 507
# Se crean grupos de 10 frases para analizar coocurrencias en segmentos más largos
df_tokens_grupos <- df_texto %>%
mutate(grupo = rep(1:ceiling(nrow(df_texto)/10),
each = 10, length.out = nrow(df_texto))) %>%
unnest_tokens(word, frase) %>%
filter(!word %in% stopwords_es,
str_detect(word, "[a-záéíóúñ]"))
# Se calcula la correlación entre palabras (phi de coocurrencia)
word_cors <- df_tokens_grupos %>%
group_by(word) %>%
filter(n() >= 20) %>% # solo palabras con al menos 20 apariciones
pairwise_cor(word, grupo, sort = TRUE)
# Se muestran las correlaciones más fuertes
head(word_cors, 15)
## # A tibble: 15 × 3
## item1 item2 correlation
## <chr> <chr> <dbl>
## 1 hamete cide 0.983
## 2 cide hamete 0.983
## 3 camila lotario 0.965
## 4 lotario camila 0.965
## 5 marcela grisóstomo 0.953
## 6 grisóstomo marcela 0.953
## 7 camila anselmo 0.923
## 8 anselmo camila 0.923
## 9 tosilos lacayo 0.903
## 10 lacayo tosilos 0.903
## 11 lotario anselmo 0.890
## 12 anselmo lotario 0.890
## 13 quijote don 0.844
## 14 don quijote 0.844
## 15 sansón carrasco 0.791
En el siguiente código se presenta una visualización de las 15 correlaciones más altas entre palabras. Tal como se identificó previamente en el análisis, los términos “hamete” y “cide” destacan como el par con la correlación más fuerte, evidenciando su estrecha relación contextual dentro de la narrativa.
# Seleccionamos los pares con mayor correlación
word_cors_top <- word_cors %>%
filter(correlation > 0.25) %>%
arrange(desc(correlation)) %>%
head(15)
# Graficar correlaciones como puntos
ggplot(word_cors_top, aes(x = reorder(paste(item1, item2, sep = " - "), correlation),
y = correlation)) +
geom_point(color = "darkgreen", size = 3) +
coord_flip() +
labs(title = "Pares de palabras con mayor correlación",
x = "Par de palabras", y = "Correlación (phi)") +
theme_minimal(base_size = 13)
# Función auxiliar para graficar correlaciones
graficar_red <- function(data, titulo, subtitulo) {
set.seed(123)
data %>%
graph_from_data_frame() %>%
ggraph(layout = "fr") +
geom_edge_link(aes(edge_alpha = correlation, edge_width = correlation), color = "darkgreen") +
geom_node_point(size = 4, color = "lightgreen") +
geom_node_text(aes(label = name), repel = TRUE, size = 3) +
theme_void() +
labs(title = titulo,
subtitle = subtitulo,
caption = "Fuente: Análisis de texto del Don Quijote de la Mancha") +
theme(plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 11))
}
# Se filtra y grafica el TOP 15 de correlaciones
word_cors_top15 <- word_cors %>%
filter(correlation > 0.25) %>%
arrange(desc(correlation)) %>%
slice_head(n = 15)
grafico_top15 <- graficar_red(
word_cors_top15,
titulo = "Red de correlaciones entre palabras",
subtitulo = "Top 15 correlaciones > 0.25"
)
# Se muestra el grafico
print(grafico_top15)
# Se filtra y grafica el TOP 150 de correlaciones
word_cors_top200 <- word_cors %>%
filter(correlation > 0.25) %>%
arrange(desc(correlation)) %>%
slice_head(n = 200)
grafico_top200 <- graficar_red(
word_cors_top200,
titulo = "Red de correlaciones entre palabras",
subtitulo = "Top 150 correlaciones > 0.25"
)
print(grafico_top200)
palabra_base <- "renegado" # Palabra a filtrar
# Se filtran las correlaciones relacionadas con esa palabra
cor_palabra <- word_cors %>%
filter(item1 == palabra_base) %>%
arrange(desc(correlation)) %>%
head(10)
# Gráfico de barras de correlaciones
ggplot(cor_palabra, aes(x = reorder(item2, correlation), y = correlation)) +
geom_col(fill = "steelblue") +
coord_flip() +
labs(title = paste("Palabras más correlacionadas con:", palabra_base),
x = "Palabra asociada",
y = "Correlación (phi)") +
theme_minimal(base_size = 13)
#-----------------------------------------------------------------------
palabra_base <- "hamete" # Palabra a filtrar
# Filtra las correlaciones relacionadas con esa palabra
cor_palabra <- word_cors %>%
filter(item1 == palabra_base) %>%
arrange(desc(correlation)) %>%
head(10)
# Gráfico de barras de correlaciones
ggplot(cor_palabra, aes(x = reorder(item2, correlation), y = correlation)) +
geom_col(fill = "steelblue") +
coord_flip() +
labs(title = paste("Palabras más correlacionadas con:", palabra_base),
x = "Palabra asociada",
y = "Correlación (phi)") +
theme_minimal(base_size = 13)
Técnicas de minería de texto empleadas
En el desarrollo del análisis de “Don Quijote de la Mancha”, se aplicaron diversas técnicas de minería de texto orientadas a extraer patrones, asociaciones y relaciones semánticas entre palabras del texto. Estas metodologías permitieron limpiar, estructurar y representar gráficamente la información contenida en los archivos importados, facilitando la identificación de términos clave y su comportamiento conjunto dentro del texto. A continuación se listan un grupo de procedimientos o técnicas utilizadas:
Tokenización y limpieza: separación del texto en frases y luego en palabras, eliminación de stopwords (palabras sin contexto) y filtrado de tokens con caracteres no alfabéticos.
Frecuencia de palabras y bigramas: conteo de palabras más comunes y de pares de palabras consecutivas (bigramas) para identificar asociaciones frecuentes.
Coocurrencia por grupos: agrupación del texto en bloques de 10 frases y uso de pairwise_count() para contar cuántas veces dos palabras aparecen juntas dentro del mismo bloque.
Correlación de coocurrencia: aplicación de pairwise_cor() para calcular el coeficiente phi entre pares de palabras, identificando aquellas que muestran una asociación estadística más fuerte.
Visualización gráfica: uso de gráficos de barras, nubes de palabras y redes para ilustrar frecuencias y relaciones entre términos.
Análisis de correlaciones entre bigramas
El análisis de correlaciones permite identificar qué palabras dentro del texto tienden a aparecer juntas con mayor fuerza estadística, revelando vínculos semánticos o narrativos relevantes. A través del cálculo del coeficiente phi, se miden asociaciones que van más allá de la simple frecuencia, mostrando patrones consistentes de aparición conjunta entre términos significativos del texto, ya que se encuentra una relación ligada a segmentos del texto lo que permite identificar relaciones narrativas.
En el informe, los pares con mayor correlación phi incluyen “hamete – cide”, “camila – lotario” y otros nombres propios.
Estas correlaciones elevadas indican que esas palabras aparecen consistentemente juntas en los mismos bloques contextuales, casi nunca por separado.
A diferencia de las coocurrencias puras (que sólo cuentan frecuencia conjunta), la correlación ajusta por las frecuencias individuales de cada término, enfatizando asociaciones narrativas.
Conclusiones breves
Las palabras que dominan en el texto reflejan los personajes y temas centrales (ej. don, quijote, sancho, caballero).
Las correlaciones más fuertes entre nombres propios (como hamete con cide) revelan relaciones narrativas más precisas y distintivas que las meras coocurrencias.
El método de correlación distingue entre términos frecuentes con alta coincidencia y términos con asociación contextual significativa.