Como siempre, el primer paso es cargar los paquetes que vamos a necesitar.

NOTA: si se utiliza iOS, es necesario tener instalado XQuarz (https://www.xquartz.org).

library(plotly)

Plotly permite construir gráficos de alta calidad, incorporando en ellos la interactividad; dispone de botones para hacer zoom en una parte de la gráfica, para redefinir los ejes X e Y, mostrar la información que contiene cada dato, representar los datos filtrando por uno o varios factores… Además, son gráficos de tipo “responsive”; es decir, que se adaptan a las dimensiones de la ventana en la que aparecen.

Se pueden elaborar gráficos animados de dos formas diferentes:

  1. Directamente con la función plot_ly

  2. Incluyendo un gráfico ya generado con ggplot (que ya hemos aprendido a programar) como argumento de la funión ggplotly, pasando inmediatamente a ser interactivo.

Con plotly, podemos representar casi cualquier gráfico que podamos imaginar, una vez dominamos su lenguaje. Esto lo facilitan sus múltiples opciones. Durante este curso solo alcanzaremos a mostrar una pequeña parte de su potencial.

Un primer ejemplo

Comenzamos programando un gráfico sencillo que muestra un punto que se mueve de manera aleatoria y uniforme dentro del cuadrado de lado unidad.

# Generamos n movimientos aleatorios dentro del cuadrado unidad
n=100
mov.ale=data.frame(x=runif(n),y=runif(n), # Coordenadas con las posiciones del punto
                   paso=1:n)              # Instantes de la simulación
# Definimos la animación con los elementos mínimos
plot_ly(data=mov.ale,x=~x,y=~y,frame=~paso) 

Aunque obtenemos la representación, en la consola aparecen algunos mensajes de error relativos al tipo (type) que pueden tomar los valores "scatter", "bar", "box", etc… Vamos a especificar estos valores y aprovecharemos para personalizar algunas características de su aspecto como el tipo de símbolo utilizado (todos los valores de pch que estudiamos en los gráficos básicos o identificados por su nombre en la función I), tamaño y color, y características del gráfico como el tamaño y la leyenda:

# Generamos n movimientos aleatorios dentro del cuadrado unidad
n=100
mov.ale=data.frame(x=runif(n),y=runif(n),
                   paso=1:n)
# Damos formato a la animación
plot_ly(data=mov.ale,x=~x,y=~y,frame=~paso,
        type="scatter", mode="markers",
        size=5,                 # Tamaño de los puntos
        symbol=I("circle-dot"), # Símbolo 
        color="green4",         # Color
        width=500, height=500,
        showlegend=FALSE) 

Resulta bastante sencillo añadir más puntos, que distinguiremos con diferentes colores:

n=100
np=10
mov.ale=data.frame(x=runif(n*np),y=runif(n*np),
                   f=rep(1:n,rep(np,n)),p=rep(1:np,n))
plot_ly(data=mov.ale,x=~x,y=~y,frame=~f,
        type="scatter", mode="markers",
        split=~p, fill=~p,
        size=5,
        width=500, height=500,
        symbol=I("circle-dot"),
        showlegend=FALSE) 

Histogramas

Como segundo ejemplo, vamos a representar varios histogramas superpuestos, de muestras que provienen de distribuciones normales con distintas variabilidades:

n=200
plot_ly(alpha = 0.6)%>% 
  add_histogram(x=~rnorm(n), histnorm="density", name=1)%>% 
  add_histogram(x=~rnorm(n,sd=1/2), histnorm="density", name=1/2)%>% 
  add_histogram(x=~rnorm(n,sd=2), histnorm="density", name=2)%>% 
  layout(barmode="overlay", 
         title="Muestras con distinta variabilidad",
         xaxis=list(title="x"),
         yaxis=list(title=list(text="densidad",
                         font=list(size=15, family="Courier", color="crimson"))))

Para poder observar cada distribución hemos definido un nivel de transparencia (con alpha). Otra opción es hacer un doble click sobre el histograma que queremos ver u ocultar, en la leyenda por colores situados en la parte superior derecha. Si pasamos el ratón por la gráfica, van apareciendo las coordenadas del punto en el que está situado, coloreadas según la muestra a la que pertenece.

Las opciones de personalización son enormes, pudiendo llegar hasta el más mínimo detalle. A lo largo de los sucesivos ejemplos, vamos a ir introduciendo algunas de las más utilizadas.

Box-plot

Continuando con el ejemplo anterior, otra opción que podría resultarnos de utilidad sería representar el mismo conjunto de datos con diagramas de caja y bigotes en vez de histogramas:

n=200
plot_ly(alpha = 0.6)%>% 
  add_boxplot(x=~rnorm(n), name=1)%>% 
  add_boxplot(x=~rnorm(n,sd=1/2), name=1/2)%>% 
  add_boxplot(x=~rnorm(n,sd=2), name=2)%>% 
  layout(barmode="overlay", 
         title="Muestras con distinta variabilidad",
         xaxis=list(title="x"))

En este tipo de representación, al pasar el ratón por las cajas, obtenemos las medidas descriptivas que las componen: los bigotes llegan hasta los valores mínimo y máximo no extraños, las líneas de las cajas son los cuartiles (con la mediana marcada dentro) y las coordenadas de los valores extraños.

De nuevo, Plotly nos ofrece distintas opciones para darle formato, como podrían ser añadir los datos a los diagramas de caja:

n=200
plot_ly(alpha = 0.6)%>% 
  add_boxplot(x=~rnorm(n), boxpoints="all", 
              marker = list(color = 'rgb(7,40,89)',symbol=".",size=2),
              jitter=0.3, pointpos=0, name=1)%>% 
  add_boxplot(x=~rnorm(n,sd=1/2), boxpoints="all", 
              marker = list(color = 'orange3',symbol=".",size=2),
              jitter=0.3, pointpos=0, name=1/2)%>% 
  add_boxplot(x=~rnorm(n,sd=2),  boxpoints="all", 
              marker = list(color = 'rgb(7,89,9)',symbol=".",size=3),
              jitter=0.3, pointpos=0, name=2)%>% 
  layout(barmode="overlay", 
         title="Muestras con distinta variabilidad",
         xaxis=list(title="x"))

Gráficos de puntos y líneas sencillos

Comenzamos genereando un conjunto de datos con varias variables, con distintas distribuciones

n=100
datos<-data.frame(x=1:n,
                  y1=rnorm(n),
                  y2=runif(n,0,2),
                  y3=sample(5,n,rep=TRUE),
                  y4=rep(-2,n),
                  y5=rbinom(n,5,2/3))

Representamos las secuencias de datos, personalizando el estilo de cada una de ellas:

plot_ly(data=datos, x=~x) %>%
  add_trace(y=~y1, name="Normal", mode='lines') %>%
  add_trace(y=~y2, name="Uniforme", mode='lines+markers') %>%
  add_trace(y=~y5, name="Binomial", mode='markers')

Otra opción es representar alguno de los conjuntos de datos con los que ya hemos trabajado. Por ejemplo:

# cargamos los datos
library(readxl)
#Importemos el conjunto de datos
data <- read_excel("/Users/maribelparraarevalo/Library/Mobile Documents/com~apple~CloudDocs/GraficosR/Datos/RiesgoPobrezaTasa.xls")
#un factor
plot_ly(data,x=~riesgo1,y=~riesgo2,type="scatter",mode = "markers+text",color=~ccaa)

Además de las opciones comunes a todas las animaciones, una cuestión interesante es que podemos incluir o eliminar una serie de datos pulsando en la leyenda sobre ella. En el ejemplo, podemos seleccionar o quitar las comunidades autónomas que se muestran. Al situar el cursor sobre un elemento del gráfico (en este caso sobre un punto), obtenemos la información de ese dato (sus coordenadas).

Podemos incorporar una animación, con el comando frame.

plot_ly(data,x=~riesgo1,y=~riesgo2,type="scatter",color=~ccaa,frame = ~agno)

Funciones

Supongamos ahora que queremos enseñar cómo depende la forma de la distribución normal de su desviación típica. Para ello, vamos a crear una animación con la función de densidad normal para varios valores de la desviación típica por debajo y por encima de la unidad:

# valores de la desviación estándar que se mostrarán en la simulación
sigma=c(1/8,1/4, 1/2, 1, 2, 4, 8)
ns=length(sigma)
f=function(x, mean=0, sd=1)
  1/(sd*sqrt(2*pi))*exp(-((x-mean)/sd)^2/2)
  
# valores en los que evaluaremos la función de densidad
x <- seq(-5,5, length.out = 1000)
# creamos los datos a representar en cada paso de la animación:
# valoramos la función de densidad para  
# los distintos valores de x y cada valor de sigma
aval <- list()
for(step in 1:ns){
  aval[[step]] <-list(visible = FALSE,
                      name = paste0('sigma=', sigma[step]),
                      x=x,
                      y=f(x, sd=sigma[step]))
}
# hacermos visible la densidad estándar que
# se mostrará como punto de partida de la animación
aval[5][[1]]$visible = TRUE
# creamos las curvas (una para cada valor de sigma)
steps <- list()
fig <- plot_ly()
fig <- add_annotations(fig, x=-4, y=1.5, "Distribución normal")
for (i in 1:ns) {
  fig <- add_lines(fig,x=aval[i][[1]]$x,  y=aval[i][[1]]$y, visible = aval[i][[1]]$visible, 
                   name = aval[i][[1]]$name, type = 'scatter', mode = 'lines', hoverinfo = 'name',
                   line=list(color='00CED1'), showlegend = FALSE) 
  
  step <- list(args = list('visible', rep(FALSE, length(aval))),
               method = 'restyle', label=sigma[i])
  step$args[[2]][i] = TRUE  
  steps[[i]] = step 
}  
# añadimos el botón de control
fig <- fig %>%
  layout(sliders = list(list(active = 4,
                             currentvalue = list(prefix = "Desviación estándar: "),
                             pad=sigma,
                             steps = steps)))
# mostramos la figura
fig

Gráficos en 3D

Si solo nos interesa su forma, podemos representarla de una manera bastante sencilla:

x <- seq(-20, 20, length.out = 201)
y <- x
f<- function(x,y) 
{
  r=sqrt(x^2+y^2)
  sin(r)/r
}
z <- outer(x, y, f)
fig <- plot_ly(z=~z)
fig <- fig %>% add_surface()

fig

Y añadirle algunos detalles más:

fig <- plot_ly(z = ~z) %>% add_surface(
  contours = list(
    z = list(
      show=TRUE,
      usecolormap=TRUE,
      highlightcolor="#ff0000",
      project=list(z=TRUE)
    )
  )
)
fig <- fig %>% layout(
  scene = list(
    camera=list(
      eye = list(x=1.87, y=0.88, z=-0.64)
    )
  )
)
fig

Sin embargo, podemos observar que las escalas mostradas en los ejes x e y no son correctas. Para conseguir arreglar este problema, necesitamos complicar un poco el código:

# definimos los ejes
ejex <- list(
  nticks = 4,
  range = c(-20,20)
)

ejey <- list(
  nticks = 4,
  range = c(-20,20)
)

ejez <- list(
  nticks = 4,
  range = c(-1,1)
)

n=101
aux <- seq(-20, 20, length.out = n)

x <- rep(aux,n)
y <- rep(aux,rep(n,n))
f<- function(x,y) 
{
  r=sqrt(x^2+y^2)
  sin(r)/r
}
z <- f(x,y)

fig <- plot_ly(x = ~x, y = ~y, z = ~z, type = 'mesh3d') 
fig <- fig %>% layout(scene = list(xaxis=ejex,yaxis=ejey,zaxis=ejez))

fig

Si situamos el ratón sobre estas figuras podemos girarlas en cualquier dirección para poder observar la superficie con todo lujo de detalles.

Animando gráficos creados con ggplot

Hasta ahora, hemos trabajado con la opción más compleja con la intención de adquirir más soltura con la gramática de gráficos. Sin embargo, como ya adelantamos al principio, también podemos partir de una representación obtenida con ggplot y animarla. Por ejemplo, recordemos el diagrama de dispersión por grupos a partir de datos simulados con el que ya hemos trabajado:

#Indicamos el número de datos que deseamos generar.
n <- 120
#A continuación, vamos a generar distintas variables numéricas (continuas, discretas), así como variables categóricas. 
v1 <- rep(c("A","B","C","D","E","F"),rep(n/6,6))
v2 <- runif(n,0,10)
v3 <- c(rep(0,20),1/4*v2[21:40],2+1/2*v2[41:60]+1,
        v2[61:80]+2,2*v2[81:100]+3,4*v2[101:120]+4)+rnorm(n)
v4 <- v2^3
#Para poder emplear la librería ggplot2 es necesario considerar el conjunto de datos como un data.frame 
data <- data.frame(factor=v1,x=v2,y=v3,z=v4)
library(ggpubr)
ggplot(data)+
  aes(x=x,y=y,colour=factor)+
  geom_point(alpha=0.75)+
  geom_smooth(formula=y~x, method="lm",se=TRUE, level=0.99,)+
  labs(title="Diagrama de dispersión",x="x",y="y",family="Times New Roman", font=2)+
  scale_colour_discrete(name="Modelo",labels=c("A","B","C","D","E","F"))+
  stat_regline_equation(label.x=c(0,0,0,0,0,0),
                        label.y=c(40,36,32,28,24,20))+
  theme_light()

Lo único que tenemos que hacer es almacenarlo en un objeto y aplicar sobre él la función ggplotly:

ggplot(data)+
  aes(x=x,y=y,colour=factor)+
  geom_point(alpha=0.75)+
  geom_smooth(formula=y~x, method="lm",se=TRUE, level=0.99,)+
  labs(title="Diagrama de dispersión", x="x",y="y", family="Times New Roman", font=2)+
  scale_colour_discrete(name="Modelo",labels=c("A","B","C","D","E","F"))+
  stat_regline_equation(label.x=c(1.5,1.5,1.5,1.5,1.5,1.5),
                        label.y=c(40,36,32,28,24,20),
                        output.type="latex")+
  theme_light() -> fig
ggplotly(fig)

Para mejorar la estética del resultado, hemos modificado la posición donde escribir las rectas de regresión muestrales y el tipo de texto.