Packages

library(rtweet)         # EXTRACCIÓN DE TWEETS
library(dplyr)          # MANIPULACIÓN DE DATAFRAMES
library(tidyr)          # LIMPIEZA DE DATOS
library(ggplot2)        # GRÁFICOS
library(tm)             # MANIPULACIÓN DE TEXTO
library(wordcloud)      # GRÁFICOS: NUBES DE PALABRAS
library(tidytext)       # LIMPIEZA DE TEXTO
library(stopwords)      # NLP: ELIMINA PALABRAS INTRASCENDENTES ("POR", "SI", "DE", ETC.)
library(widyr)          # MANIPULACIÓN DE TEXTO: CORRELACIONES ENTRE PALABRAS
library(igraph)         # CREACIÓN DE GRAFOS
library(ggraph)         # REPRESENTACIÓN DE GRAFOS
library(syuzhet)        # MANIPULACIÓN DE TEXTO: ANÁLISIS DE SENTIMIENTOS
library(RColorBrewer)   # PALETA DE COLORES
library(grid)           # GRÁFICOS
library(gridExtra)      # GRÁFICOS

Lectura

refugiados <- read_twitter_csv("refug_twitter_0821.csv")
refugiados$created_at <- strftime(refugiados$created_at, format = "%Y-%m-%d")
refugiados$created_at <- as.Date(refugiados$created_at)

Separar tweets originales de retweets y respuestas

# ANÁLISIS DE TEXTO:
# you first need to distinguish between organic tweets, retweets and replies. 
# Remove retweets
ref_organico <- refugiados[refugiados$is_retweet==FALSE, ]

# Remove replies
ref_organico <- ref_organico[is.na(ref_organico$reply_to_status_id),]

Limpieza de texto

ref_organico$text <- gsub("https\\S*", "", ref_organico$text) # Hyperlinks
ref_organico$text <- gsub("@\\S*", "", ref_organico$text) # Menciones
ref_organico$text <- gsub("amp", "", ref_organico$text) # amp
ref_organico$text <- gsub("[\r\n]", "", ref_organico$text) # otros
ref_organico$text <- gsub("#", "", ref_organico$text)
ref_organico$text <- gsub("[^[:alpha:][:space:]]*", "", ref_organico$text)

Cear un corpus único de palabras, manteniendo la referencia al tweet de origen

palabrasRef <- ref_organico %>% unnest_tweets(word, text) %>% 
    filter(!word %in% c(stopwords("spanish"),"si", "través", "va", "van","ser","uf","así", "hace", "tres", "co", "q", "von","der","ee","uu")) %>% 
    select(status_id, word)

ANALIZAR CORRELACION ENTRE PALABRAS

Una función útil de widyr es la función pairwise_count(). El prefijo pairwise_ significa que resultará en una fila por cada par de palabras en la columna de palabras (variable). Esto nos permite contar pares comunes de palabras que aparecen conjuntamente dentro del mismo tweet dada la columna status_id (variable).

paresRef <- palabrasRef %>% 
    pairwise_count(word, status_id, sort = TRUE)

paresRef2 <- palabrasRef %>% 
    pairwise_count(word, status_id, sort = TRUE) %>% 
    filter(item1>item2) # Este filter() elimina los pares repetidos e invertidos
word_correl <- palabrasRef %>% 
    group_by(word) %>% 
    filter(n() >= 50) %>% 
    pairwise_cor(word, status_id, sort = TRUE)

word_correl
## # A tibble: 311,922 x 3
##    item1        item2        correlation
##    <chr>        <chr>              <dbl>
##  1 unido        reino              0.970
##  2 reino        unido              0.970
##  3 ucab         cdh                0.964
##  4 cdh          ucab               0.964
##  5 esclavitud   moderna            0.927
##  6 moderna      esclavitud         0.927
##  7 adolescentes cdh                0.888
##  8 cdh          adolescentes       0.888
##  9 latina       américa            0.877
## 10 américa      latina             0.877
## # ... with 311,912 more rows

Visualizar un network de co-occurrencias con “ggraph”

Para analizar la red semántica de tweets entre “refugiados”, también podemos visualizar todas las co-ocurrencias entre las palabras. Y como una visualización común, podemos organizar las palabras en una red o “gráfico”. Aquí nos referiremos a un “gráfico” no en el sentido de una visualización, sino como una combinación de nodos conectados.

Se puede construir un gráfico a partir de un objeto ordenado ya que tiene tres variables: - from: el nodo del que proviene un borde - to: el nodo al que se dirige un borde - weight: un valor numérico asociado con cada borde (enlace)

Aquí usaremos el paquete igraph que proporciona muchas funciones poderosas para manipular y analizar redes.

Una forma de crear un objeto igraph a partir de nuestros datos ordenados es la función graph_from_data_frame(), que toma un marco de datos de bordes con columnas para “from”, “to” y atributos o fortalezas de borde (grade, en este caso correlation):

word_correl %>% 
    filter(correlation > .3) %>% 
    graph_from_data_frame()
## IGRAPH 235442d DN-- 124 406 -- 
## + attr: name (v/c), correlation (e/n)
## + edges from 235442d (vertex names):
##  [1] unido       ->reino        reino       ->unido       
##  [3] ucab        ->cdh          cdh         ->ucab        
##  [5] esclavitud  ->moderna      moderna     ->esclavitud  
##  [7] adolescentes->cdh          cdh         ->adolescentes
##  [9] latina      ->américa      américa     ->latina      
## [11] adolescentes->ucab         ucab        ->adolescentes
## [13] sentirá     ->inevitable   inevitable  ->sentirá     
## [15] rica        ->costa        costa       ->rica        
## + ... omitted several edges
set.seed(180662) # Fijar rl método de trazado de nodos en la red 

