Analisi del sentiment basata sul lessico utilizzando R |  di Okan Bulut |  Febbraio 2024

 | Intelligenza-Artificiale

Per semplicità ci concentreremo sulla prima ondata della pandemia (marzo 2020 – giugno 2020). Le trascrizioni di tutti i briefing dei media erano disponibili al pubblico sul sito web della pandemia COVID-19 del governo dell’Alberta (https://www.alberta.ca/covid). Questo set di dati viene fornito con un file licenza dati aperti che consenta al pubblico l’accesso e l’utilizzo delle informazioni, anche per scopi commerciali. Dopo aver importato queste trascrizioni in R, ho trasformato tutto il testo in minuscolo e quindi ho applicato la tokenizzazione delle parole utilizzando il file testo ordinato E tokenizzatori Pacchetti. La tokenizzazione delle parole ha suddiviso le frasi nei briefing con i media in singole parole per ciascuna voce (ad esempio, il giorno dei briefing con i media). Successivamente, ho applicato la lemmatizzazione ai token per risolvere ogni parola nella sua forma canonica utilizzando il metodo textstem pacchetto. Infine, ho rimosso le stopword più comuni, come “mio”, “per”, “quello”, “con” e “per”, utilizzando il pacchetto stopword. Il set di dati finale è disponibile Qui. Ora importiamo i dati in R e quindi esaminiamo il relativo contenuto.

load("wave1_alberta.RData")

head(wave1_alberta, 10)

Un’anteprima del set di dati (immagine dell’autore)

Il set di dati ha tre colonne:

  • mese (il mese della conferenza stampa)
  • data (la data esatta della conferenza stampa) e
  • parola (parole o simboli utilizzati nel briefing con i media)

Analisi descrittiva

Ora possiamo calcolare alcune statistiche descrittive per comprendere meglio il contenuto del nostro set di dati. Inizieremo trovando le prime 5 parole (in base alla loro frequenza) per ogni mese.

library("dplyr")

wave1_alberta %>%
group_by(month) %>%
count(word, sort = TRUE) %>%
slice_head(n = 5) %>%
as.data.frame()

Le 5 parole migliori per mese (Immagine dell’autore)

Il risultato mostra che parole come salute, continuazione e test sono state comunemente utilizzate nei briefing dei media in questo periodo di 4 mesi. Possiamo anche espandere il nostro elenco alle 10 parole più comuni e visualizzare visivamente i risultati:

library("tidytext")
library("ggplot2")

wave1_alberta %>%
# Group by month
group_by(month) %>%
count(word, sort = TRUE) %>%
# Find the top 10 words
slice_head(n = 10) %>%
ungroup() %>%
# Order the words by their frequency within each month
mutate(word = reorder_within(word, n, month)) %>%
# Create a bar graph
ggplot(aes(x = n, y = word, fill = month)) +
geom_col() +
scale_y_reordered() +
facet_wrap(~ month, scales = "free_y") +
labs(x = "Frequency", y = NULL) +
theme(legend.position = "none",
axis.text.x = element_text(size = 11),
axis.text.y = element_text(size = 11),
strip.background = element_blank(),
strip.text = element_text(colour = "black", face = "bold", size = 13))

Parole più comuni in base alla frequenza (Immagine dell’autore)

Poiché alcune parole sono comuni in tutti e quattro i mesi, il grafico qui sopra potrebbe non mostrarci necessariamente le parole importanti che sono uniche per ciascun mese. Per trovare parole così importanti, possiamo usare la frequenza dei termini – frequenza inversa del documento (TF-IDF) – una tecnica ampiamente utilizzata in PNL per misurare quanto sia importante un termine all’interno di un documento rispetto a una raccolta di documenti (per informazioni più dettagliate su TF -IDF, controlla il mio precedente post sul blog). Nel nostro esempio, tratteremo i briefing dei media per ogni mese come un documento e calcoleremo TF-IDF per i token (cioè le parole) all’interno di ciascun documento. La prima parte dei codici R seguenti crea un nuovo set di dati, wave1_tf_idfcalcolando TF-IDF per tutti i token e selezionando i token con i valori TF-IDF più alti entro ogni mese. Successivamente, utilizziamo questo set di dati per creare un grafico a barre con i valori TF-IDF per visualizzare le parole comuni uniche per ogni mese.

# Calculate TF-IDF for the words for each month
wave1_tf_idf <- wave1_alberta %>%
count(month, word, sort = TRUE) %>%
bind_tf_idf(word, month, n) %>%
arrange(month, -tf_idf) %>%
group_by(month) %>%
top_n(10) %>%
ungroup

# Visualize the results
wave1_tf_idf %>%
mutate(word = reorder_within(word, tf_idf, month)) %>%
ggplot(aes(word, tf_idf, fill = month)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ month, scales = "free", ncol = 2) +
scale_x_reordered() +
coord_flip() +
theme(strip.background = element_blank(),
strip.text = element_text(colour = "black", face = "bold", size = 13),
axis.text.x = element_text(size = 11),
axis.text.y = element_text(size = 11)) +
labs(x = NULL, y = "TF-IDF")

Parole più comuni basate su TIF-IDF (Immagine dell’autore)

Questi risultati sono più informativi perché i token mostrati nella figura riflettono argomenti unici discussi ogni mese. Ad esempio, nel marzo 2020, i briefing dei media riguardavano principalmente la limitazione dei viaggi, il ritorno da conferenze affollate e i casi di COVID-19 sulle navi da crociera. Nel giugno 2020, il focus dei briefing con i media si è spostato sull’obbligo delle mascherine, sulle persone che protestavano contro le restrizioni legate alla pandemia e così via.

Prima di tornare all’analisi del sentiment, diamo un’occhiata a un’altra variabile descrittiva: la durata di ciascun briefing con i media. Questo ci mostrerà se i briefing con i media sono diventati più lunghi o più brevi nel tempo.

wave1_alberta %>%
# Save "day" as a separate variable
mutate(day = substr(date, 9, 10)) %>%
group_by(month, day) %>%
# Count the number of words
summarize(n = n()) %>%
ggplot(aes(day, n, color = month, shape = month, group = month)) +
geom_point(size = 2) +
geom_line() +
labs(x = "Days", y = "Number of Words") +
theme(legend.position = "none",
axis.text.x = element_text(angle = 90, size = 11),
strip.background = element_blank(),
strip.text = element_text(colour = "black", face = "bold", size = 11),
axis.text.y = element_text(size = 11)) +
ylim(0, 800) +
facet_wrap(~ month, scales = "free_x")
Numero di parole nei briefing con i media per giorno (immagine per autore)

La figura sopra mostra che la durata dei briefing con i media è variata in modo sostanziale nel tempo. Soprattutto nei mesi di marzo e maggio si registrano fluttuazioni maggiori (ad esempio briefing molto lunghi o brevi), mentre a giugno i briefing giornalieri con i media sono abbastanza simili in termini di durata.

Analisi del sentiment con tidytext

Dopo aver analizzato in modo descrittivo il set di dati, siamo pronti per iniziare con l’analisi del sentiment. Nella prima parte utilizzeremo il file testo ordinato pacchetto per eseguire l’analisi del sentiment e calcolare i punteggi del sentiment. Importeremo prima i lessici in R e poi li uniremo al nostro set di dati. Utilizzando il lessico di Bing, dobbiamo trovare la differenza tra il numero di parole positive e negative per produrre un punteggio del sentiment (ovvero, sentiment = il numero di parole positive – il numero di parole negative).

# From the three lexicons, Bing is already available in the tidytext page
# for AFINN and NRC, install the textdata package by uncommenting the next line
# install.packages("textdata")
get_sentiments("bing")
get_sentiments("afinn")
get_sentiments("nrc")

# We will need the spread function from tidyr
library("tidyr")

# Sentiment scores with bing (based on frequency)
wave1_alberta %>%
mutate(day = substr(date, 9, 10)) %>%
group_by(month, day) %>%
inner_join(get_sentiments("bing")) %>%
count(month, day, sentiment) %>%
spread(sentiment, n) %>%
mutate(sentiment = positive - negative) %>%
ggplot(aes(day, sentiment, fill = month)) +
geom_col(show.legend = FALSE) +
labs(x = "Days", y = "Sentiment Score") +
ylim(-50, 50) +
theme(legend.position = "none", axis.text.x = element_text(angle = 90)) +
facet_wrap(~ month, ncol = 2, scales = "free_x") +
theme(strip.background = element_blank(),
strip.text = element_text(colour = "black", face = "bold", size = 11),
axis.text.x = element_text(size = 11),
axis.text.y = element_text(size = 11))

Punteggi del sentiment basati sul lessico di Bing (immagine dell’autore)

La figura sopra mostra che i sentimenti espressi nei briefing con i media erano generalmente negativi, il che non è necessariamente sorprendente dal momento che i briefing con i media riguardavano il numero di persone decedute, i tassi di ospedalizzazione, potenziali epidemie, ecc. In determinati giorni (ad esempio, il 24 marzo , 2020 e 4 maggio 2020), i briefing con i media sono stati particolarmente più negativi in ​​termini di sentiment.

Successivamente utilizzeremo il lessico AFINN. A differenza di Bing che etichetta le parole come positive o negative, AFINN assegna un peso numerico a ciascuna parola. Il segno del peso indica la polarità dei sentimenti (cioè positivo o negativo), mentre il valore indica l’intensità dei sentimenti. Ora vediamo se questi valori ponderati producono punteggi di sentiment diversi.

wave1_alberta %>%
mutate(day = substr(date, 9, 10)) %>%
group_by(month, day) %>%
inner_join(get_sentiments("afinn")) %>%
group_by(month, day) %>%
summarize(sentiment = sum(value),
type = ifelse(sentiment >= 0, "positive", "negative")) %>%
ggplot(aes(day, sentiment, fill = type)) +
geom_col(show.legend = FALSE) +
labs(x = "Days", y = "Sentiment Score") +
ylim(-100, 100) +
facet_wrap(~ month, ncol = 2, scales = "free_x") +
theme(legend.position = "none",
strip.background = element_blank(),
strip.text = element_text(colour = "black", face = "bold", size = 11),
axis.text.x = element_text(size = 11, angle = 90),
axis.text.y = element_text(size = 11))
Punteggi del sentiment basati sul lessico AFINN (immagine dell’autore)

I risultati basati sul lessico AFINN sembrano essere abbastanza diversi! Tenendo conto del “peso” dei token, la maggior parte dei briefing dei media si rivela positiva (vedi barre verdi), anche se ci sono ancora alcuni giorni con sentiment negativi (vedi barre rosse). Le due analisi che abbiamo svolto finora hanno dato risultati molto diversi per due motivi. Innanzitutto, come ho già detto, il lessico di Bing si concentra sulla polarità delle parole ma ignora l’intensità delle parole (antipatia e odio sono considerate parole negative con uguale intensità). A differenza del lessico Bing, il lessico AFINN tiene conto dell’intensità, che influisce sul calcolo dei punteggi del sentiment. In secondo luogo, il lessico Bing (6786 parole) è abbastanza più grande del lessico AFINN (2477 parole). Pertanto, è probabile che alcuni token contenuti nei briefing dei media siano inclusi nel lessico di Bing, ma non nel lessico di AFINN. Ignorare questi token potrebbe aver influito sui risultati.

Il lessico finale che proveremo a utilizzare è il testo ordinato il pacchetto è NRC. Come accennato in precedenza, questo lessico utilizza la teoria psicoevolutiva di Plutchik per etichettare i simboli in base alle emozioni di base come rabbia, paura e anticipazione. Conteremo il numero di parole o simboli associati a ciascuna emozione e quindi visualizzeremo i risultati.

wave1_alberta %>%
mutate(day = substr(date, 9, 10)) %>%
group_by(month, day) %>%
inner_join(get_sentiments("nrc")) %>%
count(month, day, sentiment) %>%
group_by(month, sentiment) %>%
summarize(n_total = sum(n)) %>%
ggplot(aes(n_total, sentiment, fill = sentiment)) +
geom_col(show.legend = FALSE) +
labs(x = "Frequency", y = "") +
xlim(0, 2000) +
facet_wrap(~ month, ncol = 2, scales = "free_x") +
theme(strip.background = element_blank(),
strip.text = element_text(colour = "black", face = "bold", size = 11),
axis.text.x = element_text(size = 11),
axis.text.y = element_text(size = 11))
Punteggi del sentiment basati sul lessico NRC (immagine dell’autore)

La figura mostra che i briefing con i media sono per lo più positivi ogni mese. Il dottor Hinshaw ha usato parole associate a “fiducia”, “anticipazione” e “paura”. Nel complesso, il modello di queste emozioni sembra rimanere molto simile nel tempo, indicando la coerenza dei briefing con i media in termini di tipo e intensità delle emozioni trasmesse.

Un altro pacchetto per l’analisi del sentiment basata sul lessico è sentimento (Rinker, 2021). non mi piace il testo ordinato pacchetto, questo pacchetto tiene conto degli spostamenti di valenza (ad esempio, la negazione), che possono facilmente invertire la polarità di una frase con una parola. Ad esempio, la frase “non sono infelice” in realtà è positiva, ma se la analizziamo parola per parola, potrebbe sembrare che la frase abbia un sentimento negativo a causa delle parole “non” e “infelice”. Allo stesso modo, “Non mi piace questo libro” è una frase negativa, ma l’analisi delle singole parole, “difficilmente” e “mi piace”, può produrre un punteggio di sentiment positivo. IL sentimento pacchetto risolve le limitazioni relative al rilevamento del sentiment con i valence shifter (vedere la pagina Github dell’autore del pacchetto Tyler Rinker per ulteriori dettagli su sentimento: https://github.com/trinker/sentimentr).

Per beneficiare del sentimento pacchetto, abbiamo bisogno delle frasi vere e proprie nelle conferenze stampa piuttosto che dei singoli token. Pertanto, ho dovuto creare una versione senza token del set di dati, che è disponibile Qui. Per prima cosa importeremo questo set di dati in R, otterremo frasi individuali per ogni briefing con i media utilizzando il file get_sentences() funzione, quindi calcolare i punteggi del sentiment per giorno e mese tramite sentiment_by().

library("sentimentr")
library("magrittr")

load("wave1_alberta_sentence.RData")

# Calculate sentiment scores by day and month
wave1_sentimentr <- wave1_alberta_sentence %>%
mutate(day = substr(date, 9, 10)) %>%
get_sentences() %$%
sentiment_by(text, list(month, day))

# View the dataset
head(wave1_sentimentr, 10)

Un’anteprima del set di dati (immagine dell’autore)

Nel set di dati che abbiamo creato, “ave_sentiment” è il punteggio medio del sentiment per ogni giorno di marzo, aprile, maggio e giugno (ovvero i giorni in cui è stato tenuto un briefing con i media). Utilizzando questo set di dati, possiamo visualizzare i punteggi del sentiment.

wave1_sentimentr %>%
group_by(month, day) %>%
ggplot(aes(day, ave_sentiment, fill = ave_sentiment)) +
scale_fill_gradient(low="red", high="blue") +
geom_col(show.legend = FALSE) +
labs(x = "Days", y = "Sentiment Score") +
ylim(-0.1, 0.3) +
facet_wrap(~ month, ncol = 2, scales = "free_x") +
theme(legend.position = "none",
strip.background = element_blank(),
strip.text = element_text(colour = "black", face = "bold", size = 11),
axis.text.x = element_text(size = 11, angle = 90),
axis.text.y = element_text(size = 11))
Punteggi del sentiment basati sul sentiment (immagine dell’autore)

Nella figura sopra, le barre blu rappresentano punteggi di sentiment altamente positivi, mentre le barre rosse rappresentano punteggi di sentiment relativamente più bassi. I modelli osservati nei punteggi del sentiment generati da sentimento somigliano molto a quelli derivati ​​dal lessico AFINN. In particolare, questa analisi si basa sui briefing originali dei media piuttosto che esclusivamente su token, tenendo conto dei mutamenti di valenza nel calcolo dei punteggi del sentiment. La convergenza tra i modelli di sentiment identificati da sentimento e quelli dell’AFINN non sono del tutto inaspettati. Entrambi gli approcci incorporano sistemi e meccanismi di ponderazione simili che tengono conto dell’intensità delle parole. Questo allineamento rafforza la nostra fiducia nei risultati iniziali ottenuti tramite AFINN, convalidando la coerenza e l’affidabilità delle nostre analisi sentimento.

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *