ETC5523: Communicating with Data

Workshop 8: Build and document pokemonvgc

Author

Michael Lydeamore

Published

1 August 2001

🎯 Objectives

By the end of this workshop, you will be able to:

  • distinguish raw data inputs from data shipped in an R package;
  • document and export a package function with roxygen2;
  • replace repeated package::function() calls with selective imports;
  • write a README quick start; and
  • check, install and use a package.

Get the starter package

Clone https://github.com/MikeLydeamore/pokemonvgc and open pokemonvgc.Rproj in Positron or RStudio.

You can clone through the Git: Clone command, or run this in a terminal:

git clone https://github.com/MikeLydeamore/pokemonvgc.git

Install the workshop tools if you do not already have them:

Important

Run the remaining commands with pokemonvgc open as your current project. Do not create another package inside it.

Exercise 8A: Tour the package (5 minutes)

Find each of these components and write one sentence describing its role:

Component Question to answer
R/ What code will be available to package users?
data/ Which objects will be loaded with the package?
data-raw/ Which inputs and scripts produce those objects?
DESCRIPTION What does this tell R and a potential user?
README.Rmd Which reader encounters this document first?

Load the package without installing it:

Use pokemon_elo and recent_champions to answer:

  1. How many rating observations and distinct players are included?
  2. Which recent champion has no rating history in this snapshot?
  3. Can you open ?recent_players yet? Why or why not?

There are 2,043 observations for 51 players. Takuma Yamazaki is in the champion metadata but not the rating snapshot. The function has no help page because its roxygen block has not been written and generated yet.

Exercise 8B: Rebuild the package data (7 minutes)

Open data-raw/pokemon-elo.R and follow the data from its prepared inputs to the objects saved by usethis::use_data().

Then run the script from the package root:

The calculation may take several seconds. Confirm that it recreates:

  • data/pokemon_elo.rda; and
  • data/recent_champions.rda.

Discuss with a partner:

  • Why is the match history kept in data-raw/ rather than shipped to users?
  • Why is recent_champions stored as package data rather than embedded in recent_players()?
  • Which file would you edit if the champion lookup changed?

The match history is an input to the reproducible build process; users need the smaller derived rating history, not all source records. Keeping the champion lookup as data separates metadata from the function’s logic and makes it available for other analyses. Update data-raw/pokemon-elo.R, rerun it, and commit the regenerated .rda file rather than editing data/ directly.

Exercise 8C: Document and export the function (10 minutes)

Open R/recent-players.R. Read the function as evidence before writing its documentation. What does one input row represent? When is a player eligible? What does one output row represent?

Add a roxygen2 block containing:

  • a task-oriented title and short description;
  • @param entries for ratings and min_events;
  • an @return entry stating what one row represents;
  • two runnable @examples, one using a non-default threshold; and
  • @export.

Generate the documentation and reload the package:

Inspect the new files in man/ and NAMESPACE. Do not edit either generated file by hand.

One possible first version is:

#' Find each eligible player's most recent rating
#'
#' Keeps the latest rating for every player with at least `min_events`
#' observations in the supplied rating histories.
#'
#' @param ratings A data frame containing `player_id`, `player_name`, `date`,
#'   and `rating`.
#' @param min_events Minimum number of observations a player must have.
#' @return A data frame with one row per eligible player, including their most
#'   recent date and rating and their total number of observations.
#' @examples
#' recent_players(pokemon_elo)
#' recent_players(pokemon_elo, min_events = 10)
#' @export

After devtools::document(), NAMESPACE should contain export(recent_players) and man/recent_players.Rd should exist.

Exercise 8D: Use selective imports (8 minutes)

The first version of the function uses dplyr:: repeatedly. dplyr is already recorded under Imports in DESCRIPTION. Now use roxygen to name only the functions imported into the package namespace.

  1. Add this line to the roxygen block:

    #' @importFrom dplyr filter group_by mutate n slice_max ungroup
  2. Remove the six dplyr:: prefixes from the function body.

  3. Run devtools::document() again.

  4. Inspect the generated NAMESPACE, then run devtools::load_all().

Explain the different jobs performed by:

  • Imports: dplyr in DESCRIPTION;
  • @importFrom dplyr ... in the roxygen block; and
  • importFrom(dplyr, ...) in NAMESPACE.

The finished function can read:

#' @importFrom dplyr filter group_by mutate n slice_max ungroup
#' @export
recent_players <- function(ratings, min_events = 15) {
  ratings |>
    group_by(player_id, player_name) |>
    mutate(events = n()) |>
    slice_max(date, n = 1, with_ties = FALSE) |>
    ungroup() |>
    filter(events >= min_events)
}

DESCRIPTION records that the package depends on dplyr. The roxygen tag is the editable source declaring which names the code uses. document() turns that tag into machine-readable importFrom() entries in NAMESPACE.

Exercise 8E: Test from a user’s perspective (5 minutes)

Run the documented function and identify the current top three eligible players:

Check that:

  • the result has one row per player;
  • every returned player has at least 15 observations; and
  • changing min_events changes the eligibility rule.

Ask a partner to use only ?recent_players to explain the events column and the result of changing min_events. Revise the first unclear sentence.

The top three are Wolfe Glick, Eric Rios and Paul Chua. With the default threshold, the function returns 51 rows. Here events counts rating observations in the supplied data, so the documentation should not imply that it counts every event a player has ever attended.

Exercise 8F: Write the README quick start (7 minutes)

Complete the TODOs in README.Rmd. A first-time reader should learn:

  1. what the package helps them do;
  2. how to install it from GitHub;
  3. how to find the latest eligible ratings; and
  4. why the Elo values require care when interpreted.

Use this copyable installation command:

remotes::install_github("MikeLydeamore/pokemonvgc")

Keep the quick start short and runnable. Then regenerate README.md:

A useful quick start might include:

library(pokemonvgc)

recent_players(pokemon_elo) |>
  dplyr::slice_max(rating, n = 3, with_ties = FALSE)

The surrounding text should say that this returns the three highest latest ratings among players with at least 15 observations. It should also describe the ratings as estimates from an incomplete match archive, not an official ranking.

Exercise 8G: Check and install (8 minutes)

Check the package as a complete product:

Fix every error and warning. Read every note and decide whether it needs action. When the check passes, install the package:

Restart R, then confirm that the installed package works without devtools::load_all():

Completion check

You have finished when:

  • the data-raw script reproduces both packaged datasets;
  • recent_players() has a useful help page and is exported;
  • DESCRIPTION and generated NAMESPACE record the selective imports;
  • README.md contains a copyable installation command and runnable example;
  • devtools::check() reports no errors or warnings; and
  • the installed package works in a clean R session.