Lecturer: Michael Lydeamore
Department of Econometrics and Business Statistics
Aim
Why
pkgdown make your work installable and discoverableThis is a taste of ETC4500/5450 (Advanced R Programming). We will learn the bare minimum required to get to documentation.
Documentation is vital
ausbirthplace packageIt’s never been easier to make a package. The usethis package can do all of the heavy lifting for us
Tip
This means we can easily make packages that might only be just “for us”
This package is for teaching demo only
will set up the directory structure we need for a package.
- R/
- data/
- data-raw/
- man/
- vignettes/
- DESCRIPTION
- NAMESPACE
- README.md
Tip
Different forms of communication live in different parts of the package.
Functions in the R/ folder become part of the package.
Tip
Add @export to functions that users should call directly.
Your package could load in some data (like palmerpenguins::penguins for example).
User-facing data objects go in data; the scripts that produce them go in data-raw.
The generated script should finish by saving the object into the package:
devtools lets us load the package without installing it first:
You can also use the default hotkey Ctrl+Shift+L to load.
You should reload often - try to avoid manually sourcing files unless you have to!
Three files play different communication roles:
NAMESPACE: machine-generated instructions about exported functions and importsDESCRIPTION: structured metadata about the package and its dependenciesREADME: the introduction people see before they install the packageHadley gives a good template for a README:
DESCRIPTION tells tools and users what the package is:
Package: mypackage
Title: What the Package Does (One Line, Title Case)
Version: 0.0.0.9000
Authors@R:
person("First", "Last", role = c("aut", "cre"))
Description: What the package does (one paragraph).
License: MIT + file LICENSE
Imports: ggplot2
Important
Replace every placeholder; a valid package needs a meaningful title and description.
A licence tells other people what they may do with your code.
Tip
For Assignment 4, include a licence file rather than leaving the generated placeholder in DESCRIPTION.
Inevitably, you’ll want to use a function from another package. Again there’s a usethis function for that too:
You still have to reference exported functions using ::, or import specific functions with @importFrom.
For example:
Comments in code are communication. There’s no need to re-state very clear code, but if you are doing anything even mildly complicated, chances are you won’t be able to understand the code quickly.
Example:
palmerpenguins::penguins |>
dplyr::select(species, bill_depth_mm, bill_length_mm, flipper_length_mm, island) |>
tidyr::pivot_longer(!c(species, island)) |>
dplyr::group_by(species, island) |>
dplyr::summarise(means = mean(value, na.rm = TRUE)) |>
tidyr::pivot_wider(names_from = species, values_from = means)Comments in code are communication. There’s no need to re-state very clear code, but if you are doing anything even mildly complicated, chances are you won’t be able to understand the code quickly.
Example:
palmerpenguins::penguins |>
dplyr::select(species, bill_depth_mm, bill_length_mm, flipper_length_mm, island) |>
# Reshape the measurements so they can be summarised together
tidyr::pivot_longer(!c(species, island)) |>
dplyr::group_by(species, island) |>
dplyr::summarise(means = mean(value, na.rm = TRUE)) |>
# Present species as columns for easier comparison
tidyr::pivot_wider(names_from = species, values_from = means)Useful comments explain intent, assumptions, or a surprising decision.
Avoid narrating code that is already clear. As with all communication, conciseness and clarity are key.
When we don’t know how to use a function, we look up the function documentation by typing ?group_by.
But where does this documentation come from?
R gives a standard way of documenting packages: you write .Rd files in the man/ directory. These files use a custom syntax, which is very similar to LaTeX.
This separates code from comments, making it easy to forget to update a function’s documentation if you change the function. roxygen2 puts the documentation with your code, and generates .Rd files.
roxygen2 skeletonpercent_change <- function(old, new, digits = 1) {
if (any(old == 0)) {
stop("old cannot contain zero")
}
round((new - old) / old * 100, digits = digits)
}Before reading the source, what would a user need to know?
#' Calculate percentage change
#'
#' Calculates change from an old value to a new value, relative
#' to the old value. Positive results indicate an increase.
#'
#' @param old Numeric vector of baseline values; must not contain zero.
#' @param new Numeric vector of new values.
#' @param digits Number of decimal places used to round the result.
#' @return A numeric vector giving percentage change relative to `old`.
#' @export
percent_change <- function(old, new, digits = 1) {
# ...
}#' @examples
#' percent_change(80, 100)
#' # A 25% increase
#'
#' percent_change(c(80, 50), c(100, 45), digits = 0)
#' # A 25% increase followed by a 10% decreaseTip
Examples should be runnable and help users interpret the result.
.Rd fileJust writing roxygen2 doesn’t generate the man files. To get these, we have to run
devtools::document()
If you are in RStudio, you can press ctrl+shift+d
Important
For a function to be accessible by installing your package, you need to include the @export tag!
Just like functions, we have to document our data. You can put this anywhere (after all, there is no function that generates our data), but the convention is in R/data.R.
For our data,
#' Number of Australian residents by country of birth from the 2016 and 2021 census
#'
#' @format A data frame with 105 rows and 4 columns:
#' \describe{
#' \item{birth}{Country of birth}
#' \item{count}{Number of residents from that birth country}
#' \item{percentage}{Percentage of residents from that birth country}
#' \item{census}{Year of census (either 2016 or 2021)}
#' }
#' @source Australian Bureau of Statistics, Census data,
#' <https://www.abs.gov.au/census/find-census-data>
"censusdata"Documentation is great to look at something specific (like a function).
What if you’ve just installed a package and would like to know how to use it?
Enter the Vignette
Vignettes are “how-to” guides to packages. Perhaps they document a specific workflow, or how to solve a specific problem.
Check out the dplyr vignettes
| User’s question | Best starting point |
|---|---|
| Why might I use this package? | README |
| How do I complete this workflow? | Vignette |
| What does this function or argument do? | Reference documentation |
Tip
Good packages provide routes for both new and returning users.
Write for a reader who may never have used your package before.
Tip
If a workflow is difficult to explain, the package may also be difficult to use.
This creates a Quarto vignette in vignettes/ and adds the required package metadata.
Documentation that renders is not enough: check the package as a complete product.
Important
Fix every error and warning. Read every note and decide whether it needs action.
Once the package is in a public GitHub repository, another user can install it with:
They should not need your source files, working directory, or instructions sent separately.
Tip
Put a copyable installation command in the README.
pkgdownpkgdown combines your package metadata and documentation into a website.
This configures a GitHub Action that rebuilds and publishes the site when the package changes.
DESCRIPTION + README + man/ + vignettes/
↓
checked package + installable GitHub repository + pkgdown website
Tip
Keep the source documents in the package; regenerate the outputs when the source changes.
digits argument accepts.README → reference page → vignette → README installation instructions
Summary
document() and check() before sharing the package.pkgdown makes its documentation accessible.What happens when you type print(object) in R?
print() is an S3 generic.
We can’t cover S3 precisely here (come back in Advanced R Programming for that), but we can think of it as:
a different print function is called depending on what object looks like.
Which method is chosen is based on the class of object.
We can prepend our own class while preserving the object’s existing classes.
The print method selects what a user needs to see; it does not change the underlying object.
print methods are communication too. Compare the user-facing summary with the internal component names:
All of our communication principles apply to print methods:
Often this will need multiple rounds of feedback (like all writing).
To include a custom print method in a package, document it with @export. roxygen2 will usually generate the required S3 registration in NAMESPACE.
This is out of scope for the unit, but you can read more in the roxygen2 S3 documentation.

ETC5523 Week 8