From ec71f000d7aeda68a4bf87c917b79b8b6718fbc4 Mon Sep 17 00:00:00 2001 From: Finn Roberts Date: Fri, 30 May 2025 08:58:14 -0400 Subject: [PATCH 1/4] Add monetary value adjustment support --- R/api_define_extract.R | 13 ++++ R/api_process_extract.R | 1 + man/var_spec.Rd | 5 ++ tests/fixtures/amv-errors.yml | 82 +++++++++++++++++++++++ tests/fixtures/amv-extract.yml | 42 ++++++++++++ tests/testthat/test_api_process_extract.R | 48 +++++++++++++ vignettes/ipums-api-micro.Rmd | 27 ++++++++ 7 files changed, 218 insertions(+) create mode 100644 tests/fixtures/amv-errors.yml create mode 100644 tests/fixtures/amv-extract.yml diff --git a/R/api_define_extract.R b/R/api_define_extract.R index f1450be2..2fef7ce4 100644 --- a/R/api_define_extract.R +++ b/R/api_define_extract.R @@ -840,6 +840,9 @@ 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. This is only available for +#' variables that represent dollar amounts. #' @param preselected Logical indicating whether the variable is preselected. #' This is not needed for external use. #' @@ -900,6 +903,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 +915,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 +2809,7 @@ validate_ipums_spec.var_spec <- function(x, "attached_characteristics", "data_quality_flags", "case_selection_type", + "adjust_monetary_values", "preselected" )] @@ -2850,6 +2856,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 +3434,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/man/var_spec.Rd b/man/var_spec.Rd index ab8170d2..cb5aee73 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,10 @@ 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. This is only available for +variables that represent dollar amounts.} + \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 From 48ce9c1f15fade64ae056e671f2c9f10f8ebe67a Mon Sep 17 00:00:00 2001 From: Finn Date: Fri, 30 May 2025 14:26:53 -0400 Subject: [PATCH 2/4] update README badge --- README.Rmd | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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) From 7126794b42b5086927d38f94be047f9d490265b1 Mon Sep 17 00:00:00 2001 From: Finn Date: Fri, 30 May 2025 14:30:40 -0400 Subject: [PATCH 3/4] Update NEWS.md --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 7dd927e0..b83e0d1d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -24,6 +24,10 @@ - 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. ## Function + argument retirements From b8f8c423036551cd9dc6a4013b8ccc04f8f73b36 Mon Sep 17 00:00:00 2001 From: Finn Date: Tue, 3 Jun 2025 08:32:20 -0400 Subject: [PATCH 4/4] Revise amv argument wording --- NEWS.md | 5 ++++- R/api_define_extract.R | 3 +-- man/var_spec.Rd | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/NEWS.md b/NEWS.md index b83e0d1d..e427928e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -27,7 +27,10 @@ - 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. + 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 2fef7ce4..060fd834 100644 --- a/R/api_define_extract.R +++ b/R/api_define_extract.R @@ -841,8 +841,7 @@ define_extract_nhgis <- function(description = "", #' 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. This is only available for -#' variables that represent dollar amounts. +#' variable's inflation-adjusted equivalent, if available. #' @param preselected Logical indicating whether the variable is preselected. #' This is not needed for external use. #' diff --git a/man/var_spec.Rd b/man/var_spec.Rd index cb5aee73..529a4409 100644 --- a/man/var_spec.Rd +++ b/man/var_spec.Rd @@ -57,8 +57,7 @@ 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. This is only available for -variables that represent dollar amounts.} +variable's inflation-adjusted equivalent, if available.} \item{preselected}{Logical indicating whether the variable is preselected. This is not needed for external use.}