Introduction

Recently, several R packages have made creating new R packages — with all the required architecture and implementing best practices — much easier. They include:

Nevertheless, there is quite a lot to learn to make use of them. There are a number of excellent guides, and one is by Jim Hester, which is a video presentation recorded at the RStudio 2018 conference. I like the video format and the title is enticing: “Make an R package in 20 minutes.” In fact, the build is closer to 12 minutes, as Jim takes some time to motivate the talk. In the end the pace is really pretty fast. This “companion guide” collects the steps for reference.

In truth, creating your first R package will probably take more than twenty minutes. You might need to pause and rewind the video several times. You’ll probably stop and mull over what has been created for you when using the various helpful functions. You might even scrap a first attempt and begin again from scratch. But the ability to reuse your work and share your work in an accessible format is so exciting, isn’t it? Let’s begin!

Step 00: Press play on the video guide

Your video guide is here; the package build starts at about minute 8.

Step 0: Have some functions that you’d like to package up like the below from Hester’s ‘note’ example

If you are interested in creating a package, you have probably already written some functions. Maybe you’d like to make them available and want to make them available to the world. So let’s call this Step 0.

For this tutorial you can use Jim Hester’s code reproduced here:

rate <- 44100
multiplier <- 2 * pi / rate
bpm <- 80
default_volume <- 5

notes <- c("A" = 0, "A#" = 1, "Bb" = 1, "B" = 2, "Cb" = 2, "B#" = 3, "C" = 3,
  "C#" = 4, "Db" = 4, "D" = 5, "D#" = 6, "Eb" = 6, "E" = 7, "Fb" = 7, "E#" = 8,
  "F" = 8, "F#" = 9, "Gb" = 9, "G" = 10, "G#" = 11, "Ab" = 11)

calc_frequency <- function(note, octave) {
  # 440hz is A above middle C
  440 * 2^((unname(notes[note]) + (octave * 12)) / 12)
}

calc_volume <- function(x) {
  # x should be between 1 and 10
  stopifnot(x >= 1, x <= 10)
  
  x / 10
}

calc_length <- function(rate, length, bpm) {
  seq(1, as.integer(rate * length * 60 / bpm))
}

calc_multiplier <- function(rate) {
  2 * pi / rate
}

note <- function(note, length = 1, octave = 0, volume = default_volume) {
  frequency <- calc_frequency(note, octave)
  volume <- calc_volume(volume)
  length <- calc_length(rate, length, bpm)
  multiplier <- calc_multiplier(rate)
  res <- sin(frequency * multiplier * length) * volume
  structure(res, class = "note")
}

# install.packages(audio)
library(audio)

play <- audio::play

print.note <- function(x, ...) {
  audio::play(x, ...)
}

Step 1: Create package architecture using usethis::create_package()

We create the new directory for the new “note package” with create_package.

usethis::create_package("~/Google Drive/note")

A new RStudio session should open, with the working directory of “note”, and the following file architecture for the package.

.
├── DESCRIPTION
├── NAMESPACE
├── R
└── note.Rproj

Step 2: Describe what the package does in the DESCRIPTION file

Then you might want to check out the contents of the DESCRIPTION file.

The DESCRIPTION looks like this…

Package: note
Title: What the Package Does (One Line, Title Case)      <- Hey reader!  Change this part!
Version: 0.0.0.9000
Authors@R: 
    person(given = "First",
           family = "Last",
           role = c("aut", "cre"),
           email = "first.last@example.com",
           comment = c(ORCID = "YOUR-ORCID-ID"))
Description: What the package does (one paragraph).
License: What license it uses
Encoding: UTF-8
LazyData: true

And you should probably update the title – ie replace “What the Package Does (One Line, Title Case)”. Of course you should do this for the “Description” too, especially if you package is intended for an audience of more than one.

Step 3: Add your functions to an R Script saved in the R folder

So now you have an R script in your package directory:

.
├── DESCRIPTION
├── NAMESPACE
├── R
│   └── note.R           <- what you added
└── note.Rproj

Protip: You can have multiple .R scripts in your R folder that contain package functions. The files in R/ directory are built in alphabetic order by default. But if you have dependencies in your R scripts you may need to change this order, and for that you can use the @include roxygen2 tag.

Something like this at the top of the script that requires functions from another .R file.

#' @include class-a.R
setClass("B", contains = "A")

See http://r-pkgs.had.co.nz/man.html for more detail!

Step 4: Make the package “active” and test your functions interactively with devtools::load_all()

Then make the package active using devtools::load_all() in

devtools::load_all()

(You can also click “build” tab in rstudio, and then “load all”)

Then you can try out a function from your package.

Step 5: Run a check on the new package using devtools::check()

Let’s see if there are any problems in this package.

devtools::check()

Uh-oh! You will see warnings! They relate specifying dependencies which we’ll address below.

Step 6: Add needed dependencies

Step 6a. Declare dependencies using usethis::use_package(“package_name”)

It is not good practice to use library() or require() to use functions from other package. Instead we first declare dependencies. Using the function usethis::use_package will add dependencies to your DESCRIPTION file.

usethis::use_package("audio")

Automatically, you should see that the end of the contents of the DESCRIPTION file has added “audio”:

Package: note
Title: Play Music in R
Version: 0.0.0.9000
Authors@R: 
    person(given = "First",
           family = "Last",
           role = c("aut", "cre"),
           email = "first.last@example.com",
           comment = c(ORCID = "YOUR-ORCID-ID"))
