Análisis de chats en WhatsApp: Parte 1 — Análisis de texto y visualización de Datos con R

Hoy en día, cualquier cosa de la cuál podamos obtener datos, puede ser medible teniendo el conocimiento y las herramientas adecuadas. WhatsApp no es la excepción, gracias a la posibilidad que nos brinda, de exportar conversaciones completas.
Te quiero presentar a rwhatsapp, un paquete pequeño pero muy útil, que proporciona lo necesario para trabajar con datos de texto de WhatsApp en R como Data Frame.
Primero lo primero. ¿Cómo exporto mis conversaciones?
Toda conversación la puedes exportar de manera muy sencilla, desde tu WhatsApp en cualquier conversación abierta, desde el menú de opciones/Más/Exportar chat. Inmediato a esto podrás enviarte el historial completo como archivo de texto con extensión “.txt”.
La función principal en el paquete es la función rwa_read(), que permite importar archivos TXT directamente, así que basta con proporcionar la ruta a un archivo para cargar lo mensajes directamente como un Data Frame.
Para este post, una amiga muy amablemente ( a quien agradezco la confianza) me ha compartido su archivo txt del chat con una persona con la que suele tener una “relación casual sin compromisos” desde hace dos años o más. Para fines prácticos analizaremos esta conversación, visualizando algunos datos relevantes.

Preparación y lectura de datos
Realizaremos la importación de algunas librerías de las que haremos uso, estableceremos el archivo de texto que leeremos, y para hacer esto un poco más interesante, segmentaremos por estaciones del año, a partir del verano de 2018 a la primavera de 2020.
library(rwhatsapp)
library(lubridate)
library(tidyverse)
library(tidytext)
library(kableExtra)
library(RColorBrewer)
library(knitr)# LEEMOS EL CHAT A TRAVÉS DEL TXT EXPORTADO DESDE LA APP
miChat <- rwa_read(“miChat_1.txt”)# PREPARACIÓN DE DATOS PARA ANÁLISIS POR DATE/TIME
miChat <- miChat %>%
mutate(day = date(time)) %>%
mutate(
# SEGMENTACIÓN POR MES
estacion = case_when(
day >= dmy(18082018) & day <= dmy(22092018) ~ “Verano 2018”,
day >= dmy(23092018) & day <= dmy(20122018) ~ “Otoño 2018”,
day >= dmy(21122018) & day <= dmy(20032019) ~ “Invierno 2018”,
day >= dmy(21032019) & day <= dmy(21062019) ~ “Primavera 2019”,
day >= dmy(22062019) & day <= dmy(23092019) ~ “Verano 2019”,
day >= dmy(23092019) & day <= dmy(20122019) ~ “Otoño 2019”,
day >= dmy(21122019) & day <= dmy(20032020) ~ “Invierno 2020”,
day >= dmy(21032020) ~ “Primavera 2020”,
T ~ “Fuera de rango”)
) %>%
mutate( estacion = factor(estacion) ) %>%
filter(!is.na(author))
Frecuencia de mensajes diarios
Veamos la frecuencia diaria de mensajes, asignando una paleta de colores personalizada para una primer gráfica que muestre los mensajes por día de manera muy visual durante las estaciones del año establecidas.
# PALETA DE COLORES
paleta.estaciones <- brewer.pal(8,"Set1")[c(7,5,1,3,4,2,6,8)]# VERIFICANDO CUÁNTOS MENSAJES SE ENVIARON DURANTE EL PERIODO DE TIEMPO
miChat %>%
group_by(estacion) %>%
count(day) %>%
ggplot(aes(x = day, y = n, fill=estacion)) +
geom_bar(stat = "identity") +
scale_fill_manual(values=paleta.estaciones) +
ylab("Número de mensajes") + xlab("Fecha") +
ggtitle("Mensajes por día", "Frecuencia por estación del año") +
theme_minimal() +
theme( legend.title = element_blank(),
legend.position = "bottom")
Obtendremos el siguiente plot como resultado. ¡Algo ocurrió en Otoño de 2019, que dejaron de hablar con tanta frecuencia eh!

Frecuencia de mensajes por día de la semana
Veamos la frecuencia diaria de mensajes, pero además de visualizar por estación del año, enterémonos del día de la semana en específico en que ha habido mayor interacción.
# MENSAJES POR DÍA DE LA SEMANA
miChat %>%
mutate( wday.num = wday(day),
wday.name = weekdays(day)) %>%
group_by(estacion, wday.num, wday.name) %>%
count() %>%
ggplot(aes(x = reorder(wday.name, -wday.num), y = n, fill=estacion)) +
geom_bar(stat = “identity”) +
scale_fill_manual(values=paleta.estaciones) +
ylab(“”) + xlab(“”) +
coord_flip() +
ggtitle(“Número de mensajes por día de la semana”, “Frecuencia por estación del año”) +
theme_minimal() +
theme( legend.title = element_blank(),
legend.position = “bottom”)
Obtendremos el siguiente plot como resultado. ¡Sábados y domingos ha habido una mayor interacción!

Frecuencia de mensajes por hora del día
Ahora veamos la frecuencia diaria de mensajes, pero ahondemos en visualizar la hora más recurrente registrada de interacción.
# MANTENER EL ORDEN DE DÍAS DE LA SEMANA Y RENOMBRARLOS
diasemana <- c(“domingo”,”lunes”,”martes”,”miércoles”,”jueves”,”viernes”,”sábado”,”domingo”)
names(diasemana) <- 1:7# MENSAJES POR HORA DEL DÍA
miChat %>%
mutate( hour = hour(time),
wday.num = wday(day),
wday.name = weekdays(day)) %>%
count(estacion, wday.num, wday.name, hour) %>%
ggplot(aes(x = hour, y = n, fill=estacion)) +
geom_bar(stat = “identity”) +
scale_fill_manual(values=paleta.estaciones) +
ylab(“Número de mensajes”) + xlab(“Horario”) +
ggtitle(“Número de mensajes por hora del día”, “Frecuencia según estación del año”) +
facet_wrap(~wday.num, ncol=7, labeller = labeller(wday.num=diasemana))+
theme_minimal() +
theme( legend.title = element_blank(),
legend.position = “bottom”,
panel.spacing.x=unit(0.0, “lines”))
Obtendremos el siguiente plot como resultado. ¡Entre 8 p.m. y 9 p.m. observamos que se tiene mayormente un hábito de interacción.

¿Quién ha enviado mayor cantidad de mensajes?
Analicemos ahora cuál de nuestros usuarios ha mostrado una mayor intención de interacción, según la cantidad de mensajes enviados. Para preservar la confidencialidad de los sujetos, también cambiaremos los nombres de los usuarios a “Él” y “Ella” simplemente.
# CAMBIEMOS EL NOMBRE DE LOS USUARIOS POR CONFIDENCIALIDAD
levels(miChat$author)[2] <- “Ella”
levels(miChat$author)[1] <- “Él# MENSAJES POR USUARIO
miChat %>%
mutate(day = date(time)) %>%
group_by(estacion) %>%
count(author) %>%
ggplot(aes(x = reorder(author, n), y = n, fill=estacion)) +
geom_bar(stat = "identity") +
scale_fill_manual(values=paleta.estaciones) +
ylab("Número total de mensajes") + xlab("Usuario") +
coord_flip() +
ggtitle("Número total de mensajes por usuario.", "¿Quién es más comunicativo? Frecuencia por estación del año") +
theme_minimal() +
theme( legend.title = element_blank(),
legend.position = "bottom")
Tendremos el siguiente plot, observando claramente que ligeramente, pero por un poco más, Él ha enviado mayor cantidad de mensajes a Ella.

