Visualización de Datos Interactiva.

En la presente sesión vamos a visualizar algunos ejemplos de visualización interactiva.

Definición de Visualización de Datos Interactiva.

El objetivo de la visualización de datos consiste en mostrar la naturaleza de los mismos, facilitando su comprensión, exploración y análisis.

La visualización de datos nos permite hallar la existencia o inexistencia de patrones de comportamiento entre dos o más variables, poder realizar comparaciones entre sujetos de interés, encontrar valores extremos, y presentar información de manera gráfica.

La visualización estática es la visualización que consiste en presentar la información de manera plana, donde la información que se desea enseñar está toda presente de manera gráfica en todo momento.

Este es el formato es ideal para formatos físicos impresos o para presentar la informaciòn en redes sociales donde no se permita el uso de un formato interactivo.

La visualización interactiva es la visualización que permite al usuario realizar ciertas funciones de interacción con la representación gráfica de los datos. Como lo refiere el autor Ben Shneiderman, estas funciones deben ser principalmente: vista general, zoom y selección.

Este tipo de visualizaciones se crean principalmente para ser visualizadas en un navegador web, los cuales transforman código fuente escrito en HTML, CSS y JavaScript, los lenguajes de la web, en visualizaciones gráficas.

Para el propósito de este curso, veremos la visualización interactiva generada a partir de paquetes especiales de R que transforman código escrito en este lenguaje a archivos escritos en HTML, CSS y JS.

Referencias: http://informatica.blogs.uoc.edu/2017/06/01/la-visualizacion-interactiva-de-datos-como-herramienta-de-analisis/

1. Opciones del script.

Vamos a configurar nuestra sesión para que tenga ciertos parámetros:

# Opciones ----
# No usar notación científica. 
options(scipen = 999) 

# LOCALE EN ESPAÑOL (símbolos, fechas):
# En Mac suele funcionar la opción que marca Segasi: 
Sys.setlocale("LC_ALL", "es_ES.UTF-8")
## [1] "es_ES.UTF-8/es_ES.UTF-8/es_ES.UTF-8/C/es_ES.UTF-8/en_US.UTF-8"
# En Windows/Linux suele funcionar esta función: 
Sys.setlocale("LC_ALL","Spanish")
## [1] ""

2. Librerías a utilizar:

A continuación vamos a utilizar las siguientes librerías:

library(tidyverse) # Manejo de bases de datos 
library(plotly) # Graficas interactivas
library(htmlwidgets) # Widgets de html (para guardar las paginas)
library(dygraphs) # Para hacer series de tiempo
library(highcharter) # Para hacer treemaps y otras visualizaciones
library(networkD3) # Generar redes con D3
library(igraph) # Para manejo de información de redes

library(janitor) # Limpieza de nombres en las tibbles
library(readxl) # Leer exceles
library(scales) # Formatos especiales en los ejes
library(xts) # Creación de objetos de series de tiempo
library(hrbrthemes) # Temas de ggplot
library(viridis) # Paletas viridis
library(gapminder) # Bases de datos de gapminder. 
library(rtweet)

Para instalarlas, pueden ejecutar el siguiente código:

pacman::p_load("rtweet", "gapminder", "viridis",
               "hrbrthemes", "xts", "scales",
               "janitor", "readxl", "igraph",
               "networkD3","highcharter",
               "dygraphs","htmlwidgets",
               "htmlwidgets", "plotly",
               "tidyverse")

3. Visualizaciones.

Ahora vamos a practicar con algunas de las visualizaciones típicas que podemos ejecutar a través de paquetes de visualización interactiva.

3.1 Gráfica de puntos y de burbujas:

Para nuestra primera gráfica, vamos a generar un diagrama de burbujas/dispersión relacionando los valores de Esperanza de vida (lifeExp) vs. PIB Per Cápita (gdpPercap).

Primero: Extraemos los datos:

# Grafica 01: Burbujas ----

# El conjunto de datos se extrajo de la librería gapminder. 
data <- gapminder %>% 
  filter(year=="2007") %>% 
  select(-year)

Segundo: Hacemos la gráfica en ggplot

# Most basic bubble plot
plt = data %>%
  arrange(desc(pop)) %>%
  mutate(country = factor(country, country)) %>%
  ggplot(aes(x=gdpPercap, y=lifeExp, size=pop, fill=continent)) +
  geom_point(alpha=0.5, shape=21, color="black") +
  scale_size(range = c(.1, 24), name="Population (M)") +
  scale_fill_viridis(discrete=TRUE, guide=FALSE, option="A") +
  theme_ipsum() +
  theme(legend.position="bottom") +
  ylab("Life Expectancy") +
  xlab("Gdp per Capita") +
  theme(legend.position = "bottom")

