diff --git a/.Rbuildignore b/.Rbuildignore index 5ce99d49..1e477ecf 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -12,7 +12,7 @@ ^LICENSE\.md$ ^\d{4}/*$ ^cran-comments\.md$ -^vignettes/ergast-data-analysis.Rmd.orig$ +^vignettes/jolpica-data-analysis.Rmd.orig$ ^vignettes/introduction.Rmd.orig$ ^doc$ ^Meta$ diff --git a/DESCRIPTION b/DESCRIPTION index d5d6363a..1087f685 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Authors@R: c( person("Philip", "Bulsink", , "bulsinkp@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-9668-2429")) ) -Description: Obtain Formula 1 data via the 'Ergast API' and the unofficial API via the 'fastf1' 'Python' library . +Description: Obtain Formula 1 data via the 'Jolpica API' and the unofficial API via the 'fastf1' 'Python' library . Config/reticulate: list( packages = list( diff --git a/NEWS.md b/NEWS.md index 7b3cd593..7eb9bab5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ # f1dataR 1.6.0.9000 +* Deprecated Ergast and moved to Jolpica API for Ergast Functions * Bugfix in plot_fastest() * Bugfix in README diff --git a/R/clear_f1_cache.R b/R/clear_f1_cache.R index 6f498fcd..066e8dee 100644 --- a/R/clear_f1_cache.R +++ b/R/clear_f1_cache.R @@ -1,6 +1,6 @@ #' Clear f1dataR Cache #' -#' @description Clears the cache for f1dataR telemetry and Ergast API results. +#' @description Clears the cache for f1dataR telemetry and Jolpica API results. #' Note that the cache directory can be set by setting `option(f1dataR.cache = [cache dir])`, #' but the default is a temporary directory. #' diff --git a/R/load_circuits.R b/R/load_circuits.R index 18a2a5d5..b7b37584 100644 --- a/R/load_circuits.R +++ b/R/load_circuits.R @@ -15,7 +15,7 @@ load_circuits <- function(season = get_current_season()) { season = season ) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) diff --git a/R/load_constructors.R b/R/load_constructors.R index 530002f7..660473e3 100644 --- a/R/load_constructors.R +++ b/R/load_constructors.R @@ -7,14 +7,36 @@ #' @export #' @return A tibble with one row per constructor load_constructors <- function() { - url <- "constructors.json?limit=300" - data <- get_ergast_content(url) + lim <- 100 + url <- glue::glue("constructors.json?limit={lim}", lim = lim) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) } - return(data$MRData$ConstructorTable$Constructors %>% + total <- data$MRData$total %>% as.numeric() + offset <- data$MRData$offset %>% as.numeric() + + full <- data$MRData$ConstructorTable$Constructors + + # Iterate over the request until completed + while (offset + lim <= total) { + offset <- offset + lim + + url <- glue::glue("constructors.json?limit={lim}&offset={offset}", + lim = lim, offset = offset + ) + data <- get_jolpica_content(url) + + if (is.null(data)) { + return(NULL) + } + + full <- dplyr::bind_rows(full, data$MRData$ConstructorTable$Constructors) + } + + return(full %>% dplyr::select("constructorId", "name", "nationality") %>% janitor::clean_names()) } diff --git a/R/load_drivers.R b/R/load_drivers.R index e5d56963..121c5e5d 100644 --- a/R/load_drivers.R +++ b/R/load_drivers.R @@ -14,10 +14,10 @@ load_drivers <- function(season = get_current_season()) { cli::cli_abort('{.var season} must be between 1950 and {get_current_season()} (or use "current")') } - url <- glue::glue("{season}/drivers.json?limit=40", + url <- glue::glue("{season}/drivers.json?limit=50", season = season ) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) diff --git a/R/load_laps.R b/R/load_laps.R index 4cdddd65..e228c3a1 100644 --- a/R/load_laps.R +++ b/R/load_laps.R @@ -22,31 +22,37 @@ load_laps <- function(season = get_current_season(), round = "last", race = life cli::cli_abort('{.var season} must be between 1996 and {get_current_season()} (or use "current")') } + lim <- 100 + # Function Code - url <- glue::glue("{season}/{round}/laps.json?limit=1000", - season = season, round = round + url <- glue::glue("{season}/{round}/laps.json?limit={lim}", + season = season, round = round, lim = lim ) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) } total <- data$MRData$total %>% as.numeric() - if (total - 1000 > 0 && total - 1000 <= 1000) { - lim <- total - 1000 - url2 <- glue::glue("{season}/{round}/laps.json?limit={lim}&offset=1000", - lim = lim, season = season, round = round + offset <- data$MRData$offset %>% as.numeric() + + full <- data$MRData$RaceTable$Races$Laps[[1]][2] + + # Iterate over the request until completed + while (offset + lim <= total) { + offset <- offset + lim + + url <- glue::glue("{season}/{round}/laps.json?limit={lim}&offset={offset}", + lim = lim, season = season, round = round, offset = offset ) - data2 <- get_ergast_content(url2) + data <- get_jolpica_content(url) - if (is.null(data2)) { + if (is.null(data)) { return(NULL) } - full <- dplyr::bind_rows(data$MRData$RaceTable$Races$Laps[[1]][2], data2$MRData$RaceTable$Races$Laps[[1]][2]) - } else { - full <- data$MRData$RaceTable$Races$Laps[[1]][2] + full <- dplyr::bind_rows(full, data$MRData$RaceTable$Races$Laps[[1]][2]) } laps <- tibble::tibble() diff --git a/R/load_pitstops.R b/R/load_pitstops.R index d280ce3a..d2e7cd8e 100644 --- a/R/load_pitstops.R +++ b/R/load_pitstops.R @@ -24,10 +24,10 @@ load_pitstops <- function(season = get_current_season(), round = "last", race = } # Function Code - url <- glue::glue("{season}/{round}/pitstops.json?limit=80", + url <- glue::glue("{season}/{round}/pitstops.json?limit=100", season = season, round = round ) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) diff --git a/R/load_quali.R b/R/load_quali.R index 68a638c3..45173d67 100644 --- a/R/load_quali.R +++ b/R/load_quali.R @@ -19,7 +19,7 @@ load_quali <- function(season = get_current_season(), round = "last") { season = season, round = round ) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) diff --git a/R/load_results.R b/R/load_results.R index d938e030..795e958e 100644 --- a/R/load_results.R +++ b/R/load_results.R @@ -18,7 +18,7 @@ load_results <- function(season = get_current_season(), round = "last") { url <- glue::glue("{season}/{round}/results.json?limit=40", season = season, round = round ) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) diff --git a/R/load_schedule.R b/R/load_schedule.R index c9d15af6..46e6a6b9 100644 --- a/R/load_schedule.R +++ b/R/load_schedule.R @@ -14,7 +14,7 @@ load_schedule <- function(season = get_current_season()) { url <- glue::glue("{season}.json?limit=30", season = season) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) } diff --git a/R/load_sprint.R b/R/load_sprint.R index 466b22a2..714c84aa 100644 --- a/R/load_sprint.R +++ b/R/load_sprint.R @@ -22,7 +22,7 @@ load_sprint <- function(season = get_current_season(), round = "last") { season = season, round = round ) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) diff --git a/R/load_standings.R b/R/load_standings.R index d7fa1a76..d028979d 100644 --- a/R/load_standings.R +++ b/R/load_standings.R @@ -25,7 +25,7 @@ load_standings <- function(season = get_current_season(), round = "last", type = season = season, round = round, type = type ) - data <- get_ergast_content(url) + data <- get_jolpica_content(url) if (is.null(data)) { return(NULL) diff --git a/R/plot_fastest.R b/R/plot_fastest.R index dff82310..0909d127 100644 --- a/R/plot_fastest.R +++ b/R/plot_fastest.R @@ -62,7 +62,7 @@ plot_fastest <- function(season = get_current_season(), round = 1, session = "R" season_drivers <- load_drivers(season = season) if (is.null(season_drivers)) { - # Ergast is down + # Jolpica is down lap_time <- "" if (get_fastf1_version() < "3.4") { driver_name <- driver diff --git a/R/utils.R b/R/utils.R index 892ea7e3..0c5aa53e 100644 --- a/R/utils.R +++ b/R/utils.R @@ -17,10 +17,10 @@ #' Further processing is performed by specific functions get_ergast_content <- function(url) { # Function Deprecation Warning - lifecycle::deprecate_soft("at the end of 2024", "get_ergast_content()", + lifecycle::deprecate_warn("at the end of 2024", "get_ergast_content()", details = c( "i" = "By the end of 2024 the Ergast Motor Racing Database API will be shut down.", - " " = "This package will update with a replacement when one is available." + " " = "Update f1dataR to use the new jolpica-f1 API data source" ) ) @@ -94,6 +94,92 @@ get_ergast_content <- function(url) { } +#' Get Jolpica Content +#' +#' @description Gets Jolpica-F1 content and returns the processed json object if +#' no errors are found. This will automatically fall back from https:// +#' to http:// if Jolpica suffers errors, and will automatically retry up to 5 +#' times by each protocol +#' +#' Note in 2024 this replaced the deprecated Ergast API. Much of the historical data +#' is duplicated in Jolpica +#' +#' @param url the Jolpica URL tail to get from the API (for example, +#' `"{season}/circuits.json?limit=40"` is called from `load_circuits()`). +#' @keywords internal +#' @return the result of `jsonlite::fromJSON` called on Jolpica's return content. +#' Further processing is performed by specific functions +get_jolpica_content <- function(url) { + # Function Code + + # note: + # Throttles at 200 req/hr requested. + # Caches requests at option = 'f1dataR.cache' location, if not 'current', 'last', or 'latest' result requested + # Automatically retries request up to 5 times. Back-off provided in httr2 documentation + # Automatically retries at http if https fails after retries. + + + jolpica_raw <- httr2::request("https://api.jolpi.ca/ergast/f1/") %>% + httr2::req_url_path_append(url) %>% + httr2::req_retry(max_tries = 10, backoff = function(x) runif(1, 1, 2^x)) %>% + httr2::req_user_agent(glue::glue("f1dataR/{ver}", ver = utils::installed.packages()["f1dataR", "Version"])) %>% + httr2::req_throttle(4 / 1) %>% + httr2::req_error(is_error = ~FALSE) + + jolpica_res <- NULL + + tryCatch( + { + jolpica_res <- jolpica_raw %>% + httr2::req_perform() + }, + error = function(e) { + cli::cli_alert_danger(glue::glue("f1dataR: Error getting data from Jolpica:\n{e}", e = e)) + } + ) + + # Restart retries to Jolpica with http (instead of https) + # No testing penalty for Jolpica functioning correct + # nocov start + if (is.null(jolpica_res) || httr2::resp_is_error(jolpica_res) || httr2::resp_body_string(jolpica_res) == "Unable to select database") { + cli::cli_alert_warning("Failure at Jolpica with https:// connection. Retrying as http://.") + tryCatch( + { + jolpica_res <- jolpica_raw %>% + httr2::req_url("http://api.jolpi.ca/ergast/f1/") %>% + httr2::req_url_path_append(url) %>% + httr2::req_perform() + }, + error = function(e) { + cli::cli_alert_danger(glue::glue("f1dataR: Error getting data from Jolpica:\n{e}", e = e)) + } + ) + } + + if (is.null(jolpica_res)) { + cli::cli_alert_danger("Couldn't connect to Jolpica to retrieve data.") + return(NULL) + } + + if (httr2::resp_is_error(jolpica_res)) { + cli::cli_alert_danger(glue::glue("Error getting Jolpica data, http status code {code}.\n{msg}", + code = httr2::resp_status(jolpica_res), + msg = httr2::resp_status_desc(jolpica_res) + )) + return(NULL) + } + + if (httr2::resp_body_string(jolpica_res) == "Unable to select database") { + cli::cli_alert_danger("Jolpica is having database trouble. Please try again at a later time.") + return(NULL) + } + # nocov end + + # else must be ok + return(jsonlite::fromJSON(httr2::resp_body_string(jolpica_res))) +} + + #' Get Current Season #' #' @description Determines current season by System Date. Note returns the season prior to the current year diff --git a/README.Rmd b/README.Rmd index fd89a467..c0229075 100644 --- a/README.Rmd +++ b/README.Rmd @@ -29,7 +29,7 @@ withr::local_timezone("UTC") # f1dataR -An R package to access Formula 1 Data from the Ergast API and the official F1 data stream via the FastF1 Python library. +An R package to access Formula 1 Data from the Jolpica API (formerly Ergast) and the official F1 data stream via the FastF1 Python library. @@ -60,10 +60,10 @@ library(f1dataR) Data is pulled from: -* [Ergast API](https://ergast.com/mrd/) +* [Jolpica F1 API](https://api.jolpi.ca/ergast/) * [F1 Data Stream](https://www.formula1.com/en/timing/f1-live) via the [Fast F1 python library](https://docs.fastf1.dev/index.html) -Note the Ergast Motor Racing Database API will be shutting down at the end of 2024. When a new data source is identified the package will be migrated to that source. +Note the Ergast Motor Racing Database API will be shutting down at the end of 2024. A new data source (Jolpica-F1 project) was identified and implemented. ## Functions diff --git a/README.md b/README.md index 0bdfc5ec..0ee467dc 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # f1dataR -An R package to access Formula 1 Data from the Ergast API and the -official F1 data stream via the FastF1 Python library. +An R package to access Formula 1 Data from the Jolpica API (formerly +Ergast) and the official F1 data stream via the FastF1 Python library. @@ -34,13 +34,13 @@ library(f1dataR) Data is pulled from: -- [Ergast API](https://ergast.com/mrd/) +- [Jolpica F1 API](https://api.jolpi.ca/ergast/) - [F1 Data Stream](https://www.formula1.com/en/timing/f1-live) via the [Fast F1 python library](https://docs.fastf1.dev/index.html) Note the Ergast Motor Racing Database API will be shutting down at the -end of 2024. When a new data source is identified the package will be -migrated to that source. +end of 2024. A new data source (Jolpica-F1 project) was identified and +implemented. ## Functions @@ -56,20 +56,20 @@ season and last race. Lap data is limited to 1996-present. ``` r load_laps() -#> # A tibble: 841 × 6 +#> # A tibble: 1,177 × 6 #> driver_id position time lap time_sec season #> -#> 1 leclerc 1 1:51.912 1 112. 2024 -#> 2 hamilton 2 1:52.700 1 113. 2024 -#> 3 perez 3 1:53.439 1 113. 2024 -#> 4 piastri 4 1:54.248 1 114. 2024 -#> 5 russell 5 1:54.763 1 115. 2024 -#> 6 sainz 6 1:55.270 1 115. 2024 -#> 7 norris 7 1:55.727 1 116. 2024 -#> 8 alonso 8 1:56.577 1 117. 2024 -#> 9 max_verstappen 9 1:57.183 1 117. 2024 -#> 10 albon 10 1:57.321 1 117. 2024 -#> # ℹ 831 more rows +#> 1 norris 1 1:40.424 1 100. 2024 +#> 2 max_verstappen 2 1:41.413 1 101. 2024 +#> 3 hamilton 3 1:42.702 1 103. 2024 +#> 4 russell 4 1:43.822 1 104. 2024 +#> 5 piastri 5 1:45.268 1 105. 2024 +#> 6 hulkenberg 6 1:45.723 1 106. 2024 +#> 7 alonso 7 1:46.384 1 106. 2024 +#> 8 leclerc 8 1:47.090 1 107. 2024 +#> 9 colapinto 9 1:47.696 1 108. 2024 +#> 10 perez 10 1:48.150 1 108. 2024 +#> # ℹ 1,167 more rows ``` or @@ -122,9 +122,6 @@ load_driver_telemetry(season = 2022, round = 4, driver = "PER") #> # ℹ 11 more variables: drs , source , relative_distance , #> # status , x , y , z , distance , #> # driver_ahead , distance_to_driver_ahead , … -``` - -``` r load_driver_telemetry(season = 2018, round = 7, "Q", "HAM", laps = "fastest") #> # A tibble: 534 × 19 diff --git a/doc/setup_fastf1.Rmd b/doc/setup_fastf1.Rmd index 0f814591..74d1357b 100644 --- a/doc/setup_fastf1.Rmd +++ b/doc/setup_fastf1.Rmd @@ -53,8 +53,7 @@ setup_fastf1() This will create a virtual environment using your system's default Python version, and install `fastf1` in that python environment. It will also tell `reticulate` to use that -environment instead of just running in your main system. For more information on Python -environments read [this article (infoworld.com)](https://www.infoworld.com/article/3239675/virtualenv-and-venv-python-virtual-environments-explained.html). +environment instead of just running in your main system. Many resources exist online to explain python environments in more detail. # Repeat/Recurring Issues diff --git a/doc/setup_fastf1.html b/doc/setup_fastf1.html index 549fb92a..6d9cf2b2 100644 --- a/doc/setup_fastf1.html +++ b/doc/setup_fastf1.html @@ -380,9 +380,8 @@

Accept all defaults

This will create a virtual environment using your system’s default Python version, and install fastf1 in that python environment. It will also tell reticulate to use that -environment instead of just running in your main system. For more -information on Python environments read this -article (infoworld.com).

+environment instead of just running in your main system. Many resources +exist online to explain python environments in more detail.

diff --git a/man/clear_cache.Rd b/man/clear_cache.Rd index 2a1732e4..7f67f2c6 100644 --- a/man/clear_cache.Rd +++ b/man/clear_cache.Rd @@ -13,7 +13,7 @@ clear_cache() No return value, called to erase cached data } \description{ -Clears the cache for f1dataR telemetry and Ergast API results. +Clears the cache for f1dataR telemetry and Jolpica API results. Note that the cache directory can be set by setting \verb{option(f1dataR.cache = [cache dir])}, but the default is a temporary directory. diff --git a/man/get_jolpica_content.Rd b/man/get_jolpica_content.Rd new file mode 100644 index 00000000..e2471a80 --- /dev/null +++ b/man/get_jolpica_content.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{get_jolpica_content} +\alias{get_jolpica_content} +\title{Get Jolpica Content} +\usage{ +get_jolpica_content(url) +} +\arguments{ +\item{url}{the Jolpica URL tail to get from the API (for example, +\code{"{season}/circuits.json?limit=40"} is called from \code{load_circuits()}).} +} +\value{ +the result of \code{jsonlite::fromJSON} called on Jolpica's return content. +Further processing is performed by specific functions +} +\description{ +Gets Jolpica-F1 content and returns the processed json object if +no errors are found. This will automatically fall back from https:// +to http:// if Jolpica suffers errors, and will automatically retry up to 5 +times by each protocol + +Note in 2024 this replaced the deprecated Ergast API. Much of the historical data +is duplicated in Jolpica +} +\keyword{internal} diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index e5c4e8b9..1be1171b 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -32,10 +32,10 @@ skip_if_no_py <- function() { } -# helper function to skip if ergast isn't working -skip_if_no_ergast <- function() { - testthat::skip_if_offline("ergast.com") # This will also skip on CRAN - if (is.null(get_ergast_content("current/circuits.json?limit=40"))) { - testthat::skip("No ergast connection available for testing") +# helper function to skip if jolpica isn't working +skip_if_no_jolpica <- function() { + testthat::skip_if_offline("jolpi.ca") # This will also skip on CRAN + if (is.null(get_jolpica_content("current/circuits.json?limit=40"))) { + testthat::skip("No Jolpica connection available for testing") } } diff --git a/tests/testthat/test-clear_f1_cache.R b/tests/testthat/test-clear_f1_cache.R index c61749aa..14f93b2f 100644 --- a/tests/testthat/test-clear_f1_cache.R +++ b/tests/testthat/test-clear_f1_cache.R @@ -1,5 +1,5 @@ test_that("Cache Clearing works for memoised functions to file", { - skip_if_no_ergast() + skip_if_no_jolpica() # Note: cache clearing for fastf1 is not our responsibility, it's performed # by a call to fastf1 itself. @@ -20,7 +20,7 @@ test_that("Cache Clearing works for memoised functions to file", { test_that("load_ciruits (off cache) works", { - skip_if_no_ergast() + skip_if_no_jolpica() # Set testing specific parameters - this disposes after the test finishes change_cache("off", persist = FALSE) @@ -33,7 +33,7 @@ test_that("load_ciruits (off cache) works", { test_that("load_ciruits (memory cache) works", { - skip_if_no_ergast() + skip_if_no_jolpica() # Set testing specific parameters - this disposes after the test finishes withr::local_options("f1dataR.cache" = NULL) @@ -55,7 +55,7 @@ test_that("load_ciruits (bad path cache) works", { test_that("load_ciruits (filesystem cache) works", { - skip_if_no_ergast() + skip_if_no_jolpica() # Set testing specific parameters - this disposes after the test finishes withr::local_options("f1dataR.cache" = NULL) diff --git a/tests/testthat/test-load_circuits.R b/tests/testthat/test-load_circuits.R index e8c94798..ddec654d 100644 --- a/tests/testthat/test-load_circuits.R +++ b/tests/testthat/test-load_circuits.R @@ -7,7 +7,7 @@ test_that("load_ciruits works", { dir.create(file.path(tempdir(), "tst_load_circuits"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_circuits")) - skip_if_no_ergast() + skip_if_no_jolpica() circuits_2021 <- load_circuits(2021) @@ -37,7 +37,7 @@ test_that("load_circuits works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_circuits(2021), "f1dataR: Error getting data from Ergast") + expect_message(load_circuits(2021), "f1dataR: Error getting data from Jolpica") expect_null(load_circuits(2021)) }) }) diff --git a/tests/testthat/test-load_constructors.R b/tests/testthat/test-load_constructors.R index c2839e8e..e9b07f5a 100644 --- a/tests/testthat/test-load_constructors.R +++ b/tests/testthat/test-load_constructors.R @@ -7,7 +7,7 @@ test_that("load_constructors works", { dir.create(file.path(tempdir(), "tst_load_constructors"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_constructors")) - skip_if_no_ergast() + skip_if_no_jolpica() constructors <- load_constructors() @@ -15,6 +15,7 @@ test_that("load_constructors works", { expect_equal(ncol(constructors), 3) expect_equal(constructors[1, ]$constructor_id, "adams") + expect_true(nrow(unique(constructors)) >= 212) }) test_that("load_constructors works without internet", { @@ -34,7 +35,7 @@ test_that("load_constructors works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_constructors(), "f1dataR: Error getting data from Ergast") + expect_message(load_constructors(), "f1dataR: Error getting data from Jolpica") expect_null(load_constructors()) }) }) diff --git a/tests/testthat/test-load_drivers.R b/tests/testthat/test-load_drivers.R index 093a100c..2a567977 100644 --- a/tests/testthat/test-load_drivers.R +++ b/tests/testthat/test-load_drivers.R @@ -6,7 +6,7 @@ test_that("Drivers Load works", { dir.create(file.path(getwd(), "tst_load_drivers"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(getwd(), "tst_load_drivers")) - skip_if_no_ergast() + skip_if_no_jolpica() drivers_2021 <- load_drivers(2021) @@ -40,7 +40,7 @@ test_that("load_drivers works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_drivers(2021), "f1dataR: Error getting data from Ergast") + expect_message(load_drivers(2021), "f1dataR: Error getting data from Jolpica") expect_null(load_drivers(2021)) }) }) diff --git a/tests/testthat/test-load_laps.R b/tests/testthat/test-load_laps.R index 2f8ea3b0..65d0f60f 100644 --- a/tests/testthat/test-load_laps.R +++ b/tests/testthat/test-load_laps.R @@ -7,16 +7,20 @@ test_that("load_laps works", { dir.create(file.path(tempdir(), "tst_load_laps"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_laps")) - skip_if_no_ergast() + skip_if_no_jolpica() laps_2021_1 <- load_laps(2021, 1) skip_if(is.null(laps_2021_1)) - expect_equal(nrow(laps_2021_1), 1026) + expect_equal(nrow(unique(laps_2021_1)), 1026) expect_equal(laps_2021_1$driver_id[3], "leclerc") expect_equal(laps_2021_1$position[1], "1") + expect_equal(laps_2021_1$driver_id[1026], "raikkonen") + expect_equal(laps_2021_1$position[1026], "11") + expect_equal(laps_2021_1$time_sec[1026], 95.96) + expect_error(load_laps(3050, 3), "`season` must be between 1996 and *") expect_error(load_laps(2021, race = 1)) @@ -46,7 +50,7 @@ test_that("load_laps works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_laps(2021, 1), "f1dataR: Error getting data from Ergast") + expect_message(load_laps(2021, 1), "f1dataR: Error getting data from Jolpica") expect_null(load_laps(2021, 1)) }) }) diff --git a/tests/testthat/test-load_pitstops.R b/tests/testthat/test-load_pitstops.R index 91f14493..314b83c4 100644 --- a/tests/testthat/test-load_pitstops.R +++ b/tests/testthat/test-load_pitstops.R @@ -7,7 +7,7 @@ test_that("load_pitstops works", { dir.create(file.path(tempdir(), "tst_load_pitstops"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_pitstops")) - skip_if_no_ergast() + skip_if_no_jolpica() pitstop_2021_1 <- load_pitstops(2021, 1) @@ -39,7 +39,7 @@ test_that("load_pitstops works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_pitstops(2021, 1), "f1dataR: Error getting data from Ergast") + expect_message(load_pitstops(2021, 1), "f1dataR: Error getting data from Jolpica") expect_null(load_pitstops(2021, 1)) }) }) diff --git a/tests/testthat/test-load_quali.R b/tests/testthat/test-load_quali.R index 68ee5f32..279d5700 100644 --- a/tests/testthat/test-load_quali.R +++ b/tests/testthat/test-load_quali.R @@ -7,7 +7,7 @@ test_that("load_quali works", { dir.create(file.path(tempdir(), "tst_load_quali"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_quali")) - skip_if_no_ergast() + skip_if_no_jolpica() quali_2021_1 <- load_quali(2021, 1) @@ -44,7 +44,7 @@ test_that("load_quali works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_quali(2021, 1), "f1dataR: Error getting data from Ergast") + expect_message(load_quali(2021, 1), "f1dataR: Error getting data from Jolpica") expect_null(load_quali(2021, 1)) }) }) diff --git a/tests/testthat/test-load_results.R b/tests/testthat/test-load_results.R index 21c9d288..7b05c490 100644 --- a/tests/testthat/test-load_results.R +++ b/tests/testthat/test-load_results.R @@ -7,7 +7,7 @@ test_that("load_results works", { dir.create(file.path(tempdir(), "tst_load_results"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_results")) - skip_if_no_ergast() + skip_if_no_jolpica() results_2021_1 <- load_results(2021, 1) @@ -47,7 +47,7 @@ test_that("load_results works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_results(2003, 1), "f1dataR: Error getting data from Ergast") + expect_message(load_results(2003, 1), "f1dataR: Error getting data from Jolpica") expect_null(load_results(2003, 1)) }) }) diff --git a/tests/testthat/test-load_schedule.R b/tests/testthat/test-load_schedule.R index 67bc58fc..87b7adb4 100644 --- a/tests/testthat/test-load_schedule.R +++ b/tests/testthat/test-load_schedule.R @@ -7,7 +7,7 @@ test_that("load_schedule works", { dir.create(file.path(tempdir(), "tst_load_schedule"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_schedule")) - skip_if_no_ergast() + skip_if_no_jolpica() schedule_2021 <- load_schedule(2021) @@ -46,7 +46,7 @@ test_that("load_schedule works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_schedule(2021), "f1dataR: Error getting data from Ergast") + expect_message(load_schedule(2021), "f1dataR: Error getting data from Jolpica") expect_null(load_schedule(2021)) }) }) diff --git a/tests/testthat/test-load_sprint.R b/tests/testthat/test-load_sprint.R index 4b3ba8d0..731902a6 100644 --- a/tests/testthat/test-load_sprint.R +++ b/tests/testthat/test-load_sprint.R @@ -6,7 +6,7 @@ test_that("load_sprint works", { dir.create(file.path(tempdir(), "tst_load_sprint"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_sprint")) - skip_if_no_ergast() + skip_if_no_jolpica() # A sprint exists for season = 2021, round = 10 sprint_2021_10 <- load_sprint(2021, 10) @@ -40,7 +40,7 @@ test_that("load_sprint works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_sprint(2021, 10), "f1dataR: Error getting data from Ergast") + expect_message(load_sprint(2021, 10), "f1dataR: Error getting data from Jolpica") expect_null(load_sprint(2021, 10)) }) }) diff --git a/tests/testthat/test-load_standings.R b/tests/testthat/test-load_standings.R index 42515f86..720af0db 100644 --- a/tests/testthat/test-load_standings.R +++ b/tests/testthat/test-load_standings.R @@ -6,7 +6,7 @@ test_that("load_standings works", { dir.create(file.path(tempdir(), "tst_load_standings"), recursive = TRUE) withr::local_options(f1dataR.cache = file.path(tempdir(), "tst_load_standings")) - skip_if_no_ergast() + skip_if_no_jolpica() standings_2021 <- load_standings(2021) @@ -38,7 +38,7 @@ test_that("load_standings works without internet", { suppressWarnings({ suppressMessages({ httptest2::without_internet({ - expect_message(load_standings(2021), "f1dataR: Error getting data from Ergast") + expect_message(load_standings(2021), "f1dataR: Error getting data from Jolpica") expect_null(load_standings(2021)) }) }) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 2e795e6f..f6725c22 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -11,7 +11,11 @@ test_that("utility functions work", { expect_true(is.numeric(get_current_season())) expect_gte(get_current_season(), 2022) - # get_ergast_content() is inherently tested in load_x functions too + # get_jolpica_content() is inherently tested in load_x functions too + url <- "2022/circuits.json?limit=40" + + expect_warning(get_ergast_content(url), regexp = "was deprecated in f1dataR") + # Test for ergast deprecation # Test add_col_if_absent() testdf <- tibble::tibble("a" = 1:5, "b" = letters[1:5]) diff --git a/vignettes/ergast-data-analysis-driver_laptime_scatterplot-1.png b/vignettes/ergast-data-analysis-driver_laptime_scatterplot-1.png deleted file mode 100644 index 1c54f554..00000000 Binary files a/vignettes/ergast-data-analysis-driver_laptime_scatterplot-1.png and /dev/null differ diff --git a/vignettes/ergast-data-analysis-grid_to_finish_one-1.png b/vignettes/ergast-data-analysis-grid_to_finish_one-1.png deleted file mode 100644 index cb3a98a6..00000000 Binary files a/vignettes/ergast-data-analysis-grid_to_finish_one-1.png and /dev/null differ diff --git a/vignettes/jolpica-data-analysis-driver_laptime_scatterplot-1.png b/vignettes/jolpica-data-analysis-driver_laptime_scatterplot-1.png new file mode 100644 index 00000000..c2325167 Binary files /dev/null and b/vignettes/jolpica-data-analysis-driver_laptime_scatterplot-1.png differ diff --git a/vignettes/ergast-data-analysis-drivers_laptimes-1.png b/vignettes/jolpica-data-analysis-drivers_laptimes-1.png similarity index 100% rename from vignettes/ergast-data-analysis-drivers_laptimes-1.png rename to vignettes/jolpica-data-analysis-drivers_laptimes-1.png diff --git a/vignettes/jolpica-data-analysis-grid_to_finish_one-1.png b/vignettes/jolpica-data-analysis-grid_to_finish_one-1.png new file mode 100644 index 00000000..f568ab28 Binary files /dev/null and b/vignettes/jolpica-data-analysis-grid_to_finish_one-1.png differ diff --git a/vignettes/ergast-data-analysis-grid_to_finish_season-1.png b/vignettes/jolpica-data-analysis-grid_to_finish_season-1.png similarity index 100% rename from vignettes/ergast-data-analysis-grid_to_finish_season-1.png rename to vignettes/jolpica-data-analysis-grid_to_finish_season-1.png diff --git a/vignettes/ergast-data-analysis-quali_compare-1.png b/vignettes/jolpica-data-analysis-quali_compare-1.png similarity index 100% rename from vignettes/ergast-data-analysis-quali_compare-1.png rename to vignettes/jolpica-data-analysis-quali_compare-1.png diff --git a/vignettes/ergast-data-analysis-remade_rounds_points-1.png b/vignettes/jolpica-data-analysis-remade_rounds_points-1.png similarity index 100% rename from vignettes/ergast-data-analysis-remade_rounds_points-1.png rename to vignettes/jolpica-data-analysis-remade_rounds_points-1.png diff --git a/vignettes/ergast-data-analysis-round_position-1.png b/vignettes/jolpica-data-analysis-round_position-1.png similarity index 61% rename from vignettes/ergast-data-analysis-round_position-1.png rename to vignettes/jolpica-data-analysis-round_position-1.png index 6ec5ac32..33875dd4 100644 Binary files a/vignettes/ergast-data-analysis-round_position-1.png and b/vignettes/jolpica-data-analysis-round_position-1.png differ diff --git a/vignettes/ergast-data-analysis-rounds_points-1.png b/vignettes/jolpica-data-analysis-rounds_points-1.png similarity index 100% rename from vignettes/ergast-data-analysis-rounds_points-1.png rename to vignettes/jolpica-data-analysis-rounds_points-1.png diff --git a/vignettes/ergast-data-analysis.Rmd b/vignettes/jolpica-data-analysis.Rmd similarity index 80% rename from vignettes/ergast-data-analysis.Rmd rename to vignettes/jolpica-data-analysis.Rmd index a680a5aa..254f9f0b 100644 --- a/vignettes/ergast-data-analysis.Rmd +++ b/vignettes/jolpica-data-analysis.Rmd @@ -1,8 +1,8 @@ --- -title: "Ergast Data Analysis" +title: "Jolpica Data Analysis" output: rmarkdown::html_vignette vignette: > - %\VignetteIndexEntry{Ergast Data Analysis} + %\VignetteIndexEntry{Jolpica Data Analysis} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -11,7 +11,9 @@ vignette: > # Introduction -This vignette provides a few demonstrations of possible data analysis projects using `f1dataR` and the data pulled from the [Ergast API](https://ergast.com/mrd/). All of the data used comes from Ergast and is not supplied by Formula 1. However, this data source is incredibly useful for accessing host of data. +This vignette provides a few demonstrations of possible data analysis projects using `f1dataR` and the data pulled from the [Jolpica API](https://api.jolpi.ca/). All of the data used comes from Jolpica and is not supplied by Formula 1. However, this data source is incredibly useful for accessing a host of data. + +This package (and vignette) previously used the Ergast Motor Racing Database. Unfortunately, Ergast was shuttered in 2024. Jolpica was developed as a direct replacement. We'll load all the required libraries for our data analysis: @@ -22,11 +24,11 @@ library(dplyr) ``` # Sample Data Analysis -Here are a few simple data analysis examples using Ergast's data. +Here are a few simple data analysis examples using Jolpica's data. - > Note that, when downloading multiple sets of data, we'll put a short `Sys.sleep()` in the loop to reduce load on their servers. Please be a courteous user of their free service and have similar pauses built into your analysis code. Please read their [Terms and Conditions](https://ergast.com/mrd/terms/) for more information. + > Note that, when downloading multiple sets of data, we'll put a short `Sys.sleep()` in the loop to reduce load on their servers. Please be a courteous user of their free service and have similar pauses built into your analysis code. Please read their [Terms and Conditions](https://github.com/jolpica/jolpica-f1/blob/main/docs/rate_limits.md) for more information. -We can make multiple repeat calls to the same function (with the same arguments) as the `f1dataR` package automatically caches responses from Ergast. You'll see this taken advantage of in a few areas. +We can make multiple repeat calls to the same function (with the same arguments) as the `f1dataR` package automatically caches responses from Jolpica. You'll see this taken advantage of in a few areas. If you have example projects you want to share, please feel free to submit them as an issue or pull request to the `f1dataR` [repository on Github](https://github.com/scasanova/f1dataR). @@ -54,7 +56,7 @@ ggplot(results, aes(x = position, y = grid)) + ```
-A plot of grid position (y axis) vs race finishing position (x axis) for the 2020 Austrian Grand Prix +A plot of grid position (y axis) vs race finishing position (x axis) for the 2020 Austrian Grand Prix

A plot of grid position (y axis) vs race finishing position (x axis) for the 2020 Austrian Grand Prix

@@ -87,7 +89,7 @@ ggplot(results, aes(y = position, x = grid)) + ```
-A plot of grid position (y axis) vs race finishing position (x axis) for all 2020 Grands Prix +A plot of grid position (y axis) vs race finishing position (x axis) for all 2020 Grands Prix

A plot of grid position (y axis) vs race finishing position (x axis) for all 2020 Grands Prix

@@ -97,7 +99,7 @@ As expected, this produces a much stronger signal confirming our earlier hypothe ## Driver Points Progress -Ergast contains the points for drivers' or constructors' championship races as of the end of every round in a season. We can pull a season's worth of data and compare the driver pace throughout the season, looking at both position or total points accumulation. We'll do that for 2021, which had good competition throughout the year for P1. +Jolpica contains the points for drivers' or constructors' championship races as of the end of every round in a season. We can pull a season's worth of data and compare the driver pace throughout the season, looking at both position or total points accumulation. We'll do that for 2021, which had good competition throughout the year for P1. ``` r @@ -125,10 +127,13 @@ ggplot(points, aes(x = round, y = position, color = driver_id)) + ylab("Position") + scale_y_reverse(breaks = seq_along(length(unique(points$position)))) + theme_dark_f1(axis_marks = TRUE) +#> Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_line()`). +#> Warning: Removed 2 rows containing missing values or values outside the scale range +#> (`geom_point()`). ```
-Driver ranking after each Grand Prix of the 2021 season +Driver ranking after each Grand Prix of the 2021 season

Driver ranking after each Grand Prix of the 2021 season

@@ -147,13 +152,13 @@ ggplot(points, aes(x = round, y = points, color = driver_id)) + ```
-Total points for each driver after each Grand Prix in the 2021 season +Total points for each driver after each Grand Prix in the 2021 season

Total points for each driver after each Grand Prix in the 2021 season

-Both of these are a bit hard to read and use `driver_id` values that aren't pretty on the plot. We can use some of the FastF1 look-up functions to improve our graphics (recalling that Kubica raced for Alfa Romeo for races 13 & 14 mid-season). We'll first build a data.frame of all drivers and styles for the season, and join that to the points data.frame after we generate driver abbreviations from the Ergast `driver_id`. +Both of these are a bit hard to read and use `driver_id` values that aren't pretty on the plot. We can use some of the FastF1 look-up functions to improve our graphics (recalling that Kubica raced for Alfa Romeo for races 13 & 14 mid-season). We'll first build a data.frame of all drivers and styles for the season, and join that to the points data.frame after we generate driver abbreviations from the Jolpica `driver_id`. ``` r @@ -210,7 +215,7 @@ ggplot(points, aes(x = round, y = points, color = code, shape = code, linetype = ```
-Remade points plot for each driver after each Grand Prix in the 2021 season, with better driver names and colors +Remade points plot for each driver after each Grand Prix in the 2021 season, with better driver names and colors

Remade points plot for each driver after each Grand Prix in the 2021 season, with better driver names and colors

@@ -241,7 +246,7 @@ ggplot(bot, aes(x = lap, y = time_sec)) + ```
-Laptimes for George Russell, for each lap from the 2021 Spanish Grand Prix +Laptimes for George Russell, for each lap from the 2021 Spanish Grand Prix

Laptimes for George Russell, for each lap from the 2021 Spanish Grand Prix

@@ -272,7 +277,7 @@ ggplot(laps, aes(x = code, y = time_sec, color = code, fill = code)) + ```
-Laptime distributions for all drivers from the 2021 Spanish Grand Prix (racing laps only) +Laptime distributions for all drivers from the 2021 Spanish Grand Prix (racing laps only)

Laptime distributions for all drivers from the 2021 Spanish Grand Prix (racing laps only)

@@ -313,6 +318,6 @@ ggplot(quali, aes(x = code, y = t_diff, color = code, fill = code)) + ```
-Gap to Pole at the end of qualifying for the 2021 Styrian Grand Prix +Gap to Pole at the end of qualifying for the 2021 Styrian Grand Prix

Gap to Pole at the end of qualifying for the 2021 Styrian Grand Prix

diff --git a/vignettes/ergast-data-analysis.Rmd.orig b/vignettes/jolpica-data-analysis.Rmd.orig similarity index 90% rename from vignettes/ergast-data-analysis.Rmd.orig rename to vignettes/jolpica-data-analysis.Rmd.orig index 4444860a..f8ced931 100644 --- a/vignettes/ergast-data-analysis.Rmd.orig +++ b/vignettes/jolpica-data-analysis.Rmd.orig @@ -1,8 +1,8 @@ --- -title: "Ergast Data Analysis" +title: "Jolpica Data Analysis" output: rmarkdown::html_vignette vignette: > - %\VignetteIndexEntry{Ergast Data Analysis} + %\VignetteIndexEntry{Jolpica Data Analysis} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -14,7 +14,7 @@ knitr::opts_chunk$set( out.width = "100%", fig.width = 6, fig.height = 4, - fig.path = "ergast-data-analysis-", + fig.path = "jolpica-data-analysis-", dpi = 250, dev = "png" ) @@ -24,7 +24,9 @@ withr::local_timezone("UTC") # Introduction -This vignette provides a few demonstrations of possible data analysis projects using `f1dataR` and the data pulled from the [Ergast API](https://ergast.com/mrd/). All of the data used comes from Ergast and is not supplied by Formula 1. However, this data source is incredibly useful for accessing host of data. +This vignette provides a few demonstrations of possible data analysis projects using `f1dataR` and the data pulled from the [Jolpica API](https://api.jolpi.ca/). All of the data used comes from Jolpica and is not supplied by Formula 1. However, this data source is incredibly useful for accessing a host of data. + +This package (and vignette) previously used the Ergast Motor Racing Database. Unfortunately, Ergast was shuttered in 2024. Jolpica was developed as a direct replacement. We'll load all the required libraries for our data analysis: @@ -34,11 +36,11 @@ library(dplyr) ``` # Sample Data Analysis -Here are a few simple data analysis examples using Ergast's data. +Here are a few simple data analysis examples using Jolpica's data. - > Note that, when downloading multiple sets of data, we'll put a short `Sys.sleep()` in the loop to reduce load on their servers. Please be a courteous user of their free service and have similar pauses built into your analysis code. Please read their [Terms and Conditions](https://ergast.com/mrd/terms/) for more information. + > Note that, when downloading multiple sets of data, we'll put a short `Sys.sleep()` in the loop to reduce load on their servers. Please be a courteous user of their free service and have similar pauses built into your analysis code. Please read their [Terms and Conditions](https://github.com/jolpica/jolpica-f1/blob/main/docs/rate_limits.md) for more information. -We can make multiple repeat calls to the same function (with the same arguments) as the `f1dataR` package automatically caches responses from Ergast. You'll see this taken advantage of in a few areas. +We can make multiple repeat calls to the same function (with the same arguments) as the `f1dataR` package automatically caches responses from Jolpica. You'll see this taken advantage of in a few areas. If you have example projects you want to share, please feel free to submit them as an issue or pull request to the `f1dataR` [repository on Github](https://github.com/scasanova/f1dataR). @@ -99,7 +101,7 @@ Sys.sleep(1) ## Driver Points Progress -Ergast contains the points for drivers' or constructors' championship races as of the end of every round in a season. We can pull a season's worth of data and compare the driver pace throughout the season, looking at both position or total points accumulation. We'll do that for 2021, which had good competition throughout the year for P1. +Jolpica contains the points for drivers' or constructors' championship races as of the end of every round in a season. We can pull a season's worth of data and compare the driver pace throughout the season, looking at both position or total points accumulation. We'll do that for 2021, which had good competition throughout the year for P1. ```{r round_position, eval=require('ggplot2', quietly = TRUE), optipng = '', fig.cap='Driver ranking after each Grand Prix of the 2021 season' } # Load the data @@ -145,7 +147,7 @@ ggplot(points, aes(x = round, y = points, color = driver_id)) + Sys.sleep(1) ``` -Both of these are a bit hard to read and use `driver_id` values that aren't pretty on the plot. We can use some of the FastF1 look-up functions to improve our graphics (recalling that Kubica raced for Alfa Romeo for races 13 & 14 mid-season). We'll first build a data.frame of all drivers and styles for the season, and join that to the points data.frame after we generate driver abbreviations from the Ergast `driver_id`. +Both of these are a bit hard to read and use `driver_id` values that aren't pretty on the plot. We can use some of the FastF1 look-up functions to improve our graphics (recalling that Kubica raced for Alfa Romeo for races 13 & 14 mid-season). We'll first build a data.frame of all drivers and styles for the season, and join that to the points data.frame after we generate driver abbreviations from the Jolpica `driver_id`. ```{r lookups, message = FALSE} driver_style <- rbind( diff --git a/vignettes/precompile.R b/vignettes/precompile.R index 4fae0c32..c3719a90 100644 --- a/vignettes/precompile.R +++ b/vignettes/precompile.R @@ -6,16 +6,17 @@ # Install optipng for your system to enable auto-compression of png files output by knitr. # See https://bookdown.org/yihui/rmarkdown-cookbook/optipng.html -knitr::knit("vignettes/ergast-data-analysis.Rmd.orig", "vignettes/ergast-data-analysis.Rmd") +knitr::knit("vignettes/jolpica-data-analysis.Rmd.orig", "vignettes/jolpica-data-analysis.Rmd") knitr::knit("vignettes/introduction.Rmd.orig", "vignettes/introduction.Rmd") knitr::knit("vignettes/plotting-turn-info.Rmd.orig", "vignettes/plotting-turn-info.Rmd") knitr::knit("vignettes/alonso-penalty-2024.Rmd.orig", "vignettes/alonso-penalty-2024.Rmd") cat("Successfully compiled vignettes. Now moving figures to ./vignettes") -vig_images<-list.files(pattern = "(ergast-data-analysis|introduction|plotting-turn-info|alonso-penalty)-[(a-z)(A-Z)(0-9)_]*-1.png") +vig_images<-list.files(pattern = "(jolpica-data-analysis|introduction|plotting-turn-info|alonso-penalty)-[(a-z)(A-Z)(0-9)_]*-1.png") file.copy(paste0("./", vig_images), paste0("./vignettes/", vig_images), overwrite = T) unlink(vig_images) + # If you have the optipng tool installed on your system run this to reduce png size # xfun::optipng("./vignettes/")