Una serie temporal es una secuencia de observaciones registradas en instantes de tiempo consecutivos. La visualización de estas series tiene la intención de 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.
Existen varias formas de representar el tiempo en las series temporales.
Podemos realizar gráfico estándar en el que el tiempo está representado en el eje horizontal.
Por otro lado, el tiempo puede concebirse como una variable de agrupamiento o condicionamiento. Esta solución permite que se muestren varias variables junto con un diagrama de dispersión, usando diferentes paneles para subconjuntos de datos (tiempo como variable condicionante) o usando diferentes atributos para grupos de datos (tiempo como agrupación de varias variables).
Finalmente, el tiempo puede usarse como una variable complementaria que agrega información a un gráfico donde se enfrentan varias variables.
Para representar las series temporales vamos a necesitar las siguentes librerias:
Es importante el orden de la instalación de las librerias que sigue:
#install.packages("lattice")
library(lattice)
#install.packages("ggplot2")
library(ggplot2)
# latticeExtra debe ser cargado después de ggplot2
#install.packages("latticeExtra")
library(latticeExtra)
#install.packages("RColorBrewer")
library(RColorBrewer)
# configuración de lattice y latticeExtra
myTheme <- custom.theme.2(
pch=19, cex=0.7, region=rev(brewer.pal(9, 'YlOrRd')),
symbol=brewer.pal(n=8, name="Dark2"))
myTheme$strip.background$col = myTheme$strip.shingle$col =
myTheme$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=myTheme, default.args=modifyList(
lattice.options()$default.args, myArgs))
#install.packages("zoo")
library(zoo)
Vamos a usar los siguientes conjuntos de datos para practicar:
Necesitamos descargarlos previamente pulsando en el nombre y comprobar que están dentro de la carpeta del directorio de R.
Cargamos los datos:
load('aranjuez.RData')
load('navarra.RData')
load('CO2.RData')
Es el método de visualización de las series temporales más usado. Consiste en representar en el eje horizontal el tiempo y en el eje vertical los valores que toma la variable de interés. En esta sección veremos como mostrarlas series temporales multivariantes de dos tipos: para las variables de diferentes escalas y variables de la misma escala.
Estos gráficos consisten en representar las variables a lo largo del tiempo por separado para observar la relación de cada una de ellos con el tiempo. La superposición de variables con características diferentes no es muy útil (a menos que sus valores hayan sido reescalados previamente).
Veamos el primer ejemplo en el que analizaremos los datos diarios en un periodo de ocho años de una estación meteorológica ubicada en Aranjuez (Madrid).
xyplot(aranjuez, layout = c(1, ncol(aranjuez)))
Otra forma, utilizando el paquete zoo:
autoplot(aranjuez) + facet_free()
En este ejemplo utilizaremos mediciones de radiación solar de diferentes estaciones meteorológicas. El objetivo de este gráfico es mostrar el comportamiento de la colección de variables como un todo. Tenemos que fijarnos en la densidad de las líneas porque la transparencia suaviza los problemas de superposición y revela los grupos de densidad porque las regiones con más líneas superpuestas son más oscuras. El siguiente código muestra las variaciones alrededor de la media de tiempo.
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
El siguente gráfico es útil para examinar cómo cambia un gran número de series temporales a lo largo del tiempo, permitiendo analizarsas por separado y entre sí, observando facilmente los comportamientos extraordinarios y los patrones.
Los valores negativos se invierten y se colocan por encima de la línea de referencia. El signo se codifica utilizando diferentes tonos (valores positivos en azul y valores negativos en rojo). Las diferencias en magnitud se muestran como diferencias en la intensidad del color (colores más oscuros para mayores diferencias). Cada panel tiene diferentes escalas y los colores en cada panel representan desviaciones del origen. El siguiente código muestra las variaciones de la radiación solar alrededor del promedio de tiempo con un gráfico utilizando una fila para cada serie temporal.
horizonplot(navarra - avRad,
layout = c(1, ncol(navarra)),
origin = 0, ## Deviations in each panel are calculated
## from this value
colorkey = TRUE,
col.regions = brewer.pal(6, "RdBu"))
El gráfico anteriortambién es útil para revelar las diferencias entre una serie temporal de una variable y otra de referencia. Por ejemplo, podríamos estar interesados en la desviación de la temperatura observada del promedio a largo plazo, o en otras palabras, el cambio de temperatura con el tiempo. Vamos a ilustrar este enfoque con la serie temporal de temperaturas medias diarias medidas en la estación meteorológica 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 permite mejor visualización de las series temporales distinguiendo entre los 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"))
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'))
myTheme <- 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 = myTheme)
Otra forma de organizar los datos agrupando por mas:
df <- data.frame(Vals = diffTa,
Day = day(timeIndex),
Year = year(timeIndex),
Month = month(timeIndex))
#install.packages("scales")
library(scales)
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())
En este apartado veremos los gráficos interactivos donde los valores se muestran de acuerdo con la posición del ratón sobre la serie temporal. También se pueden seleccionar las regiones para ampliar el período de tiempo.
#install.packages("dygraphs")
#install.packages("htmlwidgets")
#install.packages("widgetframe")
library(dygraphs)
library(widgetframe)
dyTemp <- dygraph(aranjuez[, c("TempMin", "TempAvg", "TempMax")],
main = "Temperature in Aranjuez",
ylab = "ºC")
widgetframe::frameWidget(dyTemp)
Con el siguiente código se reduce el valor de semitransparencia de las líneas no seleccionadas y se aumenta el ancho de la línea seleccionada.
dyTemp %>%
dyHighlight(highlightSeriesBackgroundAlpha = 0.2,
highlightSeriesOpts = list(strokeWidth = 2)) %>%
widgetframe::frameWidget()
Un enfoque alternativo para representar las variaciones de una serie de tiempo es con una región sombreada.
dygraph(aranjuez[, c("TempMin", "TempAvg", "TempMax")],
main = "Temperature in Aranjuez",
ylab = "ºC") %>%
dySeries(c("TempMin", "TempAvg", "TempMax"),
label = "Temperature") %>%
widgetframe::frameWidget()
Otra forma de obtener un gráfico interactivo con capacidades de selección y zoom:
#install.packages("highcharter")
#install.packages("xts")
library(highcharter)
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()
Para realizar el siguiente gráfico necesitamos transformar la serie temporal en una columna (data.frame) que incluya el índice de tiempo.
#install.packages("plotly")
library(plotly)
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
plot_ly(aranjuezDF) %>%
add_lines(x = ~ Index,
y = ~ Value,
color = ~ Series) %>%
widgetframe::frameWidget()
En esta sección vamos a representar la relación entre las variables a través de las matrices de diagramas de dispersión. 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 que nos permitirá comparar de forma inmediata y paralela las diferencias entre diagramas. Vamos a usar el tiempo como una variable de agrupación. 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.
El siguiente código muestra la relación entre el conjunto de variables meteorológicas con diferentes colores de cada 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')
## Red-Blue palette with black added (12 colors)
colors <- c(brewer.pal(n = 11, 'RdBu'), '#000000')
## Rearrange according to months (darkest for summer)
colors <- colors[c(6:1, 12:7)]
splom(~ aranjuezDF[1:10], ## Do not include "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)
Otra versión de este gráfico:
#install.packages("GGally")
library(GGally)
ggpairs(aranjuezDF,
columns = 1:10, ## Do not include "Month"
upper = list(continuous = "points"),
mapping = aes(colour = Month, alpha = 0.1))
Los gráficos anteriores usan colores para codificar meses. En cambio, ahora mostraremos diagramas de dispersión separados con un panel para cada mes.
El conjunto de datos debe transformarse 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).
# install.packages("reshape2")
library(reshape2)
aranjuezRshp <- melt(aranjuezDF,
measure.vars = c('TempMax',
'TempAvg',
'TempMin'),
variable.name = 'Statistic',
value.name = 'Temperature')
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()
La siguiente versión permite obtener los nombres de las variables en el lado 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)
})
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 (INB) 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 INB a lo largo de los años utilizando el tiempo como una variable complementaria.
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 polilíneas 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:
Codificamos los valores de las variables categóricas con diferentes colores (tenemos que repetir algunos colores para completar el número de niveles de la variable Country.Name. lo que 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 que se representará a lo largo del eje x ( CO2.capita) y extraer colores de la paleta de acuerdo con el orden de este valor.
## Rank of average values of CO2 per capita
CO2mean <- aggregate(CO2.capita ~ Country.Name,
data = CO2data, FUN = mean)
palOrdered <- pal[rank(CO2mean$CO2.capita)]
## simpleTheme encapsulates the palette in a new theme for xyplot
myTheme <- simpleTheme(pch = 19, cex = 0.6, col = palOrdered)
## lattice version
pCO2.capita <- 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,
par.settings = myTheme,
type = 'b')
pCO2.capita
## ggplot2 version
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('CO2 emissions (metric tons per capita)') +
ylab('GNI per capita, PPP (current international $)') +
theme_bw()
Este resultado puede mejorarse con etiquetas que muestren los años para mostrar la evolución del tiempo.
pCO2.capita <- pCO2.capita +
glayer_(panel.text(...,
labels = CO2data$Year[subscripts],
pos = 2, cex = 0.5, col = 'gray'))
pCO2.capita
Otra versión:
gCO2.capita <- gCO2.capita + geom_text(aes(label = Year),
colour = 'gray',
size = 2.5,
hjust = 0, vjust = 0)
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. En adición, el lector debe llevar a cabo una tarea compleja: Elija la línea, memorizar su color, búsquelo en la leyenda, y lea el país nombre.
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 directlabelsincluye 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 pCO2.capitaobjeto, se obtienen los mejores resultados con extreme.grid.
# install.packages("directlabels")
library(directlabels)
direct.label(pCO2.capita,
method = 'extreme.grid')
Oforma:
direct.label(gCO2.capita, method = 'extreme.grid')
Esta sección describe cómo 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 “La alegría de las estadísticas”.
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 de viaje de diferentes colores y con un radio proporcional a los valores de la variable CO2.PPP. Anteriormente, debería ver el video magistral " 200 países, 200 años, 4 minutos ".
El plotlypaquete 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 a add_markerstravés de la animation_sliderfunción.
Las variables CO2.capitay GNI.capitase representan en el eje x y 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.PPPse codifica con los tamaños de los círculos, mientras que Country.Namese 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 el marco y los tiempos de transición, y con animation_sliderpara definir el control deslizante.
p <- animation_opts(p,
frame = 1000,
transition = 800,
redraw = FALSE)
p <- animation_slider(p,
currentvalue = list(prefix = "Year "))
widgetframe::frameWidget(p)
Perpiñán Lamigueiro, Oscar (02/03/2020). Displaying time series with R. R-bloggers. Recuperado de https://www.r-bloggers.com/displaying-time-series-with-r/