Chapter 6 Packages

6.2 Project exercise

Just to experiment with making an R package, we’ll try to make a small package that implements some of the features of package {dplyr} that we learned in chapter 4. We can call this package {minidplyr}. You can see the version I ended up with.

  1. After having read the following two sections (6.3 and 6.4), create a first function that helps you select variables of a data frame by using a character vector of variable names or an integer vector of variable positions. Which accessor could you use? Document this function and use it. Bonus: can you use base R function subset to use variables names without quoting them?

  2. Check your package with Ctrl/Cmd + Shift + E and fix all problems. At this point, the only problem should be a WARNING that just says to you that you need to fill the DESCRIPTION file with proper information. Do that and run checks again. You could submit this package to CRAN in its present form; congratulations on your new R package!

  3. Learn how to make unit tests in section 6.5.1 and do that for your new function select2. Which silly cases you should test? Here, you can use usethis::use_package("dplyr", type = "Suggests") to add package {dplyr} to the suggested packages (because you will use this package in tests only).

  4. Make a function filter2 that enables to filter rows of a data frame. Add some documentation and tests for this function as well.

  5. Learn about continuous checking of your package in section 6.6.1. Follow the instructions, commit and push your changes. Go check your new badges on GitHub!

  6. Learn how to make a website out of your package in section 6.6.2 and build one for this package (or another of your packages).

  7. Implement more functions if you find this project interesting. For example, make a function mutate2 with the help of base R function transform (or within). Try to make the previous functions more general by taking many arguments at once (in ...). Make sure to keep your existing code as internal functions in order to break your code in manageable parts.

6.3 Quick start

In my first package, I just put some functions I used again and again in my work.

To quickly start your package, just follow these steps:

  1. Create an RStudio project (not a package). Here, I advise you to create a new project on GitHub and then to clone it as an RStudio project. It is a good practice to put all your (public) stuff on GitHub (as we learned in section 2.3).

  2. Run the following lines of R code.

    usethis::use_description()
    usethis::use_namespace()
    dir.create("R")
    usethis::use_package_doc()
    usethis::use_roxygen_md()
  3. Restart RStudio and change the following options.

  4. Then use Ctrl/Cmd + Shift + B to build and reload your package.

  5. Create a simple function and put it in an .R file in the R/ directory. Inside the function, use Code -> Insert Roxygen Skeleton. Build and reload your package and check the documentation of your new function and that you can use it.

6.4 Basic stuff

6.4.1 DESCRIPTION file

See this chapter.

6.4.2 R code

  • Put your R code in the R/ directory. Basically it would be mostly functions. Don’t use random lines of code like in R scripts.

  • Never explicitly load a package with library() or require(). Use usethis::use_package() to add one package to your DESCRIPTION file. Then, refer to some function with <package>::<function>() in your code, or by using the @import <package> or @import <package> <function> roxygen tags.

  • If one R function need another function in another R file, use the @import <basename>.R to make sure it is built and documented before (it is for example useful if you define new generics and methods in different files).

  • If you modify global options() or graphics par(), save the old values and reset when you’re done:

    old <- options(stringsAsFactors = FALSE)
    on.exit(options(old), add = TRUE)

6.4.3 Documentation

Documentation is super useful for other people (including future-you, in 6 months when you won’t remember what you implemented in your package). Make sure to document your code as soon as you write it, otherwise you will never do it.

Forget about the man/ (manual) directory, files in this directory will be automatically generated thanks to the roxygen comments you use on top of your R functions.

Learn more with this chapter. Note that you can now use the Markdown syntax in the documentation. For example, instead of having to use \code{foo}, you can use directly `foo` in the roxygen comments.

To use (and export) functions already implemented in other packages, for example the pipe from package {magrittr}, you can use usethis::use_package("magrittr") and put the following code somewhere in an R file of your package.

#' @importFrom magrittr %>%
#' @export
magrittr::`%>%`

Fun: [How to] Include a dancing banana in your R package documentation.

6.4.4 NAMESPACE file

You can also forget about this for now because it should be automatically generated by {roxygen}.

If you want to understand what’s going on, read this chapter.

6.5 Other stuff

6.5.1 Testing