¿Cuáles son los emojis más usados en el chat?
Los emojis son símbolos gráficos Unicode , que se usan actualmente como abreviatura para expresar conceptos e ideas, hay cientos de emojis y rwhatsapp nos permite explorar la popularidad en los intercambios de mensajes de cualquier conversación.
Antes de la clasificación de Emojis, vamos a quitar sus variaciones. Las variaciones son cuando cambia, por ejemplo, el color de la piel o el cabello, estas variaciones están codificadas por la composición de varios caracteres Unicode. Cuando un dispositivo lee estos caracteres, se compone y se muestra como uno solo. Este proceso se llama “ligadura” y tiene algunas implicaciones interesantes . Antes de la clasificación, conservaremos solo el primer Unicode del Emoji, eliminando todo lo demás.
# LIBRERÍA PARA FETCH DE IMAGEN PNG DE EMOJI DESDE https://abs.twimg.com
library(ggimage)# EMOJI RANKING
plotEmojis <- miChat %>%
unnest(emoji, emoji_name) %>%
mutate( emoji = str_sub(emoji, end = 1)) %>%
mutate( emoji_name = str_remove(emoji_name, “:.*”)) %>%
count(emoji, emoji_name) %>%
# PLOT TOP 30 EMOJIS
top_n(30, n) %>%
arrange(desc(n)) %>% # CREA UNA URL DE IMAGEN CON EL UNICODE DE EMOJI
mutate( emoji_url = map_chr(emoji,
~paste0( “https://abs.twimg.com/emoji/v2/72x72/”, as.hexmode(utf8ToInt(.x)),”.png”))
)# PLOT DEL RANKING DE EMOJIS MÁS USADOS
plotEmojis %>%
ggplot(aes(x=reorder(emoji_name, n), y=n)) +
geom_col(aes(fill=n), show.legend = FALSE, width = .2) +
geom_point(aes(color=n), show.legend = FALSE, size = 3) +
geom_image(aes(image=emoji_url), size=.045) +
scale_fill_gradient(low=”#2b83ba”,high=”#d7191c”) +
scale_color_gradient(low=”#2b83ba”,high=”#d7191c”) +
ylab(“Número de veces que el emoji fue usado”) +
xlab(“Emoji y significado”) +
ggtitle(“Emojis más utilizados de manera general”, “Emojis más usados por todos”) +
coord_flip() +
theme_minimal() +
theme()
Ahora tendremos el siguiente plot, mostrando puntualmente cuáles han sido los 30 emojis más utilizados en el chat de nuestros usuarios. ¡Después de todo, hoy en día un emoji dice más que mil palabras, eh!

Emojis más utilizados en el chat, por usuario
Podemos hacer el mismo análisis anterior , pero específicamente por usuario del chat, mostrando cuáles son los Emojis más utilizados por cada uno.
# EMOJI RANK POR USUARIO
plotEmojis <- miChat %>%
unnest(emoji, emoji_name) %>%
mutate( emoji = str_sub(emoji, end = 1)) %>% #
count(author, emoji, emoji_name, sort = TRUE) %>%# PLOT DEL TOP 8 EMOJIS POR USUARIO
group_by(author) %>%
top_n(n = 8, n) %>%
slice(1:8) %>%
# CREA UNA URL DE IMAGEN CON EL UNICODE DE EMOJI
mutate( emoji_url = map_chr(emoji,
~paste0(“https://abs.twimg.com/emoji/v2/72x72/”,as.hexmode(utf8ToInt(.x)),".png")) )# PLOT DE LA DATA
plotEmojis %>%
ggplot(aes(x = reorder(emoji, -n), y = n)) +
geom_col(aes(fill = author, group=author), show.legend = FALSE, width = .20) +# USAR PARA HACER FETCH DE UNA IMAGEN PNG DE EMOJI https://abs.twimg.com
geom_image(aes(image=emoji_url), size=.13) +
ylab(“Número de veces que se usó el emoji”) +
xlab(“Emoji”) +
facet_wrap(~author, ncol = 5, scales = “free”) +
ggtitle(“Emojis más usados en la conversación, por usuario”) +
theme_minimal() +
theme(axis.text.x = element_blank())
Obtendremos el siguiente plot con el top 8 de emojis más utilizados. ¡Al parecer ambos se divierten mucho eh!

¿Cuáles son las palabras más usadas en el chat?
Tal como hicimos con el Emojis, también podemos hacer el análisis de frecuencia de las palabras más utilizadas en el chat, así como para cada uno de los usuarios. El paquete tidytext hace que este análisis sea muy simple y fácil de aplicar. Realizaremos la clasificación de palabras, añadiendo todas aquellas que deseemos discriminar, que pudieran no ser relevantes, como artículos, pronombres, etc.
library(tidytext)
library(stopwords)# REMOVEMOS PALABRAS SIN SIGNIFICADO RELEVANTE, COMO ARTÍCULOS, PRONOMBRES, ETC.
remover_palabras <- c(stopwords(language = “pt”),
“multimedia”, “y”, “la”, “el”,”en”, “es”, “si”, “lo”, “ya”, “pero”, “esa”, “los”,”yo”,”mi”, “un”, “con”, “las”, “omitido”, “más”,”eso”, “al”, “una”, “del”, “qué”, “todo”, “así”, “le”, “su”, “va”, “porque”, “todos”, “hay”, “les”, “pue”, “ese”, “son”, “está”, “pues”, “ahí”, “sí”,”ver”, “estás”, “algo”, “vas”, “ir”,”voy”, “creo”,”fue”,”solo”, “ni”,”sólo”,”nada”, “aqui”, “q”, “tú”)# CONTEO DE PALABRAS
miChat %>%
unnest_tokens(input = text, output = word) %>%
filter(!word %in% remover_palabras) %>%
count(word) %>% # PLOT DEL TOP 30 DE PALABRAS MÁS USADAS EN CONVERSACIÓN
top_n(30,n) %>%
arrange(desc(n)) %>%
ggplot(aes(x=reorder(word,n), y=n, fill=n, color=n)) +
geom_col(show.legend = FALSE, width = .1) +
geom_point(show.legend = FALSE, size = 3) +
scale_fill_gradient(low=”#2b83ba”,high=”#d7191c”) +
scale_color_gradient(low=”#2b83ba”,high=”#d7191c”) +
ggtitle(“Palabras más usadas en la conversación de manera general”) +
xlab(“Palabras”) +
ylab(“Número de veces que se usó la palabra”) +
coord_flip() +
theme_minimal()
Entonces ahora obtendremos el siguiente plot, mostrando las 30 palabras más utilizadas. ¿Qué rayos será “murci”, una palabra clave para referirse a Batman?

Palabras más usadas en el chat, por usuario
Ahora hagamos el mismo análisis que hemos realizado , pero por usuario de la conversación, mostrando cuales son las palabras mayormente expresadas por cada uno.
# CONTEO DE PALABRAS POR USUARIO
miChat %>%
unnest_tokens(input = text,
output = word) %>%
filter(!word %in% remover_palabras) %>%
count(author, word, sort = TRUE) %>%
# TOP 20 PALABRAS MÁS USADAS POR USUARIO
group_by(author) %>%
top_n(n = 20, n) %>%
slice(1:20) %>%
ungroup() %>%
arrange(author, desc(n)) %>%
mutate(order=row_number()) %>%
ggplot(aes(x = reorder(word, n), y = n, fill = author, color = author)) +
geom_col(show.legend = FALSE, width = .1) +
geom_point(show.legend = FALSE, size = 3) +
xlab(“Palabras”) +
ylab(“Número de veces que se usó la palabra”) +
coord_flip() +
facet_wrap(~author, ncol = 3, scales = “free”) +
ggtitle(“Palabras más usadas por usuario en la conversación”) +
theme_minimal()
Ahora tendremos el siguiente plot con el top 20 de palabras expresadas por usuario.

Me gustaría dejar hasta aquí esta primer parte, amigos, para que sea más digerible lo que sigue, que será análisis de léxico y análisis de sentimientos, usando la misma conversación.
Si se quieren ver más fancy, pueden usar “‘plotly’” para darle un poco más de vida a sus plots. Por acá dejo muestra de ello en un flexdashboard que armé para presentar los plots de manera más atractiva para este artículo: https://rpubs.com/cosmoduende/whatsapp-data-analysis-r-part1
Así que, a poner en práctica el análisis de datos con sus grupos, parejas o quien quieran. Pueden encontrarse con datos muy interesantes.
Aquí pueden encontrar el código completo: https://github.com/cosmoduende/r-whatsapp-analysis-parte1
¡Actualización: Parte 2, ya disponible!
31 de Mayo. Si te gustó este artículo, te invito a visitar la segunda parte en donde se aborda diversidad de léxico y análisis de sentimientos sobre este mismo chat:
Otros artículos que he escrito
Si te ha gustado este post, agradeceré mucho tus claps. Además te invito a visitar también otros artículos que he escrito:
Gracias y hasta la próxima.