Análisis de Sentimiento en Twitter

Análisis de Sentimiento en Twitter

Introducción

Twitter proporciona un volumen de datos inmenso que puede permitir un análisis textual de los mismos y poder extraer mucha información. Twitter, al igual que otras redes sociales, nos permite obtener información sobre el comportamiento del público online. Esta información es muy importante para poder mantener una escucha activa sobre la ciudadanía en su relación con las AAPP. Combinando los múltiples canales de expresión social del ciudadano: twitter, facebook, instagram, whatsapp, telegram, e incluso otros canales más formales como la quejas o instancias que nos puedan hacer llegar, podemos disponer de un análisis sobre las demandas sociales e incluso intuir, con ayuda de la minería de datos, el estado de ánimo de los ciudadanos con los mensajes que hacen llegar a las AAPP. Nos centraremos en Twitter principalmente por el acceso sencillo a los Tweets a través de su API y a la cantidad de documentación que encontramos para realizar el análisis. Existe software para hacer este análisis, pero la idea de este ejercicio es profundizar en el entorno de desarrollo con R y validar un concepto que podemos aplicar con las múltiples fuentes de información pública y social que puede disponer una AAPP.

El artículo lo dividiremos en tres partes:

  • Obtención de los tweets
  • Análisis de tweets y representación gráfica
  • Análisis de sentimiento de los tweets

Obtención de los tweets

Lo primero que debemos hacer es obtener acceso a la API de Twitter. Para ello debemos seguir los pasos:

  1. Disponer de cuenta en Twitter. Si no es así, se debe crear una. Utilizaré la propia @rgonzalezmas
  2. Creación de nueva App en https://developer.twitter.com/. Aquí debemos crear un proyecto para obtener las claves necesarias. Twitter Developer
  3. Accedemos a Keys and Access Tokens (Icono Llave) y podremos obtener o regenerar todas las claves de acceso.

Una vez tenomos las claves podemos comenzar con el desarrollo del script en R, usando el entorno R-Studio.

#Requerimos las librerías necesarias para trabajar
if(!require("tweet"))install.packages("rtweet")
if(!require("dplyr"))install.packages("dplyr")
if(!require("tidyverse")) install.packages("tidyverse")
if(!require("tidytext")) install.packages("tidytext")
if(!require("wordcloud"))install.packages("wordcloud")
if(!require("RColorBrewe"))install.packages("RColorBrewer")
if(!require("wordcloud2"))install.packages("wordcloud2")
if(!require("tm"))install.packages("tm")
if(!require("slam"))install.packages("slam")
if(!require("syuzhet"))install.packages("syuzhet")

library(RColorBrewer)
library(wordcloud2)
library(wordcloud)
library(tm)
library(slam)
library(syuzhet)
library(dplyr)
library (rtweet)
library(tidyverse)
library(tidytext)


#Keys Obtenidas del entorno Developer de Twitter
consumerKey <-"XXXXXXXXXXXXXXXXXXXXXXXX"
consumerSecret <-"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
accessToken <- "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
accessTokenSecret <-"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"


#Obtenemos el token del servicio Twitter 
twitter_token <- create_token(
  app = "TweetDummyProject",
  consumer_key = consumerKey,
  consumer_secret = consumerSecret,
  access_token = accessToken,
  access_secret = accessTokenSecret,
  set_renv = TRUE)  

Una vèz tenemos el acceso a la API, podemos proceder a la consulta de los Tweets generados por una cuenta. En este caso, utilizaremos para nuestro ejercicio la cuenta de @VallsAjuntament. Concretamente, obtendremos los 3000 últimos tweets realizados.

#Obtenemos los 3000 últimos tweet de la cuenta @VallsAjuntament
Valls <- get_timeline("@VallsAjuntament", n= 3000)

