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
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)
# 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),]
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)
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)
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
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
# 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 con las palabra más frecuentes
dattext %>%
top_n(25) %>%
ggplot(aes(x=palabra,y=freq))+
geom_bar(stat = "identity")+
coord_flip()
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)
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)