Análisis de Sentimiento
Ir al Capítulo anterior.
Ir al Capítulo siguiente.
La contabilización y visualización de palabras de un documento ya nos ofrece información valiosa sobre su contenido. Sin embargo, mediante el análisis de sentimientos podemos profundizar en las actitudes y el tono emocional de las opiniones expresadas en el texto.
La base del análisis de sentimientos se encuentra en el uso de un léxico o vocabulario de sentimientos. El paquete tidytext
contiene cuatro léxicos o diccionarios de sentimientos, a los que podemos acceder mediante la función get_sentiments ()
.
Cargar librerías
library(dplyr)
library(tidyr)
library(tidytext)
library(forcats)
library(ggplot2)
Diccionario de sentimientos
# Bing dictionary
get_sentiments("bing")
## # A tibble: 6,786 × 2
## word sentiment
## <chr> <chr>
## 1 2-faces negative
## 2 abnormal negative
## 3 abolish negative
## 4 abominable negative
## 5 abominably negative
## 6 abominate negative
## 7 abomination negative
## 8 abort negative
## 9 aborted negative
## 10 aborts negative
## # ℹ 6,776 more rows
# summary
get_sentiments("bing") %>%
count(sentiment)
## # A tibble: 2 × 2
## sentiment n
## <chr> <int>
## 1 negative 4781
## 2 positive 2005
# Afinn dictionary
get_sentiments("afinn")
## # A tibble: 2,477 × 2
## word value
## <chr> <dbl>
## 1 abandon -2
## 2 abandoned -2
## 3 abandons -2
## 4 abducted -2
## 5 abduction -2
## 6 abductions -2
## 7 abhor -3
## 8 abhorred -3
## 9 abhorrent -3
## 10 abhors -3
## # ℹ 2,467 more rows
# summary
get_sentiments("afinn") %>%
summarize(min = min(value),
max = max(value))
## # A tibble: 1 × 2
## min max
## <dbl> <dbl>
## 1 -5 5
# Loughran dictionary
get_sentiments("loughran")
## # A tibble: 4,150 × 2
## word sentiment
## <chr> <chr>
## 1 abandon negative
## 2 abandoned negative
## 3 abandoning negative
## 4 abandonment negative
## 5 abandonments negative
## 6 abandons negative
## 7 abdicated negative
## 8 abdicates negative
## 9 abdicating negative
## 10 abdication negative
## # ℹ 4,140 more rows
# summary
get_sentiments("loughran") %>%
count(sentiment) %>%
arrange(desc(n))
## # A tibble: 6 × 2
## sentiment n
## <chr> <int>
## 1 negative 2355
## 2 litigious 904
## 3 positive 354
## 4 uncertainty 297
## 5 constraining 184
## 6 superfluous 56
# visualize sentiment counts
get_sentiments("loughran") %>%
count(sentiment) %>%
arrange(desc(n)) %>%
ggplot(aes(x = fct_reorder(sentiment, n), y = n)) +
geom_col(fill = "steelblue") +
coord_flip() +
labs(title = "Sentiment counts in Loughran",
x = "Sentiment",
y = "Counts")
# NRC dictionary
get_sentiments("nrc")
## # A tibble: 13,872 × 2
## word sentiment
## <chr> <chr>
## 1 abacus trust
## 2 abandon fear
## 3 abandon negative
## 4 abandon sadness
## 5 abandoned anger
## 6 abandoned fear
## 7 abandoned negative
## 8 abandoned sadness
## 9 abandonment anger
## 10 abandonment fear
## # ℹ 13,862 more rows
# summary
get_sentiments("nrc") %>%
count(sentiment, sort = TRUE)
## # A tibble: 10 × 2
## sentiment n
## <chr> <int>
## 1 negative 3316
## 2 positive 2308
## 3 fear 1474
## 4 anger 1245
## 5 trust 1230
## 6 sadness 1187
## 7 disgust 1056
## 8 anticipation 837
## 9 joy 687
## 10 surprise 532
Cada uno de estos diccionarios se ha creado cuidadosamente para usos específicos, por lo que su utilidad puede variar según el tipo de texto en el que se apliquen. De modo muy general, podríamos decir que Bing, que clasifica las palabras como positivas o negativas, puede ser útil para opiniones generales en reseñas, redes sociales o encuestas. Afinn resulta apropiado para textos informales en los que la intensidad del sentimiento es relevante. Por su parte, Loughran se suele emplear en textos financieros, como informes o declaraciones bursátiles. Finalmente, el diccionario NRC solo clasifica palabras como positivas o negativas, sino que también las asigna a emociones específicas.
Ejercicio : visualiza el recuento de sentimientos del diccionario ‘nrc’.
Diagrama de trabajo
1. Combinar el diccionario con el dataset limpio y 'tokenizado'.
2. Resumir el tono emocional
La función inner_join ()
nos permite combinar dos data frames según una o más columnas clave que tengan en común. Solo devuelve las filas que coinciden en ambos data frames. De esta forma, la dimensión del dataset se reduce significativamente, ya que solo obtenemos el sentimiento de aquellas palabras que están presentes en el diccionario. Por lo tanto, el análisis de sentimiento depende en gran medida del diccionario que utilicemos.
# cargar el objeto limpio y tokenizado
load("res/tidy_climate.rda")
# obtener el sentimiento de las palabras en nuestro dataset
<- tidy_climate %>%
tidy_sentiment inner_join(get_sentiments("loughran"))
# resumir el tono emocional del documento mediante el análisis de las palabras que contribuyen a cada sentimiento
%>%
tidy_sentiment count(sentiment, sort = T)
## # A tibble: 5 × 2
## sentiment n
## <chr> <int>
## 1 negative 714
## 2 positive 124
## 3 uncertainty 99
## 4 litigious 76
## 5 constraining 31
%>%
tidy_sentiment count(word, sentiment, sort = T)
## # A tibble: 361 × 3
## word sentiment n
## <chr> <chr> <int>
## 1 threat negative 57
## 2 question negative 40
## 3 believes uncertainty 25
## 4 drought negative 18
## 5 bad negative 17
## 6 wrong negative 15
## 7 argument negative 14
## 8 worst negative 14
## 9 slow negative 12
## 10 denying negative 11
## # ℹ 351 more rows
%>%
tidy_sentiment count(word, sentiment, sort = T) %>%
filter(sentiment %in% c("negative", "positive")) %>%
group_by(sentiment) %>%
slice(1)
## # A tibble: 2 × 3
## # Groups: sentiment [2]
## word sentiment n
## <chr> <chr> <int>
## 1 threat negative 57
## 2 excited positive 11
# visualizar el tono emocional ('negative/positive') del documento
%>%
tidy_sentiment filter(sentiment %in% c("negative", "positive")) %>%
count(word, sentiment) %>%
group_by(sentiment) %>%
slice_max(n, n = 10) %>%
arrange(sentiment) %>%
ungroup() %>%
#mutate(word2 = reorder_within(word, n, sentiment)) %>% #ordena dentro de cada grupo
ggplot(aes(x = fct_reorder(word, n), y = n, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ sentiment, scales = "free_y") +
coord_flip() +
#scale_x_reordered() +
labs(title = "Sentiment Word Counts",
subtitle = "loughran lexicon",
x = "Words", y ="")
Ejercicio : visualiza el tono emocional (‘constraining’/‘uncertainty/litigious’) usando el diccionario ‘loughran’.
Mejorando el análisis
Mediante la reestructuración o transformación de los datos, las posibilidades que ofrece el análisis, combinado con su posterior visualización, son enormes. Por ejemplo, ¿predominan algunas palabras en el tratamiento que ofrecen determinadas cadenas de televisión de modo que consigan un tono o sentimiento diferente al que ofrecen las otras cadenas? Siguiendo este ejemplo :
- ¿en qué cadena predomina el tono de surprise ?
- ¿en qué cadena predomina el tono de fear ?
- ¿en qué cadena predomina el tono positive ?
## # A tibble: 3 × 11
## # Rowwise:
## station anger anticipation disgust fear joy negative positive sadness
## <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 CNN 9.5% 6.5% 4.4% 7.3% 4.0% 16.0% 23.9% 6.4%
## 2 FOX News 9.4% 8.0% 5.7% 11.6% 5.2% 16.3% 19.8% 7.1%
## 3 MSNBC 8.8% 8.4% 4.2% 8.1% 4.8% 14.3% 25.5% 5.1%
## # ℹ 2 more variables: surprise <chr>, trust <chr>
Comentarios :
* rowwise()
→ asegura que sum() se calcule por fila
* c_across(-station)
→ selecciona todas las columnas menos station
* across()
→ convierte cada número en su porcentaje
* scales::percent()
→ convierte el formato en porcentaje
%>%
by_percent select(station, fear, positive, surprise) %>%
pivot_longer(cols = -station, names_to = "sentiment", values_to = 'percent') %>%
mutate(percent = readr::parse_number(percent) / 100) %>%
ggplot(aes(x = station, y = as.numeric(percent), fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ sentiment, scales = "free_y") +
scale_y_continuous(labels = scales::percent_format()) +
scale_fill_brewer(palette = "Set2") + # paleta de colores suaves
labs(title = "Emotional Tone of Climate Change Narratives",
subtitle = "nrc lexicon",
x = "", y ="% of words contributing to tone")
Comentarios :
parse_number()
→ permite dejar la parte numérica de un vector carácterscale_y_continuous()
→ recupera el formato ‘%’ para la visualización