library(rtweet)
## Warning: package 'rtweet' was built under R version 3.6.1
library(tidyverse)
## Registered S3 methods overwritten by 'ggplot2':
##   method         from 
##   [.quosures     rlang
##   c.quosures     rlang
##   print.quosures rlang
## Registered S3 method overwritten by 'rvest':
##   method            from
##   read_xml.response xml2
## -- Attaching packages -------------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.1.1       v purrr   0.3.2  
## v tibble  2.1.1       v dplyr   0.8.0.1
## v tidyr   0.8.3       v stringr 1.4.0  
## v readr   1.3.1       v forcats 0.4.0
## -- Conflicts ----------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter()  masks stats::filter()
## x purrr::flatten() masks rtweet::flatten()
## x dplyr::lag()     masks stats::lag()
library(ggmap)
## Warning: package 'ggmap' was built under R version 3.6.1
## Google's Terms of Service: https://cloud.google.com/maps-platform/terms/.
## Please cite ggmap if you use it! See citation("ggmap") for details.
# El nombre que le asgnamos a la app en el formulario de autorización
appname <- "Parkify"
## consumer key (en el ejemplo no es una clave real, usar la verdadera)
consumer_key <- "xzhy9GOWU87ZJr0sx2e4vK5PN"
## consumer secret (en el ejemplo no es un clave real, usar la verdadera)
consumer_secret <- "ESzyrthGVuNHG2avZ30Dr1xNgXrACjezMIAPlwOvlLaF8a1nhb"

Vamos a buscar tweets que mencionen alguno de los términos “UVA”, “hipotecario”, “escrituras”, “inmuebles” o “inmobiliario”, en un radio de 10 millas (~16 km) en torno obelisco de la Ciudad de Buenos Aires.

El parámetro n = 3000 es para limitar la búsqueda a los primeros 3000 tweets hallados.

El parámetro include_rts = FALSE descarta retweets

tweets.BA <- search_tweets(q = "UVA OR escrituras OR hipotecario OR inmuebles OR inmobiliario",
              geocode = "-34.603722,-58.381592,10mi",
              include_rts = FALSE,
              n = 3000,
              retryonratelimit = TRUE)
## Registered S3 method overwritten by 'openssl':
##   method      from
##   print.bytes Rcpp

Obtenemos un top 5 de los tweets más populares (con más seguidores), su procedencia, y el contenido del tweet:

tweets.BA %>% 
    top_n(5, retweet_count) %>% 
    arrange(desc(retweet_count)) %>% 
    select(screen_name, followers_count, location, text)
## # A tibble: 5 x 4
##   screen_name   followers_count location      text                         
##   <chr>                   <int> <chr>         <chr>                        
## 1 mauriciomacri         4909590 Buenos Aires~ NO HABRÁ AUMENTO PARA LAS FA~
## 2 mauriciomacri         4909590 Buenos Aires~ El Estado se hará cargo de l~
## 3 frigerioroge~          479639 Buenos Aires~ El Gobierno Nacional aportar~
## 4 frigerioroge~          479639 Buenos Aires~ Esta medida alcanza a más de~
## 5 frigerioroge~          479639 Buenos Aires~ #AHORA Junto a @ivankerr com~

Los tweets con mayor impacto fueron comunicaciones oficiales del paquete de medidas de emergencia tras las elecciones PASO.

Por otro lado podemos ver el horario del dia en que se realiza la mayor cantidad de tweets, los diferentes dias de la semana

ts_plot(tweets.BA, "hours")

El pico de actividad (dentro de la muestra que tomamos) se da el jueves 15 a la noche. Se ve tambien que el fin de semana hay poca actividad

Para ver la popularidad de los usuarios, obtenemos un top 5 de los usuarios más populares (con más seguidores), su procedencia, y el contenido del tweet:

tweets.BA %>% 
    top_n(5, followers_count) %>% 
    arrange(desc(followers_count)) %>% 
    select(screen_name, followers_count, location, text)
## # A tibble: 6 x 4
##   screen_name  followers_count location       text                         
##   <chr>                  <int> <chr>          <chr>                        
## 1 mauriciomac~         4909590 Buenos Aires,~ El Estado se hará cargo de l~
## 2 mauriciomac~         4909590 Buenos Aires,~ NO HABRÁ AUMENTO PARA LAS FA~
## 3 clarincom            2901591 Buenos Aires,~ Créditos UVA: el Gobierno ev~
## 4 clarincom            2901591 Buenos Aires,~ Créditos UVA: a cuántos port~
## 5 clarincom            2901591 Buenos Aires,~ @frigeriorogelio @CaroStanle~
## 6 clarincom            2901591 Buenos Aires,~ Créditos UVA: el Gobierno ev~

Y la distribución

ggplot(tweets.BA)+
    geom_histogram(aes(x = followers_count))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Se ve como la gran mayoria de usuarios tiene pocos o nada de followers, y unos pocos ´Tweetstars´ concentran muchisimos. Este tipo de distribución se ve en otras redes sociales, como YouTube, donde unos pocos usuarios generan los contenidos consumidos por la mayoria. Recientemente esta minoría, los Youtubers, se unieron al sindicato de comercio mas grande de Europa y exigen a la empresa ser tratados como socios.

