Overview

Packages are the preferred way to bundle up functions, so others can easily use them. You use packages all the time, such as every time you load them with the library() function.

The following quick introduction is based on distilling the most minimal example from the following:

The R code below walks you through creation of an R package called mypackage having the following top level file and folder (*/) structure:

mypackage
├── DESCRIPTION
├── NAMESPACE
├── data/
├── man/
├── R/
├── tests/
└── vignettes/

These files and folders correspond to the following characteristic offerings of an R package:

  • functions in R/ scripts. Functions are written here with input arguments and return objects.
  • documentation in man/. Documentation is automatically generated from the roxygen2 comments in the R scripts. Help is then available from the console via ?add (eg for add() function).
  • data in data/. Data can then be loaded via data(x) (eg for x data).
  • tests in tests/. Tests ensure checking for consistent behavior as your functions grow.
  • vignettes in vignettes/. Vignettes are a long form of documentation that allow you to walk the user through a larger amount of analysis, especially useful for showing relatedness between functions. Since they are rendered from Rmarkdown to HTML, you can show plots, tables and more richly formatted documentation.
  • sharing via install_github(). By pushing your code to Github, you can then share a single line of code using the devtools::install_github() function so others can install and use your function immediately.

Create repo

Start by creating a repository in Github. For this tutorial, I recommend:

  • Owner: (for me: bbest), Repository: mypackage
  • Description: R package to do some basic math
  • YES, Initialize this repository with a README

Now git clone this repository to your machine from within RStudio’s menu File > New Project > Version Control > Git:

Load devtools

The devtools package makes R packages easy. All the functions below are from this library. Thank you @hadley!

# load devtools package
if (!require('devtools')) install.packages('devtools')
library(devtools)

Setup the package

Setup the existing folder to be a package. (Alternatively you can use devtools::create() to create from scratch.)

setup()

Notice how this function creates the DESCRIPTION and NAMESPACE files, and R folder.

Creating package 'mypackage' in '/Users/bbest/github/mypackage'
No DESCRIPTION found. Creating with values:


Package: mypackage
Title: What the Package Does (one line, title case)
Version: 0.0.0.9000
Authors@R: person("First", "Last", email = "first.last@example.com", role = c("aut", "cre"))
Description: What the package does (one paragraph).
Depends: R (>= 3.2.3)
License: What license is it under?
LazyData: true

Notice how if you re-open RStudio to mypackage.Rproj an extra Build tab appears, per the rstudio-IDE-cheatsheet:

See more:

Functions in R/

Now create an R file with a custom add() function, load the package with devtools::load_all() and try out the function.

library(devtools)

# write add function to add.R file in R/ folder
cat("add <- function(x, y){ x+ y }", file='R/add.R')

# load the library from source
load_all()

# try function
add(1,2)

Note the function cat() which “concatenates”, or joins together, R objects and prints. This is a programmer’s way of creating a file with content. Normally you would just create this new file in the RStudio editor.

Document to man/

Add documentation to the custom add() function using roxygen2 style, generate documentation with devtools::document(), reload the package, and try out the help documentation.

# write add function with documentation
cat(
"#' Add together two numbers.
#'
#' @param x A number.
#' @param y A number.
#' @return The sum of \\code{x} and \\code{y}.
#' @examples
#' add(1, 1)
#' @export
add <- function(x, y){ 
  x+ y
}", file='R/add.R')

# update R documentation (*.Rd) in manual folder man/ 
document()

# load the library from source
load_all()

# try function
add(1,2)

# check documentation
?add

Notice how document() creates the man/add.Rd for the add() function (and would other man/*.Rd files for more functions) and updates DESCRIPTION and NAMESPACE files.

Updating roxygen version in  ~/bbest.github.io/mypackage/DESCRIPTION 
Writing NAMESPACE
Writing add.Rd

See more:

Add data to data/

Add custom data with devtools::use_data(), and load the data with base function data().

# create data
x = 1:10
y = 1:100

# store in data/*.rda
use_data(x, y)

# use data in example
load_all()
data(x,y)
add(x,y)

Notice how use_data() creates the data/*.rda files.

Saving x, y as x.rda, y.rda to ~/bbest.github.io/mypackage/data

See more:

Test in tests/

Create test infrastructure with devtools::use_testthat(), create some tests in test_add.R, and run tests with devtools::test().

# setup for tests (one time only)
use_testthat()

# write test for add function
cat(
"context('add')
test_that('add works', {
  expect_equal(add(1,1), 2)
  expect_equal(add(1,2), 3)
  expect_equal(add(1,100), 101)
})
", file = 'tests/testthat/test_add.R')

# run tests
test()

The use_testthat() creates the test/testthat folder and tests/testthat.R main script. Adding tests/testthat/test_add.R adds 3 tests, each of which get executed when running test() which reports success with a single . and finishes with DONE.

Loading mypackage
Loading required package: testthat
Testing mypackage
add : ...

DONE 

See more:

Check with Travis

Once you have tests built, wouldn’t it be nice to automatically run the tests every time a new commit is made to Github? This is called “continuous integration” in developer circles, and the idea is to continuously integrate tests with changes in code to ensure all is happy as you develop. If any of the tests fail, you get emailed and can fix the problem sooner than later. There’s a great free service by Travis CI that can be setup to run any tests on any Github repository at every new commit that gets pushed or merged.

Before you run the next command to add the Travis CI infrastructure, I recommend running a git commit and push.

use_travis()

This creates the special file .travis.yml containing the test instructions.

Adding .travis.yml to mypackage. Next: 
 * Turn on travis for this repo at https://travis-ci.org/profile
 * Add a travis shield to your README.md:
[![Travis-CI Build Status](https://travis-ci.org/bbest/mypackage.svg?branch=master)](https://travis-ci.org/bbest/mypackage)

Per the instructions, visit https://travis-ci.org/profile to create a profile to link to your Github account and turn on Travis with a little slider to your mypackage.

Let’s add the “travis shield” line above to the README.md (replace with your Github <user>, not bbest). Note the append=T argument.

cat('
[![Travis-CI Build Status](https://travis-ci.org/bbest/mypackage.svg?branch=master)](https://travis-ci.org/bbest/mypackage)
', file='README.md', append=T)

Do another git commit and push. Now visit travis-ci.org/USER/mypackage (for me travis-ci.org/bbest/mypackage). I can see that based on the Build History, I get the following “build failing” based on 2 warnings converted to errors:

Checking package
checking DESCRIPTION meta-information ... WARNING
Non-standard license specification:
  What license is it under?
Standardizable: FALSE checking for missing documentation entries ... WARNING
Undocumented code objects:
  ‘x’ ‘y’
Undocumented data sets:
  ‘x’ ‘y’
All user-level objects in a package should have documentation entries.
See chapter ‘Writing R documentation files’ in the ‘Writing R
Extensions’ manual. DONE
Status: 2 WARNINGs 

R CMD check logs
Found warnings, treating as errors (as requested).

You can also see this “build failing” status at github.com/USER/mypackage (for me github.com/bbest/mypackage) which shows the README.md at the bottom with the “travis shield”:

Let’s fix these warnings to get the build to pass:

# fix license
use_mit_license()

# add data documentation
cat("
#' 1 thru 10
#'
#' A simple dataset
'x'


#' 1 thru 100
#'
#' Another simple dataset
#'
'y'
", file = 'R/data.R')
document()

Now git commit and push again, to see if that fixes your build to build passing:

See more:

Teach with vignettes/

Finally, you can teach with vignettes, which are a longer form than just a single function or data help. Better yet, you can write them as an Rmarkdown file so they can render tables, images, etc.

# create a draft vignette
use_vignette('math')

# build vignettes, install locally
tgz = build()
install_local(tgz)

# try vignette
library(mypackage)
vignette('math')

Notice how use_vignette('math') creates vignettes/math.Rmd, an Rmarkdown file which you can freely edit. The default draft with title “Vignette Title” by “Vignette Author” should be customized, but offers some helpful tips on how to craft a vignette. See this example as rendered math.html.

See more:

Share via github_install()

Now that you’ve created a complete R package, it’s time to run a git Commit, and Push to your Github repository. Then you can share a single command for anyone else to install the R package. For my R package, files at github.com/bbest/mypackage, it would be:

devtools::install_github('bbest/mypackage', build_vignettes=T)

Notice how the first argument is of the form <owner>/<repository>.