En la primera entrega de esta serie hablamos de cómo crear una colección de documentos (corpus), preparar el texto para ser analizado eliminando términos que no aportan información o limpiándolo con algunas reglas básicas, al final sacamos una nube de palabras ‘wordcloud’ para el análisis de términos. En esta ocasión vamos a ver distintas formas de presentar los resultados de la colección de texto que preparamos, veremos otros tipos de nubes, para buscar términos en común o comparar la aparición de términos entre documentos de la colección.
En este caso para simplificar trabajaremos solo con dos de los documentos que presentamos en el post anterior, por ser los más populares escogimos ‘Romeo y Julieta’ y ‘Hamlet’, es decir, en este caso nuestro corpus estará formado por dos documentos.
Inicialmente es el mismo código que en la demo de la ‘Parte I’, creamos el VCorpus pasando el directorio donde esta los documentos, formato, codificación, etc… En este caso en el directorio solo tenemos los dos .txt que vamos a utilizar hamlet.txt y romeoandjuliet.txt disponibles en Project Gutenberg.
Lo pasamos por la función de limpieza clean_corpus(), creamos la matriz de términos por documentos TDM y la convertimos en un objeto de tipo matriz.
library(tm) library(dplyr) library(wordcloud) library(qdap) library(purrr) library(stringi) library(plotrix) #------------------------------ Clean Corpora function --------------------------------------------- clean_corpus <- function(corpus){ corpus <- tm_map(corpus, stripWhitespace) corpus <- tm_map(corpus, removePunctuation) corpus <- tm_map(corpus, content_transformer(tolower)) corpus <- tm_map(corpus, content_transformer(replace_abbreviation)) corpus <- tm_map(corpus, removeNumbers) corpus <- tm_map(corpus, removeWords, c(stopwords("en"), "shall", "o" ,"will", "let","thou", "thy", "shall", "thee" , "will", "o", "gutenbergtm", "project")) return(corpus) } #Load into a VCorpus object the collection of documents from a Directory (DirSource) txt <- "C:/R Blog/BOW Part II" shakespeare_corpus <- VCorpus(DirSource(txt, encoding = "UTF-8"), readerControl = list(language = "en")) #Clean the Corpus shakespeare_corpus <- clean_corpus(shakespeare_corpus) #Create a Term Document Matrix shakespeare_tdm <- TermDocumentMatrix(shakespeare_corpus) #Convert the tdm into a matrix shakespeare_m <- as.matrix(shakespeare_tdm)
Commonality Cloud (wordcloud)
Pasando la TDM a commonality.cloud() de la librería wordcloud obtenemos una nube similar a la que vimos en el post anterior desde el punto de vista estético, pero lo que en realidad obtenemos (como es de esperar por su nombre) son términos comunes entre los documentos, es decir, para aparecer en esta nube, deben aparecer en ambos textos.
# commonality cloud commonality.cloud(shakespeare_m, max.words = 100, colors = c("darkolivegreen2", "darkolivegreen3","darkolivegreen4"))
Comparison Cloud (wordcloud)
Como se puede sospechar por el nombre, en este caso obtenemos una nube de palabras, o dos nubes juntas si se quiere ver de otra forma, separadas por colores y etiquetadas con el nombre del documento correspondiente, que nos muestran los términos mas frecuentes de ambos textos enfrentándolos para tener una comparativa a simple vista.
# comparison cloud comparison.cloud(shakespeare_m, max.words = 100, colors = c("gold1", "dodgerblue3"), title.size = 2, scale=c(7,.5))
Pyramid Plot (plotrix)
En la línea del anterior, para realizar comparativas muy visuales y directas, podemos sacar este plot en pirámide. Para esto debemos preparar los datos de una forma concreta:
- Obtener palabras en común (que al menos aparezcan una vez en ambos documentos).
- Obtener diferencias (valor absoluto de las frecuencias en un documento menos las frecuencias del otro)
- Unimos esto y lo ordenamos de forma decreciente.
- Creamos un Dataset con la información anterior.
- Llamamos a pyramid.plot() para que haga su parte 🙂
# ------------------- Pyramid plot ----------------------- # Create common_words common_words <- subset(shakespeare_m, shakespeare_m[, 1] > 0 & shakespeare_m[, 2] > 0) # Create difference difference <- abs(common_words[, 1] - common_words[, 2]) # Combine common_words and difference common_words <- cbind(common_words, difference) # Order the data frame from most differences to least common_words <- common_words[order(common_words[, 3], decreasing = TRUE), ] # Create top25_df top25_df <- data.frame(x = common_words[1:25, 1], y = common_words[1:25, 2], labels = rownames(common_words[1:25, ])) # Create the pyramid plot pyramid.plot(top25_df$x, top25_df$y, labels = top25_df$labels, gap = 20, top.labels = c("Hamlet", "Words", "Romeo & Juliet"), main = "Common Words", laxlab = NULL, raxlab = NULL, unit = NULL)
Dendrograma
wiki on – Un dendrograma es un tipo de representación gráfica o diagrama datos en forma de árbol (gr. ??????? déndron ‘árbol’) que organiza los datos en subcategorías que se van dividiendo en otros hasta llegar al nivel de detalle deseado – wiki off
Una buena manera de analizar los términos es agruparlos por su frecuencia, para realizar este ‘word clustering’ utilizaremos este diagrama de nombre tan (poco) simpático.
En este caso aislamos los datos de la matriz de términos de Romeo y Julieta, y nos quedamos con los 25 términos más frecuentes.
Para añadir algunas mejoras cargamos el paquete dendextend, en el ejemplo resaltamos algunas ramas, y añadimos rectángulos por agrupaciones,
# order by Romeo and Juliet words decreading shakespeare_m_ordered <- shakespeare_m[order(shakespeare_m[,2], decreasing = TRUE),] # create Top25 dataframe from Romeo and Juliet top25_rj_df <- data.frame(freq = shakespeare_m_ordered[1:25,2] ,labels = rownames(shakespeare_m_ordered[1:25,])) # obtain distance between terms dist_sh <- dist(top25_rj_df) # Create hierarchical cluster hc_sh <- hclust(dist_sh) # improve dendrograms with dendextend capabilities library(dendextend) hcd_sh <- as.dendrogram(hc_sh) hcd_sh <- branches_attr_by_labels(hcd_sh,c("romeo", "love","juliet"), attr = c("col"),"firebrick1") plot(hcd_sh) # Add cluster rectangles rect.dendrogram(hcd_sh, k = 4, border = "royalblue")
Y hasta aquí llegamos con esta introducción al Text Mining con R, hemos visto como crear una colección de documentos, limpiar la información para hacerla mas útil y hemos repasado unas cuantas opciones de visualización para obtener conclusiones sobre el texto analizado. Esperamos que haya resultado interesante y que sirva como punto de partida para que curiosos y curiosas tengan por donde empezar.
Si te interesa indagar mas en este tema te recomendamos que juegues un poco con lo que se presenta en estos post:
- obtener el texto de otras fuentes
- utilizar documentos que aporten un análisis que te sea útil de forma de ir conociendo otras necesidades que pueden aparecer.
- Investigar un poco sobre ‘Stemming‘, no entramos en este apartado por simplificar, pero por ejemplo buscando sentimientos las palabras love, loving, lovely etc podríamos considerar que aportan la misma información ¿verdad?…
- SI quieres jugar un poco con literatura como en este post aquí tienes el enlace al Gutenberg Project de donde sacamos los libros en txt. También podéis indagar aquí sobre el paquete gutenbergr que facilita el trabajo si quieres trabajar con textos que estén en el proyecto.
Hasta otra! 🙂
2 comments
Hola Guillermo, gracias por toda esta información. Tengo una duda, en mi caso he separado el corpus en ngrams usando el paquete RWeka (bigram, trigram, fourgram and fivegram) en el caso que quiera hacer un bigram (2 palabras) pero me interesa quedarme con las stopwords de delante y atrás del bigram, como se puede hacer? Es decir, palabras compuestas con sus artículos para obtener mayor información (contexto).
BigramTokenizer <- function (x){
RWeka::NGramTokenizer(x, RWeka::weka_control(min =2, max=2))}
Como se puede hacer?
Gracias de antemano.
Rectifico, con las stopwords del medio, es decir, p.e:
las actividades del sector turístico
bigram: actividades (del) sector
Trigram: actividades (del) sector turístico
Si elimino previamente las stopwords, al hacer el bigram me sale actividades sector, me interesaría quedarme con las stopwords del medio de los bigrams, trigrams, fourgrams y fivegrams