Recently, several R packages have made creating new R packages — with all the required architecture and implementing best practices — much easier. They include:
usethis: Automate Package and Project Setup. Hadley Wickham and Jennifer Bryan (2019).
devtools: Tools to Make Developing R Packages Easier. Hadley Wickham, Jim Hester and Winston Chang (2019).
roxygen2: In-Line Documentation for R. Hadley Wickham, Peter Danenberg and Manuel Eugster (2018).
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!
Your video guide is here; the package build starts at about minute 8.
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, ...)
}
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
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.
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!
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.
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.
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
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, ...)
}
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")
}
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
Again check you package. You might do this often when you are creating your own package.
devtools::check()
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
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!
Now I’m drawing mostly from Karl Browman’s primer on building packages.
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*
devtools::build()
This builds a tar.gz, folder which is your compressed package!
“~/Google Drive/note_0.0.0.9000.tar.gz”
usethis::use_git()
Or you can do this in your terminal: git init.
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")
usethis::use_travis()
usethis::use_coverage()
usethis::use_rcpp()
A good starting point for these topics are the resources listed below.
Some great additional resources are the following:
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.