Combine charts in ggiraph



This post explains how to use the ggiraph package to create and link multiple interactive graphs, from very simple example to more complex ones.

For an introduction to ggiraph, check the dedicated post

Interactive section Data to Viz

Libraries


First of all, we need to load a bunch of libraries:

library(ggiraph) # install.packages('ggiraph')
library(ggplot2) # install.packages('ggplot2')
library(dplyr) # install.packages('dplyr')
library(patchwork) # install.packages('patchwork')
library(tidyr) # install.packages('tidyr')
library(sf) # install.packages('sf')
set.seed(123)

Most simple example


The ggiraph package in R is an extension of the ggplot2 package, designed to simplify the process of creating interactive charts.

It offers a set of function to add interactivity such as geom_point_interactive() for an interactive version of geom_point(), geom_sf_interactive() for an interactive version of geom_sf() etc.

In order to map the hover effect between multiple charts, we only have to use the data_id argument. For example, in our case, we specify data_id = car since we want our mapping at the car level, using the mtcars dataset.

Then we put the charts together using the patchwork package

library(ggiraph)
library(ggplot2)
library(dplyr)
library(patchwork)

data(mtcars)
mtcars$car <- rownames(mtcars)

# data_id in the aes mapping
p1 <- ggplot(mtcars, aes(wt, mpg, tooltip = car, data_id = car)) +
  geom_point_interactive(size = 4)

# data_id in the aes mapping
p2 <- ggplot(mtcars, aes(x = reorder(car, mpg), y = mpg, tooltip = car, data_id = car)) +
  geom_col_interactive() +
  coord_flip()

combined_plot <- p1 + p2 + plot_layout(ncol = 2)

interactive_plot <- girafe(ggobj = combined_plot)
htmltools::save_html(interactive_plot, "HtmlWidget/multiple-ggiraph-1.html")

Customize charts as usual


Create dataset

Let’s start by creating a simple dataset for our next charts:

# Read the full world map
world_sf <- read_sf("https://raw.githubusercontent.com/holtzy/R-graph-gallery/master/DATA/world.geojson")
world_sf <- world_sf %>%
  filter(!name %in% c("Antarctica", "Greenland"))

# Create a sample dataset
happiness_data <- data.frame(
  Country = c(
    "France", "Germany", "United Kingdom",
    "Japan", "China", "Vietnam",
    "United States of America", "Canada", "Mexico"
  ),
  Continent = c(
    "Europe", "Europe", "Europe",
    "Asia", "Asia", "Asia",
    "North America", "North America", "North America"
  ),
  Happiness_Score = rnorm(mean = 30, sd = 20, n = 9),
  GDP_per_capita = rnorm(mean = 30, sd = 20, n = 9),
  Social_support = rnorm(mean = 30, sd = 20, n = 9),
  Healthy_life_expectancy = rnorm(mean = 30, sd = 20, n = 9)
)

# Join the happiness data with the full world map
world_sf <- world_sf %>%
  left_join(happiness_data, by = c("name" = "Country"))

Create the charts

Since we only need to specify the column of our dataset in data_id, the rest of our charts is customizable as any other ggplot2 chart!

# Create the first chart (Scatter plot)
p1 <- ggplot(world_sf, aes(
  GDP_per_capita,
  Happiness_Score,
  tooltip = name,
  data_id = name,
  color = name
)) +
  geom_point_interactive(data = filter(world_sf, !is.na(Happiness_Score)), size = 4) +
  theme_minimal() +
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    legend.position = "none"
  )

# Create the second chart (Bar plot)
p2 <- ggplot(world_sf, aes(
  x = reorder(name, Happiness_Score),
  y = Happiness_Score,
  tooltip = name,
  data_id = name,
  fill = name
)) +
  geom_col_interactive(data = filter(world_sf, !is.na(Happiness_Score))) +
  coord_flip() +
  theme_minimal() +
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    legend.position = "none"
  )

# Create the third chart (choropleth)
p3 <- ggplot() +
  geom_sf(data = world_sf, fill = "lightgrey", color = "lightgrey") +
  geom_sf_interactive(
    data = filter(world_sf, !is.na(Happiness_Score)),
    aes(fill = name, tooltip = name, data_id = name)
  ) +
  coord_sf(crs = st_crs(3857)) +
  theme_void() +
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    legend.position = "none"
  )

# Combine the plots
combined_plot <- (p1 + p2) / p3 + plot_layout(heights = c(1, 2))

# Create the interactive plot
interactive_plot <- girafe(ggobj = combined_plot)
interactive_plot <- girafe_options(
  interactive_plot,
  opts_hover(css = "fill:red;stroke:black;")
)

# save as an html widget
htmltools::save_html(interactive_plot, "HtmlWidget/multiple-ggiraph-2.html")

Highlight all the items of a group


Previously, the interactivity and connection between charts were at the “country” level, meaning that when you hovered over a country, the same country was highlighted in other charts.

But what if we want to use the “continent” level instead? All we need to do is change data_id = name (country) to data_id = Continent. Additionally, we adjust the color mapping so that all countries within a given continent share the same color.

Give it a try by hovering over the charts below:

# Create the first chart (Scatter plot)
p1 <- ggplot(world_sf, aes(
  GDP_per_capita,
  Happiness_Score,
  tooltip = name,
  data_id = Continent,
  color = Continent
)) +
  geom_point_interactive(data = filter(world_sf, !is.na(Happiness_Score)), size = 4) +
  theme_minimal() +
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    legend.position = "none"
  )

# Create the second chart (Bar plot)
p2 <- ggplot(world_sf, aes(
  x = reorder(name, Happiness_Score),
  y = Happiness_Score,
  tooltip = name,
  data_id = Continent,
  fill = Continent
)) +
  geom_col_interactive(data = filter(world_sf, !is.na(Happiness_Score))) +
  coord_flip() +
  theme_minimal() +
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    legend.position = "none"
  )

# Create the third chart (choropleth)
p3 <- ggplot() +
  geom_sf(data = world_sf, fill = "lightgrey", color = "lightgrey") +
  geom_sf_interactive(
    data = filter(world_sf, !is.na(Happiness_Score)),
    aes(fill = Continent, tooltip = name, data_id = Continent)
  ) +
  coord_sf(crs = st_crs(3857)) +
  theme_void() +
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    legend.position = "none"
  )

# Combine the plots
combined_plot <- (p1 + p2) / p3 + plot_layout(heights = c(1, 2))

# Create the interactive plot
interactive_plot <- girafe(ggobj = combined_plot)
interactive_plot <- girafe_options(
  interactive_plot,
  opts_hover(css = "fill:red;stroke:black;")
)

# Save the interactive plot
htmltools::save_html(interactive_plot, "HtmlWidget/multiple-ggiraph-3.html")

Customise hover effect with CSS


Although CSS is primarily used for styling web pages, it can also be relevant in customizing visuals in R, especially when dealing with web-based outputs like interactive charts or Shiny applications.

CSS code has the following structure: a property named (such as font-size), a colon (:), and the value of that property (14px in the following example). Each element is then separated by a ;.

Note: If you haven’t already, check the post on how to use CSS in ggiraph.

In this example, we’ll use the girafe_options() function and pass it several arguments: - opts_hover() for passing the CSS when hovering - opts_tooltip() for passing the CSS of the tooltip

tooltip_css <- "
  border-radius: 12px;
  color: #333;
  background-color: white;
  padding: 10px;
  font-size: 14px;
"

hover_css <- "
  filter: brightness(75%);
  transition: all 0.3s ease;
"

# Add interactivity
interactive_plot <- interactive_plot %>%
  girafe_options(
    opts_hover(css = hover_css),
    opts_tooltip(css = tooltip_css)
  )

# Save the interactive plot
htmltools::save_html(interactive_plot, "HtmlWidget/multiple-ggiraph-4.html")

More complex CSS


Since CSS allows a super high level of customization, we can use it to create way more complex and advanced examples!

In the following case:

Tooltip CSS:

Hover CSS:

tooltip_css <- "
  background: linear-gradient(145deg, #f0f0f0, #e6e6e6);
  border: none;
  border-radius: 12px;
  box-shadow: 3px 3px 10px #d1d1d1, -3px -3px 10px #ffffff;
  color: #333;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  font-size: 14px;
  padding: 12px;
  transition: all 0.5s ease-out;
"

hover_css <- "
  filter: brightness(75%);
  cursor: pointer;
  filter: brightness(1.2) drop-shadow(0 0 5px rgba(78, 84, 200, 0.5));
  transition: all 0.5s ease-out;
"

# Add interactivity
interactive_plot <- interactive_plot %>%
  girafe_options(
    opts_hover(css = hover_css),
    opts_tooltip(css = tooltip_css)
  )

# Save the interactive plot
htmltools::save_html(interactive_plot, "HtmlWidget/multiple-ggiraph-5.html")

Hide the groups: more hover effects


One effective method to emphasize the group you’re hovering over is to reduce the transparency of other groups, allowing the user’s focus to naturally gravitate towards the desired elements!

In practice, we utilize the opts_hover_inv() (defines what happen to non-hovered groups) function along with some CSS using the opacity: 0.3; that adjusts the element’s transparency to 30%, making it semi-transparent and less prominent.

tooltip_css <- "
  border-radius: 12px;
  color: #333;
  background-color: white;
  padding: 10px;
  font-size: 14px;
  transition: all 0.5s ease-out;
"

hover_css <- "
  filter: brightness(75%);
  cursor: pointer;
  transition: all 0.5s ease-out;
  filter: brightness(1.15);
"

# Add interactivity
interactive_plot <- interactive_plot %>%
  girafe_options(
    opts_hover(css = hover_css),
    opts_tooltip(css = tooltip_css),
    opts_hover_inv(css = "opacity:0.3; transition: all 0.2s ease-out;")
  )

# Save the interactive plot
htmltools::save_html(interactive_plot, "HtmlWidget/multiple-ggiraph-6.html")

Going further


You might be interested in:

Related chart types


Grouped and Stacked barplot
Treemap
Doughnut
Pie chart
Dendrogram
Circular packing



❤️ 10 best R tricks ❤️

👋 After crafting hundreds of R charts over 12 years, I've distilled my top 10 tips and tricks. Receive them via email! One insight per day for the next 10 days! 🔥