16 Creating R Packages
16.1 Learning Objectives
In this lesson, you will learn:
- The advantages of using R packages for organizing code
- Simple techniques for creating R packages
- Approaches to documenting code in packages
16.2 Why packages?
Most R users are familiar with loading and utilizing packages in their work. And they know how rich CRAN is in providing for many conceivable needs. Most people have never created a package for their own work, and most think the process is too complicated. Really it’s pretty straighforward and super useful in your personal work. Creating packages serves two main use cases:
- Mechanism to redistribute reusable code (even if just for yourself)
- Mechanism to reproducibly document analysis and models and their results
At a minimum, you can easily produce a package for just your own useful code functions, which makes it easy to maintain and use utilities that span your own projects.
The usethis
, devtools
and roxygen2
packages make creating and maintining a package to be a straightforward experience.
16.3 Install and load packages
16.4 Create a basic package
Thanks to the great usethis package, it only takes one function call to create the skeleton of an R package using create_package()
. Which eliminates pretty much all reasons for procrastination. To create a package called
mytools
, all you do is:
✔ Setting active project to '/Users/jones/development/mytools'
✔ Creating 'R/'
✔ Creating 'man/'
✔ Writing 'DESCRIPTION'
✔ Writing 'NAMESPACE'
✔ Writing 'mytools.Rproj'
✔ Adding '.Rproj.user' to '.gitignore'
✔ Adding '^mytools\\.Rproj$', '^\\.Rproj\\.user$' to '.Rbuildignore'
✔ Opening new project 'mytools' in RStudio
This will create a top-level directory structure, including a number of critical files under the standard R package structure. The most important of which is the DESCRIPTION
file, which provides metadata about your package. Edit the DESCRIPTION
file to provide reasonable values for each of the fields,
including your own contact information.
Information about choosing a LICENSE is provided in the Extending R documentation.
The DESCRIPTION file expects the license to be chose from a predefined list, but
you can use it’s various utility methods for setting a specific license file, such
as the Apacxhe 2
license:
✔ Setting License field in DESCRIPTION to 'Apache License (>= 2.0)'
✔ Writing 'LICENSE.md'
✔ Adding '^LICENSE\\.md$' to '.Rbuildignore'
Once your license has been chosen, and you’ve edited your DESCRIPTION file with your contact information, a title, and a description, it will look like this:
Package: mytools
Title: Utility functions created by Matt Jones
Version: 0.1
Authors@R: "Matthew Jones <jones@nceas.ucsb.edu> [aut, cre]"
Description: Package mytools contains a suite of utility functions useful whenever I need stuff to get done.
Depends: R (>= 3.5.0)
License: Apache License (>= 2.0)
LazyData: true
16.5 Add your code
The skeleton package created contains a directory R
which should contain your source files. Add your functions and classes in files to this directory, attempting to choose names that don’t conflict with existing packages. For example, you might add a file environemnt_info.R
that contains a function environment_info()
that you might want to reuse. This one might leave something to be desired…, but you get the point… The
usethis::use_r()
function will help set up you files in the right places. For example, running:
● Modify 'R/environment_info.R'
creates the file R/environment_info.R
, which you can then modify to add the implementation fo the following function:
environment_info <- function(msg) {
print(devtools::session_info())
print(paste("Also print the incoming message: ", msg))
}
If your R code depends on functions from another package, then you must declare so
in the Imports
list in the DESCRIPTION
file for your package. In our example
above, we depend on the devtools
package, and so we need to list it as a dependency.
Once again, usethis
provides a handy helper method:
✔ Adding 'devtools' to Imports field in DESCRIPTION
● Refer to functions with `devtools::fun()`
16.6 Add documentation
You should provide documentation for each of your functions and classes. This is done in the roxygen2
approach of providing embedded comments in the source code files, which are in turn converted into manual pages and other R documentation artifacts. Be sure to define the overall purpose of the function, and each of its parameters.
#' A function to print information about the current environment.
#'
#' This function prints current environment information, and a message.
#' @param msg The message that should be printed
#' @keywords debugging
#' @import devtools
#' @export
#' @examples
#' environment_info("This is an important message from your sponsor.")
environment_info <- function(msg) {
print(devtools::session_info())
print(paste("Also print the incoming message: ", msg))
}
Once your files are documented, you can then process the documentation using the document()
function to generate the appropriate .Rd files that your package needs.
Updating mytools documentation
Updating roxygen version in /Users/jones/development/mytools/DESCRIPTION
Writing NAMESPACE
Loading mytools
Writing NAMESPACE
Writing environment_info.Rd
That’s really it. You now have a package that you can check()
and install()
and release()
. See below for these helper utilities.
16.7 Test your package
You can tests your code using the tetsthat
testing framework. The ussethis::use_testthat()
function will set up your package for testing, and then you can use the use_test()
function
to setup individual test files. For example, if you want to create tests of our
environment_info functions, set it up like this:
✔ Adding 'testthat' to Suggests field in DESCRIPTION
✔ Creating 'tests/testthat/'
✔ Writing 'tests/testthat.R'
✔ Writing 'tests/testthat/test-environment_info.R'
● Modify 'tests/testthat/test-environment_info.R'
You can now add tests to the test-environment_info.R
, and you can run all of the
tests using devtools::test()
. For example, if you add a test to the test-environment_info.R
file:
test_that("A message is present", {
capture.output(result <- environment_info("A unique message"))
expect_match(result, "A unique message")
})
Then you can run the tests to be sure all of your functions are working using devtools::test()
:
Loading mytools
Testing mytools
✔ | OK F W S | Context
✔ | 2 | test-environment_info [0.1 s]
══ Results ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Duration: 0.1 s
OK: 2
Failed: 0
Warnings: 0
Skipped: 0
Yay, all tests passed!
16.8 Checking and installing your package
Now that your package is built, you can check it for consistency and completeness using check()
, and then you can install it locally using install()
, which needs to be run from the parent directory of your module.
Your package is now available for use in your local environment.
16.10 More reading
- Hadley Wickham’s awesome book: R Packages
- Thomas Westlake’s blog Writing an R package from scratch