2

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).

enter image description here

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))

enter image description here

guyabel
  • 8,014
  • 6
  • 57
  • 86
  • 1
    Perhaps there is a solution, I cannot say with confidence that it is not ... but it can be hard enough to combine two grobs (`ggplot2`) in one graphic, now mixing with base graphics just cannot be any better. Is it really necessary to do the one graphic in base and the other in `ggplot2`? Doing the inset in the same (base or non-base) is likely much less difficult. (Or maybe not, depending on your needs.) – r2evans Apr 18 '19 at 04:39
  • 1
    @r2evans i am using dummy data and plots here - my actual problem involves much more complex plots (the plots in base R can not be done in ggplot) and data. If there is a solution to this problem it will be much easier than hacking at the plot code – guyabel Apr 18 '19 at 04:53
  • 2
    It's very hard for me to believe that there is a plot, that you can't to in both `gpplot`and `base::plot`. Combining both will be a huge pain, laying out base plots is comparatively easy, so I would try to find a solution to plot my `ggplots` in base – Julian_Hn Apr 18 '19 at 06:24
  • 1
    @Julian_Hn Really? Do you know how to do chord diagram plots that are identical to those from the circlize package (https://jokergoo.github.io/circlize_book/book/the-chorddiagram-function.html) in ggplot? – guyabel Apr 18 '19 at 10:49
  • 1
    ``*Really?*`` (your animosity there is not helpful). *In general*, most types of plots can be done in both, but as your response clearly conveys, I'm also confident there are corner-cases that have been intro'd in one and never tried (or just not well-advertised) in the other. Two of us have said that this is very difficult to even think about how to do in R, and while I confess that it may very well be possible, it might be difficult to fine-tune control things. I am *not* saying it's impossible. Just harder than other workarounds. – r2evans Apr 18 '19 at 15:19
  • 1
    One possible workaround, suggested by @Julian_Hn: find a way to do one of your plots in the other format. I searched for `ggplot2 chord` and found references (some from StackOverflow, https://stackoverflow.com/q/14599150/3358272) that reference [`ggbio`](https://bioconductor.org/packages/release/bioc/html/ggbio.html); open the quick-start and jump to page 41. If you can drop the polar aspect of it, perhaps you can use [`ggalluvial`](https://cran.r-project.org/web/packages/ggalluvial/vignettes/ggalluvial.html). I've used neither, sorry. Good luck. – r2evans Apr 18 '19 at 15:20
  • @r2evans Thanks for the suggestions – guyabel Apr 18 '19 at 22:36

1 Answers1

2

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))

enter image description here

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.

guyabel
  • 8,014
  • 6
  • 57
  • 86