R Package Development Workflow

R-Ladies Without Borders by R-Ladies Lagos and R-Ladies Paris

Author
Affiliations
Emi Tanaka

Monash University (until June 2023)

Australian National University (from July 2023)

Published

May 6, 2023

Thanks to Mouna Belaid (R-Ladies Paris) and Eyitayo Alimi (R-Ladies Lagos) for inviting me and organising!

This note will be available at https://rpubs.com/emitanaka/rladies-lagos-paris-2023.

What are some of your favorite packages?

library(tidyverse)
library(ggplot2)
library(dplyr)
library(plotly)
library(shiny)
library(naniar)
library(purrr)

What is an R-package?

A container:

  • for a set of R functions,
  • to share data,
  • to share an app,
  • and more, e.g. Rmd templates,

Motivation

What’s the goal?
  • What does your package (and functions) do?
  • Who is it for?
  • Why use it?
  • Where do we find and install it?
  • How do we use it?

💡 Idea

  • Let’s make my own pipable functions to do some matrix operations.

\[M = \begin{bmatrix}1 & 2 \\3 & 4\end{bmatrix}\]

  • The user interface would look like:
matrix(1:4, nrow = 2, ncol = 2, byrow = TRUE) %>% 
  entry_add(e(1, 1), 2) %>% 
  entry_subtract(e(2, 2), 3) %>% 
  entry_multiply_by(e(1, 2), 4) %>% 
  entry_divide_by(e(2, 1), 5)

\[\begin{bmatrix}1 + 2 & 2 \times 4 \\3/5 & 4 - 3\end{bmatrix} = \begin{bmatrix}3 & 8 \\0.6 & 1\end{bmatrix}\]

Functions

entry_add <- function(M, entry, x) { 
  M[entry[1], entry[2]] <- M[entry[1], entry[2]] +  x
  M
}

entry_subtract <- function(M, entry, x) {
  M[entry[1], entry[2]] <- M[entry[1], entry[2]] -  x
  M
}

entry_multiply_by <- function(M, entry, x) {
  M[entry[1], entry[2]] <- M[entry[1], entry[2]] *  x
  M
}

entry_divide_by <- function(M, entry, x) {
  M[entry[1], entry[2]] <- M[entry[1], entry[2]] /  x
  M
}
  
e <- function(i, j) {
  if(i < 0) stop("Row entry is not a positive integer.")
  if(j < 0) stop("Column entry is not a positive integer.")
  c(i, j)
}

R-package development helper packages

  • devtools
  • usethis
  • roxygen2
  • pkgdown
  • testthat
install.packages(c("devtools", "usethis", "roxygen2", "pkgdown", "testthat"))

Anatomy of an R-package

  • DESCRIPTION file
  • R/ directory for R files that contain your functions
  • NAMESPACE file (you don’t need to manually create this)

Optionally,

  • data/ for binary data available to the user
  • data-raw/ for raw data
  • inst/ for arbitrary additional files that you want include in your package
  • and so on.

DESCRIPTION file

  • Metadata for the package
    • Package name
    • Title and description
    • Authors
    • Dependencies (depends, imports, suggests)
    • Licensing
    • Version number
    • Bug report location, and so on.

Creating an R-package

available::available("matrixops") # check if package is available
usethis::create_package("matrixops")
usethis::use_r("new-r-file") # creating new R file in the R/ directory

# add R functions to the R file

devtools::load_all()

Documenting R functions with roxygen2

  • use #' above a function to write documentation for that function

  • roxygen2 uses @ tags to structure documentation, e.g. 

    • any text after @description is the description
    • any text after @param describes the arguments of the function
    • @export signals that it is an exported function
    • any text after @return describes the return object
  • The full list of Rd tags are found at https://roxygen2.r-lib.org/articles/rd.html

  • Then devtools::document() converts the Rd tags to appropriate sections of .Rd files written in the man/ folder

devtools::document()

Unit testing with testthat

usethis::use_test()
  • This creates a file test-active-filename.R in tests/testthat/ directory
test_that("operations works", {
  M <- matrix(1:4, nrow = 2, ncol = 2, byrow = TRUE)
  expect_equal(entry_add(M, e(1, 1), 2),
               matrix(c(3, 2, 3, 4), 2, 2, byrow = TRUE))
})
devtools::test_active_file()
devtools::test()

Master the keyboard shortcuts

  • Cmd/Ctrl + Shift + L: Load all
  • Cmd/Ctrl + Shift + D: Document
  • Cmd/Ctrl + Shift + T: Test
  • Cmd/Ctrl + Shift + B: Build and Reload
  • plus more… see RStudio IDE > Tools > Keyboard Shortcuts Help

Share and collaborate on your package

usethis::use_git()
usethis::use_github()
  • Install GitHub hosted packages like:
devtools::install_github("user/repo")

Add README

usethis::use_readme_rmd()

Building a website for your package with pkgdown

usethis::use_pkgdown()
# OR, automatic build and deploy with
usethis::use_pkgdown_github_pages()

Whole R package development workflow

available::available("pkgname") # check if package name is available (if planning to publish publicly)
usethis::create_package("pkgname")
usethis::use_git() # set up version control
usethis::use_github() # optional
usethis::use_r("myfile")
# write some functions in a script
usethis::use_data_raw() # if adding data
devtools::load_all() # try it out in the console
usethis::use_package("import-pkgname") # add package to import (or depends or suggests)
usethis::use_package_doc() # add package documentation
usethis::use_pipe() # if you want to add %>% from `magrittr`
usethis::use_vignette("vignette-name") # add vignette
usethis::use_test() # make test file for active R file
# write some test
devtools::test_active_file() # test active file
devtools::test() # test whole package
devtools::build() # build vignettes
devtools::install() # to install package
devtools::check() # to build and check a package 
usethis::use_readme_rmd() # to add a README Rmd file
styler::style_pkg() # optional (commit first, then review changes carefully)
usethis::use_pkgdown_github_pages() # for setting up pkgdown website on github
# `usethis::use_pkgdown()` if not using github pages

Useful reference