2 R packages

As I said before, R package is a very convenient 5 way to share code with others and, more importantly, myself. I create a template, minir, with the intention to minimize (no function, no data), but it still becomes a bit overwhelm for beginners. Anyhow, I will explain how to use it in great detail, especially why I choose one way instead of the other.

2.1 basics

  • dependency
    • All needed packages should be listed in Imports (we don’t use Depends).
    • Remotes only specifies where to find a package listed in Imports, if not from CRAN.
  • testthat
    • If you want to run arbitrary code, add testthat::expect_true(T) to prevent it from being skipped.
    • testthat::test_dir() dictate at least one test-*.R though devtools::test() only requires tests/testthat/.
    • use tolower(Sys.getenv('CI')) == 'true') to selectively run some tests on Travis.
  • vignette
    • You should show things happened after your package is installed, which means library(pkg) and system.file(package = 'pkg'). Counter-intuitive as it seems when you are developing a package, it’s the most natural way for the user.
  • documentation
    • Build panel => More => Configure Build Tools... => Configure... => check Build & Reload 6.
    • use foobar() rather than pkg::foobar() in @examples, since there is an unspoken rule convention to assume library(pkg) and it makes testing examples very convenient (More => Load All).
    • @export means everywhere, @noRd means nowhere, @keywords internal means ?foobar but not listed in index.
  • pkgdown
    • edit pkgdown/_pkgdown.yml 7
    • build into pkgdown/output since pkgdown refuses to overwrite non-empty directory (pkgdown/)
    • pkgdown/extra.css and pkgdown/extra.js allows minor customisation
  • data
    • devtools::ues_data() store each object with its name in separate .rds file under data/.
    • devtools::ues_data(internal=TURE) store all the internal data in a single file, R/sysdata.rda.
    • refer to rGEO.data for how I cache data
  • others
    • zzz.R suggest by R packages, aaa.R not specail meaning (not need to be first file), just contrast zzz.R.
    • I use both RStudio or VSCode, with a few convenient tasks for the latter.
    • .NotYetUsed(), .Deprecated(), .Defunct(), .Deprecated()

2.2 namespace

Usually, @importFrom should appear at the function using it (multiple times is okay, roxygen2 handles that).

But some (too) common things are put into R/aaa.R:

#' @importFrom magrittr %>%
#' @export
magrittr::`%>%`
  1. Here we import %>%, since devtools::test() won’t help us, even if we use %>% in our function without importing it. Actually, when tests are executed, testthat is loaded, even worse, it exports %>% (see testthat::`%>%` & ?`%>%`).
  2. Exporting %>% makes it easier to write tests & examples. Althougth you can live without it 8, users would be confusing when run the examples, since the basic assumption is that they have library() your package.

Opionally, you can also import other pipes:

#' @importFrom magrittr %T>%
#'
NULL

NULL is necessary, otherwise the command would be ignored.

2.3 Rcpp

  • set up
    1. create a package (copy minir)

    2. usethis::use_rcpp()

    3. create a .cpp file in src/

    4. set up appropriate src/Makevars, like:

      PKG_CXXFLAGS = -std=c++17

      You should make it as portable as possible. For example, CXX = clang++ is a bad idea here, since others may haven’t install it and your code shouldn’t depend on certain complier.

    5. add to zzz.R

      .onUnload <- function (libpath) {
        library.dynam.unload("mypackage", libpath)
      }
  • best practice
    1. use Build or Load All intead of sourcing individual .cpp file. 9
    2. .cpp file in a package shouldn’t contain // [[Rcpp::plugins(cpp17)]], that’s the job of src/Makevars
  • exporting & importing C++ code
    • refer to here
    • If you want to export some header files, put them into inst/include, example
    • In rapidly development, you may add or remove // [[Rcpp::interfaces(r, cpp)]] in a .cpp, then you should use Load All since Build or Document would fail.

2.4 documentation template

#' @title A sentence in one line.
#'
#' @description A paragraph which briefly describe what the function does,
#'   usually supplements the title.
#'
#' @param name class. One sentence or paragraph.
#' @param name1 integer scalar. When the length must be 1, add 'scalar'.
#' @param name2 string. Means _character scalar_, consistent with C++.
#' @param ...  passed onto [foo::bar()].
#'
#' @return list
#'
#'   1. x: string. name.
#'
#'   1. y: numeric. height.
#'
#' @details A (often long) section which goes into details about how the
#'   function works.
#'
#'   Many paragraphs is ok.
#'
#' @seealso [foobar2]
#'
#' @examples
#' foobar(1:5)
#'
#' \donotrun {
#'     foobar("You're a doubi.")
#' }
#'
#' @section Other points: One or many paragraphs.
#'
#' @export
foobar <- function(...) {
}

multiple functions example


  1. Maybe Python is more convenient, anyhow, I learned R first.↩︎

  2. The option is saved in .Rproj.user/***/build_options, *** is a hash string like 6AA761D1 to distinguish users.↩︎

  3. I used to put it inst/, but I realized I have no interest in building site from CRAN source package.↩︎

  4. Again, testthat exports %>%. Even if you use %>% in tests without exporting it, no one warns you.↩︎

  5. Actually, writing a pakcage is the standard solution when you want to use more than one .cpp file.↩︎