Primero que todo, cargamos las librerías que vamos a usar en nuestro ejercicio.

options(scipen = 999)
library(rtweet)
library(tidyverse)
## -- Attaching packages ----------------------------------------------------------------------------- tidyverse 1.3.0 --
## v ggplot2 3.3.0     v purrr   0.3.3
## v tibble  2.1.3     v dplyr   0.8.5
## v tidyr   1.0.2     v stringr 1.4.0
## v readr   1.3.1     v forcats 0.5.0
## -- Conflicts -------------------------------------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter()  masks stats::filter()
## x purrr::flatten() masks rtweet::flatten()
## x dplyr::lag()     masks stats::lag()
library(sf)
## Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
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(ggplot2)
library(dplyr) 
library(lubridate)
## 
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
## 
##     date
library(tidyr)

I.

Como nuestro .csv con los tweets (tweets_rmba) es un archivo muy grande y nos tilda el RStudio por “memoria insuficiente”, vamos a utilizar una función que nos brinde (a) una muestra (sample) del dataset; (b) un dataset limitado a 30.000 resultados, que es el que vamos a usar

tweets.sample <- read.csv("tweets_rmba.csv",  
                           stringsAsFactors=FALSE, header=T, nrows=20)  

tweets.colclass <- sapply(tweets.sample,class)

tweets.raw <- tbl_df(read.csv("tweets_rmba.csv", 
                    stringsAsFactors=FALSE, header=T,nrow=300000))  

Para probar si efectivamente logramos un dataset limitado a 300.000 filas, exploramos las dimensiones del mismo utilizando la función dim.

dim(tweets.raw)
## [1] 300000     16

Y para saber qué valores contiene el mismo, utilizamos la función names

names(tweets.raw)
##  [1] "X"                     "Y"                     "id"                   
##  [4] "in_reply_to_status_id" "in_reply_to_user_id"   "text"                 
##  [7] "created"               "lang"                  "source"               
## [10] "user_name"             "user_id"               "user_created"         
## [13] "user_description"      "user_location"         "user_followers"       
## [16] "user_followed"

II. Temporalidad de los datos

Comencemos entonces viendo cuál es el formato de fechas de nuestro dataset, utilizando la función head.

head(tweets.raw)
## # A tibble: 6 x 16
##       X     Y      id in_reply_to_sta~ in_reply_to_use~ text  created lang 
##   <dbl> <dbl>   <dbl>            <dbl>            <dbl> <chr> <chr>   <chr>
## 1 -58.3 -34.7 9.18e17         NA                     NA ".. ~ 2017/1~ es   
## 2 -58.6 -34.5 9.18e17         NA                     NA "Dí~  2017/1~ es   
## 3 -58.6 -34.6 9.18e17         NA                     NA "Gen~ 2017/1~ es   
## 4 -58.4 -34.6 9.18e17          9.18e17        136102079 "@di~ 2017/1~ es   
## 5 -58.4 -34.6 9.18e17          9.18e17       1180083066 "942~ 2017/1~ es   
## 6 -58.4 -34.6 9.18e17          9.18e17       1180083066 "431~ 2017/1~ es   
## # ... with 8 more variables: source <chr>, user_name <chr>, user_id <dbl>,
## #   user_created <chr>, user_description <chr>, user_location <chr>,
## #   user_followers <int>, user_followed <int>

Ahora sabemos que la fecha de publicación del tweet se encuentra en la columna created (no confundir con la variable user_created, que indica la fecha de creación de la cuenta). Podemos observar que la fecha está escrita de acuerdo al formato YYYY-MM-DD y la hora como HH:MM:SS

Vamos a formatearlo según POSIXct, un acrónimo de Portable Operating System Interface, un estándar de almacenamiento de fechas.

tweets.raw.fecha <- tweets.raw %>%
  mutate(created=as.POSIXct(created))
head(tweets.raw.fecha)
## # A tibble: 6 x 16
##       X     Y      id in_reply_to_sta~ in_reply_to_use~ text 
##   <dbl> <dbl>   <dbl>            <dbl>            <dbl> <chr>
## 1 -58.3 -34.7 9.18e17         NA                     NA ".. ~
## 2 -58.6 -34.5 9.18e17         NA                     NA "Dí~ 
## 3 -58.6 -34.6 9.18e17         NA                     NA "Gen~
## 4 -58.4 -34.6 9.18e17          9.18e17        136102079 "@di~
## 5 -58.4 -34.6 9.18e17          9.18e17       1180083066 "942~
## 6 -58.4 -34.6 9.18e17          9.18e17       1180083066 "431~
## # ... with 10 more variables: created <dttm>, lang <chr>, source <chr>,
## #   user_name <chr>, user_id <dbl>, user_created <chr>, user_description <chr>,
## #   user_location <chr>, user_followers <int>, user_followed <int>

Vamos a hacer una primera aproximación gráfica, que consiste en ver en qué días de la semana se publican más tweets. Usamos el dataset que cargamos y le aplicamos un geom_bar con la función wday. Como queremos que la semana comience el lunes y no el domingo (como se ilustra por default), alteramos el valor del lubridate.week.start a 1, que vendría a ser lunes.