Tercero: Convertimos la gráfica de ggplot a una gráfica interactiva con plotly

La librería plotly cuenta con la función ggplotly, la cual nos permite convertir gráficas de ggplot a gráficas interactivas.

# Imprimimos la gráfica de manera interactiva. 
plotly::ggplotly(plt)

Esta conversión no aplica para todos los tipos de gráficas, sin embargo, si aplica para las más comunes, como las gráficas de barras, las de puntos, las de líneas (con algunas consideraciones), los diagramas de calor o algunos tipos de mapas.

3.2 Gráficos de líneas.

Los gráficos de líneas son los gráficos ideales para poder visualizar la evolución de una variable en el tiempo. En el ejemplo que viene vamos a mostrar como elaborar una gráfica de lineas a partir de los datos de la evolución del precio del Bitcoin para el último año.

Paso 1. Preparamos los datos:

# Grafica 02: Líneas (BITCOIN)
bitcoin <- read_csv("Bases de datos/bitcoin.csv") %>% 
  mutate(Volume = as.numeric(Volume), 
         Close = as.numeric(Close)) %>% 
  filter(!is.na(Volume) | !is.na(Close)) %>% 
  mutate(popup = str_c("<b>Precio de Cierre:</b> $", 
                       prettyNum(Close, 
                                 big.mark = ","), "<br>", 
                       "<b>Fecha:</b> ", Date, "<br>",
                       "<b>Volumen de transacciones:</b> $",
                       prettyNum(Volume, big.mark = ",")))

Paso 2: Elaboramos la gráfica:

# Gráfica ---- 
plt = bitcoin %>% 
  ggplot(aes(x = Date, 
             y = Close, 
             text = popup, 
             group = "a")) + 
  geom_line() + 
  labs(title = "Bitcoin. Precio de Cierre diario.<br>Datos del 17 de mayo del 2020 al 16 de mayo del 2021.") + 
  theme_minimal() + 
  theme(plot.title = element_text(hjust = 0.5, family = "Arial")) + 
  scale_y_continuous(labels = scales::dollar_format())

Paso 3. Convertimos la gráfica en una gráfica interactiva:

plt_interactiva_btc <- plotly::ggplotly(plt, tooltip = "text")
plt_interactiva_btc

Paso 4. Guardamos el widget.

La librería htmltools es una librería que permite manipular objetos interactivos creados en R para exportarlos a otros formatos y poder visualizarlos fuera del entorno del lenguaje de programación. En este caso, aprovechando que el resultado de la función ggplotly es un objeto de la clase htmlwidget y que htmltools nos permite trabajar con el, vamos a aprovechar la función saveWidget() para guardar nuestra visualización en un archivo html.

Este archivo nos va a permitir mostrar nuestra visualización en un navegador, ya sin tener que pasar por RStudio.

class(plt_interactiva_btc) #[1] "plotly"     "htmlwidget"
# Función para guardar el widget: 
saveWidget(plt_interactiva_btc, 
           "HTMLs/plt_interactiva_btc.html")

3.3 Gráficos de barras para hacer comparaciones.

En el siguiente ejemplo, vamos a utilizar los datos de años de escolaridad promedio del Censo de Población y Vivienda 2020 de INEGI para ver el grado de educación de los municipios de algún estado en particular.

Paso 1. Preparamos la información:

En este paso, la preparación va a pasar por generar un popup para que sea la información que brote cuando un usuario haga la acción de hover sobre los elementos de la gráfica.

Igualmente, nos vamos a quedar con los datos del estado de Morelos para hacer nuestra gráfica con los datos acotados a un solo estado.

# Gráfica 03: Barras (COMPARATIVO INEGI)
educ <- readxl::read_xlsx("Bases de datos/grado_educacion_promedio.xlsx") %>% 
  mutate(popup = str_c("<b>Entidad Federativa: </b>", entidad_federativa, "<br>", 
                       "<b>Municipio: </b>", municipio, "<br>", 
                       "<b>Grado promedio educación: </b>", valor, " años")) %>% 
  filter(entidad_federativa == "Morelos")

Paso 2. Generamos la gráfica en ggplot()

Generamos una gráfica de barras horizontales en ggplot().

plt = educ %>% 
  ggplot(aes(x = reorder(municipio, valor), 
             y = valor, 
             text = popup)) + 
  geom_col(fill = "salmon") + 
  coord_flip() + 
  # geom_text(aes(label = str_c(round(valor, 2), " años")), hjust = 0.5) + 
  scale_y_continuous(breaks = 0:11, 
                     limits = c(0, 11), 
                     expand = expansion(c(0,0.001), 0)) +
  # ggthemes::theme_wsj() + 
  labs(title = "Grado Educativo para los municipios de Morelos", 
       y = "Municipios", x = "Años de escuela")

