Quer compartilhar seu conteúdo em R-bloggers? clique aqui se você tiver um blog, ou aqui se não tiver.
Ler artigos de notícias sobre as negociações comerciais pós-Brexit com a UE vão-eles-não-vão ver dias de otimismo abalados por dias de tristeza. As notícias negativas, quando se deseja um resultado positivo, deixam uma impressão mais profunda?
Gostaria de saber se poderia obter uma visão mais objetiva da análise quantitativa de dados textuais. Para fazer isso, vou examinar centenas de artigos publicados no jornal Guardian ao longo do ano para ver como o sentimento do discurso comercial mudou a cada semana.
library(tidyverse)
library(rebus)
library(wesanderson)
library(kableExtra)
library(lubridate)
library(GuardianR)
library(quanteda)
library(scales)
library(tictoc)
library(patchwork)
library(text2vec)
library(topicmodels)
theme_set(theme_bw())
cols
O Acordo de Retirada entre o Reino Unido e a União Europeia foi assinado em 24 de janeiro de 2020. Vou importar artigos de jornais relacionados ao Brexit a partir dessa data.
O jornal The Guardian pede que os pedidos abranjam no máximo 1 mês de cada vez. Portanto, primeiro criarei um conjunto de intervalos de datas mensais.
dates_df %
mutate(end_date = start_date + months(1) - 1)
dates_df %>%
kable()
data de início | data final |
---|---|
24/01/2020 | 23-02-2020 |
24-02-2020 | 23/03/2020 |
24/03/2020 | 23-04-2020 |
24-04-2020 | 23-05-2020 |
24/05/2020 | 23-06-2020 |
24/06/2020 | 23-07-2020 |
24-07-2020 | 23/08/2020 |
24-08-2020 | 23-09-2020 |
Vou importar os artigos do jornal em blocos mensais. Observe que o acesso à API do Guardian requer uma chave que pode ser solicitada aqui.
tic()
article_df %
pmap_dfr(., function(start_date, end_date) {
Sys.sleep(1)
get_guardian(
"brexit",
from.date = start_date,
to.date = end_date,
api.key = key
)
})
toc()
Os dados precisam de uma pequena limpeza, por exemplo, para remover artigos com vários tópicos, tags html e espaços não quebráveis.
trade_df %
filter(!str_detect(id, "/live/"), sectionId %in% c("world", "politics", "business")) %>%
mutate(
body = str_remove_all(body, "<.>") %>% str_to_lower(),
body = str_remove_all(body, "[^a-z0-9 .-]"),
body = str_remove_all(body, "nbsp")
)
Um corpus então me dá uma coleção de textos em que cada documento é um artigo de jornal.
trade_corp %
corpus(docid_field = "shortUrl", text_field = "body")
Embora eu só importe artigos que mencionam o Brexit desde a assinatura do Acordo de Retirada, alguns desses artigos não serão relacionados a negociações comerciais com a UE. Por exemplo, há negociações em andamento com muitos países ao redor do mundo. Portanto, vou usar embeddings de palavras para ajudar a estreitar o foco para o contexto específico do acordo comercial entre o Reino Unido e a UE.
O negociador-chefe da UE é Michel Barnier, portanto, identificarei quantitativamente palavras próximas a “Barnier” no contexto desses artigos de notícias do Brexit.
window %
fcm(context = "window", window = window, count = "weighted", weights = window:1)
glove
## INFO [10:06:33.114] epoch 1, loss 0.3817
## INFO [10:06:34.959] epoch 2, loss 0.2510
## INFO [10:06:36.759] epoch 3, loss 0.2225
## INFO [10:06:38.577] epoch 4, loss 0.2021
## INFO [10:06:40.438] epoch 5, loss 0.1847
## INFO [10:06:42.303] epoch 6, loss 0.1710
## INFO [10:06:44.124] epoch 7, loss 0.1605
## INFO [10:06:45.936] epoch 8, loss 0.1524
## INFO [10:06:47.754] epoch 9, loss 0.1457
## INFO [10:06:49.594] epoch 10, loss 0.1403
wv_context %
sim2(search_coord, method = "cosine") %>%
as_tibble(rownames = NA) %>%
rownames_to_column("term") %>%
rename(similarity = 2) %>%
arrange(desc(similarity)) %>%
slice(1:10) %>%
kable()
prazo | similaridade |
---|---|
celeiro | 1.0000000 |
negociador | 0,7966461 |
Michel | 0,7587372 |
geada | 0,7093119 |
eus | 0,6728152 |
chefe | 0,6365480 |
Bruxelas | 0,5856139 |
negociadores | 0,5598537 |
equipe | 0,5488111 |
acusado | 0,5301669 |
A incorporação de palavras é uma técnica de modelagem aprendida que coloca palavras em um espaço vetorial multidimensional de forma que palavras contextualmente semelhantes possam ser encontradas por perto. Não surpreendentemente, a palavra mais próxima contextualmente é “Michel”. E como ele é o negociador-chefe da UE, encontramos “eu’s”, “chief” e “negociador” também nas palavras mais contextualmente semelhantes.
O algoritmo de incorporação de palavras, por meio da coocorrência de palavras, identificou o nome do homólogo britânico de Michel Barnier, David Frost. Portanto, filtrar artigos para “Barnier”, “Frost” e “UK-EU” deve ajudar a estreitar o foco.
context_df %
filter(str_detect(body, "barnier|frost|uk-eu"))
context_corp %
corpus(docid_field = "shortUrl", text_field = "body")
Posso então usar o de quanteda kwic
função para revisar as frases-chave no contexto para garantir que estou concentrando-me nos textos que desejo. URLs curtos estão incluídos abaixo para que eu possa clicar em qualquer um para ler o artigo real apresentado pelo The Guardian.
set.seed(123)
context_corp %>%
tokens(
remove_punct = TRUE,
remove_symbols = TRUE,
remove_numbers = TRUE
) %>%
kwic(pattern = phrase(c("trade negotiation", "trade deal", "trade talks")),
valuetype = "regex", window = 7) %>%
as_tibble() %>%
left_join(article_df, by = c("docname" = "shortUrl")) %>%
slice_sample(n = 10) %>%
select(docname, pre, keyword, post, headline) %>%
kable()
docname | pré | palavra chave | postar | título |
---|---|---|---|---|
https://gu.com/p/ee3qc | ecj a menos que tenhamos tal | Acordo comercial | que não vale o papel, é | Brexit: Boris Johnson enfrenta teste do Eurotunnel |
https://gu.com/p/end82 | Londres, um processo separado para os problemáticos | negociações comerciais | que começou em Londres em | O eurodeputado irlandês na fila para o papel de finanças da UE desocupado devido ao escândalo de bloqueio |
https://gu.com/p/ezjdz | disse que as desvantagens com a UE livre | Acordo comercial | o acordo de livre comércio dos Estados Unidos e nosso | Projeto de lei do Brexit que prejudica enormemente a reputação do Reino Unido, diz ex-embaixador |
https://gu.com/p/d7d9t | pessoas que temos negociado | acordos comerciais | para sempre ela disse enquanto as pessoas criticavam o | Negociações comerciais do Brexit: UE apoiará a Espanha em reivindicações de Gibraltar |
https://gu.com/p/eyzhq | minimizou a perspectiva de alcançar um | Acordo comercial | com a UE a tempo de dezembro | Nº 10 culpa UE e minimiza as perspectivas de acordo comercial do Brexit |
https://gu.com/p/ez2v6 | tornará mais difícil atacar | acordos comerciais | daqui para frente, ele contou ao canal notícias depois | Brexit: negociadores do Reino Unido ‘acreditam que a atitude precipitada reiniciará as negociações comerciais’ |
https://gu.com/p/d7n4t | alinhamento com as regras da UE em qualquer brexit | Acordo comercial | enquanto Bruxelas ameaçou colocar tarifas sobre | Libra cai enquanto Boris Johnson assume posição dura sobre acordo comercial da UE |
https://gu.com/p/dnvbj | relacionamento pessoal ao se comunicar remotamente relacionado pós-brexit | negociações comerciais | com eu a caminho de reprovar johnson | Teme que as negociações do Brexit possam fracassar em junho, mas o Reino Unido ainda está otimista |
https://gu.com/p/d94j9 | esta situação e trabalhamos em um | Acordo comercial | com eles, claro, o reino unido | Ursula von der Leyen zomba da posição de Boris Johnson sobre o acordo comercial da UE |
https://gu.com/p/ezkxc | ameaça prejudicar as perspectivas britânicas de | acordos comerciais | com o nós e eu coloca | Reunião de terça-feira: Rancor à medida que avança projeto de lei infrator |
A Quanteda disponibiliza um dicionário de sentimentos que, além de identificar palavras positivas e negativas, encontra também negativos-negativos e negativos-positivos como, por exemplo, “ineficaz”. Para cada semana de artigos, vou calcular a proporção de sentimentos positivos.
tic()
sent_df %
dfm(dictionary = data_dictionary_LSD2015) %>%
as_tibble() %>%
left_join(context_df, by = c("doc_id" = "shortUrl")) %>%
mutate(
date = ceiling_date(as_date(webPublicationDate), "week"),
pct_pos = (positive + neg_negative) / (positive + neg_negative + negative + neg_positive)
)
sent_df %>%
select(doc_id, starts_with("pos"), starts_with("neg")) %>%
slice(1:10) %>%
kable()
summary_df %
group_by(date) %>%
summarise(pct_pos = mean(pct_pos), n = n())
toc()
## 0.708 sec elapsed
Traçar a proporção variável do sentimento positivo ao longo do tempo me surpreendeu um pouco. O resultado foi mais equilibrado do que eu esperava, o que talvez confirme a impressão mais profunda deixada em mim pelos artigos negativos.
O gráfico do violino superior mostra o peso médio do sentimento em vários artigos para cada semana. Individualmente, os artigos variam de 20% a 80% positivos, com períodos discerníveis de sentimento relativamente negativo e relativamente positivo.
O gráfico inferior mostra o volume de artigos. À medida que nos aproximamos do ponto crítico, o volume parece estar aumentando.
p1 %
ggplot(aes(date, pct_pos)) +
geom_violin(aes(group = date), alpha = 0.5, fill = cols[1]) +
geom_line(data = summary_df, aes(date, pct_pos), colour = cols[1], linetype = "dashed") +
geom_hline(yintercept = 0.5, linetype = "dotted", colour = cols[4]) +
scale_y_continuous(labels = percent_format(), limits = c(0.2, 0.8)) +
labs(title = "Changing Sentiment Towards a UK-EU Trade Deal",
subtitle = "Week-to-week Since the Withdrawal Agreement",
x = NULL, y = "Positive Sentiment")
p2 %
ggplot(aes(date, n)) +
geom_line(colour = cols[1]) +
labs(x = "Weeks", y = "Article Count",
caption = "Source: Guardian Newspaper")
p1 / p2 +
plot_layout(heights = c(2, 1))
Alguns escritores exibem mais variação de sentimento do que outros.
byline_df %
mutate(byline = word(byline, 1, 2) %>% str_remove_all(PUNCT)) %>%
group_by(byline, date) %>%
summarise(pct_pos = mean(pct_pos), n = n())
top_3 %
count(byline, sort = TRUE) %>%
ungroup() %>%
filter(!is.na(byline)) %>%
slice(c(3, 2)) %>%
pull(byline)
byline_df %>%
filter(byline %in% top_3) %>%
ggplot(aes(date, pct_pos, colour = byline)) +
geom_line() +
geom_hline(yintercept = 0.5, linetype = "dotted", colour = cols[2]) +
scale_y_continuous(labels = percent_format(), limits = c(0.2, 0.8)) +
scale_colour_manual(values = cols[c(1, 4)]) +
labs(title = "Changing Sentiment Towards a UK-EU Trade Deal",
subtitle = "Week-to-week Since the Withdrawal Agreement",
x = "Weeks", y = "Positive Sentiment", colour = "Byline",
caption = "Source: Guardian Newspaper")
Relacionados