Para este anĂ¡lisis se utilizarĂ¡ la evaluaciĂ³n de uno de mis cursos. Especificamente, de la clase de economĂa que di en 2019. La encuestĂ¡ incluyĂ³ dos preguntas abiertas: 1) ¿cuĂ¡les son las oportunidades de mejora de este curso? y 2) ¿cuĂ¡les son las fortaleza de este curso? Para este ejercicio, no se harĂ¡ distinciĂ³n entre las dos preguntas.
Como no vamos hacer diferencia entre los comentarios de oportunidad de mejora y las fortalezas del curso, vamos a extraer Ăºnicamente la columna que contiene el texto de los comentarios, creamos un vector fuente (vector source) y creamos un cuerpo volatil.
La funciĂ³n iconv convertirĂ¡ la codificaciĂ³n de nuestros caracteres de ASCII a UTF-8, que es un formato de codificaciĂ³n de caracteres de codificaciĂ³n variable y que evitarĂ¡ que tengamos problemas con el cĂ³digo mĂ¡s adelanta (puede buscar en google UTF-8 si quieres saber mĂ¡s).
La funciĂ³n de content y corchete doble [[]] nos permite visualizar el contenido especĂfico de cualquier comentario. En este caso, vemos el comentario en la posiciĂ³n 225.
texto <- iconv(comentarios$Comentario, "ASCII", "UTF-8", sub="")
library(tm)
comentarios_source <- VectorSource(texto)
comentarios_corpus <- VCorpus(comentarios_source)
content(comentarios_corpus[[225]])
## [1] "62 Vision global de la economia"
Ahora que tenemos un cuerpo, podemos proceder a limpiarlo. Para esto haremos uso de una funciĂ³n que nos permitirĂ¡ limpiar todo el cuerpo y ahorrar espacio de cĂ³digo. La funciĂ³n lleva el nombre de clean_corpus e incluye funciones de tm y bĂ¡sicas de r. Las funciones que usemos dependerĂ¡n de cada caso particular, aunque hay algunas muy comunes.
tolower convertirĂ¡ todas las letras a minĂºscula, removePunctuation eliminarĂ¡ toda la puntuaciĂ³n, removeNumber eliminarĂ¡ nĂºmeros y stripWhitespace eliminarĂ¡ espacios extras.
También procedermos a eliminar las palabras de relleno. Se pueden agregar tantas palabras de relleno adicionales como se considere necesario para que sean eliminadas del cuerpo. Por ejemplo, el nombre del docente, doctor y el nombre de la clase.
clean_corpus <- function(corpus){
corpus <- tm_map(corpus, removePunctuation)
corpus <- tm_map(corpus, content_transformer(tolower))
corpus <- tm_map(corpus, removeWords, words = c(stopwords("es"), "doctor"))
corpus <- tm_map(corpus, stripWhitespace)
return(corpus)
}
cuerpo_limpio <- clean_corpus(comentarios_corpus)
Es el momento de hacer algo con los comentarios. Primero convertimos el cuerpo limpio a una matriz utilizando la funcion TermDocumentMatrix, que convertirĂ¡ el cuerpo limpio en una matriz. Esa matriz tendrĂ¡ las palabras en las filas y los comentarios en las columnas. La funciĂ³n rowSums se encargarĂ¡ de calcular la suma de las filas para obtener la frecuencia de las palabras. Se orderĂ¡n de mayor a menor frecuencia utilizando la funciĂ³n sort y finalmente se harĂ¡ una grĂ¡fico de barras con la funciĂ³n barplot.
comentarios_tdm <- TermDocumentMatrix(cuerpo_limpio)
comentarios_m <- as.matrix(comentarios_tdm)
frecuencia_terminos <- rowSums(comentarios_m)
frecuencia_terminos <- sort(frecuencia_terminos, decreasing = TRUE)
frecuencia_terminos[1:10]
## clase ninguna mas bien temas mejor economia docente
## 30 30 26 18 15 14 13 12
## manera misma
## 11 11
barplot(frecuencia_terminos[1:10], col = "tan", las = 2)
Otra formad de visualizaciĂ³n es la nube de palabras. El cĂ³digo a continuaciĂ³n nos ayudarĂ¡ a obtener la nuve de palabras. Definitivamente es una forma mĂ¡s interactiva de presentar las frecuencias de las palabras.
library(wordcloud)
head(frecuencia_terminos, n = 10)
## clase ninguna mas bien temas mejor economia docente
## 30 30 26 18 15 14 13 12
## manera misma
## 11 11
terms_vec <- names(frecuencia_terminos)
wordcloud(terms_vec, frecuencia_terminos, max.words = 50, colors = "red")
Puede darse la posibilidad de que existanp palabras que no contribuyen mucho a nuestro anĂ¡lisis, como clase (que la esperabamos que apareciera) y ninguna (significa que los estudiantes no tenian comentarios). Eso lo hacemos con el cĂ³digo a continuaciĂ³n. Simplemente tenemos que anexar las palabras que creamos no son interesantes. En el ejemplo, se eliminarĂ¡n ambas. Notese que es necesario hacer estos ajustes desde la parte en que estamos preparando el cuerpo del texto para convertirlo en matriz.
stops <- c(stopwords(kind = 'es'), 'ninguna', 'clase')
tail(stops)
## [1] "tenida" "tenidos" "tenidas" "tened" "ninguna" "clase"
cleaned_cuerpo_limpio <- tm_map(cuerpo_limpio, removeWords, stops)
comentarios_tdm <- TermDocumentMatrix(cleaned_cuerpo_limpio)
comentarios_m <- as.matrix(comentarios_tdm)
frecuencia_terminos <- rowSums(comentarios_m)
frecuencia_terminos <- sort(frecuencia_terminos, decreasing = TRUE)
terms_vec <- names(frecuencia_terminos)
wordcloud(terms_vec, frecuencia_terminos, max.words = 50, colors = "red")
Let's improve the word cloud by addiong some color.
wordcloud(terms_vec, frecuencia_terminos,
max.words = 50,
colors = c("grey80", "darkgoldenrod1", "tomato"))
Alternativamente, se puede hacer uso de paletas de colores predeterminadas. El paquete viridisLite ofrece alternativas interesantes. Con la funciĂ³n cividis, Ăºnicamente tenemos que indicarlela cantidad de colores que queremos utilizar. Algo prĂ¡ctico de utilizar este paquete, es que los colores seleccionados puede ser identificados incluso por personas con ceregera de colores.
library(viridisLite)
color_pal <- cividis(n = 5)
wordcloud(terms_vec, frecuencia_terminos,
max.words = 50,
colors = color_pal)
CreaciĂ³n de un dendograma del texto. Como la matriz del texto tiene muchos ceros, utilizamos removeSparseTerms, esto reducirĂ¡ la candidad de documentos que tenga exceso de ceros en la matriz.
comentarios_tdm2 <- removeSparseTerms(comentarios_tdm, sparse = 0.975)
tdm_m <- as.matrix(comentarios_tdm2)
comentarios_dist <- dist(tdm_m)
hc <- hclust(comentarios_dist)
plot(hc)
Identificar relaciones interesantes puede ser dificil en el dendrograma. Una alternativa es agregar un poco de color para identificar el cluster en que se encuentra las palabras que nos interesan. El primer paso es guardar las distancias como dendrograma con la funcion as.dendogram. Con la funciĂ³ labels identificamos cuales son las palabras que fueron incluidas en el dendrograma y seleccionamos las que queremos resaltar con color.
hcd <- as.dendrogram(hc)
labels(hcd)
## [1] "mas" "bien" "conocimiento" "profesor" "misma"
## [6] "buena" "curso" "manera" "docente" "excelente"
## [11] "economia" "mejor" "temas"
hcd_colored <- branches_attr_by_labels(hcd, c("excelente", "economia"), color = "red")
plot(hcd_colored, main = "Dendrograma coloreado")
rect.dendrogram(hcd_colored, k = 2, border = "grey50")
Otra forma de encontrar asociaciones entre palabras es utilizando correlaciones. qdap nos permite encontrarlas utilizando la funciĂ³n findAssoc. La funciĂ³n toma el TermDocumentMatrix, la palabra que nos interesa y el valor de correlaciĂ³n que queremos ver reportado. Luego podemos utilizar un grĂ¡fico para presentar los resultados de las correlaciones.
asociaciones <- findAssocs(comentarios_tdm, "excelente", 0.2)
asociaciones
## $excelente
## opinin transmite maestro 110 conocimientos
## 0.58 0.58 0.49 0.40 0.32
## aprendido area brinda bueno capacitados
## 0.28 0.28 0.28 0.28 0.28
## entiende metodo msima pienso profesionales
## 0.28 0.28 0.28 0.28 0.28
## real realidad sandoval vida zamorano
## 0.28 0.28 0.28 0.28 0.28
asociaciones_df <- list_vect2df(asociaciones, col2 = "palabra", col3 = "correlacion")
ggplot(asociaciones_df, aes(correlacion, palabra)) +
geom_point(size = 3) +
theme_classic()
Hasta ahora, todo el anĂ¡lisis se ha hecho utilizando una sola palabra a la vez. Con el cĂ³digo a continuaciĂ³n, hĂ¡remos tokens de dos palabras a la vez. Primero creamos la funciĂ³n y luego creamos la matrix de documento aplicando la funciĂ³n de tokenizaciĂ³n adentro de la funciĂ³n de DocumentTermMatrix.
Para ver la diferencia, hagamos una nube de palabras sencilla que ejemplifique la diferencia de analizar las frecuencias de palabras con una sola palabra y dos palabras. AdemĂ¡s, el sub-set muestra la principales palabras con que son asociadas palabras de interĂ©s. Por ejemplo, sandoval se asocia con palabras como brinda, excelente, experto y explica.
tokenizer <- function(x) {
NGramTokenizer(x, Weka_control(min = 2, max = 2))
}
bigram_dtm <- DocumentTermMatrix(
cuerpo_limpio,
control = list(tokenize = tokenizer)
)
bigram_dtm
## <<DocumentTermMatrix (documents: 322, terms: 816)>>
## Non-/sparse entries: 862/261890
## Sparsity : 100%
## Maximal term length: 25
## Weighting : term frequency (tf)
bigram_dtm_m <- as.matrix(bigram_dtm)
freq <- colSums(bigram_dtm_m)
bi_words <- names(freq)
str_subset(bi_words, "^sandoval")
## [1] "sandoval brinda" "sandoval excelente" "sandoval experto"
## [4] "sandoval explica"
wordcloud(bi_words, freq, max.words = 30)