motivation
- change the default ggplot style into a style close to publication ready
library(tidyverse, quietly=TRUE)
library(grid)
define my_theme
my_theme <- function(){
list(
theme_classic(base_size=10) +
theme(line = element_line(size = 0.25),
axis.ticks.length=unit(-1, "mm"),
axis.text.x = element_text(margin=unit(c(1,1,1,1), "mm")),
axis.text.y = element_text(margin=unit(c(1,1,1,1), "mm")),
legend.position = "none",
strip.background = element_blank()
))
}
prepare data and ggplot
d <- tibble(t=1:100,
y=sin(seq(0, 10*pi, length.out=100)) + rnorm(100, 0, 0.5),
y_sys=sin(seq(0, 10*pi, length.out=100)))
g1 <- ggplot(d) +
geom_point(aes(t, y), pch=1) +
geom_line(aes(t, y_sys), alpha=0.5) +
geom_line(aes(t, y_sys), lty=3)
g1

modify plot using my_theme
g2 <- g1 + my_theme()
g2

compare the two plots
my_subplot <- function(nrow=3, ncol=1,
height=7, width=7,
ggs) {
# height and width are in unit inch
# ggs: ggplot objects wrapped as list
# order of ggs is like matrix(1:length(ggs), nrow, ncol)
# todo: spanning
my_layout <- grid.layout(nrow, ncol,
heights=unit(rep(height, nrow)/nrow,
rep("inch", nrow)),
widths=unit(rep(width, ncol)/ncol,
rep("inch", ncol)))
fg <- frameGrob(my_layout)
n <- length(ggs)
for (i in 1:n) {
my_row <- ifelse(i %% nrow == 0, nrow, i %% nrow)
my_col <- ifelse(i %% nrow == 0, i %/% nrow, (i %/% nrow) + 1)
fg <- placeGrob(fg,
ggplotGrob(ggs[[i]]),
row=my_row, col=my_col)
}
fg
}
c.f. https://rpubs.com/katzkagaya/851632
grid.newpage()
grid.draw(
my_subplot(
2, 1,
3.5, 5,
ggs=list(g1, g2)
)
)

