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
94101# ' @export
95102gmd <- 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\u 00fcller, 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\u 00fcller, 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\u 00fcller, 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\n Available versions are: %s\n The 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\n Check 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\n Check 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