+ - 0:00:00
Notes for current slide
Notes for next slide

My Organization's First R package

Document R Code

rstudio::conf(2020L)

1 / 57

2 / 57

?mean

3 / 57

?mean

Image by Kieran Healy

3 / 57

roxygen2: In-Line Documentation for R

4 / 57

roxygen2: In-Line Documentation for R

Write documentation with your functions

5 / 57

roxygen2: In-Line Documentation for R

Write documentation with your functions

render with document()

6 / 57

Insert roxygen skeleton

Code > Insert Roxygen Skeleton

7 / 57

Insert roxygen skeleton

Code > Insert Roxygen Skeleton

Ctrl/Cmd + Shift + Alt/Opt + R

8 / 57

Insert roxygen skeleton

Code > Insert Roxygen Skeleton

Ctrl/Cmd + Shift + Alt/Opt + R

8 / 57

9 / 57

10 / 57

11 / 57

12 / 57

13 / 57

14 / 57

15 / 57

16 / 57

17 / 57

18 / 57

Write roxygen, run document()

19 / 57

Write roxygen, run document()

theme_mako <- function(base_size = 14) {
ggplot2::theme_dark(base_size = base_size) +
ggplot2::theme(
panel.background = ggplot2::element_rect(fill = "#0D98BA")
)
}
20 / 57

Write roxygen, run document()

#' A dark theme with a mako-like background
#'
#' @param base_size base font size
#'
#' @return a ggplot2 theme
#' @export
#'
#' @examples
#'
#' ggplot2::quickplot(iris$Sepal.Length) + theme_mako()
#'
theme_mako <- function(base_size = 14) {
ggplot2::theme_dark(base_size = base_size) +
ggplot2::theme(
panel.background = ggplot2::element_rect(fill = "#0D98BA")
)
}
21 / 57

22 / 57

man/theme_mako.Rd

## % Generated by roxygen2: do not edit by hand
## % Please edit documentation in R/themes.R
## \name{theme_mako}
## \alias{theme_mako}
## \title{A dark theme with a mako-like background}
## \usage{
## theme_mako(base_size = 14)
## }
## \arguments{
## \item{base_size}{base font size}
## }
## \value{
## a ggplot2 theme
## }
## \description{
## A dark theme with a mako-like background
## }
## \examples{
##
## ggplot2::quickplot(iris$Sepal.Length) + theme_mako()
##
## }
23 / 57

?theme_mako

24 / 57

?theme_mako

24 / 57

Syntax

LaTeX like. See more at https://r-pkgs.org/man.html

use_roxygen_md() lets you write in Markdown. See more at https://roxygen2.r-lib.org/articles/rd-formatting.html

25 / 57

Your Turn 1

Open the NAMESPACE file. What do you see?

Let's add documentation. Run use_roxygen_md()

Open r/themes.R. Insert a roxygen skeleton for theme_avalanche().

Change the title to "AVALANCHE ggplot2 themes"

Hit Enter/Return twice after the title. Make sure the new lines start with #'. Add this text: "Minimalistic ggplot themes for use on AVALANCHE reports."

Run document() or press Ctrl/Cmd + Shift + D. Read the help page for your function with ?theme_avalanche.

Finally, look at the NAMESPACE file again. What changed?

26 / 57

Your Turn 1

exportPattern("^[^\\.]")
27 / 57

Your Turn 1

#' AVALANCHE ggplot2 themes
#'
#' Minimalistic ggplot themes for use on AVALANCHE reports.
#'
#' @param base_size
#' @param ...
#'
#' @return
#' @export
#'
#' @examples
theme_avalanche <- function(base_size = 14, ...) {
ggplot2::theme_minimal(base_size = base_size, ...) +
ggplot2::theme(panel.grid.minor = ggplot2::element_blank())
}
28 / 57

Your Turn 1

# Generated by roxygen2: do not edit by hand
export("%>%")
export(db_con)
export(get_resident_data)
export(theme_avalanche)
export(theme_avalanche_h)
export(theme_avalanche_v)
import(data.table)
importFrom(magrittr,"%>%")
29 / 57