options(scipen = 20)

ggplot(tweets.raw.fecha) + 
    geom_bar(aes(x = wday(created, week_start = getOption("lubridate.week.start", 1), label = TRUE, abbr = FALSE))) +
    labs(title="Tweets por día de la semana",
       subtitle="Muestra de 300.000 tweets",
       x="Día de la semana",
       y="Cantidad de tweets",
       caption = "Fuente: Twitter")

Un segundo gráfico nos permitirá conocer en qué momentos del dia se dan los picos de publicaciones.

Vamos a volver a nuestro archivo tweets.raw (que tenía fecha y hora en nuestra columna create) para extraer la hora de esa columna.

tweets.raw <- tweets.raw %>%
mutate(hora_base = hour(ymd_hms(created)))
head(tweets.raw)
## # A tibble: 6 x 17
##       X     Y      id in_reply_to_sta~ in_reply_to_use~ text  created lang 
##   <dbl> <dbl>   <dbl>            <dbl>            <dbl> <chr> <chr>   <chr>
## 1 -58.3 -34.7 9.18e17         NA                     NA ".. ~ 2017/1~ es   
## 2 -58.6 -34.5 9.18e17         NA                     NA "Dí~  2017/1~ es   
## 3 -58.6 -34.6 9.18e17         NA                     NA "Gen~ 2017/1~ es   
## 4 -58.4 -34.6 9.18e17          9.18e17        136102079 "@di~ 2017/1~ es   
## 5 -58.4 -34.6 9.18e17          9.18e17       1180083066 "942~ 2017/1~ es   
## 6 -58.4 -34.6 9.18e17          9.18e17       1180083066 "431~ 2017/1~ es   
## # ... with 9 more variables: source <chr>, user_name <chr>, user_id <dbl>,
## #   user_created <chr>, user_description <chr>, user_location <chr>,
## #   user_followers <int>, user_followed <int>, hora_base <int>

Como es complicado hacerlo con toda la base, vamos a seleccionar un día al azar, en este caso, el 22 de octubre de 2017, día de elecciones legislativas en Argentina. Utilizamos la función grepl del paquete dplyr que buscará la fecha que queremos dentro de la la columna created. Una vez realizado esto graficamos con ggplot.

por_hora <-  tweets.raw %>%
  filter(grepl('2017/10/22',created)) %>% 
  count(hora = hour(ymd_hms(created))) %>%
  group_by(hora)
head(por_hora)
## # A tibble: 6 x 2
## # Groups:   hora [6]
##    hora     n
##   <int> <int>
## 1     0   608
## 2     1   417
## 3     2   327
## 4     3   228
## 5     4   161
## 6     5   114
ggplot(por_hora) +
    geom_line(aes(x = hora, y = n)) +
    labs(title = "Distribución horaria de los tweets",
         subtitle = "Ciudad de Buenos Aires, 22 de octubre de 2017",
         x = "Hora", y = "Número de tweets",
         color = "Dias de la semana",
         caption = "Fuente: Twitter")

A priori se podría afirmar que muchos de los tweets están concentrados en torno al fin de semana (viernes, sábado y domingo) y, al menos en el caso de la muestra del 22 de octubre de 2017, que la actividad se incrementa hacia la tardecita/noche. Debemos resaltar, sin embargo, que estas conclusiones se basan en una muestra de una muestra, es decir, 300.000 observaciones de un recorte de tweets de entre 2015 y 2017, por lo que tomaríamos con cuidado estas conclusiones.

III. Analizar la distribución espacial de los datos

Primero graficar las densidades de las ubicaciones de los tweets vamos a primero filtrar los tweets que sean mayores a menores a cero ya que las coordenadas de buenos aires son siempre negativas. En segundo lugar realizamos un bbox con con las coordenadas que obtenmos de los tweets. De esta manera obtenemos la region en donde entran todos los tweets que tenemos en nuestro dataframe. Graficamos y vemos que los tweets abarcan casi todo el gran buenos aires.

library(ggmap)


tweets.crop <- tweets.raw.fecha %>% 
    filter(X <0, Y <0) %>% 
    filter (source == "Twitter for iPhone"| source =="Twitter for Windows Phone"| source =="Twitter for Android")


bbox = make_bbox(tweets.crop$X, tweets.crop$Y)


CABA <- get_stamenmap(bbox = bbox, 
                      maptype = "toner-lite", zoom = 10)
## Source : http://tile.stamen.com/toner-lite/10/343/614.png
## Source : http://tile.stamen.com/toner-lite/10/344/614.png
## Source : http://tile.stamen.com/toner-lite/10/345/614.png
## Source : http://tile.stamen.com/toner-lite/10/346/614.png
## Source : http://tile.stamen.com/toner-lite/10/347/614.png
## Source : http://tile.stamen.com/toner-lite/10/343/615.png
## Source : http://tile.stamen.com/toner-lite/10/344/615.png
## Source : http://tile.stamen.com/toner-lite/10/345/615.png
## Source : http://tile.stamen.com/toner-lite/10/346/615.png
## Source : http://tile.stamen.com/toner-lite/10/347/615.png
## Source : http://tile.stamen.com/toner-lite/10/343/616.png
## 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/347/616.png
## Source : http://tile.stamen.com/toner-lite/10/343/617.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
## Source : http://tile.stamen.com/toner-lite/10/347/617.png
## Source : http://tile.stamen.com/toner-lite/10/343/618.png
## Source : http://tile.stamen.com/toner-lite/10/344/618.png
## Source : http://tile.stamen.com/toner-lite/10/345/618.png
## Source : http://tile.stamen.com/toner-lite/10/346/618.png
## Source : http://tile.stamen.com/toner-lite/10/347/618.png
## Source : http://tile.stamen.com/toner-lite/10/343/619.png
## Source : http://tile.stamen.com/toner-lite/10/344/619.png
## Source : http://tile.stamen.com/toner-lite/10/345/619.png
## Source : http://tile.stamen.com/toner-lite/10/346/619.png
## Source : http://tile.stamen.com/toner-lite/10/347/619.png

Ahora vamos a ver la distribución espacial de los tweets en el plano obtenido anteriormente. Debido a la escala del mapa podemos identificar solo algunos elementos en el mapa, como capital federal y las distintas densidades que se observan alrededor de las autopistas.

ggmap(CABA) +
    geom_point(data = tweets.crop, aes(x = X, y = Y),
               color = "red", size = 1, alpha = 0.1) +
    labs(title = "Distribucion Geografica de Tweets", subtitle = "Gran Buenos Aires", caption = "Fuente: Base de datos Twitter")

Ahora ya podemos hacer el grafico, para esto vamos a utilizar la función in que le dice a ggplot que use utilice las fuentes que se encuentren en la columna source. Para mejorar la visualización del grafico utilizamos la función guides que nos permite modificar el tamaño de las referencias en el plano para una mejor lectura.

ggmap(CABA) +
    geom_point(data = filter(tweets.crop, source %in% source), 
               aes(x = X, y = Y, color = source),
               size = 1, alpha = 0.1)+
    guides(color = guide_legend(override.aes = list(size=5, alpha = 1))) +
    scale_color_brewer(palette = "Set1") +
    labs(title = "Distribucion Geografica de Tweets segun Fuente", subtitle = "Gran Buenos Aires", caption = "Fuente: Base de datos Twitter")

Podemos ver en el grafico como la fuente mas comun es android seguido de windows phone. Un dato interesenta es que la fuente iphone casi no aparece representada en el mapa debido a lo miniscula que es con respecto al resto.

ggmap(CABA) +
    geom_point(data = filter(tweets.crop, source %in% source), 
               aes(x = X, y = Y, color = source),
               size = 0.5, alpha = 0.1)+
    guides(color = guide_legend(override.aes = list(size=3, alpha = 1))) +
    scale_color_brewer(palette = "Set1") +
    labs(title = "Distribucion Geografica de Tweets segun Fuente", subtitle = "Gran Buenos Aires", caption = "Fuente: Base de datos Twitter") + facet_wrap(~source)

La distribución geográfica según la fuente de los tweets (es decir, si los teléfonos son Android, iPhone o Windows Phone) parecen acompañar el nivel socieconómico de los emisores, con los iPhones concentrados en torno al corredor norte del AMBA y el resto de los aparatos distribuidos parejamente en torno a las áreas de mayor población.

IV. Comparar la densidad de los datos en el tiempo

tweets.time <- tweets.raw %>% 
    filter(X <0, Y <0) %>% 
    filter (source == "Twitter for Windows Phone"| source =="Twitter for iPhone"| source =="Twitter for Android")


ggmap(CABA) +
    geom_point(data = filter(tweets.time, source %in% source),
                   aes(x = X, y = Y, color = source),alpha = 5, size = .2) +
    facet_wrap(~hora_base, nrow = 4) +
    labs(title = "Distribucion Geografica de Tweets segun Fuente",
         subtitle = "Según hora del día", caption = "Fuente: Base de datos Twitter")+
    guides(color = guide_legend(override.aes = list(size=3, alpha = 1)))

library(ggplot2)
library(gganimate)



ggmap(CABA) +
    geom_point(data = filter(tweets.time, source %in% source),
                   aes(x = X, y = Y, color = source),alpha = 5, size = .2) +
    transition_time(hora_base)  +
    labs(title = "Distribucion Geografica de Tweets segun Fuente",
         subtitle = "Hora del dia: {frame_time}", caption = "Fuente: Base de datos Twitter")+
    guides(color = guide_legend(override.aes = list(size=3, alpha = 1)))

En el caso de las horas del día no extraña que haya muy poca actividad en la madrugada y temprano a la mañana y sí más hacia la tarde/noche, con un cierto desplazamiento del microcentro hacia la periferia conforme avanza la jornada.