dat <- get_player_stats("Lionel Messi")
expect_equal(names(dat), c("player_name", "season", "goals", "assists", "minutes_played"))
expect_equal(class(dat), "data.frame")
# using information you'd have to look up, you could also do:
expect_equal(nrow(dat), 17)
goals_in_2017 <- dat |>
filter(year == 2017) |>
pull(goals)
expect_equal(goals_in_2017, 50)Unit 3 Exam - Practice
A programmer is making a package called baRca to help them analyze information about songs from their favorite soccer team, FC Barcelona.
The first four functions they create in the package are:
roster()which takes a year, and returns a vector of names of all the players on the team in that year.get_player_stats(), which takes the name of a player as input and returns a tibble of statistics for that player (goals, assists, and minutes played) for each season they played for Barcelona.team_results(), which takes a year as input and returns a tibble of all the team’s games in that year and the score.best_player(), which takes a year as input and returns the name of the player with the best points per minute ((goals + assists)/minutes_played) in that year.
1. Workflow
For each package creation step below, describe what it does and why it is necessary.
-
usethis::create_package("baRca"). - Edit the DESCRIPTION file
-
usethis::use_r("roster"). - Edit the `roster.R`` file.
- Click
Code > Insert roxygen skeletonin RStudio. - Edit the
roster.Rfile. -
usethis::use_package("readr"). -
usethis::use_test("roster"). - Edit the
test-roster.Rfile. -
Ctrl/Cmd-Shift-Dordevtools::document()or click theDocumentbutton in RStudio. -
Ctrl/Cmd-Shift-Bordevtools::build()or click theBuildbutton in RStudio. -
Ctrl/Cmd-Shift-Tordevtools::test()or click theTestbutton in RStudio.
- Creates the “baRca” folder, the DESCRIPTION and NAMESPACE files, and the R/folder.
- Adds credit for authorship and descriptions of the package.
- Creates the
roster.Rfile. - The actual function code is written.
- Puts some placeholder roxygen-style comments in
roster.R. - Documents the inputs, outputs, and dependencies.
- Makes sure the
baRcafunction installsreadrtoo. Specifically, this adds a line in the DESCRIPTION that saysbaRcadepends onreadr. - Creates the
testthatfolder setup and unit test filetest-roster.R. - Write some unit tests!
- Uses the roxygen comments to automatically write
roster.mdin theman/folder. - Builds and installs the package.
- Runs all the
testthatunit tests.
2. Unit testing
For the function get_player_stats(), describe:
- Two unit tests you would write using
expect_equal().
Some possibilities include…
- Two unit tests you would write using
expect_error().
This answer should test two different kinds of bad input, such as:
# no input
expect_error(get_player_stats())
# wrong input type
expect_error(get_player_stats(2017))
# wrong input structure
expect_error(get_player_stats(c("Lionel Messi", "Robert Lewandowski")))
# an input that is correct structure, but not correct for the data
expect_error(get_player_stats("Justin Bieber"))3. Data Management
The programmer is designing their team_results() function and has discovered that all the results of FC Barcelona’s games can be found in the pages of espn.com. They are debating how they should use to access these data for their team_results() function.
Describe three different ways the programmer could design their package, their data storage, and/or their team_results() function to load the data from a particular year.
State which option (of the three) you would recommend they use and why.
Write the function to webscrape from espn.com.
Collect the data ahead of time from espn.com, and save it on GitHub or Dropbox. Then, write the function to read the
.csvfrom a URL.Collect the data ahead of time from espn.com, and save it in the
inst/exdatafolder of the package. Then, in the function, usesystem.fileandread_csv()to read the data from the package file.
The third option is probably best here, because it guarantees the data that is needed will always be available in the package.
4. Documentation
The programmer’s source code for the get_player_stats() function is below:
best_player <- function(year) {
all_players <- roster(year)
all_stats <- map(.x = all_players, .f = get_player_stats) |>
bind_rows()
all_stats |>
filter(season == year) |>
mutate(
ppm = (goals + assists)/minutes_played
) |>
slice_max(ppm, with_ties = FALSE) |>
pull(player_name)
}Write a complete roxygen2 style documentation for this function.
#' Find the best player (in points per minute) from a given year
#'
#' @param year A numeric value for the desired year
#'
#' @return A single string with the name of the top player
#'
#' @importFrom dplyr filter mutate slice_max pull bind_rows
#' @importFrom purrr map
#'
#' @examples
#'
#' best_player(2017)
#'
#' @export5. Speed
Assume all functions are working as expected and are properly documented.
The programmer now uses their package to find the best player in each of the last 75 years, by running:
map(.x = 1950:2025, .f = best_player)Unfortunately, the programmer finds this code to be a bit slow.
Describe, in words or by sketching approximate code, three ways you would modify this code or one of the functions it uses to speed it up.
Some possibilities include…
In the code above, change
maptofurrr::future_map().In the
best_player()function, changemaptofurrr::future_map().Store the datasets of player statistics as
.parquetfiles rather than.csv. Then, in theget_player_stats()function, usearrow::read_parquet()instead ofdplyr::read_csv().Replace the data wrangling code in the
best_player()function withdata.tablesyntax:
all_stats <- data.table(all_stats)
all_stats[season == year, ppm := (goals + assists) / minutes_played]
all_stats[which.max(ppm), player_name]6. Package Design
Suggest two more functions for this package. For each, provide:
- The name and a brief description of what the function does.
Example: “
rostergets FC Barcelona roster for a given year.”
- The argument(s) it expects as input and what it returns as output.
Example: “Input is the year as a number, output is a vector of strings containing player names.”
- An example of how and why someone might use the function.
Example: “Someone might use this function to find out which players played the most seasons with the team.”
Answers will vary here, but we are looking for…
Functions that are feasible with the described package.
A reasonable descriptive function name.
A convincing use case to motivate the need for the function.
Clearly defined object types and structures for the input and output.