Paso 2. Convertimos la visualización a una visualización interactiva con la función plotly::ggplotly()

ggplotly(plt, tooltip = "text")

Información extra de plotly

En un contexto de uso en shiny, plotly cuenta con las funciones plotly::renderPlotly() para generar visualizaciones dentro de los procesos del servidor, y plotly::plotlyOutput() para colocar las visualizaciones de plotly dentro de la interfaz de usuario.

3.4 Nubes de palabras.

A continuación, vamos a ver como elaborar una gráfica de nubes de palabras. En este caso, utilizaremos las funciones de la librería wordcloud2, la cual genera, a partir de un vector de texto, una nube de palabras interactiva.

Los datos que vamos a utilizar en este caso van a ser tweets sobre el tema de vacunación contra COVID19 en México para el día de hoy (18 de mayo, 2021).

Paso #1. Extraemos los datos de los tweets.

Para extraer información del API de Twitter, utilizamos la función search_tweets de la librería rtweet, como vemos a continuación.

# Gráfica 04: WordClouds (TWITTER) ----
vac = search_tweets(q = "vacunación", 
                    n = 15000, 
                    include_rts = FALSE)
# Escribimos y guardamos los datos
write.xlsx(vac, "Bases de datos/tweets_vacunacion.xlsx")

Si no queremos hacer todo el proceso de extracción de tweets, podemos leer directamente la base que escribimos en el paso anterior:

vac <- read_xlsx("Bases de datos/tweets_vacunacion.xlsx")

Para la creación de la nube de palabras, yo suelo utilizar la siguiente función (que ya cuenta con muchos de los procesos de limpieza de información que se necesitan para preparar los datos).

Te sugiero que la tengas a la mano y que cuando necesites hacer una nube de palabras, solo la recojas y la reutilices.

La función está optimizada para trabajar con palabras en español, por lo que si quieres utilizarla para otro lenguaje, o con otra paleta de colores o de manera más personalizada, tendrás que ubicar la parte donde cambia y hacer las modificaciones correspondientes.

# Nube de palabras: 
create_wordcloud <- function(data, stop_words = c(), num_words = 100, background = "white",  mask = NULL) {
  # Checar si esta instalado Pacman
  if (!require("pacman")) install.packages("pacman")
  pacman::p_load(wordcloud2, tm, stringr)
  
  # Pre-Función para eliminar simbolos raros
  quitar_signos <- function(x)  stringr::str_remove_all(x, pattern = rebus::char_class("¿¡"))
  
  # If text is provided, convert it to a dataframe of word frequencies
  # Si se provee el texto, convertirlo a un dataframe de frecuencia de palabras 
  if (is.character(data)) {
    # Convertimos a Corpus
    corpus <- Corpus(VectorSource(data))
    # Convertimos el texto dentro del Corpus a Minusculas
    corpus <- tm_map(corpus, tolower)
    # Removemos la puntuacion (.,-!?)
    corpus <- tm_map(corpus, removePunctuation)
    # Removemos los numeros
    corpus <- tm_map(corpus, removeNumbers)
    # Removemos los signos de admiracion e interrogacion al reves
    corpus <- tm_map(corpus, quitar_signos)    
    # Removemos las stopwords (palabras muy muy comunes que se usan para dar coherencia
    # a las oraciones. Para saber cuales, escribir: stopwords("spanish))
    corpus <- tm_map(corpus, removeWords, c(stopwords("spanish"), stop_words))
    # Generamos una matriz para hacer el conteo
    tdm <- as.matrix(TermDocumentMatrix(corpus))
    # Obtenemos el numero de la frecuencia de cada palabra
    data <- sort(rowSums(tdm), decreasing = TRUE)
    # Generamos una tabla con la palabra en una columna y su frecuencia de uso en otra 
    data <- data.frame(word = names(data), freq = as.numeric(data))
  }
  
  freq_palabras <<- data
  
  # Make sure a proper num_words is provided
  # Nos aseguramos que un numero adecuado de palabras `num_provider` es generado`
  if (!is.numeric(num_words) || num_words < 3) {
    num_words <- 3
  }  
  
  # Grab the top n most common words
  # Recortamos la base de datos de palabras a un numero `n` especificado
  data <- head(data, n = num_words)
  if (nrow(data) == 0) {
    return(NULL)
  }
  wordcloud2(data, backgroundColor = background, color = "random-dark", fontFamily = "Asap", size = 3) 
}

Finalmente, aplicamos la función:

# Generamos el wordcloud ----
create_wordcloud(vac$text, # Vector de texto
                 num_words = 200 # Numero de palabras a incluir
                 )