En esta actividad se realizará un análisis de minería de texto sobre la obra “Don Quijote de la Mancha”. El propósito es examinar las relaciones entre bigramas y descubrir patrones significativos en el texto.
Se cargan las librerias necesarias para la ejecucón.
library(pdftools)
library(dplyr)
library(tidytext)
library(stringr)
library(ggplot2)
library(tidyr)
library(widyr)
library(ggraph)
library(igraph)
library(stopwords)
library(wordcloud)
library(RColorBrewer)
# === 1. Lectura de los PDF ===
texto01 <- pdf_text("DONQUIJOTE_PARTE1.pdf")
texto02 <- pdf_text("DONQUIJOTE_PARTE2.pdf")
# Unir páginas dentro de cada parte
texto01 <- paste(texto01, collapse = " ")
texto02 <- paste(texto02, collapse = " ")
library(pdftools)
# === 2. Definir función de limpieza ===
limpiar_texto <- function(texto) {
# Eliminar 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)
}
# === 3. Aplicar limpieza a cada parte ===
texto01_limpio <- limpiar_texto(texto01)
texto02_limpio <- limpiar_texto(texto02)
# === 4. Unir las partes limpias ===
texto <- paste(texto01_limpio, texto02_limpio, collapse = "\n\n")
# === 5. Exportar resultado ===
writeLines(texto, "don_quijote_limpio.txt", useBytes = TRUE)
# Separar por oraciones y convertir a dataframe
frases <- unlist(strsplit(texto, "\\.")) # ← corrección aquí
df_texto <- data.frame(frase = frases, stringsAsFactors = FALSE)
# Quitar encabezados y espacios sobrantes
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
# Nube de palabras * Se realiza un grafico de nube de palabras, que
permite representar de practica y estetica las palabras mas frecuentes,
que son representadas por la palabras con el tamaño mas grande y las
palabras menos frecuentes representadas por el texto con tamaño mas
pequeño.
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áéíóúñ]"))
#write.csv(df_tokens_coocurencia_grupos, "df_tokens_coocurencia_grupos.csv", row.names = FALSE)
#head(df_tokens_coocurencia_grupos, 1000)
#print(n_distinct(df_tokens_coocurencia_grupos$grupo))
# Calcular coocurrencias por grupo
word_pairs <- df_tokens_coocurencia_grupos %>%
pairwise_count(word, grupo, sort = TRUE)
# Mostrar 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
# ============================================================
# 11. Correlaciones entre palabras (agrupadas y graficadas)
# ============================================================
# Crear 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áéíóúñ]"))
# Calcular 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)
# Mostrar 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
# ============================================================
# Graficar red de correlaciones más fuertes (Top 100)
# ============================================================
library(dplyr)
library(igraph)
library(ggraph)
library(tidygraph)
# =========================================
# 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))
}
# =========================================
# Filtrar y graficar TOP 15 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"
)
# Mostrar gráfico
print(grafico_top15)
# =========================================
# Filtrar y graficar TOP 200 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 200 correlaciones > 0.25"
)
# Mostrar gráfico
print(grafico_top200)
# ============================================================
# 11A. Gráfico de barras - correlaciones más fuertes por palabra
# ============================================================
# Selecciona una palabra base para analizar correlaciones
palabra_base <- "quijote" # 👈 puedes cambiarla por otra: "sancho", "don", etc.
# 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)
# ============================================================
# 11B. Heatmap - correlaciones entre las palabras más comunes
# ============================================================
# Seleccionar las 15 palabras más frecuentes
top_palabras <- df_tokens_grupos %>%
count(word, sort = TRUE) %>%
top_n(15) %>%
pull(word)
# Filtrar solo correlaciones entre esas palabras
cor_top <- word_cors %>%
filter(item1 %in% top_palabras,
item2 %in% top_palabras)
# Crear matriz de correlación
library(reshape2)
matriz_cor <- acast(cor_top, item1 ~ item2, value.var = "correlation", fill = 0)
# Graficar heatmap
library(ggplot2)
ggplot(melt(matriz_cor), aes(Var1, Var2, fill = value)) +
geom_tile(color = "white") +
scale_fill_gradient2(low = "lightblue", high = "darkblue", mid = "white",
midpoint = 0.2, limit = c(0, 1), space = "Lab") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
labs(title = "Mapa de calor de correlaciones entre palabras frecuentes",
x = "Palabra 1", y = "Palabra 2", fill = "Correlación")
# ============================================================
# 11C. Scatterplot - pares de palabras más correlacionadas
# ============================================================
# Seleccionamos los pares con mayor correlación
word_cors_top <- word_cors %>%
filter(correlation > 0.25) %>%
arrange(desc(correlation)) %>%
head(20)
# 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)
En esta sección redacta tus conclusiones personales, por ejemplo:
Las palabras más frecuentes reflejan los temas centrales del texto, destacando términos como don, quijote, sancho, y caballero.
Los bigramas con mayor frecuencia muestran relaciones semánticas clave, como don quijote, sancho panza, mi señor, lo que revela la interacción entre personajes.