library(tufte)
#knitr::opts_chunk$set(results = "hide", echo = FALSE)
library(knitr)
options(replace.assign = FALSE, width = 50)

opts_chunk$set(fig.path = "knitr_figure/graphics-",
               cache.path = "knitr_cache/graphics-",
               fig.align = "center",
               dev = "pdf", fig.width = 5, fig.height = 5,
               fig.show = "hold", cache = FALSE, par = TRUE)
knit_hooks$set(crop = hook_pdfcrop)

knit_hooks$set(par = function(before, options, envir) {
    if (before && options$fig.show != "none") {
        par(mar = c(3, 3, 2, 1), cex.lab = .95, cex.axis = .9,
            mgp = c(2, .7, 0), tcl = -.01, las = 1)
}}, crop = hook_pdfcrop)

Building an R package

knitr::include_graphics("logo.png")

My first package

RStudio projects

RStudio projects make it straightforward to switch between analyses. You can create an RStudio project:

\noindent Each project has its own working directory, workspace, history, and source documents. When we create an RStudio package project, this will create a new directory that contains five files.

\noindent Your package directory will also contain two directories.

Tasks

  1. Create a "package" project, via File -> New Project -> New Directory -> R package Call the package pkg and select the directory you want to store the package in. Then click Create Project Congratulations - you have just created your first package called pkg
  2. Click Build -> Build and Reload
  3. Now type r library("pkg") hello()
  4. Look at the NAMESPACE file. Notice that the function hello has been exported.
  5. The hello function has also been documented r help("hello") The associated documentation file is in the man directory.

roxygen2

Keeping the NAMESPACE file and documentation up-to-date is a painful experience. To ameliorate the process, we use the roxygen2 package to automatically generate the necessary entries. Above function definitions we add roxygen2 tags. The tags are of the form:

#' @export
#' @details
#' @aliases

\noindent Notice the tags are just R comments.

Tasks

  1. Check that you have the necessary R packages installed r library("devtools") library("roxygen2") If you don't have them installed, then install them in the usual way r install.packages(c("devtools", "roxygen2"))
  2. Click on Build -> Configure build tools then select Generate documentation with Roxygen and click OK. Now when we build our package, RStudio will automatically run r library("roxygen2") roxygenise(".")
  3. In the file R/hello.R add r #' @export just above the hello function, i.e. r #' @export hello <- function() { print("Hello, world!") } The export tag above the hello function indicates that we want to export^[Export means the users loading this package, can access this function.] this particular function.
  4. Now delete the man directory and the NAMESPACE file^[The reason for deleting these is that we will automatically generate them using roxygen2 - more details below.]. Select^[The keyboard shortcut for this is Ctrl + B] Build -> Build and reload You should now be able to load your package with r library("pkg") \noindent and call the hello function r hello()
  5. Open the NAMESPACE file. You should see the entry r # Generated by roxygen2: do not edit by hand export(hello)

Adding new functions

\newthought{All R} functions that we create in our package are saved in the R/ directory^[The files can have a .R or .r file extension. Personally, I prefer .R]. This directory can contain multiple files.

Tasks

  1. Create a new file in the R/ directory called basic.R. In this file add the following code r #' @export add = function(x, y) { return(x + y) } \noindent Build and reload your package^[Remember the keyboard shortcut Ctrl + B]. After reloading your package, the following code should run r library("pkg") add(1, 2)
  2. Create a new function called check_numeric r check_numeric = function(x) all(is.numeric(x)) \noindent and save it in the basic.R file.
  3. Now use check_numeric in the add function r add = function(x, y) { if (!check_numeric(c(x, y))) stop("Not numeric") x + y } \noindent Rebuild your package and check that the add function still works.
  4. Notice that we haven't exported the check_numeric function^[Remember to export a function, the function name should be in the NAMESPACE file.], so this will raise an error r library("pkg") check_numeric(1) \noindent but we can access any non-exported function in a package using the ::: operator r pkg:::check_numeric(1) \noindent We can access^[The benefit of doing this is that we haven't loaded the package.] any exported function using :: r pkg::add(1, 1)
  5. Now create a function subtract and export this function. Rebuild your package and check that this works OK.
  6. Delete your package and re-add the functions: add, check_numeric and subtract^[The purpose of this is to highlight how easy it is to create packages.].

\newpage

Documentation

Using roxygen2 simplifies documentation^[The first package I wrote didn't use roxygen2 and it was a very painful experience.]. The premise of roxygen2 is simple: describe your functions in comments next to their definitions and roxygen2 will process your source code and comments to produce Rd files in the man/ directory. In theory, you should never directly edit the Rd files.

\begin{table}[b] \centering \begin{tabular}{@{} ll} \toprule Tag name & Description \ \midrule \texttt{@title} & Short title for documentation page. \ \texttt{@description} & Longer description page. Skip a line for a \ &\qquad new paragraph.\ \texttt{@param} & Function parameter description. \ \texttt{@inheritParams} & Use the parameter definition from another function. \ \texttt{@export} & Add the function to the \texttt{NAMESPACE} file.\ \texttt{@return} & What does the function return, e.g. a data frame. \ \texttt{@examples} & Function examples (will be run when building). \ \texttt{@rdname} & Point multiple functions to the same help file,\ & \qquad e.g. \texttt{?substr}. \ \texttt{@seealso} & Pointers to other documentation pages. \ \texttt{@importFrom} & Import functions from other packages. \ \bottomrule \end{tabular} \caption{Useful \texttt{roxygen2} tags for documenting functions.}\label{T1-1} \end{table}

Tasks

  1. Copy the following roxygen2 descriptions to your add function r #' @title A function for adding #' #' @description A really good adding function. #' Perhaps the best function ever! #' #' A work of pure genius. #' @param x a number #' @param y another number #' @return a number #' @export #' @examples #' add(5, 10) #' ## Can also use negative numbers #' add(-5, 10) add = function(x, y) { if (!check_numeric(c(x, y))) stop("Not numeric") x + y }
  2. Rebuild your package and look at the help file for the add function, i.e. ?add. Run the examples via r example(add)
  3. Add a help page for the subtract function.^[Build \& reload.]
  4. Create a function called multiply and add an associated help page.^[Build \& reload.]
  5. Create a function called times r times = function(x, y) multiply(x, y) \noindent Export the times function.^[Build \& reload.]
  6. Use the @rdname tag above the times function to point to the multiply help page, i.e. r #' @rdname multiply \noindent Build and reload. Look at ?multiply and ?times. Now add @examples to the times function. Look at the new times help page.

Importing functions

We often want to use functions from other R packages. When we do this we need to be explicit, i.e. state what we want and from where. The great thing about R packages, is that when we install a package, the dependencies are also automatically installed.

Tasks

  1. Install the package jrAdvPackage. First, we need the drat package r install.packages("drat") \noindent Then we add the jr-packages repo^[Run the .libPaths() function to see the repository location.] r drat::addRepo("jr-packages") \noindent Then install as usual r install.packages("jrAdvPackage")
  2. The jrAdvPackage contains a very useful function called div that we want to use r library("jrAdvPackage") div(10, 2) \noindent To use the div function within our package, we have to import it first r #' @importFrom jrAdvPackage div \noindent and add an entry to the DESCRIPTION file Imports: jrAdvPackage Create a function divide that uses the div function.

The DESCRIPTION file

The DESCRIPTION file contains high level information about your package. For example, the package name, a brief description, the licence, and your email address.

\noindent Open the DESCRIPTION file and update fields with relevant information. An example is given below.
Package: pkg
Type: Package
Title: My First package
Version: 0.1
Date: 2016-11-01
Authors@R: person(given="Colin", family="Gillespie",
email="colin@jumpingrivers.com", role = c("aut", "cre"))
Maintainer: Colin Gillespie <colin@jumpingrivers.com>
Description: This is my very first package. It contains
exceedingly useful functions, such as add and subtract.
Make sure you add a couple of spaces to indent the
Description otherwise you will waste hours of your life
trying to find the bug.
License: GPL-2 | GPL-3
LazyData: TRUE

Package checks

One of great things about R packages, is that there are a number of package checks that are available. These include

Tasks

  1. Run the standard package checks on your package, via Build -> Check Package Check that you package passes all tests. ^[CTRL + E] Fix any errors, warnings or notes.
  2. Add the following example to the add function r #' add("A", "B") \noindent Build the package. Does the package still build? Check the package. Does the package pass all tests?

\newpage

Data and demos

Data in packages

Packages can also contain example data sets^[Full details are given at http://goo.gl/Y4Srx4]. Data files^[Note that R code should be "self-sufficient" and not make use of extra functionality provided by the package, so that the data file can also be used without having to load the package.] can be one of three types as indicated by their extension.

\noindent Data files live in the data/ directory.

\noindent Each data file should also have an associated help page. The easiest way to generate a help page is to use roxygen2 and a dummy R function. Typically, I have a file called data_help_files.R, which has entries for the each data set. For example,^[This is entry is taken from the poweRlaw package.]

## Entry in data_help_files.R
## Name is name of the data set.
#' @name moby
#' @aliases moby_sample
#' @title Moby Dick word count
#' @description The frequency of occurrence of unique words
#' in the novel Moby Dick by Herman Melville.
#'
#' The data set moby_sample is 2000 values sampled from the
#' moby data set.
#' @docType data
#' @format A vector
#' @source M. E. J. Newman, "Power laws, Pareto distributions
#' and Zipf's law." Contemporary Physics 46, 323 (2005).
NULL

Tasks

  1. Create a data/ directory.
  2. Create the following data frame r example_data = data.frame(x = runif(10), y = runif(10)) \noindent Now save^[Use the save function.] the data frame example_data in the data/ directory r save(example_data, file = "data/example_data.RData")
  3. Create a file called data_help_files.R in the R/ directory and document your new data set.
  4. Build and reload your package. Can you access the help page and the data set?
  5. Check that your package still passes all tests^[CTRL + E].

Demos

A demo is similar to function examples, but is typically longer and shows how to use multiple functions together. Demos are plain .R files that live in the demo/ package directory. The demos are accessed with the demo() function.

In demo/ directory, there should also be an 00Index file, that lists the demos^[There is a planned demoTitle tag for roxygen2, but currently this hasn't been implemented.]. For example,^[Note the white space separation in the 00Index. Use at least four spaces to avoid annoying error messages.]
demo1 My very first demo
demo2 My very second demo

Tasks

  1. Create a demo/ directory.
  2. Create a file called first.R and save it in the demo/ directory. In this file show how you can use some of your newly created function.
  3. Add a 00Index file to the demo/ directory.
  4. Build and check your package.

\newpage

Vignettes

If you want to include more extensive examples or even just further documentation, then you should consider creating a vignette:

a vignette is a small illustration placed at the beginning or end of a book or chapter. ^[http://dictionary.reference.com/browse/vignette]

\noindent We can view vignettes from other packages using the vignette function

vignette(package = "knitr")

\noindent or to view in your web browser

browseVignettes(package = "knitr")

Example: markdown vignettes

Vignettes are stored in the vignettes/ directory. The simplest vignette uses R-markdown and is formatted by the knitr package. To create a package vignette, we simply place the file in the vignettes/ directory. ^[The output style is html_vignette. This is more lightweight (in terms of file size) than the standard html_document] For example, suppose we have a file intro.Rmd that contains the following text: ^[The UTF-8 line specifies the file encoding.]

---
title: "My very first vignette"
author: "Colin Gillespie and Robin Lovelace"
output: rmarkdown::html_vignette
# nolint start
vignette: <
# nolint end
  %\VignetteIndexEntry{My very first vignette}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

My first package

This is my first package vignette. I can include mathematics, such as $x^2$. R code is also nicely formatted and displayed.

x = runif(10)
x

and plots

plot(x)
  1. The first few lines register knitr as the vignette engine and provide an entry for the list of vignettes.
  2. We have used markdown to add simple styling^[See http://goo.gl/y9FO5] and the RStudio markdown reference sheet for further commands.. For example **first** becomes first and \$x\textasciicircum 2\$ becomes $x^2$.
  3. R code is executed in the ``` regions.

Tasks

  1. Create a vignettes/ directory in your package.
  2. Create an R markdown file File -> New File -> R markdown and save it in the vignettes/ directory.
  3. Copy the markdown example above into your file and knit that file.
  4. Add Suggests: knitr VignetteBuilder: knitr to the DESCRIPTION file^[Notice the knitr is only a suggested package, since it's not essential.]. This will tell your package to build vignettes using knitr.
  5. Vignettes won't actually be built unless you are creating a source bundle^[If you submit your package to CRAN, then the vignettes will be built ready for distribution.]. To install a package with vignettes included, we first create the source package Build -> Build Source Package and then install the package from source r install.packages("pkg_1.0.tar.gz", repos=NULL, type="source") \noindent Build and install your package. Check that you can access the vignette.
  6. Check and ensure that your package passes all tests^[CTRL + E].

Package level documentation

You package typically also has documentation associated with the package name. For example,

library("pkg")
?pkg

\noindent should bring up an overview of your package. Again we use roxygen2 to generate the man page. Go to

http://goo.gl/W2tJrF

\noindent to view the entry for the poweRlaw package.

Tasks

  1. Create a file called pkg-package.R in the R/ directory.
  2. Using the poweRlaw package as an example, create a man page for your package^[What do you think the @aliases tag does?].
  3. Do one final check and ensure that your package passes all tests.


jr-packages/jrAdvPackage documentation built on Dec. 27, 2019, 8:05 a.m.