diff --git a/NEWS.md b/NEWS.md index 7dd927e0..e427928e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -24,6 +24,13 @@ - Adds `read_ihgis_codebook()` to load codebook files containing file-level metadata for downloaded IHGIS extracts. This function is currently experimental. + +- Enables monetary value adjustment for supported IPUMS USA and IPUMS + CPS variables. Use the `adjust_monetary_values` argument to `var_spec()` + to include an additional adjusted variable in your extract. See the + [IPUMS CPS](https://cps.ipums.org/cps/adjusted_monetary_values.shtml) + and [IPUMS USA](https://usa.ipums.org/usa/adjusted_monetary_values.shtml) + documentation for more information on monetary adjustment. ## Function + argument retirements diff --git a/R/api_define_extract.R b/R/api_define_extract.R index f1450be2..060fd834 100644 --- a/R/api_define_extract.R +++ b/R/api_define_extract.R @@ -840,6 +840,8 @@ define_extract_nhgis <- function(description = "", #' @param data_quality_flags Logical indicating whether to include data quality #' flags for the given variable. By default, data quality flags are not #' included. +#' @param adjust_monetary_values Logical indicating whether to include the +#' variable's inflation-adjusted equivalent, if available. #' @param preselected Logical indicating whether the variable is preselected. #' This is not needed for external use. #' @@ -900,6 +902,7 @@ var_spec <- function(name, case_selection_type = NULL, attached_characteristics = NULL, data_quality_flags = NULL, + adjust_monetary_values = NULL, preselected = NULL) { if (!is_null(case_selections) && !is_empty(case_selections)) { case_selection_type <- case_selection_type %||% "general" @@ -911,6 +914,7 @@ var_spec <- function(name, attached_characteristics = attached_characteristics, data_quality_flags = data_quality_flags, case_selection_type = case_selection_type, + adjust_monetary_values = adjust_monetary_values, preselected = preselected, class = "var_spec" ) @@ -2804,6 +2808,7 @@ validate_ipums_spec.var_spec <- function(x, "attached_characteristics", "data_quality_flags", "case_selection_type", + "adjust_monetary_values", "preselected" )] @@ -2850,6 +2855,12 @@ validate_ipums_spec.var_spec <- function(x, must_be_missing_msg = " when `case_selections` is not provided", type = "character", choices = c("general", "detailed") + ), + list( + field = "adjust_monetary_values", + required = FALSE, + length = 1, + type = "logical" ) ) @@ -3422,6 +3433,7 @@ extract_list_from_json.micro_json <- function(extract_json, validate = FALSE) { case_selection_type = names(def$variables[[.x]]$caseSelections), attached_characteristics = unlist(def$variables[[.x]]$attachedCharacteristics), data_quality_flags = def$variables[[.x]]$dataQualityFlags, + adjust_monetary_values = def$variables[[.x]]$adjustMonetaryValues, preselected = def$variables[[.x]]$preselected ) ) diff --git a/R/api_process_extract.R b/R/api_process_extract.R index dad08167..e20a1d93 100644 --- a/R/api_process_extract.R +++ b/R/api_process_extract.R @@ -666,6 +666,7 @@ format_for_json.var_spec <- function(x) { dataQualityFlags = x$data_quality_flags, caseSelections = case_selections, attachedCharacteristics = as.list(x$attached_characteristics), + adjustMonetaryValues = x$adjust_monetary_values, preselected = x$preselected ) ) diff --git a/README.Rmd b/README.Rmd index c950f7df..94b03be6 100644 --- a/README.Rmd +++ b/README.Rmd @@ -20,7 +20,7 @@ knitr::opts_chunk$set( Status:Active](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/ipumsr)](https://CRAN.R-project.org/package=ipumsr) [![R build -status](https://github.com/ipums/ipumsr/workflows/R-CMD-check/badge.svg)](https://github.com/ipums/ipumsr/actions) +status](https://github.com/ipums/ipumsr/workflows/R-CMD-check/badge.svg)](https://github.com/ipums/ipumsr/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/ipums/ipumsr/branch/main/graph/badge.svg)](https://app.codecov.io/gh/ipums/ipumsr?branch=main) diff --git a/README.md b/README.md index 4190c7a8..38d999ae 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Status:Active](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/ipumsr)](https://CRAN.R-project.org/package=ipumsr) [![R build -status](https://github.com/ipums/ipumsr/workflows/R-CMD-check/badge.svg)](https://github.com/ipums/ipumsr/actions) +status](https://github.com/ipums/ipumsr/workflows/R-CMD-check/badge.svg)](https://github.com/ipums/ipumsr/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/ipums/ipumsr/branch/main/graph/badge.svg)](https://app.codecov.io/gh/ipums/ipumsr?branch=main) diff --git a/man/var_spec.Rd b/man/var_spec.Rd index ab8170d2..529a4409 100644 --- a/man/var_spec.Rd +++ b/man/var_spec.Rd @@ -13,6 +13,7 @@ var_spec( case_selection_type = NULL, attached_characteristics = NULL, data_quality_flags = NULL, + adjust_monetary_values = NULL, preselected = NULL ) @@ -55,6 +56,9 @@ names of the form "AGE_MOM" and "AGE_MOM2".} flags for the given variable. By default, data quality flags are not included.} +\item{adjust_monetary_values}{Logical indicating whether to include the +variable's inflation-adjusted equivalent, if available.} + \item{preselected}{Logical indicating whether the variable is preselected. This is not needed for external use.} diff --git a/tests/fixtures/amv-errors.yml b/tests/fixtures/amv-errors.yml new file mode 100644 index 00000000..ecf4c079 --- /dev/null +++ b/tests/fixtures/amv-errors.yml @@ -0,0 +1,82 @@ +http_interactions: +- request: + method: post + uri: https://api.ipums.org/extracts?collection=atus&version=2 + body: + encoding: '' + string: '{"description":"","dataStructure":{"rectangular":{"on":"P"}},"dataFormat":"fixed_width","sampleMembers":{"includeNonRespondents":false,"includeHouseholdMembers":false},"samples":{"at2004":{}},"variables":{"AGE":{"adjustMonetaryValues":true}},"caseSelectWho":"individuals","collection":"atus","version":2}' + headers: + Accept: application/json, text/xml, application/xml, */* + Authorization: <<>> + Content-Type: application/json + response: + status: + status_code: 400 + category: Client error + reason: Bad Request + message: 'Client error: (400) Bad Request' + headers: + cache-control: no-cache + content-type: application/json; charset=utf-8 + date: Fri, 30 May 2025 12:35:20 GMT + referrer-policy: strict-origin-when-cross-origin + server: nginx/1.22.1 + vary: Origin + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-permitted-cross-domain-policies: none + x-ratelimit-limit: '-1' + x-ratelimit-remaining: '0' + x-ratelimit-reset: '0' + x-request-id: 7ace3d95-de09-4080-9921-84e546bab908 + x-runtime: '0.486823' + x-xss-protection: '0' + content-length: '148' + body: + encoding: '' + file: no + string: '{"type":"SemanticValidationError","status":{"code":400,"name":"Bad + Request"},"detail":["Monetary value adjustment is not supported for IPUMS + ATUS"]}' + recorded_at: 2025-05-30 12:35:21 GMT + recorded_with: vcr/1.7.0, webmockr/2.0.0 +- request: + method: post + uri: https://api.ipums.org/extracts?collection=cps&version=2 + body: + encoding: '' + string: '{"description":"","dataStructure":{"rectangular":{"on":"P"}},"dataFormat":"fixed_width","sampleMembers":{"includeNonRespondents":false,"includeHouseholdMembers":false},"samples":{"cps2012_03s":{}},"variables":{"AGE":{"adjustMonetaryValues":true}},"caseSelectWho":"individuals","collection":"cps","version":2}' + headers: + Accept: application/json, text/xml, application/xml, */* + Authorization: <<>> + Content-Type: application/json + response: + status: + status_code: 400 + category: Client error + reason: Bad Request + message: 'Client error: (400) Bad Request' + headers: + cache-control: no-cache + content-type: application/json; charset=utf-8 + date: Fri, 30 May 2025 12:35:20 GMT + referrer-policy: strict-origin-when-cross-origin + server: nginx/1.22.1 + vary: Origin + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-permitted-cross-domain-policies: none + x-ratelimit-limit: '-1' + x-ratelimit-remaining: '0' + x-ratelimit-reset: '0' + x-request-id: c0ed18c1-416c-4370-8d7b-a47a085e1ff0 + x-runtime: '0.411693' + x-xss-protection: '0' + content-length: '142' + body: + encoding: '' + file: no + string: '{"type":"SemanticValidationError","status":{"code":400,"name":"Bad + Request"},"detail":["Monetary value adjustment is not supported for AGE."]}' + recorded_at: 2025-05-30 12:35:21 GMT + recorded_with: vcr/1.7.0, webmockr/2.0.0 diff --git a/tests/fixtures/amv-extract.yml b/tests/fixtures/amv-extract.yml new file mode 100644 index 00000000..b65c6b69 --- /dev/null +++ b/tests/fixtures/amv-extract.yml @@ -0,0 +1,42 @@ +http_interactions: +- request: + method: post + uri: https://api.ipums.org/extracts?collection=cps&version=2 + body: + encoding: '' + string: '{"description":"amv testing","dataStructure":{"rectangular":{"on":"P"}},"dataFormat":"fixed_width","sampleMembers":{"includeNonRespondents":false,"includeHouseholdMembers":false},"samples":{"cps2022_03s":{}},"variables":{"AGE":{},"HOURWAGE":{"adjustMonetaryValues":true}},"caseSelectWho":"individuals","collection":"cps","version":2}' + headers: + Accept: application/json, text/xml, application/xml, */* + Authorization: <<>> + Content-Type: application/json + response: + status: + status_code: 200 + category: Success + reason: OK + message: 'Success: (200) OK' + headers: + cache-control: max-age=0, private, must-revalidate + content-type: application/json; charset=utf-8 + date: Fri, 30 May 2025 12:35:24 GMT + etag: W/"b8fc19e11776e4dabb0afb3c7b826f79" + referrer-policy: strict-origin-when-cross-origin + server: nginx/1.22.1 + vary: Origin + x-content-type-options: nosniff + x-frame-options: SAMEORIGIN + x-permitted-cross-domain-policies: none + x-ratelimit-limit: '-1' + x-ratelimit-remaining: '0' + x-ratelimit-reset: '0' + x-request-id: 58448277-611e-4058-af9a-439944340bad + x-runtime: '1.447855' + x-xss-protection: '0' + content-length: '651' + body: + encoding: '' + file: no + string: '{"number":179,"status":"queued","email":"robe2037@umn.edu","downloadLinks":{},"extractDefinition":{"version":2,"dataStructure":{"rectangular":{"on":"P"}},"dataFormat":"fixed_width","caseSelectWho":"individuals","description":"amv + testing","samples":{"cps2022_03s":{}},"variables":{"YEAR":{"preselected":true},"SERIAL":{"preselected":true},"MONTH":{"preselected":true},"CPSID":{"preselected":true},"ASECFLAG":{"preselected":true},"ASECWTH":{"preselected":true},"PERNUM":{"preselected":true},"CPSIDP":{"preselected":true},"CPSIDV":{"preselected":true},"ASECWT":{"preselected":true},"AGE":{},"HOURWAGE":{"adjustMonetaryValues":true}},"collection":"cps"}}' + recorded_at: 2025-05-30 12:35:24 GMT + recorded_with: vcr/1.7.0, webmockr/2.0.0 diff --git a/tests/testthat/test_api_process_extract.R b/tests/testthat/test_api_process_extract.R index a203dcdd..9d8a53e2 100644 --- a/tests/testthat/test_api_process_extract.R +++ b/tests/testthat/test_api_process_extract.R @@ -213,6 +213,54 @@ test_that("Can submit an ATUS extract", { ) }) +test_that("Can submit extract with monetary value adjustment", { + x <- define_extract_micro( + "cps", + "amv testing", + samples = "cps2022_03s", + variables = list("AGE", var_spec("HOURWAGE", adjust_monetary_values = TRUE)) + ) + + expect_true(x$variables$HOURWAGE$adjust_monetary_values) + expect_null(x$variables$AGE$adjust_monetary_values) + + vcr::use_cassette("amv-extract", { + suppressMessages( + submitted <- submit_extract(x) + ) + }) + + expect_true(submitted$variables$HOURWAGE$adjust_monetary_values) + expect_null(submitted$variables$AGE$adjust_monetary_values) +}) + +test_that("Error on unsupported monetary value adjustment requests", { + vcr::use_cassette("amv-errors", { + expect_error( + submit_extract( + define_extract_micro( + "atus", + "", + samples = "at2004", + variables = var_spec("AGE", adjust_monetary_values = TRUE) + ) + ), + "Monetary value adjustment is not supported for IPUMS ATUS" + ) + expect_error( + submit_extract( + define_extract_micro( + "cps", + "", + samples = "cps2012_03s", + variables = var_spec("AGE", adjust_monetary_values = TRUE) + ) + ), + "Monetary value adjustment is not supported for AGE" + ) + }) +}) + test_that("Submission of time-use variable with wrong owner throws error", { skip_if_no_api_access() diff --git a/vignettes/ipums-api-micro.Rmd b/vignettes/ipums-api-micro.Rmd index 01b20ff1..a5fac822 100644 --- a/vignettes/ipums-api-micro.Rmd +++ b/vignettes/ipums-api-micro.Rmd @@ -413,6 +413,33 @@ Each data quality flag corresponds to one or more variables, and the codes for each flag vary based on the sample. See the documentation for the IPUMS collection of interest for more information about data quality flag codes. +### Monetary value adjustment + +IPUMS CPS and IPUMS USA offer the option to standardize income or other +dollar values to 2010 dollars. This option both retains the original IPUMS +variable in your extract and adds a new, adjusted variable called +`{VARIABLE NAME}_cpiu_2010` that contains the inflation-adjusted values. +Inflation adjusted values are only available for continuous variables that +represent dollar amounts. + +To request an adjusted variable in an extract, use the `adjust_monetary_values` +argument: + +```{r} +#| eval: false +define_extract_micro( + "cps", + description = "monetary value adjustment example", + samples = "cps2012_03s", + variables = var_spec("HOURWAGE", adjust_monetary_values = TRUE) +) +``` + +Further information on monetary value adjustment can be found on +the [IPUMS CPS](https://cps.ipums.org/cps/adjusted_monetary_values.shtml) +and [IPUMS USA](https://usa.ipums.org/usa/adjusted_monetary_values.shtml) +websites. + ## Time use variables For IPUMS Time Use collections (ATUS, AHTUS, and MTUS), users can request