I want to produce an array of plots using base R, each with their own insert plot based on ggplot, aligned to the right, slightly lower than top right so that I can add some text above. Something like this (I have hand drawn the ggplot inserts using MS Paint).
I think this might be possible using viewports
- similar to this question.
# ggplot code for inserts
library(tidyverse)
g1 <- ggplot(data = mtcars, mapping = aes(x = cyl)) +
geom_density(colour = "red") +
theme_void()
g2 <- ggplot(data = mtcars, mapping = aes(x = disp)) +
geom_density(colour = "red") +
theme_void()
g3 <- ggplot(data = mtcars, mapping = aes(x = hp)) +
geom_density(colour = "red") +
theme_void()
g4 <- ggplot(data = mtcars, mapping = aes(x = drat)) +
geom_density(colour = "red") +
theme_void()
g5 <- ggplot(data = mtcars, mapping = aes(x = wt)) +
geom_density(colour = "red") +
theme_void()
g6 <- ggplot(data = mtcars, mapping = aes(x = qsec)) +
geom_density(colour = "red") +
theme_void()
I have tried playing with viewport functions, however, I am unable to place the insert relative to each sub figure (i think they are all placed based on the overall graphics device)...
library(grid)
par(mfrow = c(2, 3))
vps <- baseViewports()
plot(x = mtcars$mpg, y = mtcars$cyl)
pushViewport(vps$figure)
print(g1, vp = viewport(height = unit(0.2, "npc"), width = unit(0.2, "npc"), x = 1, y = 0.8, just = 1))
plot(x = mtcars$mpg, y = mtcars$disp)
pushViewport(vps$figure)
# upViewport()
# popViewport()
print(g2, vp = viewport(height = unit(0.2, "npc"), width = unit(0.2, "npc"), x = 1, y = 0.8, just = 1))
plot(x = mtcars$mpg, y = mtcars$hp)
print(g3, vp = viewport(height = unit(0.2, "npc"), width = unit(0.2, "npc"), x = 1, y = 0.8, just = 1))
plot(x = mtcars$mpg, y = mtcars$drat)
print(g4, vp = viewport(height = unit(0.2, "npc"), width = unit(0.2, "npc"), x = 1, y = 0.8, just = 1))
plot(x = mtcars$mpg, y = mtcars$wt)
print(g5, vp = viewport(height = unit(0.2, "npc"), width = unit(0.2, "npc"), x = 1, y = 0.8, just = 1))
plot(x = mtcars$mpg, y = mtcars$qsec)
print(g6, vp = viewport(height = unit(0.2, "npc"), width = unit(0.2, "npc"), x = 1, y = 0.8, just = 1))
One solution (that does not involve compromising on the plots you want to make) is to use the layout
argument in viewpoint()
to set up a grid for the sub plots, that overlays the par(mfrow)
/par(mfcol)
grid.
Setting up the viewpoint grid as a multiple of the par(mfrow)
dimensions allows you to nicely place your subplots in the desired grid position. The scale of the viewpoint grid will dictate the size of the subplot - so a bigger grid will lead to smaller subplots.
# base R plots
par(mfrow = c(2, 3))
plot(x = mtcars$mpg, y = mtcars$cyl)
plot(x = mtcars$mpg, y = mtcars$disp)
plot(x = mtcars$mpg, y = mtcars$hp)
plot(x = mtcars$mpg, y = mtcars$drat)
plot(x = mtcars$mpg, y = mtcars$wt)
plot(x = mtcars$mpg, y = mtcars$qsec)
# set up viewpoint grid
library(grid)
pushViewport(viewport(layout=grid.layout(20, 30)))
# add ggplot subplots (code for these objects in question) at `layout.pos.row`, `layout.pos.col`
print(g1, vp = viewport(height = unit(0.2, "npc"), width = unit(0.05, "npc"),
layout.pos.row = 2, layout.pos.col = 9))
print(g2, vp = viewport(height = unit(0.2, "npc"), width = unit(0.05, "npc"),
layout.pos.row = 2, layout.pos.col = 19))
print(g3, vp = viewport(height = unit(0.2, "npc"), width = unit(0.05, "npc"),
layout.pos.row = 2, layout.pos.col = 29))
print(g4, vp = viewport(height = unit(0.2, "npc"), width = unit(0.05, "npc"),
layout.pos.row = 12, layout.pos.col = 9))
print(g5, vp = viewport(height = unit(0.2, "npc"), width = unit(0.05, "npc"),
layout.pos.row = 12, layout.pos.col = 19))
print(g6, vp = viewport(height = unit(0.2, "npc"), width = unit(0.05, "npc"),
layout.pos.row = 12, layout.pos.col = 29))
If you have simple base R plots then another route is to use the ggplotify to convert the base plot to ggplot and then use cowplot or patchwork for the placement. I could not get this working for my desired plots, which uses a more complex set of plotting functions (in base R) than those in the dummy example above.