A partir de este conjunto de datos, empezamos hacer un análisis básico de la cuenta y mostraremos los resultados de forma visual. Nosotros hemos decidido realizar el anáilis sobre la cuenta del Ajuntament de Valls, pero podríamos analizar otro conjunto de datos usando la funcion search_tweets, que permite extraer información de los últimos 6 a 9 días. Se especifica el número de tweets a extraer (n), el lenguage(en, es, cat) y un patrón de búsqueda. Más información en la propia documentación En el ejemplo siguiente obtendŕiamos los 1000 tweets de los últimos 6-9 días de ‘Valls’ en catalan. También podemos acotar el ámbito geográfico (usando el parámetro geocode) de los Tweets para hacer un análisis más enfocado a la ciudad de Valls.

Valls <- search_tweets("Valls", n=1000, include_rts=FALSE, lang="ca")

Análisis de tweets y representación gráfica

A parte de analizar el sentimiento de los tweets, podemos obtener un informe analizando diferentes aspectos sobre la cuenta que queremos evaluar (@VallsAjuntament)

#Obtenemos los últimos 3200 tweets
Valls <- get_timeline("@VallsAjuntament", n= 3200)

# Hacemos un análisis de la producción de la cuenta
# Borramos los retweets
Valls_tweets_organic <- Valls[Valls$is_retweet==FALSE, ] 

# Borramos los reply
Valls_tweets_organic <- subset(Valls_tweets_organic, is.na(Valls_tweets_organic$reply_to_status_id)) 
# Ordenamos los tweets por los que tienen más favoritos
Valls_tweets_organic <- Valls_tweets_organic %>% arrange(-favorite_count)
# Ordenamos los tweets por los que tienen más retweets
Valls_tweets_organic <- Valls_tweets_organic %>% arrange(-retweet_count)

Si inspeccionamos la variable Valls_tweets_organic veremos los tweets generados ordenados por favoritos y retweets.

Tweets más destacados

Podemos obtener igualmente los retweets realizados por la cuenta y los reply.

# Mantenemos retweets
Valls_retweets <- Valls[Valls$is_retweet==TRUE,]
# Mantenemos reply
Valls_replies <- subset(Valls, !is.na(Valls$reply_to_status_id))

Tenemos separado en variables los tweets (Sin retweet o replies) orgánicos, los retweets y los replies. Con esto podemos mostrar de formato gráfico qué tipo de cuenta estamos analizando valorando si crea contenido propio o solo se dedica a retwitear otros contenidos.

# Creamos un  data frame
data <- data.frame(
  category=c("Organic", "Retweets", "Replies"),
  count=c(nrow(Valls_tweets_organic),nrow(Valls_retweets), nrow(Valls_replies)
))

# Añadimos columnos
data$fraction = data$count / sum(data$count)
data$percentage = data$count / sum(data$count) * 100
data$ymax = cumsum(data$fraction)
data$ymin = c(0, head(data$ymax, n=-1))

# Especificamos en la leyenda los porcentajes
# % of organic, retweets y replies 
Type_of_Tweet <- paste(data$category, data$percentage, "%")
ggplot(data, aes(ymax=ymax, ymin=ymin, xmax=4, xmin=3, fill=Type_of_Tweet)) +
  geom_rect() +
  coord_polar(theta="y") + 
  xlim(c(2, 4)) +
  theme_void() +
  theme(legend.position = "right")

Análisis Orgánico

Como se observa el contenido de la cuenta es mayoritariamente propio, retweeteando muy poco y contestando de forma moderada. Parece adecuado para una cuenta institucional.

Siguiendo el análisis de la cuenta, podemos evaluar el nivel de producción de la misma, visualizando cuando se publica. En nuestro caso, dado el volumen analizado mostraremos el número de publicaciones menusales.

# Number of tweets by month
colnames(Valls)[colnames(Valls)=="screen_name"] <- "Twitter_Account"
ts_plot(dplyr::group_by(Valls, Twitter_Account), "month") +
  ggplot2::theme_minimal() +
  ggplot2::theme(plot.title = ggplot2::element_text(face = "bold")) +
  ggplot2::labs(
    x = NULL, y = NULL,
    title = "Frequency of Tweets from Valls Ajuntament",
    subtitle = "Tweet counts aggregated by month",
    caption = "\nValls Ajuntament analytics by rgonzalez"
  )

Análisis Orgánico

