Recently, several R packages have made creating new R packages --- with all the required architecture and implementing best practices --- much easier. 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." That sounds good. 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{target="_blank"}; 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
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 warnings!
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 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 test_that::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{target="_blank"}.
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.
Push package directory to github{target="_blank"}.
You might be able to use usethis::use_github().
usethis::use_github()
But instead, I use the github instructions here.
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")
What is travis? What about the coverage report? What about use_rcpp?
usethis::use_travis() usethis::use_coverage() usethis::use_rcpp()
A good starting point for these topics is the resources listed below.
Some great additional resources are the following:
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.