Argument descriptions

#' [other roxygen code]
#' @param x The name of a database to retrieve
get_data <- function(x) {
# code to get data
}
30 / 57

Argument descriptions: @inheritParams

#' [other roxygen code]
#' @param x The name of a database to retrieve
get_data <- function(x) {
# code to get data
}
#' [other roxygen code]
filter_table <- function(x) {
tbl <- get_data(x)
# code to filter data
}
31 / 57

Argument descriptions: @inheritParams

#' [other roxygen code]
#' @param x The name of a database to retrieve
get_data <- function(x) {
# code to get data
}
#' [other roxygen code]
#' @inheritParams get_data
filter_table <- function(x) {
tbl <- get_data(x)
# code to filter data
}
32 / 57

Examples

Examples can be any kind of R code

33 / 57

Examples

Examples can be any kind of R code

#' [other roxygen code]
#' @examples
#'
#' library(dplyr)
#' get_data("daily_actice_users") %>%
#' filter(date == lubridate::today())
get_data <- function(x) {
# code to get data
}
33 / 57

Examples

Examples can be any kind of R code

#' [other roxygen code]
#' @examples
#'
#' library(dplyr)
#' get_data("daily_actice_users") %>%
#' filter(date == lubridate::today())
get_data <- function(x) {
# code to get data
}

But any packages used need to be imported or suggested!

34 / 57

Examples

If you don't want to run examples, wrap them in dontrun{} or donttest{}

#' [other roxygen code]
#' @examples
#'
#' dontrun{
#' get_data("daily_active_users")
#' }
get_data <- function(x) {
# code to get data
}
35 / 57

Your Turn 2

Let's keep working on the documentation for theme_avalanche():

Remove @param base_size and replace it with: @inheritParams ggplot2::theme_minimal

For @param ..., add: Additional arguments passed to [ggplot2::theme_minimal()]

For @return, add: a ggplot theme.

For @examples, add two line breaks (make sure the new lines have roxygen comments!). Add this code: ggplot2::qplot(iris$Sepal.Length) + theme_avalanche()

Rebuild the documentation and check the help page.

36 / 57

Your Turn 2

#' AVALANCHE ggplot2 themes
#'
#' Minimalistic ggplot themes for use on AVALANCHE reports.
#'
#' @inheritParams ggplot2::theme_minimal
#' @param ... Additional arguments passed to [ggplot2::theme_minimal()]
#'
#' @return a ggplot theme.
#' @export
#'
#' @examples
#'
#' ggplot2::qplot(iris$Sepal.Length) + theme_avalanche()
#'
theme_avalanche <- function(base_size = 14, ...) {
ggplot2::theme_minimal(base_size = base_size, ...) +
ggplot2::theme(panel.grid.minor = ggplot2::element_blank())
}
37 / 57

Image from R Packages, ed. 2

38 / 57

Quoth Jenny Bryan:

  1. Use functions.
  2. A few little functions >> a monster function
  3. Small well-named helper >> commented code
39 / 57

Helper functions

plot_daus <- function(daily_users) {
daily_users <- daily_users %>%
dplyr::mutate(date = as.Date(time)) %>%
dplyr::group_by(date)
dplyr::select(user_id) %>%
dplyr::distinct() %>%
dplyr::summarize(n = dplyr::n())
ggplot2::ggplot(ggplot2::aes(daily_users, x, n)) +
ggplot2::geom_col()
}
40 / 57

Helper functions

plot_daus <- function(daily_users) {
daily_users <- count_daus(daily_users)
ggplot2::ggplot(ggplot2::aes(daily_users, x, n)) +
ggplot2::geom_col()
}
count_daus <- function(daily_users) {
daily_users %>%
dplyr::mutate(date = as.Date(time)) %>%
dplyr::group_by(date)
dplyr::select(user_id) %>%
dplyr::distinct() %>%
dplyr::summarize(n = dplyr::n())
}
41 / 57

Show of Hands

Which of these functions will be added to NAMESPACE?

