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
First of all, we need to load a bunch of libraries:
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")
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"))
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")
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")
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")
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:
background
: Creates a gradient from light gray to
slightly darker grayborder
: Removes any borderborder-radius
: Rounds the corners of the tooltipbox-shadow
: Adds a subtle 3D effect with light and
shadowcolor
: Sets the text color to dark grayfont-family
: Specifies the font types to usefont-size
: Sets the text sizepadding
: Adds space inside the tooltiptransition
: Enables smooth changes in properties over
0.5 secondsHover CSS:
filter: brightness(75%)
: Makes the element darkerstroke
: Adds a light blue outlinestroke-width
: Sets the thickness of the outlinecursor
: Changes the mouse cursor to a pointing
handfilter
: Increases brightness and adds a soft glow
effecttransition
: Enables smooth changes in properties over
0.5 secondstooltip_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")
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")
You might be interested in:
👋 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! 🔥