Introducción

Una serie temporal está formada por observaciones en un período de tiempo registradas de forma consecutiva. La visualización de series temporales pretende revelar cambios de una o más variables cuantitativas a través del tiempo, y mostrar las relaciones entre las variables y su evolución a través del tiempo. El gráfico de serie temporal estándar muestra el tiempo a lo largo del eje horizontal. Por otro lado, el tiempo puede concebirse como un grupo o una variable condicionante. Esta solución permite mostrar varias variables junto con un diagrama de dispersión, utilizando diferentes paneles para subconjuntos de datos (tiempo como variable condicionante) o utilizando diferentes atributos para grupos de datos (tiempo como una agrupación variable). Por último, el tiempo puede usarse como una variable complementaria que agrega información a un gráfico donde se comparan varias variables.

Las siguientes secciones proporcionan una variedad de ejemplos para ilustrar un conjunto de técnicas útiles que trabajan con tres conjuntos de datos disponibles en el directorio de trabajo.

Los paquetes más relevantes son: “lattice”, “latticeExtra” y “ggplot2” para gráficos estéticos; “zoo” y “xts” para leer y organizar datos como series de tiempo; “RColorBrewer” para definir los colores y paquetes de “dygraphs”, “highcharter”, y “plotly”, establecido en el marco “htmlwidgets”, para generar animaciones o gráficos interactivos.

Empezamos por cargar los paquetes principales.

library(lattice)
library(ggplot2)
# latticeExtra debe cargarse después de ggplot2 para evitar el enmascaramiento de `layer`
library(latticeExtra)
## Warning: package 'latticeExtra' was built under R version 3.6.3
library(RColorBrewer)

mitema<-custom.theme.2(
  pch=19, cex=0.7, region=rev(brewer.pal(9, 'YlOrRd')),
  symbol=brewer.pal(n=8, name="Dark2"))

mitema$strip.background$col=mitema$strip.shingle$col=
  mitema$strip.border.col="transparent"

myArgs <- list(
  as.table=TRUE, between=list(x=0.5, y=0.2),
  xscale.components = function(...)
    modifyList(xscale.components.default(...), list(top=FALSE)),
  yscale.components = function(...)
    modifyList(yscale.components.default(...), list(right=FALSE)))

lattice.options(default.theme=mitema, default.args=modifyList(
  lattice.options()$default.args, myArgs))

library(zoo)
## Warning: package 'zoo' was built under R version 3.6.3

Estos son los datos que vamos a utilizar: - aranjuez.RData - navarra.RData - CO2.RData

# Abrimos las bases de datos

load("aranjuez.RData")
load("navarra.RData")
load("CO2.RData")

El tiempo en el eje horizontal

El método de visualización más frecuente de una serie temporal es utilizar el eje horizontal para representar el índice de tiempo. Esta sección muestra dos variantes de este enfoque para mostrar series temporales multivariadas: múltiples series de tiempo con diferentes escalas y variables con la misma escala.

Gráfico de tiempo de variables con diferentes escalas

Existe multitud de investigaciones científicas interesadas en la relación de varias variables meteorológicas. Un enfoque adecuado sería mostrar la evolución temporal de todos ellos usando un panel para cada una de las variables. La superposición de variables con diferentes características no es muy útil (a menos que sus valores hayan sido rescatados previamente), por lo que este enfoque se pospone para la siguiente sección (variables con la misma escala).

Para el primer ejemplo, utilizaremos los ochos años de datos diarios de una estación meteorológica ubicada en Aranjuez (Madrid). Esta serie temporal multivariada se puede mostrar con el método “xyplot” de la libreria “lattice” para objetos la libreria “zoo” con un panel para cada variable.

##  El argumento de diseño organiza los paneles en filas
xyplot(aranjuez, layout = c(1, ncol(aranjuez)))

El paquete ggplot2 proporciona el método genérico autoplot para automatizar la visualización de ciertas clases con un simple comando. El paquete zoo proporciona un método de trazado automático para la clase zoo con un resultado similar al obtenido con xyplot.

## facet_free permite que cada panel tenga su propio rango
autoplot(aranjuez) + facet_free()

Series temporales de variables con la misma escala

Como ejemplo de series de tiempo de variables con la misma escala, vamos a utilizar medidas de radiación solar de diferentes estaciones meteorológicas.

El primer intento de mostrar esta serie de tiempo multivariante hace uso del método “xyplot.zoo”. El objetivo de este gráfico es mostrar el comportamiento de la colección como un todo: las series se superponen en el mismo panel (superpose = TRUE) sin leyenda (auto.key = FALSE), usando líneas finas y transparencia parcial. La transparencia suaviza los problemas de sobreparcelación y revela grupos de densidad porque las regiones con más líneas superpuestas son más oscuras. El siguiente código muestra las variaciones alrededor del promedio de tiempo (avRad).

avRad <- zoo(rowMeans(navarra, na.rm = 1), index(navarra))
pNavarra <- xyplot(navarra - avRad,
                   superpose = TRUE, auto.key = FALSE,
                   lwd = 0.5, alpha = 0.3, col = 'midnightblue') 
pNavarra

Este resultado se puede mejorar con un gráfico horizontal. El gráfico horizontal es útil para examinar como una gran cantidad de series cambia con el tiempo, de forma que permite comparar entre las series de tiempo individuales y el análisis independiente de cada serie. Además, los comportamientos extraordinarios y los patrones predominantes se distinguen fácilmente.

Este gráfico muestra varias series apiladas que colapsan el eje y para liberar espacio vertical:

Debido a que los paneles comparten la misma estructura de diseño, una vez que se comprende esta técnica, es fácil establecer comparaciones o detectar eventos extraordinarios. Este método es lo que Tufte describía como pequeños múltiplos.

El siguiente código muestra las variaciones de la radiación solar alrededor del promedio de tiempo con un gráfico horizontal utilizando una fila para cada serie de tiempo. En el código elegimos “origin = 0” y dejamos el argumento “horizonscale” indefinido (por defecto). Con esta combinación, cada panel tiene diferentes escalas y los colores en cada panel representan desviaciones del origen. Esto se representa en la clave de color con el símbolo \(\Delta_i\), donde el subíndice denota la existencia de múltiples paneles con diferentes escalas.

horizonplot(navarra - avRad,
            layout = c(1, ncol(navarra)),
            origin = 0, ## Se calculan las desviaciones en cada panel
            ## de este valor
            colorkey = TRUE,
            col.regions = brewer.pal(6, "RdBu"))

El gráfico horizontal también es útil para revelar las diferencias entre una serie de tiempo univariante y otra referencia. Por ejemplo, podríamos estar interesado en la salida de la temperatura observada del promedio a largo plazo, o en otras palabras, el cambio de temperatura por hora. Ilustremos esta aproximación con la serie de tiempo de las temperaturas medias medidas en la estación meteorologica de Aranjuez. La referencia es el promedio diario a largo plazo calculado con “ave”.

Ta <- aranjuez$TempAvg
timeIndex <- index(aranjuez)
longTa <- ave(Ta, format(timeIndex, '%j'))
diffTa <- (Ta - longTa) 

El siguiente código usa horzontplot con el método cortar y apilar para distinguir entre años.

years <- unique(format(timeIndex, '%Y'))
  
horizonplot(diffTa, cut = list(n = 8, overlap = 0),
            colorkey = TRUE, layout = c(1, 8),
            scales = list(draw = FALSE, y = list(relation = 'same')),
            origin = 0, strip.left = FALSE) +
    layer(grid.text(years[panel.number()], x  =  0, y  =  0.1, 
                    gp = gpar(cex = 0.8),
                    just = "left")) 

Una alternativa es un gráfico de nivel que muestra la serie temporal utilizando partes de su índice de tiempo tanto como variable independiente como condicionante. El siguiente código muestra las diferencias con el día del mes en el eje horizontal y el año en el eje vertical, con un panel diferente para cada mes. Por lo tanto, cada celda de la siguiente figura corresponde a un determinado día de la serie temporal.

year <- function(x)as.numeric(format(x, '%Y'))
day <- function(x)as.numeric(format(x, '%d'))
month <- function(x)as.numeric(format(x, '%m')) 
mitema <- modifyList(custom.theme(region = brewer.pal(9, 'RdBu')),
                     list(
                       strip.background = list(col = 'gray'),
                       panel.background = list(col = 'gray')))

maxZ <- max(abs(diffTa))

levelplot(diffTa ~ day(timeIndex) * year(timeIndex) | factor(month(timeIndex)),
          at = pretty(c(-maxZ, maxZ), n = 8),
          colorkey = list(height = 0.3),
          layout = c(1, 12), strip = FALSE, strip.left = TRUE,
          xlab = 'Day', ylab = 'Month', 
          par.settings = mitema)

La versión de ggplot requiere un data.frame con el día, mes y año organizados en diferentes columnas.

df <- data.frame(Vals = diffTa,
                 Day = day(timeIndex),
                 Year = year(timeIndex),
                 Month = month(timeIndex)) 

Los valores de este data.frame se muestra como un gráfico de nivel gracias a la función geom_raster.

library(scales) 
## La escala de paquetes es necesaria para la función pretty_breaks.
ggplot(data = df,
       aes(fill = Vals,
           x = Day,
           y = Year)) +
  facet_wrap(~ Month, ncol = 1, strip.position = 'left') +
  scale_y_continuous(breaks = pretty_breaks()) + 
  scale_fill_distiller(palette = 'RdBu', direction = 1) + 
  geom_raster() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank()) 

Gráficos Interactivos

Esta sección describe las alternativas de las figuras estáticas incluidas en las secciones anteriores con varios paquetes: “dygraphs”,“highcharter” y “plotly”. Estos paquetes son interfaces de R para librerías de JavaScript basadas en el paquete “htmlwidgets”.

Dygraph

El paquete “dygraphs” es una interfaz para JavaScript de dygraphs biblioteca y proporciona instalaciones para trazar series temporales. Funciona automáticamente con objetos de series temporales xts, o con objetos que se pueden forzar a esta clase. El resultado es un gráfico interactivo, donde los valores se muestran de acuerdo con la posición del ratón sobre las series de tiempo. Las regiones se pueden seleccionar para ampliar un período de tiempo.

library(dygraphs)
## Warning: package 'dygraphs' was built under R version 3.6.3
dyTemp <- dygraph(aranjuez[, c("TempMin", "TempAvg", "TempMax")],
                  main = "Temperatura en Aranjuez",
                  ylab = "ºC")

widgetframe::frameWidget(dyTemp)

Puedes personalizar las gráficas canalizando comandos adicionales en el gráfico original. La función “dyOptions” proporciona varias opciones para el gráfico, y la función “dyHighlight” configura unas opciones para que al poner el ratón sobre la serie, los datos se resalten. Por ejemplo, con el siguiente código el valor de las líneas no selecionadas se reduce y pasan a ser semitrasparentes, y la línea seleccionada aumenta de ancho.

dyTemp %>%
  dyHighlight(highlightSeriesBackgroundAlpha = 0.2,
              highlightSeriesOpts = list(strokeWidth = 2)) %>%
  widgetframe::frameWidget()

Un enfoque alternativo para representar las variables superior e inferior de esta serie de tiempo es con una región sombreada. La función “dySeries” acepta un vector de tres caracteres que especifica un conjunto de nombres de columnas de entrada para usar como el valor más bajo, y el más alto para una serie con una regi?n sombreada a su alrededor.

dygraph(aranjuez[, c("TempMin", "TempAvg", "TempMax")],
        main = "Temperatura en Aranjuez",
        ylab = "ºC") %>%
  dySeries(c("TempMin", "TempAvg", "TempMax"),
           label = "Temperatura") %>%
  widgetframe::frameWidget()

Gráficos Dinámicos

El paquete “highcharter” es una interfaz para los gráficos dinámicos de la librería de JavaScript, con un amplio repertorio de soluciones gráficas. La visualización de series temporales con este paquete se puede lograr con la combinación de la función genérica “highchart” y varias llamadas a la función “hc_add_series_xts” a través del operador %>%. El resultado vuelve a ser un gráfico interactivo que permite enfocar y seleccionar las series.

library(highcharter)
## Warning: package 'highcharter' was built under R version 3.6.3
library(xts)

aranjuezXTS <- as.xts(aranjuez)

highchart() %>%
  hc_add_series(name = 'TempMax',
                aranjuezXTS[, "TempMax"]) %>%
  hc_add_series(name = 'TempMin',
                aranjuezXTS[, "TempMin"]) %>%
  hc_add_series(name = 'TempAvg',
                aranjuezXTS[, "TempAvg"]) %>%
  widgetframe::frameWidget()

plotly

El paquete “plotly” es una interfaz para la librería plotly de JavaScript, también con un amplio repertorio de soluciones gráficas. Este paquete no proporciona ninguna función específicamente enfocada a series de tiempo. Por lo tanto, el objeto de la serie temporal debe transformarse en un marco de datos, con un data.frame, que incluya una columna para el ?índice de tiempo. Si el data.frame est? en formato ancho (una columna por variable), cada variable se representar? con una llamada a la función “add_lines”. Sin embargo, si data.frame está? en formato largo (una columna para valores y una columna para nombres de variables) solo se requiere una llamada a “add_lines”. El siguiente código sigue este enfoque utilizando la combinación de “fortify”, para convertir el objeto “zoo” a un data.frame, y “melt”, para transformar el formato ancho a largo.

aranjuezDF <- fortify(aranjuez[,
            c("TempMax",
              "TempAvg",
              "TempMin")],
           melt = TRUE)

summary(aranjuezDF) 
##      Index                Series         Value        
##  Min.   :2004-01-01   TempMax:2898   Min.   :-12.980  
##  1st Qu.:2005-12-29   TempAvg:2898   1st Qu.:  7.107  
##  Median :2008-01-09   TempMin:2898   Median : 13.560  
##  Mean   :2008-01-03                  Mean   : 14.617  
##  3rd Qu.:2010-01-03                  3rd Qu.: 21.670  
##  Max.   :2011-12-31                  Max.   : 41.910  
##                                      NA's   :10

El siguiente código produce un gráfico interactivo con la función genérica “plot_ly” conectado con “add_lines” a través del operador %>%.

library(plotly)

plot_ly(aranjuezDF) %>%
   add_lines(x = ~ Index,
       y = ~ Value,
       color = ~ Series) %>%
  widgetframe::frameWidget()

El tiempo como condicionante o variable de agrupación

Anteriormente aprendimos a mostrar la evolución temporal de múltiples series de tiempo con diferentes escalas. Pero, ¿qué pasa si en lugar de mostrar la evolución del tiempo queremos representar la relación entre las variables? Esta sección sigue este enfoque: primero, una matriz de diagrama de dispersión que usa grupos que se define de acuerdo con el tiempo como una variable de agrupación; A continuación, se produce un diagrama de dispersión mejorado con el tiempo como variable de acondicionamiento utilizando la técnica de múltiplos pequeños.

Scatterplot matrix: el tiempo como una variable de agrupación

Las matrices de diagrama de dispersión se basan en la técnica de pequeños múltiplos: pequeñas representaciones en miniatura de imágenes múltiples que se muestran todas a la vez, lo que permite al lector comparar de forma inmediata y paralela las diferencias entre cuadros. Una matriz de diagrama de dispersión es una visualización de todos los diagramas de dispersión bivariados por pares dispuestos en una matriz \(p \times p\) para las variables \(p\). Cada subtrama muestra la relación entre el par de variables en la intersección de la fila y la columna indicada por los nombres de las variables en los paneles diagonales.

Esta herramienta gráfica se implementa en la función “splom”. El siguiente código muestra la relación entre el conjunto de variables meteorológicas utilizando una paleta secuencial del catálogo ColorBrewer (RbBu, con negro agregado para completar una paleta de doce colores) para codificar el mes. El orden de colores de esta paleta se elige para mostrar los meses de verano con colores intensos y para distinguir entre la primera y la segunda mitad del año con rojo y azul, respectivamente.

aranjuezDF <- as.data.frame(aranjuez)
aranjuezDF$Month <- format(index(aranjuez), '%m') 
## Paleta rojo-azul con negro agregado (12 colores)
colors <- c(brewer.pal(n = 11, 'RdBu'), '#000000')
## Reorganizar seg?n meses (la m?s oscura para el verano)
colors <- colors[c(6:1, 12:7)] 
splom(~ aranjuezDF[1:10], ## No incluir "Month"
      groups = aranjuezDF$Month,
      auto.key = list(space = 'right', 
                      title = 'Month', cex.title = 1),
      pscale = 0, varname.cex = 0.7, xlab = '',
      par.settings = custom.theme(symbol = colors,
                                  pch = 19),
      cex = 0.3, alpha = 0.1)

La versión “ggplot2” de este gráfico se produce gracias a la función “ggpairs” proporcionada por el paquete “GGally”.

library(GGally)
## Warning: package 'GGally' was built under R version 3.6.3
ggpairs(aranjuezDF,
        columns = 1:10, ## No incluir "Month"
        upper = list(continuous = "points"),
        mapping = aes(colour = Month, alpha = 0.1)) 
## Warning: Removed 3 rows containing missing values (geom_point).
## Warning: Removed 7 rows containing missing values (geom_point).
## Warning: Removed 2 rows containing missing values (geom_point).
## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing missing values (geom_point).
## Warning: Removed 3 rows containing missing values (geom_point).
## Warning: Removed 3 rows containing non-finite values (stat_density).
## Warning: Removed 7 rows containing missing values (geom_point).
## Warning: Removed 5 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing missing values (geom_point).
## Warning: Removed 3 rows containing missing values (geom_point).

## Warning: Removed 3 rows containing missing values (geom_point).

## Warning: Removed 3 rows containing missing values (geom_point).

## Warning: Removed 3 rows containing missing values (geom_point).
## Warning: Removed 17 rows containing missing values (geom_point).
## Warning: Removed 7 rows containing missing values (geom_point).

## Warning: Removed 7 rows containing missing values (geom_point).
## Warning: Removed 7 rows containing non-finite values (stat_density).
## Warning: Removed 9 rows containing missing values (geom_point).
## Warning: Removed 18 rows containing missing values (geom_point).
## Warning: Removed 7 rows containing missing values (geom_point).

## Warning: Removed 7 rows containing missing values (geom_point).

## Warning: Removed 7 rows containing missing values (geom_point).

## Warning: Removed 7 rows containing missing values (geom_point).
## Warning: Removed 17 rows containing missing values (geom_point).
## Warning: Removed 2 rows containing missing values (geom_point).
## Warning: Removed 5 rows containing missing values (geom_point).
## Warning: Removed 9 rows containing missing values (geom_point).
## Warning: Removed 2 rows containing non-finite values (stat_density).
## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 2 rows containing missing values (geom_point).

## Warning: Removed 2 rows containing missing values (geom_point).

## Warning: Removed 2 rows containing missing values (geom_point).

## Warning: Removed 2 rows containing missing values (geom_point).
## Warning: Removed 16 rows containing missing values (geom_point).
## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing missing values (geom_point).
## Warning: Removed 18 rows containing missing values (geom_point).
## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 11 rows containing non-finite values (stat_density).
## Warning: Removed 11 rows containing missing values (geom_point).

## Warning: Removed 11 rows containing missing values (geom_point).

## Warning: Removed 11 rows containing missing values (geom_point).

## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 17 rows containing missing values (geom_point).
## Warning: Removed 3 rows containing missing values (geom_point).
## Warning: Removed 7 rows containing missing values (geom_point).
## Warning: Removed 2 rows containing missing values (geom_point).
## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing missing values (geom_point).
## Warning: Removed 3 rows containing missing values (geom_point).
## Warning: Removed 7 rows containing missing values (geom_point).
## Warning: Removed 2 rows containing missing values (geom_point).
## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing missing values (geom_point).
## Warning: Removed 3 rows containing missing values (geom_point).
## Warning: Removed 7 rows containing missing values (geom_point).
## Warning: Removed 2 rows containing missing values (geom_point).
## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing missing values (geom_point).
## Warning: Removed 3 rows containing missing values (geom_point).
## Warning: Removed 7 rows containing missing values (geom_point).
## Warning: Removed 2 rows containing missing values (geom_point).
## Warning: Removed 11 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing missing values (geom_point).

## Warning: Removed 14 rows containing missing values (geom_point).
## Warning: Removed 17 rows containing missing values (geom_point).

## Warning: Removed 17 rows containing missing values (geom_point).
## Warning: Removed 16 rows containing missing values (geom_point).
## Warning: Removed 17 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing missing values (geom_point).

## Warning: Removed 14 rows containing missing values (geom_point).

## Warning: Removed 14 rows containing missing values (geom_point).

## Warning: Removed 14 rows containing missing values (geom_point).
## Warning: Removed 14 rows containing non-finite values (stat_density).

Diagrama de dispersión con el tiempo como variable condicionante

Los gráficos anteriores usan colores para codificar meses. En cambio, ahora mostraremos diagramas de dispersión separados con un panel para cada mes. Además, el tipo de estadística (promedio, máximo, mínimo) se incluye como una variable de condicionamiento adicional.

Primero, el conjunto de datos debe reformarse desde el formato ancho (una columna para cada variable) al formato largo (solo una columna para los valores de temperatura con una fila para cada observación). Esta tarea se logra fácilmente con la función “melt” incluida en el paquete “reshape2”.

library(reshape2)
## Warning: package 'reshape2' was built under R version 3.6.3
aranjuezRshp <- melt(aranjuezDF,
                     measure.vars = c('TempMax',
                                      'TempAvg',
                                      'TempMin'),
                     variable.name = 'Statistic',
                     value.name = 'Temperature')

Esta matriz de paneles se puede mostrar con “ggplot” usando “facet_grid”. El siguiente código utiliza una transparencia parcial para hacer frente a la superposición, pequeños segmentos horizontales y verticales (geom_rug) para mostrar la densidad de puntos en ambas variables y una línea suave en cada panel.

ggplot(data = aranjuezRshp, aes(Radiation, Temperature)) +
   facet_grid(Statistic ~ Month) +
   geom_point(col = 'skyblue4', pch = 19, cex = 0.5, alpha = 0.3) +
   geom_rug() +
   stat_smooth(se = FALSE, method = 'loess',
     col = 'indianred1', lwd = 1.2) +
   theme_bw() 
## Warning: Removed 10 rows containing non-finite values (stat_smooth).
## Warning: Removed 10 rows containing missing values (geom_point).

La versión con “lattice” necesita la función “useOuterStrips” del paquete “latticeExtra”, que imprime los nombres de las variables de acondicionamiento en los márgenes exteriores superior e izquierdo.

useOuterStrips(
  xyplot(Temperature ~ Radiation | Month * Statistic,
         data = aranjuezRshp,
         between = list(x = 0),
         col = 'skyblue4', pch = 19,
         cex = 0.5, alpha = 0.3)) +
  layer({
    panel.rug(..., col.line = 'indianred1',
              end = 0.05, alpha = 0.6)
    panel.loess(..., col = 'indianred1',
                lwd = 1.5, alpha = 1)
  }) 

El tiempo como variable complementaria

En esta sección, el tiempo se utilizar? como una variable complementaria que agrega información a un gráfico donde se enfrentan varias variables. Ilustraremos este enfoque con la evolución de la relación entre el ingreso nacional bruto y las emisiones de dióxido de carbono (\(CO_2\)) para un conjunto de países extraídos de la base de datos de datos abiertos del Banco Mundial. Intentaremos varias soluciones para mostrar la relación entre las emisiones de \(CO_2\) y el ingreso nacional bruto a lo largo de los años utilizando el tiempo como una variable complementaria.

Línea Múltiples

Nuestro primer enfoque es mostrar todos los datos en un panel con un diagrama de dispersión utilizando nombres de países como factor de agrupación. Los puntos de cada país están conectados con las líneas múltiples para revelar la evolución del tiempo.

xyplot(GNI.capita  ~ CO2.capita, data = CO2data,
xlab = "Carbon dioxide emissions (metric tons per capita)",
ylab = "GNI per capita, PPP (current international $)",
groups = Country.Name, type = 'b') 

Se pueden agregar tres mejoras a este resultado gráfico:

  1. Definir una mejor paleta de colores para mejorar la discriminación visual entre países.
  2. Mostrar información de tiempo con etiquetas para enseñar los valores del año.
  3. Etiquetar cada “polyline” con el nombre del país en lugar de una leyenda.
  4. Elegir colores

    La variable categórica Country.Name se codificar? con una paleta cualitativa, es decir, los primeros cinco colores de la paleta Set1 del paquete RColorBrewer. Como hay más países que los colores, tenemos que repetir algunos colores para completar el número de niveles de la variable Country.Name. El resultado es una paleta con colores no únicos y, por lo tanto, algunos países compartirán el mismo color. Esto no es un problema porque las curvas se etiquetarán y los países con el mismo color se mostrarán a una distancia suficiente.

    nCountries <- nlevels(CO2data$Country.Name)
    pal <- brewer.pal(n = 5, 'Set1')
    pal <- rep(pal, length = nCountries) 

    Los colores adyacentes de esta paleta se eligen para ser fácilmente distinguibles. Por lo tanto, la conexión entre colores y países debe ser tal que las líneas cercanas se codifiquen con colores adyacentes de la paleta. Un enfoque simple es calcular el promedio anual de la variable a representar a lo largo del eje x (CO2.capita) y extraer colores de la paleta de acuerdo con el orden de este valor.

    ## Rango de valores promedio de CO2 per c?pita
    CO2mean <- aggregate(CO2.capita ~ Country.Name,
                         data = CO2data, FUN = mean)
    palOrdered <- pal[rank(CO2mean$CO2.capita)]  
    
    ## simpleTheme encapsula la paleta en un nuevo tema para xyplot
    mitema <- simpleTheme(pch = 19, cex = 0.6, col = palOrdered) 
    ## version lattice
    pCO2.capita <- xyplot(GNI.capita  ~ CO2.capita,
                          data = CO2data,
                          xlab = "Emisiones de di?xido de carbono (toneladas m?tricas per c?pita)",
                          ylab = "INB per c?pita, PPP ($ internacional actual)",
                          groups = Country.Name,
                          par.settings = mitema,
                          type = 'b')
    
    pCO2.capita

    ##  version ggplot2
    gCO2.capita <- ggplot(data = CO2data,
                          aes(x = CO2.capita,
                              y = GNI.capita,
                              color = Country.Name)) +
      geom_point() + geom_path() +
      scale_color_manual(values = palOrdered, guide = FALSE) +
      xlab('Emisiones de CO2 (toneladas m?tricas per c?pita)') +
      ylab('INB per c?pita, PPP ($ internacional actual)') +
      theme_bw()

    Etiquetas para mostrar información de tiempo

    Este resultado puede mejorarse con etiquetas que muestren los años para mostrar la evolución del tiempo. La función de panel “panel.text” imprime las etiquetas de año con la combinación de “+ .trellis, glayer_” y “panel.text”. Usando “glayer_” en lugar de “glayer” nos aseguramos de que las etiquetas se impriman debajo de las líneas.

    ##  version lattice
    pCO2.capita <- pCO2.capita +
      glayer_(panel.text(...,
                         labels = CO2data$Year[subscripts],
                         pos = 2, cex = 0.5, col = 'gray'))
    pCO2.capita 

    ##  version ggplot2
    gCO2.capita <- gCO2.capita + geom_text(aes(label = Year),
                    colour = 'gray',
                    size = 2.5,
                    hjust = 0, vjust = 0)

    Nombres de países: etiquetas de posicionamiento

    La solución común para vincular cada curva con el valor del grupo es agregar una leyenda. Sin embargo, una leyenda puede ser confusa con demasiados elementos. Además, el lector debe llevar a cabo una tarea compleja: elegir la línea, memorizar su color, buscarla en la leyenda y leer el nombre del país.

    Un mejor enfoque es etiquetar cada l?nea usando texto cercano con la misma codificación de color. Un método adecuado es colocar las etiquetas evitando la superposición entre etiquetas y líneas. El paquete “directlabels” incluye un amplio repertorio de métodos de posicionamiento para hacer frente a la superposición. La función principal, “direct.label”, es capaz de determinar un método adecuado para cada gráfico, aunque el usuario puede elegir un método diferente de la colección o incluso definir un método personalizado. Para el “objeto pCO2.capita”, los mejores resultados se obtienen con “extreme.grid”.

    library(directlabels)
    ## Warning: package 'directlabels' was built under R version 3.6.3
    ##  version lattice
    direct.label(pCO2.capita,
          method = 'extreme.grid') 

    ##  version ggplot2
    direct.label(gCO2.capita, method = 'extreme.grid') 

    Gráficos interactivos: animación

    Esta sección describe como mostrar los datos a través de animaciones con funcionalidades interactivas con una solución que se asemeja al producto de diagrama de movimiento publicado por Gapminder.

    Gapminder es una fundación independiente con sede en Estocolmo, Suecia. Su misión es “desacreditar mitos devastadores sobre el mundo ofreciendo acceso gratuito a una visión del mundo basada en hechos”. Proporcionan herramientas gratuitas en línea, datos y videos “para comprender mejor el mundo cambiante”. El desarrollo inicial de Gapminder fue el software Trendalyzer, utilizado por Hans Rosling en varias secuencias de su documental “The Joy of Stats”.

    La técnica de visualización de información utilizada por Trendalyzer es un gráfico interactivo de burbujas. De manera predeterminada, muestra cinco variables: dos variables numéricas en los ejes vertical y horizontal, tamaño y color de la burbuja, y una variable de tiempo que puede manipularse con un control deslizante. El software utiliza técnicas de cepillado y enlace para mostrar el valor numérico de un país resaltado.

    Imitaremos la solución Trendalyzer / Motion Chart con el paquete “plotly”, utilizando burbujas viajeras de diferentes colores y con un radio proporcional a los valores de la variable “CO2.PPP”. Anteriormente deberías ver el video magistral “200 Countries, 200 Years, 4 Minutes”.

    El paquete “plotly” ya se ha utilizado anteriormente para crear un gráfico interactivo que representa el tiempo en el eje x. En esta sección, este paquete produce una animación que canaliza el resultado de las funciones “plot_ly” y “add_markers” a través de la función “animation_slider”.

    Las variables “CO2.capita” y “GNI.capita” se representan en el eje x, y en el eje y, respectivamente.

    Las variables “CO2.capita” y “GNI.capita” se representan en el eje x, y en el eje y, respectivamente.

    p <- plot_ly(CO2data,
          x = ~CO2.capita,
          y = ~GNI.capita,
          sizes = c(10, 100),
          marker = list(opacity = 0.7,
                sizemode = 'diameter')) 

    “CO2.PPP” se codifica con los tamaños de los círculos, mientras que “Country.Name” se representa con colores y con etiquetas.

    p <- add_markers(p,
          size = ~CO2.PPP,
          color = ~Country.Name,
          text = ~Country.Name, hoverinfo = "text",
          ids = ~Country.Name,
          frame = ~Year,
          showlegend = FALSE) 

    Finalmente, la animación se crea con “animation_opts”, para personalizar la estructura y tiempos de transición, y con “animation_slider” para definir el control deslizante.

    p <- animation_opts(p,
             frame = 1000,
             transition = 800,
             redraw = FALSE)
    
    p <- animation_slider(p,
               currentvalue = list(prefix = "Year "))
    ## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
    ## Returning the palette you asked for with that many colors
    
    ## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
    ## Returning the palette you asked for with that many colors
    widgetframe::frameWidget(p)

    Agradecimientos

    Este trabajo ha podido ser realizado gracias al material compartido por el profesor Xavier Barber de la Universidad Miguel Hernández de Elche (España) y el informe previo realizado por Oscar Perpiñán Lamigueiro, publicado en R-Bloggers el 2 de marzo de 2020. Puedes consultar su trabajo aquí?.