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"))

 

Introducción al Text Mining con R (parte 2)

 

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))

Introducción al Text Mining con R (parte 2)

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)

Introducción al Text Mining con R (parte 2)

 

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")

Introducción al Text Mining con R (parte 2)

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! 🙂

 

0 Shares:
2 comments
  1. 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.

    1. 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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

You May Also Like