Scope
vitamin_d_status() classifies serum 25(OH)D (assumed
ng/mL) into Deficient (<20), Insufficient (20-29), or Sufficient
(>=30). It validates mappings, enforces your missing-data policy, and
can scan/cap implausible values.
When to use this
- Standardize vitamin D categorization for dashboards, cohorts, or QC summaries.
- Guard ETL steps: abort or omit rows with missing/implausible 25(OH)D before downstream models.
- Spot potential unit mix-ups: nmol/L inputs often appear ~2.5x higher than ng/mL.
Required inputs and mapping
-
data: data frame/tibble containing a 25(OH)D column. -
col_map: named list mapping eithervitdorvitamin_dto the column name. The column must exist and be coercible to numeric; coercion that introduces NAs emits a warning.
Options that change behavior
-
na_action = c("keep","omit","error","ignore","warn")(default “keep”): policy for missing 25(OH)D.warn/ignorebehave like keep;omitdrops those rows;erroraborts. -
verbose = FALSE: emit progress messages.
How missingness is handled
- Missing 25(OH)D follows
na_action;omitshrinks the output;erroraborts. - Non-numeric inputs are coerced to numeric; any NAs introduced trigger a warning.
- Negative values and very high medians (suggesting nmol/L) warn when values appear implausible.
Outputs
- Tibble with ordered factor
vitamin_d_status(Deficient < Insufficient < Sufficient). - Rows drop only when
na_action = "omit"; otherwise the output aligns with input row count.
Examples
Quick categorization
library(HealthMarkers)
library(tibble)
df <- tibble::tibble(VitD = c(12, 25, 36, NA))
vitamin_d_status(
data = df,
col_map = list(vitd = "VitD"),
na_action = "keep"
)
#> # A tibble: 4 × 1
#> vitamin_d_status
#> <ord>
#> 1 Deficient
#> 2 Insufficient
#> 3 Sufficient
#> 4 NAPre-filter and classify
df2 <- tibble::tibble(d = c(15, 280, 8, 65, NA)) # 280 is implausible; pre-filter
# Pre-filter before calling
df2$d[df2$d > 250] <- NA
vitamin_d_status(
data = df2,
col_map = list(vitd = "d"),
na_action = "warn",
verbose = TRUE
)
#> # A tibble: 5 × 1
#> vitamin_d_status
#> <ord>
#> 1 Deficient
#> 2 NA
#> 3 Deficient
#> 4 Sufficient
#> 5 NAVerbose diagnostics
old_opt <- options(healthmarkers.verbose = "inform")
vitamin_d_status(
data = tibble::tibble(VitD = c(15, 32)),
col_map = list(vitamin_d = "VitD"),
verbose = TRUE
)
#> vitamin_d_status(): reading input 'data' — 2 rows × 1 variables
#> vitamin_d_status(): col_map (2 columns — 1 specified, 1 inferred from data)
#> vitamin_d -> 'VitD'
#> vitd -> 'VitD' (inferred)
#> vitamin_d_status(): computing markers:
#> vitamin_d_status [VitD]
#> vitamin_d_status(): results: vitamin_d_status 2/2
#> # A tibble: 2 × 1
#> vitamin_d_status
#> <ord>
#> 1 Deficient
#> 2 Sufficient
options(old_opt)Troubleshooting and tips
- If medians are ~150-250 without obvious outliers, verify units (nmol/L should be divided by 2.5 to get ng/mL).
- Set
na_action = "omit"when downstream steps cannot handle NA statuses. - Keep
col_mapnamed; missing or empty mappings abort with classed errors you can assert in tests.