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:
Directamente con la función plot_ly
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.
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)
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.
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"))
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)
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
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.
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.