Helper functions used throughout
documentation on the functions is interspersed through code comments
set some options
dont show messages when loading libraries
# library = function(...) suppressMessages(base::library(...))
never set strings as factors automatically (google for reason why)
options(stringsAsFactors = FALSE)
show four significant digits tops
tend not to show scientific notation, because we’re just psychologists
make output a bit wider
set a seed to make analyses depending on random number generation reproducible
set.seed(1710) # if you use your significant other's birthday make sure you stay together for the sake of reproducibility
Load packages
generate the site
set options for chunks
my formr utility package to generate e.g. the bibliography
pretty-printed output
tidyverse date times
tidyverse strings
extractor functions for models
grammar of graphics plots
tidyverse: transform data wide to long
tidyverse-style data wrangling. has a lot of naming conflicts, so always load last
some packages may be needed without being loaded
fool_packrat = function() {
# needed to install formr package
library(devtools)
# needed to actually run rmarkdown in RStudio, but for some reason not in its dependencies
library(formatR)
}
Spin R files
R scripts can be documented in markdown using Roxygen comments, as demonstrated here This function turns all R files (that don’t have an Rmd file of the same name and that don’t start with an underscore _) into HTML pages
spin_R_files_to_site_html = function() {
library(knitr)
all_Rs = c(list.files(pattern = "^[^_].+\\.R$"), ".Rprofile")
component_Rmds = list.files(pattern = "^_.+\\.Rmd$")
temporary_Rmds = c()
for (i in seq_along(all_Rs)) {
if(all_Rs[i] == ".Rprofile") {
Rmd_file = ".Rprofile.Rmd"
} else {
Rmd_file = paste0(all_Rs[i], "md")
}
if (!file.exists(Rmd_file)) {
next_document = length(temporary_Rmds) + 1
temporary_Rmds[next_document] = spin(all_Rs[i], knit = FALSE, envir = new.env(), format = "Rmd")
prepended_yaml = paste0(c("---
output:
html_document:
code_folding: 'show'
---
", readLines(temporary_Rmds[next_document])), collapse = "\n")
cat(prepended_yaml, file = temporary_Rmds[next_document])
}
}
components_and_scripts = c(temporary_Rmds, component_Rmds)
for (i in seq_along(components_and_scripts)) {
opts_chunk$set(eval = FALSE, cache = FALSE)
# if we call render_site on the .R file directly it adds a header I don't like
rmarkdown::render_site(components_and_scripts[i], quiet = TRUE)
}
opts_chunk$set(eval = TRUE, cache = TRUE)
unlink(temporary_Rmds)
}
Output options
use pander to pretty-print objects (if possible)
opts_chunk$set(
render = pander_handler
)
don’t split tables, scroll horizontally
panderOptions("table.split.table", Inf)
Knitr components
summarise regression using a “knitr component”
regression_summary = function(model, indent = "##") {
model_name = deparse(substitute(model_name))
old_opt = options('knitr.duplicate.label')$knitr.duplicate.label
options(knitr.duplicate.label = 'allow')
on.exit(options(knitr.duplicate.label = old_opt))
options = list(
fig.path = paste0(knitr::opts_chunk$get("fig.path"), model_name, "_"),
cache.path = paste0(knitr::opts_chunk$get("cache.path"), model_name, "_")
)
formr::asis_knit_child("_regression_summary.Rmd", options = options)
}
LS0tCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiAnc2hvdycKLS0tCgoKIyBIZWxwZXIgZnVuY3Rpb25zIHVzZWQgdGhyb3VnaG91dCB7LnRhYnNldCAudGFic2V0LXN0aWNreX0KZG9jdW1lbnRhdGlvbiBvbiB0aGUgZnVuY3Rpb25zIGlzIGludGVyc3BlcnNlZCB0aHJvdWdoIGNvZGUgY29tbWVudHMKCiMjIHNldCBzb21lIG9wdGlvbnMKZG9udCBzaG93IG1lc3NhZ2VzIHdoZW4gbG9hZGluZyBsaWJyYXJpZXMKCmBgYHtyIH0KIyBsaWJyYXJ5ID0gZnVuY3Rpb24oLi4uKSBzdXBwcmVzc01lc3NhZ2VzKGJhc2U6OmxpYnJhcnkoLi4uKSkKYGBgCgpuZXZlciBzZXQgc3RyaW5ncyBhcyBmYWN0b3JzIGF1dG9tYXRpY2FsbHkgKGdvb2dsZSBmb3IgcmVhc29uIHdoeSkKCmBgYHtyIH0Kb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKc2hvdyBmb3VyIHNpZ25pZmljYW50IGRpZ2l0cyB0b3BzCgpgYGB7ciB9Cm9wdGlvbnMoZGlnaXRzID0gNCkKYGBgCgp0ZW5kIG5vdCB0byBzaG93IHNjaWVudGlmaWMgbm90YXRpb24sIGJlY2F1c2Ugd2UncmUganVzdCBwc3ljaG9sb2dpc3RzCgpgYGB7ciB9Cm9wdGlvbnMoc2NpcGVuID0gNykKYGBgCgptYWtlIG91dHB1dCBhIGJpdCB3aWRlcgoKYGBge3IgfQpvcHRpb25zKHdpZHRoID0gMTEwKQpgYGAKCnNldCBhIHNlZWQgdG8gbWFrZSBhbmFseXNlcyBkZXBlbmRpbmcgb24gcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIHJlcHJvZHVjaWJsZQoKYGBge3IgfQpzZXQuc2VlZCgxNzEwKSAjIGlmIHlvdSB1c2UgeW91ciBzaWduaWZpY2FudCBvdGhlcidzIGJpcnRoZGF5IG1ha2Ugc3VyZSB5b3Ugc3RheSB0b2dldGhlciBmb3IgdGhlIHNha2Ugb2YgcmVwcm9kdWNpYmlsaXR5CmBgYAoKIyMgTG9hZCBwYWNrYWdlcwpnZW5lcmF0ZSB0aGUgc2l0ZQoKYGBge3IgfQpsaWJyYXJ5KHJtYXJrZG93bikKYGBgCgpzZXQgb3B0aW9ucyBmb3IgY2h1bmtzCgpgYGB7ciB9CmxpYnJhcnkoa25pdHIpCmBgYAoKbXkgZm9ybXIgdXRpbGl0eSBwYWNrYWdlIHRvIGdlbmVyYXRlIGUuZy4gdGhlIGJpYmxpb2dyYXBoeQoKYGBge3IgfQpsaWJyYXJ5KGZvcm1yKQpgYGAKCnByZXR0eS1wcmludGVkIG91dHB1dAoKYGBge3IgfQpsaWJyYXJ5KHBhbmRlcikKYGBgCgp0aWR5dmVyc2UgZGF0ZSB0aW1lcwoKYGBge3IgfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKYGBgCgp0aWR5dmVyc2Ugc3RyaW5ncwoKYGBge3IgfQpsaWJyYXJ5KHN0cmluZ3IpCmBgYAoKZXh0cmFjdG9yIGZ1bmN0aW9ucyBmb3IgbW9kZWxzCgpgYGB7ciB9CmxpYnJhcnkoYnJvb20pCmBgYAoKZ3JhbW1hciBvZiBncmFwaGljcyBwbG90cwoKYGBge3IgfQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKdGlkeXZlcnNlOiB0cmFuc2Zvcm0gZGF0YSB3aWRlIHRvIGxvbmcKCmBgYHtyIH0KbGlicmFyeSh0aWR5cikKYGBgCgp0aWR5dmVyc2Utc3R5bGUgZGF0YSB3cmFuZ2xpbmcuIGhhcyBhIGxvdCBvZiBuYW1pbmcgY29uZmxpY3RzLCBzbyBhbHdheXMgbG9hZCBsYXN0CgpgYGB7ciB9CmxpYnJhcnkoZHBseXIpCmBgYAoKc29tZSBwYWNrYWdlcyBtYXkgYmUgbmVlZGVkIHdpdGhvdXQgYmVpbmcgbG9hZGVkCgpgYGB7ciB9CmZvb2xfcGFja3JhdCA9IGZ1bmN0aW9uKCkgewoJIyBuZWVkZWQgdG8gaW5zdGFsbCBmb3JtciBwYWNrYWdlCglsaWJyYXJ5KGRldnRvb2xzKQoJIyBuZWVkZWQgdG8gYWN0dWFsbHkgcnVuIHJtYXJrZG93biBpbiBSU3R1ZGlvLCBidXQgZm9yIHNvbWUgcmVhc29uIG5vdCBpbiBpdHMgZGVwZW5kZW5jaWVzCglsaWJyYXJ5KGZvcm1hdFIpCn0KYGBgCgojIyBTcGluIFIgZmlsZXMKUiBzY3JpcHRzIGNhbiBiZSBkb2N1bWVudGVkIGluIG1hcmtkb3duIHVzaW5nIFJveHlnZW4gY29tbWVudHMsIGFzIGRlbW9uc3RyYXRlZCBoZXJlClRoaXMgZnVuY3Rpb24gdHVybnMgYWxsIFIgZmlsZXMgKHRoYXQgZG9uJ3QgaGF2ZSBhbiBSbWQgZmlsZSBvZiB0aGUgc2FtZSBuYW1lIGFuZCB0aGF0IGRvbid0IHN0YXJ0IHdpdGggYW4gdW5kZXJzY29yZSBfKSBpbnRvIEhUTUwgcGFnZXMKCmBgYHtyIH0Kc3Bpbl9SX2ZpbGVzX3RvX3NpdGVfaHRtbCA9IGZ1bmN0aW9uKCkgewoJbGlicmFyeShrbml0cikKCWFsbF9ScyA9IGMobGlzdC5maWxlcyhwYXR0ZXJuID0gIl5bXl9dLitcXC5SJCIpLCAiLlJwcm9maWxlIikKCWNvbXBvbmVudF9SbWRzID0gbGlzdC5maWxlcyhwYXR0ZXJuID0gIl5fLitcXC5SbWQkIikKCXRlbXBvcmFyeV9SbWRzID0gYygpCglmb3IgKGkgaW4gc2VxX2Fsb25nKGFsbF9ScykpIHsKCQlpZihhbGxfUnNbaV0gPT0gIi5ScHJvZmlsZSIpIHsKCQkJUm1kX2ZpbGUgPSAiLlJwcm9maWxlLlJtZCIKCQl9IGVsc2UgewoJCQlSbWRfZmlsZSA9IHBhc3RlMChhbGxfUnNbaV0sICJtZCIpCgkJfQoJCWlmICghZmlsZS5leGlzdHMoUm1kX2ZpbGUpKSB7CgkJCW5leHRfZG9jdW1lbnQgPSBsZW5ndGgodGVtcG9yYXJ5X1JtZHMpICsgMQoJCQl0ZW1wb3JhcnlfUm1kc1tuZXh0X2RvY3VtZW50XSA9IHNwaW4oYWxsX1JzW2ldLCBrbml0ID0gRkFMU0UsIGVudmlyID0gbmV3LmVudigpLCBmb3JtYXQgPSAiUm1kIikKCQkJcHJlcGVuZGVkX3lhbWwgPSBwYXN0ZTAoYygiLS0tCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiAnc2hvdycKLS0tCgoiLCByZWFkTGluZXModGVtcG9yYXJ5X1JtZHNbbmV4dF9kb2N1bWVudF0pKSwgY29sbGFwc2UgPSAiXG4iKQoJCQljYXQocHJlcGVuZGVkX3lhbWwsIGZpbGUgPSB0ZW1wb3JhcnlfUm1kc1tuZXh0X2RvY3VtZW50XSkKCQl9Cgl9Cgljb21wb25lbnRzX2FuZF9zY3JpcHRzID0gYyh0ZW1wb3JhcnlfUm1kcywgY29tcG9uZW50X1JtZHMpCglmb3IgKGkgaW4gc2VxX2Fsb25nKGNvbXBvbmVudHNfYW5kX3NjcmlwdHMpKSB7CgkJb3B0c19jaHVuayRzZXQoZXZhbCA9IEZBTFNFLCBjYWNoZSA9IEZBTFNFKQoJCSMgaWYgd2UgY2FsbCByZW5kZXJfc2l0ZSBvbiB0aGUgLlIgZmlsZSBkaXJlY3RseSBpdCBhZGRzIGEgaGVhZGVyIEkgZG9uJ3QgbGlrZQoJCXJtYXJrZG93bjo6cmVuZGVyX3NpdGUoY29tcG9uZW50c19hbmRfc2NyaXB0c1tpXSwgcXVpZXQgPSBUUlVFKQoJfQoJb3B0c19jaHVuayRzZXQoZXZhbCA9IFRSVUUsIGNhY2hlID0gVFJVRSkKCXVubGluayh0ZW1wb3JhcnlfUm1kcykKfQpgYGAKCiMjIE91dHB1dCBvcHRpb25zCnVzZSBwYW5kZXIgdG8gcHJldHR5LXByaW50IG9iamVjdHMgKGlmIHBvc3NpYmxlKQoKYGBge3IgfQpvcHRzX2NodW5rJHNldCgKCXJlbmRlciA9IHBhbmRlcl9oYW5kbGVyCgkpCmBgYAoKZG9uJ3Qgc3BsaXQgdGFibGVzLCBzY3JvbGwgaG9yaXpvbnRhbGx5CgpgYGB7ciB9CnBhbmRlck9wdGlvbnMoInRhYmxlLnNwbGl0LnRhYmxlIiwgSW5mKQpgYGAKCiMjIEtuaXRyIGNvbXBvbmVudHMKCnN1bW1hcmlzZSByZWdyZXNzaW9uIHVzaW5nIGEgImtuaXRyIGNvbXBvbmVudCIKCmBgYHtyIH0KcmVncmVzc2lvbl9zdW1tYXJ5ID0gZnVuY3Rpb24obW9kZWwsIGluZGVudCA9ICIjIyIpIHsKCW1vZGVsX25hbWUgPSBkZXBhcnNlKHN1YnN0aXR1dGUobW9kZWxfbmFtZSkpCglvbGRfb3B0ID0gb3B0aW9ucygna25pdHIuZHVwbGljYXRlLmxhYmVsJykka25pdHIuZHVwbGljYXRlLmxhYmVsCglvcHRpb25zKGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICdhbGxvdycpCglvbi5leGl0KG9wdGlvbnMoa25pdHIuZHVwbGljYXRlLmxhYmVsID0gb2xkX29wdCkpCglvcHRpb25zID0gbGlzdCgKCQlmaWcucGF0aCA9IHBhc3RlMChrbml0cjo6b3B0c19jaHVuayRnZXQoImZpZy5wYXRoIiksIG1vZGVsX25hbWUsICJfIiksCgkJY2FjaGUucGF0aCA9IHBhc3RlMChrbml0cjo6b3B0c19jaHVuayRnZXQoImNhY2hlLnBhdGgiKSwgbW9kZWxfbmFtZSwgIl8iKQoJKQoJZm9ybXI6OmFzaXNfa25pdF9jaGlsZCgiX3JlZ3Jlc3Npb25fc3VtbWFyeS5SbWQiLCBvcHRpb25zID0gb3B0aW9ucykKfQpgYGAK