Skip to content

Commit 79ff9d0

Browse files
Fix whitespace/precedence inconsistencies; add print/fast parity
- Trim whitespace consistently with Python/Stata; blank tokens dropped and an empty/all-blank value becomes NULL ("no filter") instead of erroring (#4) - Validate an invalid version before honoring cite (#7) - Add print_option (APA citation) for parity with Python/Stata - Add fast (local dataset cache under R_user_dir) for parity with Python/Stata Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent e9e7334 commit 79ff9d0

1 file changed

Lines changed: 73 additions & 10 deletions

File tree

R/gmd.R

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@
3030
#' with \code{variables} to load only specific variables from that source.
3131
#' @param cite A string. \code{"load"} to load the full citation list as a dataframe,
3232
#' or a specific source key (e.g., \code{"GMD"}) to display its BibTeX citation.
33+
#' @param print_option A string, \code{"GMD"} or \code{"Stata"} (case-insensitive),
34+
#' to print the corresponding APA citation and return invisibly. Parity with the
35+
#' Python/Stata \code{print} option.
36+
#' @param fast Logical (or the string \code{"yes"}). If \code{TRUE}, save the
37+
#' downloaded dataset to a local cache so subsequent calls load it from disk
38+
#' instead of re-downloading. Once cached, the file is reused automatically.
39+
#' Parity with the Python/Stata \code{fast} option.
3340
#' @return A dataframe containing the requested macroeconomic data.
3441
#'
3542
#' @examples
@@ -94,12 +101,30 @@
94101
#' @export
95102
gmd <- function(variables = NULL, country = NULL, version = NULL,
96103
raw = FALSE, iso = FALSE, vars = FALSE,
97-
sources = NULL, cite = NULL,
98-
start_year = NULL, end_year = NULL) {
104+
sources = NULL, cite = NULL, print_option = NULL,
105+
fast = FALSE, start_year = NULL, end_year = NULL) {
99106

100107
base_url <- "https://gmd-releases.s3.ap-southeast-2.amazonaws.com/data"
101108
ID_COLS <- c("ISO3", "year", "countryname", "id")
102109

110+
# [print] Print the APA citation and return early \u2014 parity with Python/Stata
111+
# (Python: print_option / Stata: print()). Case-insensitive; invalid -> error.
112+
if (!is.null(print_option)) {
113+
if (length(print_option) != 1 || is.na(print_option)) {
114+
stop("`print_option` must be a single non-NA value ('GMD' or 'Stata').")
115+
}
116+
opt <- tolower(trimws(print_option))
117+
if (opt == "gmd") {
118+
message("M\u00fcller, K., Xu, C., Lehbib, M., & Chen, Z. (2025). The Global Macro Database: A New International Macroeconomic Dataset (NBER Working Paper No. 33714).")
119+
return(invisible(NULL))
120+
}
121+
if (opt == "stata") {
122+
message("Lehbib, M. & M\u00fcller, K. (2025). gmd: The Easy Way to Access the World's Most Comprehensive Macroeconomic Database. Working Paper.")
123+
return(invisible(NULL))
124+
}
125+
stop("Invalid option for print(). valid arguments are 'GMD' or 'Stata'.")
126+
}
127+
103128
message("Global Macro Database by M\u00fcller, Xu, Lehbib, and Chen (2025)")
104129
message("Website: https://www.globalmacrodata.com")
105130
message("")
@@ -110,11 +135,23 @@ gmd <- function(variables = NULL, country = NULL, version = NULL,
110135
(!is.character(version) || length(version) != 1 || is.na(version))) {
111136
stop("`version` must be a single non-NA character string, or NULL.")
112137
}
113-
if (!is.null(country) && length(country) == 0) {
114-
stop("`country` must contain at least one ISO3 code, or be NULL.")
138+
139+
# [#4] Trim surrounding whitespace consistently with Python/Stata. Blank tokens
140+
# are dropped; an empty/all-blank value becomes NULL ("no filter") instead of
141+
# raising an error, matching Python's lenient handling.
142+
if (!is.null(version)) {
143+
version <- trimws(version)
144+
if (version == "") version <- NULL
145+
}
146+
if (!is.null(country)) {
147+
country <- trimws(country)
148+
country <- country[country != ""]
149+
if (length(country) == 0) country <- NULL
115150
}
116-
if (!is.null(variables) && length(variables) == 0) {
117-
stop("`variables` must contain at least one variable name, or be NULL.")
151+
if (!is.null(variables)) {
152+
variables <- trimws(variables)
153+
variables <- variables[variables != ""]
154+
if (length(variables) == 0) variables <- NULL
118155
}
119156

120157
validate_year <- function(value, name) {
@@ -278,6 +315,15 @@ gmd <- function(variables = NULL, country = NULL, version = NULL,
278315
if (length(cite) != 1 || is.na(cite)) {
279316
stop("`cite` must be a single non-NA source key (or 'load').")
280317
}
318+
# [#7] Validate an explicit version before honoring cite, so an invalid
319+
# version errors regardless of cite (matches Python/Stata precedence).
320+
if (!is.null(version) && !tolower(version) %in% c("list", "current")) {
321+
.v_avail <- sort(unique(.gmd_load_versions_df()$versions), decreasing = TRUE)
322+
if (!version %in% .v_avail) {
323+
stop(sprintf("Error: %s is not valid\nAvailable versions are: %s\nThe current version is: %s",
324+
version, paste(sort(.v_avail), collapse = ", "), .v_avail[1]))
325+
}
326+
}
281327
cite_resp <- .gmd_safe_get(paste0(base_url, "/helpers/bib_dataframe.csv"))
282328
if (is.null(cite_resp)) {
283329
stop("Unable to import the list of sources to cite. Check internet connection.")
@@ -472,11 +518,28 @@ gmd <- function(variables = NULL, country = NULL, version = NULL,
472518
# Main dataset
473519
# ============================================================================
474520
require_haven()
475-
main_resp <- .gmd_safe_get(data_url)
476-
if (is.null(main_resp)) {
477-
stop(sprintf("Error: Data file not found at %s\nCheck internet connection.", data_url))
521+
# [fast] Optional local cache of the dataset for faster reloads / offline use
522+
# (parity with Python/Stata `fast`). Once cached, the file is reused automatically.
523+
use_fast <- isTRUE(fast) || (is.character(fast) && tolower(trimws(fast)) == "yes")
524+
cache_dir <- tools::R_user_dir("globalmacrodata", "cache")
525+
cache_file <- file.path(cache_dir, sprintf("GMD_%s.dta", current_version))
526+
if (file.exists(cache_file)) {
527+
df <- haven::read_dta(cache_file)
528+
} else {
529+
main_resp <- .gmd_safe_get(data_url)
530+
if (is.null(main_resp)) {
531+
stop(sprintf("Error: Data file not found at %s\nCheck internet connection.", data_url))
532+
}
533+
raw_bytes <- httr2::resp_body_raw(main_resp)
534+
df <- haven::read_dta(raw_bytes)
535+
if (use_fast) {
536+
if (!dir.exists(cache_dir)) dir.create(cache_dir, recursive = TRUE, showWarnings = FALSE)
537+
# Save the original .dta bytes verbatim so a cached read is byte-identical to a
538+
# fresh download (haven::write_dta would drop dataset-level notes attributes).
539+
writeBin(raw_bytes, cache_file)
540+
message(sprintf("GMD dataset loaded and saved locally in %s.", cache_dir))
541+
}
478542
}
479-
df <- haven::read_dta(httr2::resp_body_raw(main_resp))
480543

481544
if (!is.null(country)) {
482545
country <- validate_country(country, get_country_mapping())

0 commit comments

Comments
 (0)