FairTube: The YouTubers Union Is Not Messing Around

Para aislar los tweets que poseen coordenadas geográficas (lat y long), crear mapas que muestren posición de los tweets y cantidad de seguidores del usuario que tuitea.

Primero creamos una función que extrae los valores de lat y long del campo problemático

coordenadas <- function(campo_coordenadas) {
    extraer_coordenadas <- function(lista_coords) {
        data_frame(lon = lista_coords[1],
                   lat = lista_coords[2])
    }
    
    map_df(campo_coordenadas, extraer_coordenadas)
}

Y con una cadena de funciones extraemos los datos de georeferenciamiento, los agregamos al dataframe en sus propias columnas, y por último descartamos los campos en formato lista:

tweets_prop <- tweets.BA %>% 
    cbind(coordenadas(tweets.BA$coords_coords)) %>% 
    select(-geo_coords, -coords_coords, -bbox_coords) 
## Warning: `data_frame()` is deprecated, use `tibble()`.
## This warning is displayed once per session.

Antes de visualizar, filtramos nuestros tweets para conservar sólo los que contienen coordenadas exactas de posición.

tweets_prop_geo <- tweets_prop %>% 
    filter(!is.na(lat), !is.na(lon))

El resultado evidencia que lso tweets georeferenciados son sólo una pequeña fracción del total que se produce:

nrow(tweets_prop_geo)
## [1] 6

Corremos al paquete ggmap

library(ggmap)

delimitamos los bordes externos del boundinbox vamos a usar las coordenadas de la guia ya que al ser muy pocas las observaciones que obtuvimos en esta busqueda, el zoom del bbox genera una imagen con mucho zoom y baja calidad

load(url("https://bitsandbricks.github.io/data/tweets_transporte.RData"))

coordenadas <- function(campo_coordenadas) {
    extraer_coordenadas <- function(lista_coords) {
        data_frame(lon = lista_coords[1],
                   lat = lista_coords[2])
    }
    
    map_df(campo_coordenadas, extraer_coordenadas)
}

tweets_transporte <- tweets_transporte %>% 
    cbind(coordenadas(tweets_transporte$coords_coords)) %>% 
    select(-geo_coords, -coords_coords, -bbox_coords) 

tweets_transporte_geo <- tweets_transporte %>% 
    filter(!is.na(lat), !is.na(lon))
bbox <- c(min(tweets_transporte_geo$lon),
          min(tweets_transporte_geo$lat),
          max(tweets_transporte_geo$lon),
          max(tweets_transporte_geo$lat))

Descargamos la base del mapa

mimapa <- get_stamenmap(bbox)
## Source : http://tile.stamen.com/terrain/10/344/616.png
## Source : http://tile.stamen.com/terrain/10/345/616.png
## Source : http://tile.stamen.com/terrain/10/346/616.png
## Source : http://tile.stamen.com/terrain/10/344/617.png
## Source : http://tile.stamen.com/terrain/10/345/617.png
## Source : http://tile.stamen.com/terrain/10/346/617.png

graficamos con el paquete ggpmap

ggmap(mimapa)

mimapa_terrain_lines <- get_stamenmap(bbox, maptype = "terrain-lines")
## 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
mimapa_toner_lite <- get_stamenmap(bbox, maptype = "toner-lite")
## Source : http://tile.stamen.com/toner-lite/10/344/616.png
## Source : http://tile.stamen.com/toner-lite/10/345/616.png
## Source : http://tile.stamen.com/toner-lite/10/346/616.png
## Source : http://tile.stamen.com/toner-lite/10/344/617.png
## Source : http://tile.stamen.com/toner-lite/10/345/617.png
## Source : http://tile.stamen.com/toner-lite/10/346/617.png
mimapa_watercolor <- get_stamenmap(bbox, maptype = "watercolor")
## Source : http://tile.stamen.com/watercolor/10/344/616.jpg
## Source : http://tile.stamen.com/watercolor/10/345/616.jpg
## Source : http://tile.stamen.com/watercolor/10/346/616.jpg
## Source : http://tile.stamen.com/watercolor/10/344/617.jpg
## Source : http://tile.stamen.com/watercolor/10/345/617.jpg
## Source : http://tile.stamen.com/watercolor/10/346/617.jpg
ggmap(mimapa_terrain_lines)

mapa_BA <- get_stamenmap(bbox, maptype = "toner-lite")

ggmap(mapa_BA)

y ahora agragamos la capa de tweets

ggmap(mapa_BA) +
    geom_point(data = tweets_prop_geo, aes(x = lon, y = lat))

y diferenciamos por cantidad de seguidores y de retweets

ggmap(mapa_BA) + 
    geom_point(data = tweets_prop_geo, 
               aes(x = lon, y = lat, color = followers_count, size = retweet_count),
               alpha = .5) +
    scale_color_distiller(palette = "Spectral")