#' Plot daily active users
#'
#' @param ...
#' @export
plot_daus <- function(...) {
# ... code to plot daily active users
}
#' Count daily active users
#'
#' @param ...
count_daus <- function(...) {
# ... code to count daily active users
}
42 / 57

Show of Hands

Which of these functions will be added to NAMESPACE?

#' Plot daily active users
#'
#' @param ...
#' @export
plot_daus <- function(...) {
# ... code to plot daily active users
}
#' Count daily active users
#'
#' @param ...
count_daus <- function(...) {
# ... code to count daily active users
}
43 / 57

Exported functions vs internal functions

@export = shinRa::plot_daus()

44 / 57

Exported functions vs internal functions

@export = shinRa::plot_daus()

library(shinRa)

plot_daus() ✔️

44 / 57

Exported functions vs internal functions

NO @export = shinRa:::count_daus()

45 / 57

Exported functions vs internal functions

NO @export = shinRa:::count_daus()

library(shinRa)

count_daus() 🤔🤔🤔🤔🤔🤔

45 / 57

Strategies for documenting helper functions:

  1. Don't document them 🤷
  2. @keyword internal
  3. @nomd
46 / 57

Joining documentation

47 / 57

Joining documentation

#' [other roxygen code]
#' @param x The name of a database to retrieve
get_data <- function(x) {
# code to get data
}
#' [other roxygen code]
#' @param x @inheritParam get_data
filter_table <- function(x) {
tbl <- get_data(x)
# code to filter data
}
47 / 57

Joining documentation

#' [other roxygen code]
#' @param x The name of a database to retrieve
get_data <- function(x) {
# code to get data
}
#' @rdname get_data
#' @export
filter_table <- function(x) {
tbl <- get_data(x)
# code to filter data
}
48 / 57

Joining documentation

#' [other roxygen code]
#' @param x The name of a database to retrieve
#' @name data_helpers
NULL
#' @rdname data_helpers
#' @export
get_data <- function(x) {
# code to get data
}
#' @rdname data_helpers
#' @export
filter_table <- function(x) {
tbl <- get_data(x)
# code to filter data
}
49 / 57

Joining documentation

#' [other roxygen code]
#' @param x The name of a database to retrieve
#' @name data_helpers
NULL
#' @rdname data_helpers
#' @export
get_data <- function(x) {
# code to get data
}
#' @rdname data_helpers
#' @export
filter_table <- function(x) {
tbl <- get_data(x)
# code to filter data
}
50 / 57

Your Turn 3

In R/themes.R, join the documentation of theme_avalanche_h() and theme_avalanche_v() to theme_avalanche() by replacing the roxygen code for the first two functions with "#' @rdname theme_avalanche".

Make sure both functions still have an export tag, as well!

Re-render the documentation and read the help page for ?theme_avalanche_h()

51 / 57

Your Turn 3

#' @rdname theme_avalanche
#' @export
theme_avalanche_h <- function(base_size = 14, ...) {
ggplot2::theme_minimal(base_size = base_size, ...) +
ggplot2::theme(
panel.grid.minor = ggplot2::element_blank(),
panel.grid.major.x = ggplot2::element_blank()
)
}
#' @rdname theme_avalanche
#' @export
theme_avalanche_v <- function(base_size = 14, ...) {
ggplot2::theme_minimal(base_size = base_size, ...) +
ggplot2::theme(
panel.grid.minor = ggplot2::element_blank(),
panel.grid.major.y = ggplot2::element_blank()
)
}
52 / 57

Package documentation

use_package_doc()

## ✔ Setting active project to '/private/var/folders/03/9x7925g54mncswxx06wpkxl00000gn/T/Rtmp7tPP...
## ✔ Writing 'R/shinRa-package.R'
53 / 57

help("tidyr")

54 / 57

pkgdown

55 / 57

pkgdown

use_pkgdown()

Renders documentation, README, vignettes, and more as a website.

56 / 57

Here's a good example...

https://roxygen2.r-lib.org/

There are a lot more documentation tricks.

Read the vignettes!

57 / 57

2 / 57
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow