Investigando con social media

Los contenidos generados en las redes sociales son producidos y recopilados por por usuarios individuales (o representeantes de organizaciones) que participan en plataformas de acceso público tales como Twitter, Facebook, o Instagram. Si bien esas tres son las más populares, existen muchísimas otras plataformas funcionan como repositorios de información online como Yelp (reseñas de restoranes) o Properati (listados de propiedades en venta y alquiler), entre tantos otros.

La información producida en redes sociales llama la atención de investigadores en temas sociales por el nivel de detalle que encierra. Los usuarios registran y transmiten en forma pública un amplio abanico de datos personales: su paradero, estado de ánimo, intenciones futuras, etc. Es por eso que la “minería” de datos capturados onlone se utiliza para estudiar procesos sociales, políticos, y hasta meteorológicos, monitoreando menciones a eventos climáticos.

Los desafíos de trabajar con información de repositorios sociales

Por supuesto, no todo son ventajas. El análsis de datos extraidos de redes sociales se enfrenta a varios obstáculos, entre ellos:

  • “Suciedad” de los datos. Los contenidos publicados en redes sociales suelen combinar texto, imágenes y video, lo cual requiere un esfuerzo considerable para identificar y clasificar los tipos de contenido disponibles. Incluso el contenido más fácil de tratar, el texto, requiere de limpieza previa: hay que lidiar con abreviaciones, emojis, puntuación inusual, etc.
  • Inconsistencia. Los regisros capturados desde repositorios online suelen ser inconsistentes: Muchas veces, uno o más de los valores de sus atributos (tales como “usario”, “mensaje”, “idioma”, etc) faltan en muchos de los registros. Por ejemplo, algunos contienen coordenadas espaciales que permiten ubicarlos en el espacio, pero en muchos casos no están georreferenciados. Eso dificulta saber dónde está siendo producida la información, desde donde se emite.
  • Sesgo y veracidad dudosa. Al analizar los datos, es tentador realizar inferencias acerca de lo que la población en general hace o quiere. Pero hay que tener en cuenta que las personas que producen contenidos online son un grupo particular, que tiende a ser más joven y de nivel socioeconómico mayor a la media. Por otra parte, que algo se haya dicho online dista mucho de ser cierto o sincero. Ni siquiera podemos asumir que los usuarios son individuos humanos; las redes sociales son utilizadas en creciente medida por “bots”, software automático que publica contenidos en gran volumen simulando ser una persona, o un grupo de personas, con el fin de manipular la opinión pública.

  • Volumen. Cuando uno decide acumular los datos que obtiene de redes sociales, durante meses o años, el volumen alcanzado no es trivial. Resguardar, ordenar, clasificar y extraer sentido de decenas o centenas de millones de registros es un desafío de big data.

  • Limitaciones de acceso: Las empresas que controlan los repositorios de datos producidos en redes sociales vigilan con cuidado a quienes acceden, y limitan la cantidad de información que puede extraerse. En el caso de Twitter, las consultas permitidas a su base de datos se limitan al contenido producido en la última semana, y no entrega más de 18,000 tweets por consulta.

Conectando R a Twitter

Para acceder a los sistemas de Twitter necesitamos obtener una autorización, identificándonos con nuestro usuario en la red. Este paso es inevitable, ya que sin una autorización Twitter no responderá nuestras consultas. Por suerte, el trámite para obtener acceso es inmediato, y como resultado obtendremos una serie de códigos de identificación, conocidos en su conjunto como API keys.

Obteniendo autorización

El primer paso es, si no lo hemos hecho aún, crear un usuario de Twitter en https://twitter.com/signup. Luego seguimos los pasos de éste instructivo https://towardsdatascience.com/access-data-from-twitter-api-using-r-and-or-python-b8ac342d3efe. Nota: Twitter nos preguntará cómo se llama la “app” para la cual estamos solicitand acceso. No vamos a crear ninguna app, pero aún así tenemos que elegir un nombre; usemos “RTWEET” (aunque podría ser cualquier otro). Al completar los pasos estaremos en poder de cuatro códigos: Consumer Key, Consumer Secret, Access Token y Access Token Secret. Tomemos nota de ellos.

Acceso a Twitter via R: el paquete rtweet

rtweet provee un conjunto de funciones que nos facilitan interactuar con Twitter. Si no lo tenemos instalado, lo conseguimos vía:

install.packages("rtweet")

Y lo activamos junto al resto de los paquetes que vamos a usar.

library(rtweet)
library(tidyverse)

A continuación, le pasamos a rtweet los datos de autorización que conseguimos antes para crear un “token” (en la jerga de Twitter, es una especie de comprobante de que estamos autorizados a acceder a los datos)

# El nombre que le asgnamos a la app en el formulario de autorización
appname <- "RTWEET"
## consumer key (en el ejemplo no es una clave real, usar la verdadera)
consumer_key <- "la_secuencia_de_caracteres_de_nuestra_consumer_key"
## consumer secret (en el ejemplo no es un clave real, usar la verdadera)
consumer_secret <- "la_secuencia_de_caracteres_de_nuestra_consumer_secret"
## consumer key (en el ejemplo no es una clave real, usar la verdadera)
access_token <- "la_secuencia_de_caracteres_de_nuestro_access_token"
## consumer secret (en el ejemplo no es un clave real, usar la verdadera)
access_secret <- "la_secuencia_de_caracteres_de_nuestro_access_secret"



twitter_token <- create_token(
  app = appname,
  consumer_key = consumer_key,
  consumer_secret = consumer_secret,
  access_token = access_token, 
  access_secret = access_secret)

A continuación se abrirá una ventana en nuestro navegador solicitando autorizar el acceso vía R -lo aceptamos, por supuesto.

Ahora si, estamos listos para realizar consultas en el archivo de Twitter. La función search_tweets() permite obtener tweets que cumplan los requisitos que fijemos. Por ejemplo, para buscar tweets que contienen el término “inflacion”, usamos:

tweets <- search_tweets(q = "inflacion", n = 3000)

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

También puede hacerse una búsqueda por múltiples términos. Por ejemplo, buscando “ciudad+universitaria” hace que twitter devuelva resultads donde las palabras aparecen juntas y en ese orden; como alternativa, al optar por “ciudad universitaria” se obtienen tweets donde aparezcan esas palabras en cualquier parte del texto, sin importar su orden o si aparecen contiguas.

El resultado es un dataframe de 3000 observaciones -el número máximo que habíamos solicitado- y 88 columnas. rtweet incluye users_data(), una función que muestra detalles de los usuarios que han producido los tweets que capturamos:

users_data(tweets) %>% head()

También podemos explorar los resultados en base a las 88 variables disponibles. Revisemos los nombres:

names(tweets)
##  [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"

Allí hay de todo para explorar. Por ejemplo,

Usuarios más populares

Según la cantidad de seguidores:

options(scipen = 20)
ggplot(tweets) +
    geom_histogram(aes(x = followers_count))

El gráfico muestra una distribución de “power law”, típica en los rankings de popularidad. Hay una enorme masa de usuarios con popularidad mínima (apenas un puñado de seguidores) y un número muy pequeño de usuarios que alcanza una cantidad deseguidores cientos o miles de veces superior a la de la mayoría.

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

tweets %>% 
    top_n(5, followers_count) %>% 
    arrange(desc(followers_count)) %>% 
    select(screen_name, followers_count, location, text)

Tweets más populares

En base a la cantidad de retweets que recibieron. Nos quedamos sólo con los tweets originales, descartando los que son retweets en si mismos (“is_retweet == TRUE”), y revisamos la distribución de sus retweets:

ggplot(filter(tweets, !is_retweet))+
    geom_histogram(aes(x = retweet_count))

Otra ditribución power law. Identifiquemos el tweet original más que sumó más retweets:

tweets %>% 
    filter(!is_retweet) %>% 
    filter(retweet_count == max(retweet_count)) %>% 
    select(screen_name, retweet_count, followers_count, location, text)

Nota: Si no estamos interesados en capturar retweets, podemos evitarlos al momento de consultar la base de Twitter, as’i tweets <- search_tweets(q = "inflacion", n = 500, include_rts = FALSE)

La hora del día a la que se producen los tweets

rtweet() provee una funció que hace facil mostrar la evolución temporal de nuestros tweets: ts_plot(). Podemos ver la frecuencia de tweets por segundo, hora, día, semana, mes o año eligiendo el parámetro correspondiente (“secondss”, “minutess”, “hours”, “days”, “weeks”, “months”, o “years”)

ts_plot(tweets, "minutes")

Procedencia de los usuarios

tweets %>%
  ggplot() +
  geom_bar(aes(location)) + 
    coord_flip() +
     labs(title = "Procedencia de los usuarios",
          x = "cantidad",
          y = "ubicación")

Dado que el campo “location” refleja el texto que cada usuario eligió para describir su ubicación (no se trata de las coordenadas de origen del tweet) las variabilidad es grande. Algunas escriben su país, otros su ciudad, otras su barrio… y hay quienes eligen opciones poéticas cómo “algún lugar del mundo”. En todo caso, la abundancia de opciones resulta en un gráfico muy difícil de leer.

