--- title: "OECDdata: discovery, careful selection, and reproducible analysis" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{OECDdata: discovery, careful selection, and reproducible analysis} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") library(OECDdata) library(data.table) library(magrittr) old <- oecdOptions(quiet = TRUE, open_browser = FALSE, return_data_table = TRUE, strings_as_factors = FALSE) ``` ## A reliable workflow Discover a dataflow, inspect its dimensions and dictionaries, restrict the request, inspect status and units, and only then calculate statistics. The `eurodata` package inspires the workflow, but names, classes and identities are OECD-native. The default route is SDMX REST v2. See the package README for current protocol references and a detailed interface and migration guide. All evaluated chunks in this vignette are offline. The following object contains invented teaching observations, not actual OECD statistics. ```{r example} x <- oecdExample() x[, .(REF_AREA, TIME_PERIOD, value_, flags_)] ``` ## Observations, dimensions and attributes ```{r structure} attr(x, "Dimensions") attr(x, "OECDAttributes") str(x) ``` `value_` is numeric. Dimension codes remain strings or, with the default options, factors. Numeric-looking identifiers retain leading zeros. `flags_` preserves `OBS_STATUS` without translating it into another provider's status vocabulary. The original attribute is retained too. ```{r audit} series_keys <- setdiff(attr(x, "Dimensions"), "TIME_PERIOD") x[, .(observations = .N, missing = sum(is.na(value_))), by = series_keys] x[, .N, by = OBS_STATUS] ``` An incomplete series is not automatically filled with zeros. An attribute such as `UNIT_MULT` is not automatically applied: verify whether the reported value and the unit metadata require scaling in your analysis. ## Safe reshaping and plotting This synthetic example contains one frequency, measure and group per country. Check that simplification before dropping those columns. ```{r pivot} stopifnot(x[, uniqueN(FREQ)] == 1L, x[, uniqueN(MEASURE)] == 1L, x[, uniqueN(GROUP)] == 1L, !anyDuplicated(x, by = c("TIME_PERIOD", "REF_AREA"))) dcast(x, TIME_PERIOD ~ REF_AREA, value.var = "value_") ``` ```{r plot, fig.width=6.5, fig.height=3.5} one <- x[REF_AREA == "USA"][order(TIME_PERIOD)] plot(as.Date(paste0(one$TIME_PERIOD, "-01")), one$value_, type = "l", xlab = "Month", ylab = "Synthetic index", main = "Invented example") ``` ## An adjacent-period change Sorting is necessary but not sufficient. Check that observations are exactly one month apart, so a gap is not misinterpreted as one month's growth. ```{r growth} copy(x) %>% .[, month_id := as.integer(substr(TIME_PERIOD, 1, 4)) * 12L + as.integer(substr(TIME_PERIOD, 6, 7))] %>% .[order(month_id), `:=`(previous = shift(value_), previous_month = shift(month_id)), by = series_keys] %>% .[, growth_pct := fifelse(!is.na(previous) & previous != 0 & month_id - previous_month == 1L, 100 * (value_ / previous - 1), NA_real_)] %>% .[, .(REF_AREA, TIME_PERIOD, growth_pct)] ``` ## Discovery and metadata (online, not evaluated) ```{r discovery, eval=FALSE} OECDdata::find("health expenditure") OECDdata::find(education, expenditure) browseDataList(grepl("employment", `Dataset name`, ignore.case = TRUE)) code <- "CLI" s <- oecdStructure(code) s$flow s$dimensions s$labels$REF_AREA importLabels("REF_AREA", dataset = code) describe(code, wide = TRUE) ``` A codelist describes permissible values. An Actual content constraint can establish reported marginal availability, but not that every combination of values is observed. `describe()` and `importMetabase()` fail explicitly when that coverage cannot be established. `availability = "allowed"` is an explicit alternative for dictionary-based inventories, not a substitute presented as observed coverage. ## Short codes and dataset identity (online, not evaluated) A short code is matched against the current catalogue, not a hard-coded alias list. Ambiguity is an error. Agency and dataflow-prefix selectors can resolve it without making the dataset code harder to read. Resolve once for reuse: ```{r resolve, eval=FALSE} ref <- oecdDataset("CLI", agency = "OECD.SDD.STES", prefix = "DSD_STES") ref importLabels(OECDDimCode = "REF_AREA", dataset = ref) # A query preview fetches metadata but does not download observations. oecdQuery("CLI", filters = list( .dataset = list(agency = "OECD.SDD.STES", prefix = "DSD_STES"), REF_AREA = "USA", FREQ = "M", TIME_PERIOD = c("2023-01", "2023-12") )) ``` The reserved `.dataset` block selects agency, dataflow namespace and/or version; it is not a set of series dimensions. `prefix` matches the dataflow ID prefix before `@`. The actual referenced DSD is read from the server metadata. All other filter entries retain their ordinary dimension semantics. A selector-only block leaves all observations unrestricted. A qualified reference avoids the catalogue lookup, and explicit selector/reference conflicts are errors. ## A small download (online, not evaluated) ```{r live, eval=FALSE} cli <- importData(code, list(REF_AREA = c("USA", "FRA"), FREQ = "M", TIME_PERIOD = c("2023-01", "2023-12"))) attr(cli, "SourceURL") attr(cli, "OECDDatasetCode") ``` The two time bounds define an inclusive range. An unspecified dimension is a wildcard, so the result may include multiple measures or transformations. Never collapse them into a single line or aggregate solely by country and date without checking the other key dimensions. ## Caching and provenance Requests are cached in a session temporary directory by default. Persistent storage is opt-in. Offline mode accepts expired but verified cache entries; missing or corrupt entries cause explicit errors. Cached data retain their original download time. A structure version is not a statistical vintage. ```{r provenance} attributes(x)[c("RequestedDatasetCode", "OECDDatasetCode", "DownloadTime", "SourceURL", "Dimensions")] ``` ```{r persistent, eval=FALSE} oecdOptions(cache_dir = tools::R_user_dir("OECDdata", "cache")) # First make a successful online request with the desired metadata and filters. # Then use offline = TRUE with the same request. saveRDS(list(data = cli, provenance = attributes(cli)), "cli-snapshot.rds") ``` HTTP failures remain structured errors. HTTP 204 is an explicit empty result with a warning. The client never converts an arbitrary 404 or malformed response into an apparently successful empty dataset. ## Restore session settings ```{r restore, include=FALSE} options(old) ``` Use `old <- oecdOptions(...)` with `on.exit(options(old))` inside your own functions to keep option changes local. The package itself does not change options at load time.