La publicación oscila mensualmente de 200 a 275 tweets durante los meses analizados. Este nivel obedece a una estrategia comunicativa planificada con una programación definida.

Análisis del texto

Un informe de análisis de Twitter debe incluir un análisis del contenido de los tweets y esto incluye descubrir qué palabras se utilizan con mayor frecuencia. Debido a que está analizando datos textuales, realizaremos una limpieza del texto para eliminar carácteres que no interesa, enlaces, menciones (@) o signos de puntuación. De esta forma nos centramos en las palabras clave (las que aportan significado) Una vez realizado el proceso, separamos la columna de texto en tokens, creando una fila por cada token encontrado. De esta forma podremos realizar una suma de los tokens que más aparecen y mostrar un histograma para valorar qué conceptos se destacan más. En el proceso, realizaremos un filtrado de elementos que consideramos que no aportan valor como preposiciones, artículos, pronombres etct. En nuestro caso, al estar en catalan, definimos una lista de palabras básicas. Como ejercicio es correcto, pero deberíamos disponer de un conjunto de palabras a eliminar más grande para ganar en calidad del análisis. S

##Limpieza de tweets
Valls_tweets_organic$text <-  gsub("https\\S*", "", Valls_tweets_organic$text)
Valls_tweets_organic$text <-  gsub("@\\S*", "", Valls_tweets_organic$text) 
Valls_tweets_organic$text  <-  gsub("amp", "",Valls_tweets_organic$text) 
Valls_tweets_organic$text  <-  gsub("[\r\n]", "", Valls_tweets_organic$text)
Valls_tweets_organic$text  <-  gsub("[[:punct:]]", "", Valls_tweets_organic$text)

#Tokenizamos columna text
tweets <- Valls_tweets_organic %>%
  select(text) %>%
  unnest_tokens(word, text)

#Creamos lista de palabras a obviar
stop_words<-data.frame(word=c("us","et","va","he","ha","a","bon","i","als","fins","en","des","mes","més","aquest","o","una","dels","amb","de","la","es","és","que","els","el","un","al","del","les","la","per"))
#
Las filtramos en la tabla de tokens
tweets <- tweets %>%
  anti_join(stop_words)

#Creamos un gráfico de barras con las palabras más frecuentes, en este caso, el TOP 20
tweets %>% 
  count(word, sort = TRUE) %>%
  top_n(20) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(x = word, y = n)) +
  geom_col() +
  xlab(NULL) +
  coord_flip() +
  labs(y = "Count",
       x = "Unique words",
       title = "Most frequent words found in the tweets of Valls Ajuntament",
       subtitle = "Stop words removed from the list")

Frecuencia de palabras

Otro análisis interesante a realizar puede ser qué hastags se utilizan más en la cuenta. Para ello utilizaremos la funcion wordcloud que mostrará una nube de etiquetas con diferentes tamaños, en función de la frecuencia de uso del hastag

#Hastags más usados

Valls_tweets_organic$hashtags <- as.character(Valls_tweets_organic$hashtags)
Valls_tweets_organic$hashtags <- gsub("c\\(", "", Valls_tweets_organic$hashtags)
set.seed(1234)
wordcloud(Valls_tweets_organic$hashtags, min.freq=5, scale=c(5, 1), random.order=FALSE, rot.per=0.35, 
          colors=brewer.pal(8, "Dark2"))

Hashtags

Análisis del sentimiento

El análisis de sentimineto lo podemos definir como un campo de la NLP que intenta identificar y extraer opiniones dentro de un texto dado a través de blogs, reseñas, redes sociales, foros, noticias, etc.

Entra dentro del ámbito de la minería de textos y se estudia la información contenida para identificar actitudes y reacciones, de manera masiva y automática. No se realiza un análisis ligüístico, si no estadístico

El método que usaremos se basa en el uso de vocabularios, lexicon, palabras que tienen una puntuación positiva y negativa. La librería que utilizaremos es concretamente syuzhet, que entre otras cosas permite el análisis con vocabularios multi idioma, entre ellos el català.

Existe una descripción amplia de las funcionalidades que ofrece AQUÍ. Nosotros utilizaremos de forma simplificada la implentación del análisis del sentimiento mediante get_nrc_sentiment que usa el lexicon NRC, una lista de palabras y sus asociación con 8 emociones (anger-ira, fear-miedo, anticipation-anticipación, trust-confianza, surprise-sorpresa, sadness-tristeza, joy-alegría, y disgust-disgusto)y dos sentimientos (negative, positive). Podemos obtener más información en este artículo. La función retorna un data frame devuelve una puntuación por cada ítem evaluado. LA función permite cambiar el lexicon al idioma del texto y poder análizar diferentes idiomas. Aquí mostraremos como hacerlo en catalán.

# Converting tweets to ASCII to trackle strange characters
tweets <- iconv(tweets, from="UTF-8", to="ASCII", sub="")
# removing retweets, in case needed 
tweets <-gsub("(RT|via)((?:\\b\\w*@\\w+)+)","",tweets)
# removing mentions, in case needed
tweets <-gsub("@\\w+","",tweets)
tweets <- gsub("[[:punct:]]","",tweets)
#removing numbers
tweets <- gsub("\\w*[0-9]+\\w*\\s*", "",tweets)

ew_sentiment<-get_nrc_sentiment((tweets),language="catalan")
sentimentscores<-data.frame(colSums(ew_sentiment[,]))
names(sentimentscores) <- "Score"
sentimentscores <- cbind("sentiment"=rownames(sentimentscores),sentimentscores)
rownames(sentimentscores) <- NULL
ggplot(data=sentimentscores,aes(x=sentiment,y=Score))+
  geom_bar(aes(fill=sentiment),stat = "identity")+
  theme(legend.position="none")+
  xlab("Sentiments")+ylab("Scores")+
  geom_text(aes(label = Score),
            vjust = 1.5, color = "black",
            size = 5)+
  ggtitle("Total sentiment based on scores - @VallsAjuntament")+
  theme_minimal()

Sentimientos

Como juego de pruebas hemos realizaado un pequeño experimento, cambiando la cuenta analizada por la de un partido político de alcance nacional con un mensaje social bastante más agresivo. Como se observa, los resultados del análisis son diferentes.

Sentimientos de Vox

Como se observa podemos realizar análisis potentes con las herramientas adecuadas. Cual sería el reto para una AAPP? Generar un sistema de escucha activa y continuada del ciudadano, sobre todos los canales de comunicación y atención disponibles:

  1. Redes sociales corporativas

La propuesta seria con Twitter por ejemplo, obtener el listado de los seguidores y recuperar sus mensajes publicados en un periodo de tiempo. Otra alternativa, si hay mucho diálogo en redes es usar la función de búsqueda y filtrar por nuestro municipio o por los tweets que son respuestas directas a la cuenta.

#Reply a la cuenta
at_rdt <- search_tweets(
  "to:VallsAjuntament", 
  n = 5e2,
  retryonratelimit = TRUE
)
replyData <- subset(at_rdt,(at_rdt$screen_name!="VallsAjuntament"))


## Followers:
flw <- get_followers(
  "VallsAjuntament", n = 5000, retryonratelimit = TRUE
)
#Una vez tenemos los followers, podemos obtener su timeline y analizarlo.

También seria posible analizar otras redes como Instagram o Facebook o cualquier otro canal social donde el ciudadano pueda expresar su opinión.

  1. Quejas y sugerencias El mismo sistema aquí mostrado se puede usar para tokenizar el texto de una queja / consulta y efectuar el análisis de sentimiento
  2. Instancias generales
  3. Llamadas telefónicas (Previa información sobre la grabación), usar herramientas de Voice-to-text, para obtener el texto y otra vez tokenizar. También se podría probar análisis de la propia voz, para detectar las características acústicas y identificar sentimientos.
  4. Videoatención Igual que el caso anterior

A partir de todo este análisis masivo y continuado podríamos crear un indicador dinámico que mostrase este ‘sentimiento ciudadano’ y relacionarlo con los anuncios realizados, las políticas públicas aplicadas, mapificar el estado de ánimo por barrios… En definitiva, una herramienta de escucha activa y automática de la percepción ciudadana.


See also