Análisis exploratorio de topics relacionados con el sistema sanitario a partir de datos recogidos de twitter, con la consulta ‘healthcare’
# librerias
library(qdapRegex)
library(sentimentr)
library(syuzhet)
library(lubridate)
library(tidyverse)
library(tidytext)
library(forcats)
library(tokenizers)
library(widyr)
library(igraph)
library(ggraph)
library(topicmodels)
library(forcats)
library(scales)
library(stringr)
library(extrafont)
library(reldist)
library(grid)
library(gridExtra)
library(boot)
library(ggradar)
library(tidygraph)
library(boot)
# Sys.getenv()
Sys.setenv(JAVA_HOME = "C:\\Program Files\\Java\\jre1.8.0_251")
library(tm)
library(qdap)
# funciones auxiliares
source(here::here("scripts", "funciones_auxiliares.R"))
# paleta
plots_palette <- c("#ad5d51", "grey55", "#2b559e", "#947240")
# orden comunidades
comunidad_order <- c("GOP", "Independent", "DNC", "Progressives")
Carga de datos. Estos datos han sido previamente procesados, sobre todo en lo que se refiere al texto de los tweets. Se excluyen términos relacionados con la pandemia que provoca la enfermedad COVID-19 (en el texto sin limpiar, hay un número amplio de términos para referirse a la enfermedad. Se han unificado todos en el término COVID19).
Se ha recogido también información sobre los seguidores de cuentas para definir 4 comunidades
healthcare <- readRDS(here::here("datos_procesados", "formated_text_df_data", "healthcare.rds")) %>%
filter(str_detect(text, "#?covid19|#?COVID|#?[Pp]andemic", negate = T)) %>%
mutate(comunidad = fct_relevel(comunidad, c("GOP", "Independent", "DNC", "Progressives")))
El número todal de tweets únicos recogidos, sin incluir retweets es de 248.595.
Vemos también que los tweets pertenecientes a las comunidades no independientes son minoría
healthcare %>%
count(comunidad) %>%
mutate(Porcentaje = percent(n /sum(n), accuracy = 0.1))
## # A tibble: 4 x 3
## comunidad n Porcentaje
## <fct> <int> <chr>
## 1 GOP 13252 5.3%
## 2 Independent 205112 82.5%
## 3 DNC 15862 6.4%
## 4 Progressives 14369 5.8%
Un reducido grupo de usuarios publica la mayoría de los tweets. Para todos los grupos, un 5 % de los usuarios publica al menos un 97 % de los weets
# sentimiento
freq_tweets_usuario <- get_tweet_distribution(healthcare)
# Curva de lorenz
freq_tweets_usuario %>%
ggplot(aes(x = porc_usuarios_acum, y = porc_tweets_acum)) +
geom_line(aes(color = comunidad), size = 1.3) +
scale_color_manual(values = plots_palette) +
labs(x = "% acumulado de usuarios", y = "% Acumulado de tweets",
title = "Curva de Lorenz para el nº de usarios y el nº de tweets") +
scale_x_continuous(labels = scales::percent) +
scale_y_continuous(labels = scales::percent) +
theme_bw() +
theme(text = element_text(family = "Georgia", color = "grey45"),
legend.title = element_blank())
# gini
gini_comunidades <- freq_tweets_usuario %>%
group_by(comunidad) %>%
summarise(gini = gini(x = tweets, weights = porc_usuarios)) %>%
arrange(desc(gini))
Todos los movimientos políticos hacen referencia aL tópico #medicareforall. Observaresmos más en profundidad la opinión de las comunidades acerca de esta iniciativa política. Empecemos con el análisis de sentimiento.
Para ello haremos uso de otro set de datos descargados con tweets referentes a este tema.
Como es u n tema propicio a generar opiniones contrapuestas, excluiremos tweets con hashtags que pueden hacer referencia a otras políticas sanitarias, como por ejemplo el Affodable Care Act (ACA).
Los datos que se cargan aquí ya tienen el sentimiento de cada tweet, calculado en la fase de limpieza de datos.
# carga de datos
mfa <- readRDS(here::here("datos_procesados", "formated_text_df_data", "medicareforall.rds"))
# Exclusion de tweets con terminos referentes a otras políticas sanitarias
exclusion_mfa <- "#medicaid|#aca|[Aa]ffordable ?[Cc]are ?[Aa]ct|#publicoption|[Pp]ublic ?[Oo]ption|#medicare( |$|#)|#?COVID|#[Pp]andemic"
mfa <- mfa %>%
filter(discriminate_term(text, "#medicareforall|#m4a", exclusion_mfa))
En la siguiente tabla se presenta el sentimiento medio sobre medicar for all
mfa_sent <- mfa %>%
group_by(comunidad) %>%
summarise(sent = mean(ave_sentiment),
n = n()) %>%
mutate(f = percent(n/sum(n), accuracy = 0.1))
mfa_sent
## # A tibble: 4 x 4
## comunidad sent n f
## <chr> <dbl> <int> <chr>
## 1 DNC 0.0498 526 4.7%
## 2 GOP -0.0593 99 0.9%
## 3 Independent 0.00770 4404 39.2%
## 4 Progressives 0.0295 6220 55.3%
Los resultaos reflejan por parte de los progresistas una valoración de medicare for all, menor que los demócratas (sin comprobar si esta diferencia es estadísticamente significativa). Algo que en un principio puede sorprender, pero en realidad #medicareforall se ha empezado a convertir en una marca en el partido demócrata que puede querer decir diferentes cosas según el emisor del mensaje.
Comprobemos que otros tópicos de política sanitaria se pronuncian cuando se habla de #medicareforall
# limpieza de topicos
mfa_healthcare_clean <- mfa %>%
mutate(text = str_replace_all(text, "single payer", "singlepayer")) %>%
mutate(text = str_replace_all(text, "public option", "publicoption")) %>%
mutate(text = str_replace_all(text, "(obama ?care)|(affordable ?care ?act)", "aca")) %>%
mutate(text = str_replace_all(text, "#", "")) %>%
filter(str_detect(text, "singlepayer|publicoption|aca|medicaid|medicare"))
# creacion de tabla de unigrams
mfa_clean_unigram <- get_unigrams(mfa_healthcare_clean %>% filter(comunidad != "Independent")) %>%
filter(word %in% c("singlepayer", "publicoption", "aca", "medicaid", "medicare"))
# tabla de frecuencias de los uunigrams
mfa_clean_freq <- get_frequencies(mfa_clean_unigram) %>%
left_join(mfa %>%
group_by(comunidad) %>%
count(name = "tweets"), by = "comunidad") %>%
mutate(f = n / tweets)
# grafico
mfa_clean_freq %>%
ungroup() %>%
mutate(comunidad = fct_relevel(comunidad, c("GOP", "DNC", "Progressives"))) %>%
ggplot() +
geom_rect(data = data.frame(xmin = 0.67,
xmax = 1.33, ymin = 0,
ymax = 1,
comunidad = factor(c("GOP", "DNC", "Progressives"))),
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
fill = plots_palette[c(1, 3, 4)], alpha = 1,
color = "white") +
geom_col(aes(x = 1, y = f,fill = fct_reorder2(word, f, comunidad)), position = "fill",
color = "white", width = 0.6) +
geom_text(x = 1, y = 0.9, aes(label = comunidad, color = comunidad),
family = "Georgia",
size = 4) +
labs(fill = "Topic") +
facet_wrap(~comunidad, nrow = 1) +
theme_bw() +
theme(panel.grid = element_blank()) +
scale_fill_brewer() +
guides(color = F) +
theme_bw() +
theme(
text = element_text(family = "Georgia", color = "grey55"),
plot.title = element_text(size = 11),
panel.grid = element_blank(),
panel.border = element_blank(),
strip.background = element_blank(),
strip.text = element_blank(),
legend.position = "left",
axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.title.y = element_blank()) +
scale_x_continuous(limits = c(0.5, 1.5)) +
scale_y_continuous(label = scales::percent) +
scale_color_manual(values = plots_palette[c(1, 3, 4)]) +
labs(title = "De qué otras políticas sanitaras hablan las comunidades cuando hablan de Medicare for All")
Aunque en todos los casos el tema más abundante es Medicare, el programa sanitario para jubilados, los progresistas hablan sustancialmente más que los Demócratas sobre la política Single Payer: iniciativa sanitaria del senador Bernie Sanders, que determina el gobierno sería el pagador único de la sanidad, de modo que consecuentemente, se eliminarían los seguros sanitarios privados (en contraste, algunos candidatos demócratas emplean Medicare for All como sinónimo de la política “Public Option”: un progama de sanidad universal pero que conviviría con seguros sanitarios privados). En contraste, los demócratas hablan más del Affordable Care Act (ACA), también conocido como “Obama Care”: el plan sanitario aprobado durante el gobierno de Barak Obama.
Examinemos pues la percepción del programa Single Payer en las distintas comunidades. Para ello cargaremos otro set de datos recogido expresamente de twitter con tweets que contienen el término “singlepayer”
Analizaremos el sentimiento expresado en los tweets recogidos. Como en el caso de Medicare for All, en el análisis eliminaremos tweets que hagan referencia a otras políticas sanitarias diferentes.
# carga de datos
sp <- readRDS(here::here("datos_procesados", "formated_text_df_data", "singlepayer.rds")) %>%
mutate(comunidad = fct_relevel(comunidad, comunidad_order))
# filtro de tweets con politicas sanitarias no compatibles con sigle payer
exclusion_sp <- "#medicaid|#aca|[Aa]ffordable ?[Cc]are ?[Aa]ct|#publicoption|[Pp]ublic ?[Oo]ption|#medicare( |$|#)|#COVID|COVID"
sp <- sp %>%
filter(discriminate_term(text, "[Ss]ingle ?[Pp]ayer", exclusion_sp))
sp_sent <- sp %>%
filter(comunidad != "Independent") %>%
group_by(comunidad) %>%
summarise(sent = mean(ave_sentiment),
n = n()) %>%
mutate(f = percent(n / sum(n), accuracy = 0.1))
sp_sent
## # A tibble: 3 x 4
## comunidad sent n f
## <fct> <dbl> <int> <chr>
## 1 GOP -0.00456 136 12.1%
## 2 DNC 0.0266 190 16.8%
## 3 Progressives 0.0664 802 71.1%
sp %>%
filter(comunidad != "Independent") %>%
ggplot(aes(x = comunidad, y = ave_sentiment)) +
geom_boxplot(aes(fill = comunidad)) +
scale_fill_manual(values = plots_palette[c(1, 3, 4)]) +
labs(title = "Percepción del término Single Payer",
y = "Average sentiment score per tweet") +
theme_bw() +
guides(fill = F) +
theme(
text = element_text(color = "grey55", family = "Georgia"),
axis.title.x = element_blank(),
panel.grid = element_blank(),
panel.border = element_blank())
La percepción de la política “Single Payer” por parte de los republicanos parece ser negativa. Aunque demócratas y progresistas tienen una percepción positiva, los progresistas parecen ver mejor esta política, aunque como se ve en los diagramas de caja, estas diferencias no parecen ser muy importantes.
Realizaremos un test de diferencias de medias para comprobar si estas diferencias son estadísticamente significativas, mediante replicaciones bootstrap de las muestras originales, calculando los intervalos de confianza de las distribuciones empíricas resultantes.
# test diferencias DNC Progressives
test_sp_dnc_prog <- get_test_dif(sp, c("DNC", "Progressives"))
test_sp_dnc_prog[-1]
## $IC
## 2.5% 97.5%
## DNC -0.01039608 0.06296384
## Progressives 0.04843156 0.08415479
##
## $resultado
## 97.5%
## "No se encuentran diferencias en medias"
test_sp_dnc_prog$data %>%
ggplot(aes(sentimiento)) +
geom_histogram(aes(group = comunidad, fill = comunidad), color = "white", position = "identity", alpha = 0.6, binwidth =0.002) +
scale_fill_manual(values = plots_palette[3:4]) +
labs(x = "Puntuacion sentimiento medio por tweet",
title = "Replicaciónes bootstrap del sentimiento medio para DNC y Progressives") +
theme_bw() +
theme(panel.grid = element_blank(),
panel.border = element_blank(),
legend.title = element_blank(),
legend.position = "top",
axis.title.y = element_blank(),
text = element_text(family = "Georgia", color = "grey55"))
No se encuentran diferencias significativas entre demócratas y progresistas
No ocurre lo mismo si se miden las diferencias entre republicanos y progresistas
# test diferencias DNC Progressives
set.seed(123)
test_sp_gop_prog <- get_test_dif(sp, groups = c("GOP", "Progressives"))
test_sp_gop_prog[-1]
## $IC
## 2.5% 97.5%
## GOP -0.04783467 0.03878830
## Progressives 0.04822020 0.08465806
##
## $resultado
## [1] "Diferencia significativa en medias"
test_sp_gop_prog$data %>%
ggplot(aes(sentimiento)) +
geom_histogram(aes(group = comunidad, fill = comunidad), color = "white", position = "identity", alpha = 0.6, binwidth =0.002) +
scale_fill_manual(values = plots_palette[c(1, 4)]) +
labs(x = "Puntuacion sentimiento medio por tweet",
title = "Replicaciónes bootstrap del sentimiento medio para GOP y Progressives") +
theme_bw() +
theme(panel.grid = element_blank(),
panel.border = element_blank(),
legend.title = element_blank(),
legend.position = "top",
axis.title.y = element_blank(),
text = element_text(family = "Georgia", color = "grey55"))
Si se detallan más las emociones vemos que domina la confianza, pero entre el GOP también hay miedo
sp_unigram <- get_unigrams(sp %>% filter(comunidad != "Independent"), filter_regular_expression = exclusion_sp) %>%
mutate(word = str_remove(word, "^#"))
sp_nrc <- sp_unigram %>%
inner_join(get_sentiments("nrc"), by = "word") %>%
filter(!sentiment %in% c("positive", "negative")) %>%
count(comunidad, sentiment, sort = T) %>%
mutate(sentiment = str_to_title(sentiment)) %>%
ungroup() %>%
group_by(comunidad) %>%
mutate(n = rescale(n)) %>%
pivot_wider(names_from = sentiment, values_from = n) %>%
select(comunidad, Anger, Anticipation, Disgust, Fear, Joy, Sadness, Surprise, Trust) %>%
ungroup() %>%
mutate(comunidad = fct_drop(comunidad))
ggradar(sp_nrc, group.colours = plots_palette[c(1, 3, 4)], legend.position = "top",
group.point.size = 4,
plot.title = "Sentimientos expresados hacia el tópico 'Single payer'",
legend.text.size = 11) +
theme(plot.title = element_text(size = 12, family = "Georgia", color = "grey55"),
axis.title = element_text(family = "Georgia"))
Hay cierto grado de polarización entre demócratas y republicanos, pero no así entre demócratas y progresistas.
pol_test_dif <- readRDS(here::here("datos_procesados", "formated_polarization_df_data", "polarization_testing", "health_polarization.rds"))
pol_test_dif[-1]
## $IC
## 2.5% 97.5%
## DNC-GOP 0.04959839 0.06998175
## DNC-PRG -0.03122013 -0.01980064
##
## $resultado
## [1] "Diferencia significativa en medias"
# Nivel de polarizacion mínimo en ambos casos pero se hallan diferencias significativas
pol_test_dif$data %>%
ggplot(aes(x = sentimiento)) +
geom_histogram(aes(group = comunidad, fill = comunidad), color = "white", alpha = 0.5, binwidth = 0.002, position = "identity") +
scale_fill_manual(values = c(plots_palette[1], plots_palette[4])) +
labs(x = "Polarization score", title = "Replicación bootstrap del scoring de polarización para los nodos frontera")