Probamos extraer el top 10 de lugares más frecuentes, eliminando los tweets de usuarios sin datos en su atributo “location”.

tweets %>%
    filter(location != "", !is.na(location)) %>% 
    count(location) %>% 
    top_n(10, n) %>% 
    ggplot() +
      geom_col(aes(x = reorder(location, n), y = n)) + 
      coord_flip() +
      labs(title = "Procedencia de los usuarios",
           x = "ubicación",
           y = "cantidad")

En el mapa

Finalmente, hacemos un mapa que uestre el origen de los mensajes. Algunos de los tweets tienen coordenadas geográficas (en el campo “geo_coords”), pero la mayoría no tiene datos de ubicación más allá de la procedencia declarada por el usuario. Más adelante vamos a trabajar con tweets georeferenciados, los que tienen coordenadas; pero por ahora nos conformamos con la procedencia de los usuarios, que podemos convertir en latitud y longitud consultando el servicio de geolocalización de Google. Para poder usarlo necesitamos obtener una API key (una clave de acceso al servicio), siguiendo las instrucciones que figuran aquí: https://blog.ensalza.com/api-google-maps/

Una vez que tenemos nuestra API key, la cargamos:

# Activamos el paquete ggmap
library(ggmap)

# Cargamos nuestra api key
api_key <- "codigo_de_la_api__cortar_y_pegar_aqui"
register_google(key = api_key)

Ahora podemos geolocalizar (obtener coordenadas geográficas) de los lugares de procedencia de los usuarios en nuestros tweets, usando la función geocode()

procedencia_tweets <- tweets %>% 
    sample_n(100) %>% # tomamos una muestra al azar de 100 filas
    filter(!is.na(location), location != "") %>% 
    pull(location) %>% 
    geocode()  %>% 
    group_by(lon, lat) %>% 
    summarise(cantidad = n())

head(procedencia_tweets)

Y ahora si, al mapa. No necesitamos descargar un mapa base; podemos usar un dataset con las fronteras mundiales que viene includo con ggplot.

ggplot(procedencia_tweets) +
    borders("world") +
    geom_point(aes(x = lon, y = lat, size = cantidad), alpha = .4, color = "orange") +
    labs(title = "Procedencia de los tweets capturados")

Escuchando tweets en tiempo real

Como alternativa a consultar el archivo “histórico” de Twitter, es posible conectar a su API de streaming, que entrega tweets en tiempo real al instante en que se producen. La función stream_tweets() permite iniciar una conección y capturar tweets hasta concluya el tiempo dispuesto por el parámetro “timeout”, expresado en segundos.

Por ejemplo, para “escuchar” el stream de Twitter por un minuto (60 segundos), y capturar mensajes que incluyan la frase último momento:

captura_streaming <- stream_tweets(q = "último+momento", timeout = 60)

Verificamos el resultado (sólo los campos de usuario y texto del tweet):

captura_streaming[4:5]

Capturando tweets por períodos prolongados

Cuando queremos monitorear un evento de actualidad, por ejemplo capturando tweets que mencionen una palabra o hashtag de interés, resulta necesario mantener las escucha activa durante varias horas o días. La solución para este caso es usar la función stream_tweets2(), que permite iniciar un proceso de escucha de tiempo arbitrario. Dado que no se sabe que puede fallar en un proceso que dura varios días, la función se encarga de guardar los resultados en un archivo local a medida que se obtienen, y reiniciar la conexión a Twitter en forma automática si se interrumpe por algún motivo (como un corte momentáneo de acceso a internet).

La usamos así:

terminos_de_busqueda <- "donald+trump OR vladimir+putin"

#una semana: 60 segundos * 60 * 24 * 7
tiempo <- 60 * 60 * 24 * 7

# El archivo donde guardar los resultados en disco (tendrá formato json, así que lo usamos en el nombre de archivo)
archivo <- "busqueda_tweets_DT_VP.json"
    

stream_tweets(q = terminos_de_busqueda,
              timeout = tiempo,
              file_name = archivo,
              parse = FALSE)

Una vez que el período de captura termina, podemos leer el archivo generado.

# en el paso anterior definimos que el nombre de archivo es "busqueda_tweets_DT_VP.json"
tweets <- parse_stream("busqueda_tweets_DT_VP.json")

Y con eso concluye el proceso. Ya estamos listos para analizar el contenido.

¡Generar un archivo propio de tweets no es tan difícil!