Description: What the package does (one paragraph).
License: What license it uses
Encoding: UTF-8
LazyData: true
Imports: 
    audio

Step 6b: Add dependencies and export functions in R script using “#’ @importFrom and #’ @export

Now, in the R script, we indicate import from other packages — and what functions may be exported.

Exported functions are the functions that will be available to the users once the package is loaded.

#' @importFrom audio play play.default
#' @export
audio::play

#' @export
print.note <- function(x, ...) {
  audio::play(x, ...)
}

Step 7: Document these additions using a ‘Roxygen’ skeleton

Highlight the name of the function (‘note’) you want to document.

Then, in RStudio the menu go to:

code -> Insert Roxygen skeleton

This will give you the skeleton:

#' Title
#'
#' @param note 
#' @param length 
#' @param octave 
#' @param volume 
#'
#' @return
#' @export
#'
#' @examples
note <- function(note, length = 1, octave = 0, volume = default_volume) {
  frequency <- calc_frequency(note, octave)
  volume <- calc_volume(volume)
  length <- calc_length(rate, length, bpm)
  multiplier <- calc_multiplier(rate)
  res <- sin(frequency * multiplier * length) * volume
  structure(res, class = "note")
}

Filling in the skeleton:

#' Create music
#'
#' @param note name
#' @param length in beats
#' @param octave from middle C
#' @param volume from 1 to 10
#'
#' @return a note object
#' @export
#'
#' @examples
#' note("A")
note <- function(note, length = 1, octave = 0, volume = default_volume) {
  frequency <- calc_frequency(note, octave)
  volume <- calc_volume(volume)
  length <- calc_length(rate, length, bpm)
  multiplier <- calc_multiplier(rate)
  res <- sin(frequency * multiplier * length) * volume
  structure(res, class = "note")
}

Step 8: Incorporate documentation additions into package using devtools::document()

devtools::document()  

or build -> document to make Roxygen comments part of your package.

You’ll notice the this creates a new subdirectory (man) and documents related to the exported functions.

.
├── DESCRIPTION
├── NAMESPACE
├── R
│   └── note.R
├── man
│   ├── note.Rd
│   └── reexports.Rd
└── note.Rproj

Repeat Step 5 devtools::check()

Again check you package. You might do this often when you are creating your own package.

devtools::check() 

Step 9: Create a readme file with usethis::use_readme_md()

Help your users understand the package with a readme.

usethis::use_readme_md()

You’ll now see README.md among your files, which you can open and edit.

.
├── DESCRIPTION
├── NAMESPACE
├── R
│   └── note.R
├── README.md
├── man
│   ├── note.Rd
│   └── reexports.Rd
└── note.Rproj

Step 10: Create some relevent tests with usethis::use_test()

Creates testing infrastructure. Think of some things that should be true with given inputs in a function. Use the testthat::test_that() f

usethis::use_test()

You’ll see a new folder dedicated to testing the package functions.

.
├── DESCRIPTION
├── NAMESPACE
├── R
│   └── note.R
├── README.md
├── man
│   ├── note.Rd
│   └── reexports.Rd
├── note.Rproj
└── tests
    ├── testthat
    └── testthat.R

Open testthat.R and do what Jim does!


Beyond 20 minutes

Now I’m drawing mostly from Karl Browman’s primer on building packages.

Step 11: Create a license using usethis::use_*_license()

You might see a warning if you run a check at this point, about having no license. So consider adding one.

For example:

usethis::use_mit_license(name = "Your Name, Reader")  # replace "Your Name, Reader" with *your name*

Step 12: Build and install package using devtools::build()

devtools::build()

This builds a tar.gz, folder which is your compressed package!

“~/Google Drive/note_0.0.0.9000.tar.gz”

Step 13: Sharing online?

13.a Initialize your directory as git repository

usethis::use_git()

Or you can do this in your terminal: git init.

13.b Push your package to your github account.

Push package directory to github.

You might be able to use usethis::use_github().

usethis::use_github()

But instead, I use the github instructions here.

Step 14: Tell the world how to get your package with devtools::install_github()

Now you can let your friends know where your package “lives”. And people can install your package with devtools::install_github, and “githubusername/packagename”. This can be part of your readme. If you don’t submit to cran right away, you might direct people to install with the development version using devtools::install_github.

Here is an example:

devtools::install_github("EvaMaeRey/note")

More topics you might consider investigating:

usethis::use_travis()
usethis::use_coverage()
usethis::use_rcpp()

A good starting point for these topics are the resources listed below.

Additional Resources

Some great additional resources are the following:

Additional tools used to create this guide

R Core Team (2019). R: A language and environment for statistical computing. R Foundation for Statistical Computing, Vienna, Austria. URL https://www.R-project.org/.

Jim Hester and Hadley Wickham (2019). fs: Cross-Platform File System Operations Based on ‘libuv’. R package version 1.3.1. https://CRAN.R-project.org/package=fs

JJ Allaire and Yihui Xie and Jonathan McPherson and Javier Luraschi and Kevin Ushey and Aron Atkins and Hadley Wickham and Joe Cheng and Winston Chang and Richard Iannone (2019). rmarkdown: Dynamic Documents for R. R package version 1.14. URL https://rmarkdown.rstudio.com.

Yihui Xie and J.J. Allaire and Garrett Grolemund (2018). R Markdown: The Definitive Guide. Chapman and Hall/CRC. ISBN 9781138359338. URL https://bookdown.org/yihui/rmarkdown.