En este trabajo realizaremos un análisis de tweets públicos sobre el proceso de vacunación en el Ärea Metropolitana de Buenos Aires. Para ello utilizaresmos una base de tweets suministrada dado que al momento no contamos con los permisos para realizar búsquedas conectándonos a la API de Twitter..
Comenzamos como siempre, activando los paquetes que utilizaremos para explorar, transformar y visualizar estos datos. En este caso, estaremos utilizando “tidyverse”, “sf”, “ggmap”, “leaflet”, “rtweet” y “lubridate” :
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.3 ✓ purrr 0.3.4
## ✓ tibble 3.1.0 ✓ dplyr 1.0.5
## ✓ tidyr 1.1.3 ✓ stringr 1.4.0
## ✓ readr 1.4.0 ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(sf)
## Linking to GEOS 3.8.1, GDAL 3.1.4, PROJ 6.3.1
library(ggmap)
## Google's Terms of Service: https://cloud.google.com/maps-platform/terms/.
## Please cite ggmap if you use it! See citation("ggmap") for details.
library(leaflet)
library(rtweet)
##
## Attaching package: 'rtweet'
## The following object is masked from 'package:purrr':
##
## flatten
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
A continuación importamos y exploramos la base de tweets:
tweets_vacunas <- readRDS("data/tweets_vacunas.RDS")
names(tweets_vacunas)
## [1] "user_id" "status_id"
## [3] "created_at" "screen_name"
## [5] "text" "source"
## [7] "display_text_width" "reply_to_status_id"
## [9] "reply_to_user_id" "reply_to_screen_name"
## [11] "is_quote" "is_retweet"
## [13] "favorite_count" "retweet_count"
## [15] "quote_count" "reply_count"
## [17] "hashtags" "symbols"
## [19] "urls_url" "urls_t.co"
## [21] "urls_expanded_url" "media_url"
## [23] "media_t.co" "media_expanded_url"
## [25] "media_type" "ext_media_url"
## [27] "ext_media_t.co" "ext_media_expanded_url"
## [29] "ext_media_type" "mentions_user_id"
## [31] "mentions_screen_name" "lang"
## [33] "quoted_status_id" "quoted_text"
## [35] "quoted_created_at" "quoted_source"
## [37] "quoted_favorite_count" "quoted_retweet_count"
## [39] "quoted_user_id" "quoted_screen_name"
## [41] "quoted_name" "quoted_followers_count"
## [43] "quoted_friends_count" "quoted_statuses_count"
## [45] "quoted_location" "quoted_description"
## [47] "quoted_verified" "retweet_status_id"
## [49] "retweet_text" "retweet_created_at"
## [51] "retweet_source" "retweet_favorite_count"
## [53] "retweet_retweet_count" "retweet_user_id"
## [55] "retweet_screen_name" "retweet_name"
## [57] "retweet_followers_count" "retweet_friends_count"
## [59] "retweet_statuses_count" "retweet_location"
## [61] "retweet_description" "retweet_verified"
## [63] "place_url" "place_name"
## [65] "place_full_name" "place_type"
## [67] "country" "country_code"
## [69] "geo_coords" "coords_coords"
## [71] "bbox_coords" "status_url"
## [73] "name" "location"
## [75] "description" "url"
## [77] "protected" "followers_count"
## [79] "friends_count" "listed_count"
## [81] "statuses_count" "favourites_count"
## [83] "account_created_at" "verified"
## [85] "profile_url" "profile_expanded_url"
## [87] "account_lang" "profile_banner_url"
## [89] "profile_background_url" "profile_image_url"
Como puede observarse en el gráfico que sigue la mayoría de las cuentas analizadas reunen menos de mil seguidores. Asimismo son muy pocas las que reunen menos de 10 o más de 100.000 seguidores y encontramos algunos valores extremos superando el millón, los cuales analizaremos a continuación. Es decir, que existe una gran masa de usuarios con baja popularidad (hasta mil seguidores) y un número muy pequeño de usuarios con una muy alta popilaridad (alcanzando más de cien mil seguidores, esto es una cantidad cien veces superior a la de la mayoría).
options(scipen = 20)
ggplot(tweets_vacunas) +
geom_histogram(aes(x = followers_count), color="grey40", fill="grey80")+
theme_minimal()+
theme(plot.title = element_text(face="bold", size=15)) +
theme(axis.title.x = element_text(face="bold", vjust=-0.5, colour="#DF536B", size=12)) +
theme(axis.title.y = element_text(face="bold", vjust=-0.5, colour="#DF536B", size=12)) +
labs(title = "Distribución de usuarios según cantidad de seguidores",
x = "número de seguidores",
y = "cantidad",
caption = "fuente: Twitter - 27/Juio a 5/julio de 2021")+
scale_x_log10()
## Warning: Transformation introduced infinite values in continuous x-axis
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 10 rows containing non-finite values (stat_bin).
En el encabezado que sigue podemos ver los cinco usuarios más populares en la base de tweets descargados. Para ello mostramos un encabezado (función “head”) de la base ordenada en forma descendendente (función “arrage”, parámetro “desc”) y selccionando los casos únicos (función “distinct”)
tweets_vacunas %>%
select(screen_name, followers_count) %>%
distinct() %>%
arrange(desc(followers_count)) %>%
head(5)
## # A tibble: 5 x 2
## screen_name followers_count
## <chr> <int>
## 1 C5N 2838711
## 2 PatoBullrich 1242184
## 3 FernandezAnibal 1139329
## 4 AmericaTV 1054570
## 5 NardaLepes 842049
Para determinar en qué horario se realizan la mayor cantidad de tweets debemos primero transformar en formato temporal (función “ymd_hms”) los datos de la variable “created_at” y crear una nueva variable identificando la hora de cada tweet (función “hour”). En esta última operación establecimos asimismo que tomara la hora de la zona horaria de la ciudad (función “with_tz”):
tweets_vacunas <- tweets_vacunas %>%
mutate(created_at = ymd_hms(created_at), hora = hour(with_tz(created_at, "America/Argentina/Buenos_Aires")))
Ahora sí, podemos proceder a graficar la distribución de tweets por hora. Como puede verse, la mayoría de los tweets se concentran en el rango que va desde las 8 a las 0 horas, con los mayores picos a las 12 y a las 18; mientras que entre la 1 y las 7 encontramos la menor concentración de tweets, con un mínimo registrado entre las 4 y las 5:
ggplot(data=count(tweets_vacunas, hora))+
geom_col(aes(x = hora, y = n), color="grey40", fill="grey80")+
theme_minimal()+
theme(plot.title = element_text(face="bold", size=15)) +
theme(axis.title.x = element_text(face="bold", vjust=-0.5, colour="#DF536B", size=12)) +
theme(axis.title.y = element_text(face="bold", vjust=-0.5, colour="#DF536B", size=12)) +
labs(title = "Distribución de tweets por hora",
x = "hora",
y = "cantidad",
caption = "fuente: Twitter - 27/Juio a 5/julio de 2021")
tweets_vacunas_geo <- lat_lng(tweets_vacunas) %>%
select(-geo_coords, -coords_coords, -bbox_coords) %>%
filter(!is.na(lat), !is.na(lng))
bbox <- make_bbox(lon = tweets_vacunas_geo$lng, lat = tweets_vacunas_geo$lat)
bbox
## left bottom right top
## -58.83245 -34.85154 -58.17054 -34.42451
mapa_amba <- get_stamenmap(bbox, maptype = "terrain-lines", zoom = 10)
## Source : http://tile.stamen.com/terrain-lines/10/344/616.png
## Source : http://tile.stamen.com/terrain-lines/10/345/616.png
## Source : http://tile.stamen.com/terrain-lines/10/346/616.png
## Source : http://tile.stamen.com/terrain-lines/10/344/617.png
## Source : http://tile.stamen.com/terrain-lines/10/345/617.png
## Source : http://tile.stamen.com/terrain-lines/10/346/617.png
ggmap(mapa_amba) +
geom_point(data = arrange(tweets_vacunas_geo, followers_count),
aes(x = jitter(lng, 200), y = jitter(lat, 200), color = followers_count), alpha=0.6) +
scale_color_viridis_c()+
theme_void()+
theme(plot.title = element_text(face="bold", size=15)) +
labs(title = "Distribución de tweets por localización y cantidad de seguidores",
caption = "fuente: Twitter - 27/Juio a 5/julio de 2021",
color = "cantidad de seguidores")
paleta <- colorNumeric(
palette = "viridis",
domain = tweets_vacunas_geo$followers_count)
leaflet(tweets_vacunas_geo) %>%
addTiles() %>%
addCircleMarkers(radius = ~retweet_count,
popup = ~paste("usuario:", screen_name),
color = ~paleta(followers_count),
lat = ~jitter(lat, 50),
lng = ~jitter(lng, 50)) %>%
addLegend(title = "seguidores", pal = paleta, values = ~followers_count)