You are probably already testing your code, you’re only doing it informally. The problem with this approach is that when you come back to this code in 3 months time to add a new feature, you’ve probably forgotten some of the informal tests you ran the first time around. This makes it very easy to break code that used to work. A very good practice is to use unit tests.

To make your first unit tests, use usethis::use_test() while having open the R file you want to test. Then, you can run tests of your package with Ctrl/Cmd + Shift + T.

Learn more with this chapter.

6.5.2 Rcpp

We learned about Rcpp in section 5.5. To use Rcpp code in your package, just use usethis::use_rcpp() and put the 2 roxygen tags e.g. in the file R/<package>-package.R. Then, create .cpp files with RStudio and save them the src/ directory.

Note that the // [[Rcpp::export]] makes the C++ function available to R, it doesn’t export the function as part of your package (though you could access it with <package>:::<rcpp-fun>()). If you want your package to explicitly provides an Rcpp function (as an R function), you also need roxygen comments (beginning with //' instead of #', including //' @export) on top of your Rcpp function.

If you need some C++ code from another package (e.g. from package {RcppArmadillo}), normally you would use

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

In an R package, you don’t need the first line but instead you need to add the package to the LinkingTo field of the DESCRIPTION file (e.g. with usethis::use_package("RcppArmadillo", "LinkingTo")).

6.5.3 Ignore files

There are two types of ignore:

  • Files ignored by Git, specified in the .gitignore file. For example, you don’t want to track changes for some large data files or some binaries often changing. You can ignore files by using usethis::use_git_ignore().

  • Files ignored during the build of your package, specified in the .Rbuildignore file. For example, in my packages, I generally have two directories called tmp-tests and tmp-save where I just put some random code that I used once during development. Checks will tell you if your package contains non-standard files or directories. Moreover, I generally ignore vignettes (we’ll see how to build them as part of a website for your package with {pkgdown}). There is also a {usethis} function for this: usethis::use_build_ignore().

6.5.4 The inst/ directory

When a package is installed, everything in inst/ is copied into the top-level package directory.

For example, you can have:

  • inst/extdata/: additional external data for examples and vignettes. See section 6.5.5 for more detail. You can also use inst/testdata/ for data you need in tests. To access one file in this directory, use system.file(). For example, if you have a file inst/extdata/mydata.csv in your package, an user can find it using system.file("extdata", "mydata.csv", package = "mypackage").

  • inst/include: some C++ code that others can use by LinkingTo your package. For example, when you use #include <Rcpp.h> at the top of your Rcpp code, you’re using the code in there.

  • inst/CITATION: how to cite the package. Learn more there.

6.5.5 External data

Learn more with this book chapter.

6.6 Good practices

6.6.1 Continuous checking

It’s good to check your package regularly and to fix problems as soon as some appear. Learn more about the different checks there.

An easy way to regularly check your package is to use continuous integration services like Travis-CI and AppVeyor. Indeed, each time you push to your (GitHub) repository, checks are run on different Operating Systems. To use these services, run usethis::use_travis() and usethis::use_appveyor(). To get the coverage of your tests, use Codecov by running usethis::use_coverage(). You might want to use this Travis-CI setup.

I would rarely trust a package that doesn’t use these continuous integration services.

Finally, to prevent typos in your package and especially for non-native English speakers, it can be useful to check the spelling in your package. If you think that the word “programmation” exists and that “prefered” has only one ‘r’ at the end (I did!), you should definitely use package {spelling}. Just run spelling::spell_check_setup(); this will check spelling in your package at the end of checks. If it reports words you want to ignore, just put these words in a text file inst/WORDLIST (with one word by line).

6.6.2 Pkgdown

  1. install R package {pkgdown}

  2. run usethis::use_pkgdown()

  3. run pkgdown:::build_site() and push the new files

  4. on GitHub, go to the settings of your repo, and enable GitHub Pages using the master branch /docs/ folder. This will render everything that you have in this folder as a website (after 0-2 minutes).

To get more information and especially to configure the website, see the documentation, as a {pkgdown} website, of course.

For an example, see the website of my package {bigstatsr} and the corresponding YAML file.

If you want to make a personal website, check this quick tutorial.

6.7 Release on CRAN

If you want your package to be on CRAN, read this chapter.