references
- Murrell, P. (2005). R graphics. Chapman and Hall/CRC.
- Murrell, P. (2009). R graphics. Wiley Interdisciplinary Reviews: Computational Statistics, 1(2), 216-220.
- Wickham, H., & Grolemund, G. (2016). R for data science: import, tidy, transform, visualize, and model data. ” O’Reilly Media, Inc.”.
LS0tCnRpdGxlOiAiY2hhbmdlIG9mIGF4aXMgc3R5bGUiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgbW90aXZhdGlvbgotIGNoYW5nZSB0aGUgZGVmYXVsdCBnZ3Bsb3Qgc3R5bGUgaW50byBhIHN0eWxlIGNsb3NlIHRvIHB1YmxpY2F0aW9uIHJlYWR5CgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlLCBxdWlldGx5PVRSVUUpCmxpYnJhcnkoZ3JpZCkKYGBgCgoKIyBkZWZpbmUgbXlfdGhlbWUKYGBge3J9Cm15X3RoZW1lIDwtIGZ1bmN0aW9uKCl7CiAgbGlzdCggIAogICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemU9MTApICsKICAgICAgdGhlbWUobGluZSA9IGVsZW1lbnRfbGluZShzaXplID0gMC4yNSksCiAgICAgICAgICAgIGF4aXMudGlja3MubGVuZ3RoPXVuaXQoLTEsICJtbSIpLAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW49dW5pdChjKDEsMSwxLDEpLCAibW0iKSksCiAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KG1hcmdpbj11bml0KGMoMSwxLDEsMSksICJtbSIpKSwKICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpCiAgICAgICkpCn0KYGBgCgoKIyBwcmVwYXJlIGRhdGEgYW5kIGdncGxvdApgYGB7cn0KZCA8LSB0aWJibGUodD0xOjEwMCwgCiAgICAgICAgICAgIHk9c2luKHNlcSgwLCAxMCpwaSwgbGVuZ3RoLm91dD0xMDApKSArIHJub3JtKDEwMCwgMCwgMC41KSwKICAgICAgICAgICAgeV9zeXM9c2luKHNlcSgwLCAxMCpwaSwgbGVuZ3RoLm91dD0xMDApKSkKCmcxIDwtIGdncGxvdChkKSArIAogIGdlb21fcG9pbnQoYWVzKHQsIHkpLCBwY2g9MSkgKwogIGdlb21fbGluZShhZXModCwgeV9zeXMpLCBhbHBoYT0wLjUpICsKICBnZW9tX2xpbmUoYWVzKHQsIHlfc3lzKSwgbHR5PTMpCgpnMQpgYGAKCiMgbW9kaWZ5IHBsb3QgdXNpbmcgbXlfdGhlbWUKYGBge3J9CmcyIDwtIGcxICsgbXlfdGhlbWUoKQpnMgpgYGAKCiMgY29tcGFyZSB0aGUgdHdvIHBsb3RzCgpgYGB7cn0KbXlfc3VicGxvdCA8LSBmdW5jdGlvbihucm93PTMsIG5jb2w9MSwgCiAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0PTcsIHdpZHRoPTcsCiAgICAgICAgICAgICAgICAgICAgICAgZ2dzKSB7CiAgIyBoZWlnaHQgYW5kIHdpZHRoIGFyZSBpbiB1bml0IGluY2gKICAjIGdnczogZ2dwbG90IG9iamVjdHMgd3JhcHBlZCBhcyBsaXN0CiAgIyBvcmRlciBvZiBnZ3MgaXMgbGlrZSBtYXRyaXgoMTpsZW5ndGgoZ2dzKSwgbnJvdywgbmNvbCkKICAjIHRvZG86IHNwYW5uaW5nCiAgbXlfbGF5b3V0IDwtIGdyaWQubGF5b3V0KG5yb3csIG5jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodHM9dW5pdChyZXAoaGVpZ2h0LCBucm93KS9ucm93LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiaW5jaCIsIG5yb3cpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGhzPXVuaXQocmVwKHdpZHRoLCBuY29sKS9uY29sLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJpbmNoIiwgbmNvbCkpKQogIAogIGZnIDwtIGZyYW1lR3JvYihteV9sYXlvdXQpCiAgbiA8LSBsZW5ndGgoZ2dzKQogIGZvciAoaSBpbiAxOm4pIHsKICAgIG15X3JvdyA8LSBpZmVsc2UoaSAlJSBucm93ID09IDAsIG5yb3csIGkgJSUgbnJvdykKICAgIG15X2NvbCA8LSBpZmVsc2UoaSAlJSBucm93ID09IDAsIGkgJS8lIG5yb3csIChpICUvJSBucm93KSArIDEpCiAgICBmZyA8LSBwbGFjZUdyb2IoZmcsIAogICAgICAgICAgICAgICAgICAgIGdncGxvdEdyb2IoZ2dzW1tpXV0pLAogICAgICAgICAgICAgICAgICAgIHJvdz1teV9yb3csIGNvbD1teV9jb2wpCiAgfQogIGZnCn0KYGBgCmMuZi4gaHR0cHM6Ly9ycHVicy5jb20va2F0emthZ2F5YS84NTE2MzIKCmBgYHtyfQpncmlkLm5ld3BhZ2UoKQpncmlkLmRyYXcoCiAgbXlfc3VicGxvdCgKICAgIDIsIDEsCiAgICAzLjUsIDUsCiAgICBnZ3M9bGlzdChnMSwgZzIpCiAgKQopCmBgYAoKIyByZWZlcmVuY2VzCi0gTXVycmVsbCwgUC4gKDIwMDUpLiBSIGdyYXBoaWNzLiBDaGFwbWFuIGFuZCBIYWxsL0NSQy4KLSBNdXJyZWxsLCBQLiAoMjAwOSkuIFIgZ3JhcGhpY3MuIFdpbGV5IEludGVyZGlzY2lwbGluYXJ5IFJldmlld3M6IENvbXB1dGF0aW9uYWwgU3RhdGlzdGljcywgMSgyKSwgMjE2LTIyMC4KLSBXaWNraGFtLCBILiwgJiBHcm9sZW11bmQsIEcuICgyMDE2KS4gUiBmb3IgZGF0YSBzY2llbmNlOiBpbXBvcnQsIHRpZHksIHRyYW5zZm9ybSwgdmlzdWFsaXplLCBhbmQgbW9kZWwgZGF0YS4gIiBPJ1JlaWxseSBNZWRpYSwgSW5jLiIuCg==