El paquete igraph tiene funciones de trazado integradas, pero no son para lo que está diseñado el paquete, por lo que muchos otros paquetes han desarrollado métodos de visualización para objetos gráficos. Recomendamos el paquete ggraph(Pederson 2017), porque implementa estas visualizaciones en términos de gramática de gráficos, que ya conocemos con ggplot2.

Podemos convertir un objeto igraph en uno ggraph con las funciones ggraph, después de lo cual le agregamos capas, al igual que las capas se agregan en ggplot2. Por ejemplo, para un gráfico básico, necesitamos agregar tres capas: nodos, bordes (enlaces) y texto.

# Trazar red de correlaciones
word_correl %>% 
    filter(correlation > .5) %>%
    graph_from_data_frame() %>% 
    ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = correlation), show.legend = FALSE) +
    geom_node_point(color = "plum4", size = 3) +
    geom_node_text(aes(label = name), repel = TRUE) +
    theme_void() # white background

NUBES DE PALABRAS

# Create the Text Corpus
corpusText = Corpus(VectorSource(ref_organico$text))

# Elimina mayusculas
corpusText = tm_map(corpusText,PlainTextDocument)
corpusText = tm_map(corpusText, tolower)

# Elimina interpuncción
corpusText <- tm_map(corpusText, removePunctuation)

# Elimina conjunciones y preposiciones (spanish)
myStopworld <- c(stopwords("spanish"), "si", "través", "va", "van","ser","uf","así", "hace", "tres", "co", "q", "von","der","ee","uu")
corpusText <- tm_map(corpusText, removeWords, myStopworld)

# Elimina números
corpusText = tm_map(corpusText, removeNumbers)


# Elimina espacios en blanco en exceso.
corpusText = tm_map(corpusText,stripWhitespace)


# Create the term document matrix
TDM <- TermDocumentMatrix(corpusText)
matriz <- as.matrix(TDM)
ftext <- sort(rowSums(matriz),decreasing=TRUE)
dattext <- data.frame(palabra = names(ftext),freq=ftext)

Barplot de palabras más frecuentes

# Barplot con las palabra más frecuentes
dattext %>% 
    top_n(25) %>%
    ggplot(aes(x=palabra,y=freq))+
    geom_bar(stat = "identity")+
    coord_flip()

Wordcloud

nb.cols <- 18
mycolors <- colorRampPalette(brewer.pal(8, "Dark2"))(nb.cols)
getPalette = colorRampPalette(brewer.pal(9, "Dark2"))
wordcloud(words = dattext$palabra, freq = dattext$freq, random.order=TRUE,scale=c(8,.7),
          max.words = 100, min.freq = 200,rot.per = .3, colors = brewer.pal(name = "Dark2", n = 8))
text(x=0.5,
     y=0,
     "Fuente: Tweets obtenidos de la API de RESTful de Twitter, via rtweet en agosto 2021",
     cex=0.7)

Extracción de datos con el Léxico de Sentimientos NRC

Ahora ya podemos ejecutar la función get_nrc_sentiment para obtener los sentimientos. Ahora bien, puesto que la función ejecuta por defecto el vocabulario en inglés, nosotros le indicamos con el argumento “lang” (de language) que utilice el vocabulario en español (“spanish”). A su vez, creamos un nuevo objeto para almacenar los datos extraidos. Esto será un objeto de tipo data frame. Esta función busca la presencia de las ocho emociones y los dos sentimientos para cada palabra en nuestro vector, y asigna un número mayor a 0 en caso de existir. Dependiendo de las prestaciones de tu computadora y de acuerdo con las características de nuestro texto, este proceso puede tardar entre 15 y 30 minutos.

# ANÁLISIS DE SENTIMIENTOS
emocion.df <- get_nrc_sentiment(char_v = ref_organico$text, language = "spanish")

emocion.df2 <- cbind(ref_organico, emocion.df)

#Empezamos transponiendo emocion.df
emocion.df3 <- data.frame(t(emocion.df))

#Sumamos los puntajes de cada uno de los tweets para cada emocion
emocion.df3 <- data.frame(rowSums(emocion.df3))

#Nombramos la columna de puntajes como cuenta
names(emocion.df3)[1] <- "cuenta"

#Dado que las emociones son los nombres de las filas y no una variable
#transformamos el data frame para incluirlas dentro
emocion.df3 <- cbind("sentimiento" = rownames(emocion.df3), emocion.df3)

#Quitamos el nombre de las filas
rownames(emocion.df3) <- NULL

#Verificamos el data frame
head(emocion.df3)
##    sentimiento cuenta
## 1        anger   4004
## 2 anticipation   5002
## 3      disgust   2649
## 4         fear   7403
## 5          joy   2874
## 6      sadness   6255
a <- emocion.df3[1:8,] %>% 
    ggplot(aes(x=sentimiento,y=cuenta, fill=sentimiento)) +
    geom_bar(stat = "identity")

b <- emocion.df3[9:10,] %>% 
    group_by(sentimiento) %>%
    mutate(Porcentaje= cuenta/sum(emocion.df3$cuenta[9:10])) %>% 
    ggplot(aes(x=sentimiento,y=cuenta, fill=sentimiento)) +
    geom_bar(stat = "identity")+
    geom_text(aes(label=scales::percent(Porcentaje, 0.1)), vjust=1, colour = "white")
grid.arrange(a,b, nrow=2)