10  Interactive Plots

10.1 Introduction

While static figures are required for publication, interactive plots are invaluable for data exploration, presentations, and HTML reports. This chapter shows you how to convert ggplot2 plots to interactive versions with minimal code using plotly.

Learning Objectives

By the end of this chapter, you will:

  • Understand when to use interactive vs. static plots
  • Learn to convert ggplot2 to plotly with ggplotly()
  • Know how to customize tooltips for better hover information
  • Be able to create linked plots with shared highlighting
  • Understand file size considerations for HTML widgets
  • Know how to save interactive plots as standalone HTML files

10.2 The Appeal of Interactivity

Why use interactive plots?

  • ๐Ÿ–ฑ๏ธ Hover to see exact values
  • ๐Ÿ” Zoom and pan
  • ๐Ÿ‘๏ธ Toggle traces on/off
  • ๐Ÿ“Š Great for exploration
  • ๐ŸŽฏ Excellent for presentations

10.3 Basic ggplotly Example

library(ggplot2)
library(plotly)

# Create clean data for tooltips
mtcars_clean <- mtcars
mtcars_clean$Cylinders <- factor(mtcars$cyl)

# Create ggplot
p <- ggplot(mtcars_clean, aes(wt, mpg, color = Cylinders)) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = FALSE) +
  scale_color_brewer(palette = "Set1") +
  theme_minimal() +
  labs(title = "Fuel Efficiency by Weight",
       x = "Weight (1000 lbs)",
       y = "Miles per Gallon",
       color = "Cylinders")

Try hovering over points!

  • See exact values
  • Toggle series on/off
  • Zoom and pan
# Make interactive
ggplotly(p_example)
`geom_smooth()` using formula = 'y ~ x'

10.4 Customizing Tooltips

# Control what appears on hover
mtcars_clean <- mtcars
mtcars_clean$Cylinders <- factor(mtcars$cyl)

p <- ggplot(mtcars_clean, aes(wt, mpg, color = Cylinders,
                        text = paste("Car:", rownames(mtcars),
                                   "<br>HP:", hp))) +
  geom_point(size = 3) +
  theme_minimal()

Now hover shows car name and horsepower!

Use the text aesthetic and tooltip parameter to customize what appears on hover.

ggplotly(p_tooltips, tooltip = c("text", "x", "y"))

10.5 The Saving Problem

Challenge

Interactive plots are HTML widgets, not static images!

Canโ€™t just save as PDF or PNG traditionally.

10.6 Solution: Save as HTML (Keep Interactivity)

library(htmlwidgets)

# Create a plot and make it interactive
p_interactive <- ggplotly(p_save)

# Save as self-contained HTML
saveWidget(p_interactive_save,
           "plots/10_interactive/interactive_plot.html",
           selfcontained = TRUE)

About selfcontained parameter:

  • selfcontained = TRUE: Bundles all JavaScript/CSS into one file
    • Perfect for emailing or sharing
    • No external dependencies needed
    • Larger file size
  • selfcontained = FALSE: Creates separate library files
    • Smaller main HTML file
    • Requires folder structure to be maintained

Uses:

  • Email the HTML file directly
  • Upload to website
  • Opens in any web browser!

10.7 Quarto/Markdown Integration

In Quarto, ggplotly works seamlessly:

```{r}
#| label: quarto-example
#| eval: false

library(plotly)
library(ggplot2)

# Create plot
p <- ggplot(mtcars, aes(wt, mpg)) + geom_point()

# Make it interactive
ggplotly(p)
```

Perfect for modern scientific reports!

10.8 3D Plots

# 3D scatter plot
mtcars_3d <- mtcars
mtcars_3d$Cylinders <- factor(mtcars$cyl)

plot_ly(mtcars_3d,
        x = ~wt, y = ~hp, z = ~mpg,
        color = ~Cylinders,
        type = "scatter3d",
        mode = "markers")

Rotate by clicking and dragging!

Great for exploring multivariate data in 3D space.

10.9 Linked Plots with Crosstalk

# Create shared data
mtcars$car_name <- rownames(mtcars)
shared_data <- SharedData$new(mtcars, ~car_name)

# Create linked plots
p1 <- plot_ly(shared_data, x = ~wt, y = ~mpg, type = 'scatter', mode = 'markers')
p2 <- plot_ly(shared_data, x = ~hp, y = ~mpg, type = 'scatter', mode = 'markers')

# Display with filter on top
bscols(widths = 12, filter_checkbox("cyl", "Cylinders:", shared_data, ~cyl, inline = TRUE))
subplot(p1, p2, nrows = 1, shareY = TRUE)

10.10 Hybrid Approach: Both Versions

# Create output directory if needed
dir.create("plots/10_interactive", recursive = TRUE, showWarnings = FALSE)

# Create base plot
p_static <- ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  theme_classic(base_size = 12)

# Static version for publication
ggsave("plots/10_interactive/figure1.pdf", p_static, width = 7, height = 5)

# Interactive version for supplement/website
p_interactive <- ggplotly(p_static)
saveWidget(p_interactive, "plots/10_interactive/figure1_interactive.html")

# Best of both worlds!

10.11 Sharing Interactive Plots

Options:

  1. Email HTML file (if selfcontained = TRUE)
  2. Upload to web server
  3. GitHub Pages (free hosting)
  4. Shiny app (for more complex interactions)

10.12 Limitations of ggplotly

Not all ggplot2 features convert:

  • Some geoms donโ€™t translate well
  • Complex annotations may be lost
  • Custom themes partially supported
  • Facets work but can be slow

Test your conversion!

10.13 Alternative: ggiraph

library(ggiraph)

# Create plot with interactive elements
mtcars$car_name <- rownames(mtcars)

# Create rich tooltips with HTML formatting
mtcars$tooltip_text <- paste0(
  "<b>", mtcars$car_name, "</b><br>",
  "Weight: ", round(mtcars$wt, 2), " (1000 lbs)<br>",
  "MPG: ", mtcars$mpg, "<br>",
  "HP: ", mtcars$hp, "<br>",
  "Cylinders: ", mtcars$cyl
)

p <- ggplot(mtcars, aes(wt, mpg,
                        tooltip = tooltip_text,
                        data_id = car_name)) +
  geom_point_interactive(aes(color = factor(cyl)), size = 3) +
  theme_minimal()
girafe(ggobj = p_ggiraph)

ggiraph offers better ggplot2 compatibility:

  • Built specifically for ggplot2 (not a conversion layer)
  • Preserves more complex themes and annotations
  • Better control over tooltips and interactions
  • Uses special _interactive geoms

Hover to see tooltips, click to select!

10.14 Key Takeaways

Best Practices & Summary

Core concepts:

  • Interactive plots are HTML widgets, not images
  • Use ggplotly() to convert ggplot2 plots instantly
  • Use saveWidget() with selfcontained = TRUE to save

When to use what:

  • Interactive: exploration, presentations, HTML reports
  • Static: publications, print, PDF reports

Tips:

  1. Create both versions when possible
  2. Sample large datasets to keep HTML file manageable
  3. Test thoroughly - not all ggplot2 features convert
  4. Consider ggiraph if ggplotly doesnโ€™t preserve your styling
  5. Use selfcontained = TRUE for easy sharing

10.15 Summary

Key Takeaways
  1. ggplotly() converts ggplot2 to interactive - easiest path to interactivity

    p <- ggplot(mtcars, aes(mpg, hp)) + geom_point()
    ggplotly(p)
  2. Use cases:

    • โœ… Data exploration - zoom, pan, hover for exact values
    • โœ… HTML presentations - engage your audience
    • โœ… Interactive reports - let readers explore
    • โŒ Publications - journals require static figures
    • โŒ Print - obviously doesnโ€™t work
  3. Customize tooltips:

    aes(text = paste("Name:", name, "\nValue:", value))
    ggplotly(p, tooltip = "text")
  4. Linked plots with crosstalk - highlight in one plot highlights in others

  5. File size management:

    • Large datasets create huge HTML files
    • Sample data or use toWebGL() for performance
  6. Save standalone HTML:

    htmlwidgets::saveWidget(ggplotly(p), "plot.html", selfcontained = TRUE)
  7. Alternatives: ggiraph for better ggplot2 compatibility

10.16 Exercises

Try It Yourself
  1. Convert a ggplot to interactive:

    p <- ggplot(mtcars, aes(mpg, hp, color = factor(cyl))) +
      geom_point(size = 3) +
      theme_bw()
    ggplotly(p)
  2. Add custom tooltips showing car names:

    mtcars_named <- mtcars %>% rownames_to_column("car")
    p <- ggplot(mtcars_named, aes(mpg, hp, text = car)) + geom_point()
    ggplotly(p, tooltip = "text")
  3. Try the zoom and pan tools - explore the data

  4. Save as HTML and share with a colleague

  5. Compare ggplotly() vs. ggiraph::girafe() on the same plot

10.17 Further Reading