graph with animation and interactivity

Author

TRA BI

Published

March 1, 2024

abstract
Plotly’s R graphing library makes interactive, publication-quality graphs. Examples of how to make line plots, scatter plots, area charts, bar charts, error bars, box plots, histograms, heatmaps, subplots, multiple-axes, and 3D (WebGL based) charts.
show
1library(dplyr)
library(MASS)
library(gt)
library(plotly)              
2library(gapminder)
1
grammar of data manipulation https://dplyr.tidyverse.org/articles/dplyr.html
2
data package

1 animation

show
country <- c("India", "China", "United States", "Indonesia", "Pakistan", "Nigeria", "Brazil", "Bangladesh", "Russian", "Federation", "Mexico")
countrypops %>%
  filter(country_name %in% country) %>%
  plot_ly(
    x = ~country_name,
    y = ~population,
    frame = ~year,
    type = 'bar',
    mode = 'markers',
    color = ~population,
    showlegend = F
  ) %>%
   animation_button(
    x = 0, xanchor = "right", y = 10^6, yanchor = "bottom"
  ) %>%
  animation_opts(
    transition = 10,
    frame = 20) %>%
  animation_slider(
    currentvalue = list(prefix = "YEAR ", font = list(color="red"))
  ) %>%
  layout(title = TeX("\\text{evolution de la population des 10 pays les plus peuplé du monde de 1960 à 2022}")) %>%
  config(mathjax = "cdn")
show
df <- gapminder 
fig <- df %>%
  plot_ly(
    x = ~gdpPercap, 
    y = ~lifeExp, 
    size = ~pop, 
    color = ~continent, 
    frame = ~year, 
    text = ~country, 
    hoverinfo = "text",
    type = 'scatter',
    mode = 'markers'
  )
fig <- fig %>% layout(
    xaxis = list(
      type = "log"
    )
  )

fig

2 Buttons in R

here are 4 possible methods:

restyle: modify data or data attributes relayout: modify layout attributes update: modify data and layout attributes animate: start or pause an animation (only available offline)

show
x <- seq(-2*pi, 2*pi, length.out = 1000)
df <- data.frame(x, y1 = sin(x))

plot_ly(df, x = ~x) %>%
  add_lines(y = ~y1) %>%
  layout(
    title = "Button Restyle",
    xaxis = list(domain = c(0.1, 1)),
    yaxis = list(title = "y"),
    updatemenus = list(
      list(
        type = "buttons",
        y = 0.8,
        buttons = list(
          
          list(method = "restyle",
               args = list("line.color", "blue"),
               label = "Blue"),
          
          list(method = "restyle",
               args = list("line.color", "red"),
               label = "Red")))
      ))

Update Several Data Attributes This example demostrates how to update several data attributes: colorscale, chart type, and colorscale with the “restyle” method.

show
fig <- plot_ly(z = ~volcano, type = "heatmap", colorscale='Rainbow')

# chart option buttons

chart_types <- list(
  type = "buttons",
  direction = "right",
  xanchor = 'center',
  yanchor = "top",
  pad = list('r'= 0, 't'= 10, 'b' = 10),
  x = 0.5,
  y = 1.27,
  buttons = list(

    list(method = "restyle",
         args = list("type", "heatmap"),
         label = "Heatmap"),

    list(method = "restyle",
         args = list("type", "contour"),
         label = "Contour"),

    list(method = "restyle",
         args = list("type", "surface"),
         label = "Surface")
  ))

# color option buttons  
color_types <- list(
  type = "buttons",
  direction = "right",
  xanchor = 'center',
  yanchor = "top",
  pad = list('r'= 0, 't'= 10, 'b' = 10),
  x = 0.5,
  y = 1.17,
  buttons = list(

    list(method = "restyle",
         args = list("colorscale", "Rainbow"),
         label = "Rainbow"),

    list(method = "restyle",
         args = list("colorscale", "Jet"),
         label = "Jet"),

    list(method = "restyle",
         args = list("colorscale", "Earth"),
         label = "Earth"),

    list(method = "restyle",
         args = list("colorscale", "Electric"),
         label = "Electric")
  ))

annot <- list(
  list(text = "Chart<br>Type", x=0.2, y=1.25, xref='paper', yref='paper', showarrow=FALSE),
  list(text = "Color<br>Type", x=0.2, y=1.15, xref='paper', yref='paper', showarrow=FALSE))

# plot
fig <- fig %>% layout(
  xaxis = list(domain = c(0.1, 1)),
  yaxis = list(title = "y"),
  updatemenus = list(chart_types,color_types),
  annotations = annot)

fig
show
x0 <- rnorm(400, mean=2, sd=0.4)
y0 <- rnorm(400, mean=2, sd=0.4)
x1 <- rnorm(400, mean=3, sd=0.6)
y1 <- rnorm(400, mean=6, sd=0.4)
x2 <- rnorm(400, mean=4, sd=0.2)
y2 <- rnorm(400, mean=4, sd=0.4)

# shapes components
cluster0 = list(
  type = 'circle',
  xref ='x', yref='y',
  x0=min(x0), y0=min(y0),
  x1=max(x0), y1=max(y0),
  opacity=0.25,
  line = list(color="#835AF1"),
  fillcolor="#835AF1")

cluster1 = list(
  type = 'circle',
  xref ='x', yref='y',
  x0=min(x1), y0=min(y1),
  x1=max(x1), y1=max(y1),
  opacity=0.25,
  line = list(color="#7FA6EE"),
  fillcolor="#7FA6EE")

cluster2 = list(
  type = 'circle',
  xref ='x', yref='y',
  x0=min(x2), y0=min(y2),
  x1=max(x2), y1=max(y2),
  opacity=0.25,
  line = list(color="#B8F7D4"),
  fillcolor="#B8F7D4")

# updatemenus component
updatemenus <- list(
  list(
    active = -1,
    type = 'buttons',
    buttons = list(

      list(
        label = "None",
        method = "relayout",
        args = list(list(shapes = c()))),

      list(
        label = "Cluster 0",
        method = "relayout",
        args = list(list(shapes = list(cluster0, c(), c())))),

      list(
        label = "Cluster 1",
        method = "relayout",
        args = list(list(shapes = list(c(), cluster1, c())))),

      list(
        label = "Cluster 2",
        method = "relayout",
        args = list(list(shapes = list(c(), c(), cluster2)))),

      list(
        label = "All",
        method = "relayout",
        args = list(list(shapes = list(cluster0,cluster1,cluster2))))
    )
  )
)

fig <- plot_ly(type = 'scatter', mode='markers') 
fig <- fig %>% add_trace(x=x0, y=y0, mode='markers', marker=list(color='#835AF1')) 
fig <- fig %>% add_trace(x=x1, y=y1, mode='markers', marker=list(color='#7FA6EE')) 
fig <- fig %>% add_trace(x=x2, y=y2, mode='markers', marker=list(color='#B8F7D4')) 
fig <- fig %>% layout(title = "Highlight Clusters", showlegend = FALSE,
         updatemenus = updatemenus)

fig
show
library(quantmod)

d <- quantmod::getSymbols("AAPL")

df <- data.frame(Date=index(AAPL),coredata(AAPL))

high_annotations <- list(
  x=df$Date[df$AAPL.High == max(df$AAPL.High)], 
  y=max(df$AAPL.High),
  xref='x', yref='y',
  text=paste0('High: $',max(df$AAPL.High)),
  ax=0, ay=-40
)

low_annotations <- list(
  x=df$Date[df$AAPL.Low == min(df$AAPL.Low)], 
  y=min(df$AAPL.Low),
  xref='x', yref='y',
  text=paste0('Low: $',min(df$AAPL.Low)),
  ax=0, ay=40
)

# updatemenus component
updatemenus <- list(
  list(
    active = -1,
    type= 'buttons',
    buttons = list(
      list(
        label = "High",
        method = "update",
        args = list(list(visible = c(FALSE, TRUE)),
                    list(title = "Apple High",
                         annotations = list(c(), high_annotations)))),
      list(
        label = "Low",
        method = "update",
        args = list(list(visible = c(TRUE, FALSE)),
                    list(title = "Apple Low",
                         annotations = list(low_annotations, c() )))),
      list(
        label = "Both",
        method = "update",
        args = list(list(visible = c(TRUE, TRUE)),
                    list(title = "Apple",
                         annotations = list(low_annotations, high_annotations)))),
      list(
        label = "Reset",
        method = "update",
        args = list(list(visible = c(TRUE, TRUE)),
                    list(title = "Apple",
                         annotations = list(c(), c())))))
  )
)

fig <- df %>% plot_ly(type = 'scatter', mode = 'lines') 
fig <- fig %>% add_lines(x=~Date, y=~AAPL.High, name="High",
            line=list(color="#33CFA5")) 
fig <- fig %>% add_lines(x=~Date, y=~AAPL.Low, name="Low",
            line=list(color="#F06A6A")) 
fig <- fig %>% layout(title = "Apple", showlegend=FALSE,
         xaxis=list(title="Date"),
         yaxis=list(title="Price ($)"),
         updatemenus=updatemenus)


fig

3 Fonctions

3.1 plot_ly: Initiate a plotly visualization

plot_ly(
  data = data.frame(),
  ..., attributes for a giventype
  type = c("scatter", "bar", "box", ...)
  name,
  color,
  colors = NULL,
  alpha = NULL,
  stroke,
  strokes = NULL,
  alpha_stroke = 1,
  size,
  sizes = c(10, 100),
  span,
  spans = c(1, 20),
  symbol,
  symbols = NULL,
  linetype,
  linetypes = NULL,
  split :   (Discrete) values used to create multiple traces (one trace per value).
  frame :   (Discrete) values used to create animation frames.
  width = NULL,
  height = NULL,
  source = "A"
)

plot_ly() tries to create a sensible plot based on the information you give it. If you don’t provide a trace type, plot_ly() will infer one.

show
plot_ly(economics, x = ~pop)
plot_ly(economics, x = ~date, y = ~pop)

plot_ly() doesn’t require data frame(s), which allows one to take advantage of trace type(s) designed specifically for numeric matrices

show
plot_ly(z = ~volcano)
plot_ly(z = ~volcano, type = "surface")

plotly has a functional interface: every plotly function takes a plotly object as it’s first input argument and returns a modified plotly object

show
plot_ly(economics, x = ~date, y = ~unemploy/pop)
add_lines(plot_ly(economics, x = ~date, y = ~unemploy/pop))

To make code more readable, plotly imports the pipe operator from magrittr

show
economics %>% plot_ly(x = ~date, y = ~unemploy/pop) %>% add_lines()

Attributes defined via plot_ly() set ‘global’ attributes that are carried onto subsequent traces, but those may be over-written

show
plot_ly(economics, x = ~date, color = I("black")) %>%
 add_lines(y = ~uempmed) %>%
 add_lines(y = ~psavert, color = I("red"))

Attributes are documented in the figure reference -> https://plotly.com/r/reference You might notice plot_ly() has named arguments that aren’t in this figure reference. These arguments make it easier to map abstract data values to visual attributes.

show
p <- plot_ly(palmerpenguins::penguins, x = ~bill_length_mm, y = ~body_mass_g)
add_markers(p, color = ~bill_depth_mm, size = ~bill_depth_mm)
add_markers(p, color = ~species)
add_markers(p, color = ~species, colors = "Set1")
add_markers(p, symbol = ~species)

3.2 add_trace: Add trace(s) to a plotly visualization

add_trace add_markers add_text add_paths
add_lines add_segments add_polygons add_sf
add_table add_ribbons add_image add_area
add_pie add_bars add_histogram add_histogram2d
add_heatmap add_contour add_surface add_histogram2dcontour
add_mesh add_scattergeo add_boxplot add_choroplet

scatter trace with mode of text

show
p <- plot_ly(economics, x = ~date, y = ~uempmed)
add_text(p, text = "*")

scatter trace with mode of lines

show
add_paths(p)

like add_paths(), but ensures points are connected according to x

show
add_lines(p)

if you prefer to work with plotly.js more directly, can always use add_trace() and specify the type yourself

show
add_trace(p, type = "scatter", mode = "markers+lines")
plot_ly(economics, x = ~date, y = ~uempmed, color = I("red"), showlegend = FALSE) %>% 
  add_lines() %>%
  add_markers(color = ~pop)

mappings provided to plot_ly() are “global”, but can be overwritten

show
# a number of `add_*()` functions are special cases of the scatter trace
plot_ly(economics, x = ~date) %>% 
  add_ribbons(ymin = ~pce - 1e3, ymax = ~pce + 1e3)

use group_by() (or group2NA()) to apply visual mapping once per group (e.g. one line per group)

show
txhousing %>% 
  group_by(city) %>% 
  plot_ly(x = ~date, y = ~median) %>%
  add_lines(color = I("black"))
show
plot_ly(mtcars, x = ~factor(vs), y = ~mpg) %>% 
  add_boxplot()
plot_ly(mtcars, x = ~factor(vs), y = ~mpg) %>% 
  add_trace(type = "violin")
show
mtcars %>%
  plot_ly(x = ~factor(vs)) %>%
  add_histogram()
mtcars %>%
  dplyr::count(vs) %>%
  plot_ly(x = ~vs, y = ~n) %>%
  add_bars()

add_histogram() does binning for you

but you can ‘pre-compute’ bar heights in R

show
(p <- plot_ly(geyser, x = ~waiting, y = ~duration))
add_histogram2d(p)
add_histogram2dcontour(p)
(a) Plot 1
(b) Plot 2
(c) Plot 1
Figure 1: the 2d analogy of add_histogram() is add_histogram2d()/add_histogram2dcontour() histogram2d

the 2d analogy of add_bars() is add_heatmap()/add_contour()

show
den <- kde2d(geyser$waiting, geyser$duration)
p <- plot_ly(x = den$x, y = den$y, z = den$z)
add_heatmap(p)
add_contour(p)

add_table() makes it easy to map a data frame to the table trace type

show
plot_ly(economics) %>% 
  add_table()

pie charts!

show
ds <- data.frame(labels = c("A", "B", "C"), values = c(10, 40, 60))
plot_ly(ds, labels = ~labels, values = ~values) %>%
  add_pie() %>%
  layout(title = "Basic Pie Chart using Plotly")
show
data(wind)
plot_ly(wind, r = ~r, theta = ~t) %>% 
  add_area(color = ~nms) %>%
  layout(
    polar = list(
      radialaxis = list(ticksuffix = "%"), 
      angularaxis = list(rotation = 90)
    )
  )

for more, see https://plotly.com/r/animating-views.html