I need to overlay a set of subplots over a ggraph. Every plot correspond to an especific node so they must be placed using the coordinates of every node. The following code creates the graph and the set of subplots to overlay.
# Create the graph
library(data.table)
library(igraph)
library(ggraph)
mydata <- data.table(from=c("John", "John", "Jim"), to=c("John", "Jim", "Jack"))
graph <- graph_from_data_frame(mydata)
V(graph)$class <- c("John", "Jim", "Jack")
ggraph(graph, layout = 'linear') +
geom_edge_link() +
geom_node_point() +
geom_node_text(aes(label = class))
# Plots to overlay
John <- ggplot(diamonds, aes(carat)) + geom_histogram(fill = "red") +
ggtitle("John")
Jim <- ggplot(diamonds, aes(depth)) + geom_histogram(fill = "blue") +
ggtitle("Jim")
Jack <- ggplot(diamonds, aes(price)) + geom_histogram(fill = "green") +
ggtitle("Jack")
The following image ilustrates the actual and desired result.
You can use ggraph::create_layout
to extract x and y coordinates of the nodes, and then use purrr::pmap
to apply ggplot2::annotation_custom
to each of your subplots.
library(purrr)
library(ggplot2)
library(igraph)
library(ggraph)
# Your code, mostly unchanged (removed data.table)
mydata <- data.frame(from=c("John", "John", "Jim"), to=c("John", "Jim", "Jack"))
graph <- graph_from_data_frame(mydata)
V(graph)$class <- c("John", "Jim", "Jack")
John <- ggplot(diamonds, aes(carat)) + geom_histogram(fill = "red") +
ggtitle("John")
Jim <- ggplot(diamonds, aes(depth)) + geom_histogram(fill = "blue") +
ggtitle("Jim")
Jack <- ggplot(diamonds, aes(price)) + geom_histogram(fill = "green") +
ggtitle("Jack")
# New code
graph_df <- create_layout(graph, layout = 'linear')
graph_df
is a dataframe with contents:
x y name class ggraph.orig_index circular ggraph.index 1 1 0 John John 1 FALSE 1 2 2 0 Jim Jim 2 FALSE 2 3 3 0 Jack Jack 3 FALSE 3
You can call ggraph(graph_df)
directly; under the hood ggraph(graph)
was doing the same steps.
Now we make a nested list, where the first element is a list of the ggplot objects we want to use for insets (making sure they're in the right order, relative to their order in graph_df
). The second element is a list of the x coordinates, and the third element is a list of the y coordinates. Then we apply a function that constructs grobs to inset, using the x and y coordinates to define a box where it will go in the final plot.
grobs <- pmap(.l = list(plots = list(John, Jim, Jack),
x = as.list(graph_df$x),
y = as.list(graph_df$y)),
.f = function(plots, x, y) {
annotation_custom(grob = ggplotGrob(plots + theme_grey(base_size = 4)),
xmin = x - .25, xmax = x + .25,
ymin = y + .1, ymax = y + .6)
})
Then your code from above just needs to have this object added on and some fiddling with the limits:
ggraph(graph_df) +
geom_edge_link() +
geom_node_point() +
geom_node_text(aes(label = class)) +
expand_limits(x = c(0.5,3.5)) +
coord_equal() +
grobs
Some notes:
purrr
functions.base_size =
argument in the function we created will need to be fiddled with to your liking.