Scope
Apply the Payne formula to albumin-adjust serum calcium. Works with conventional (mg/dL Ca, g/dL Alb) or SI (mmol/L Ca, g/L Alb); can auto-detect units; supports optional extreme screening.
When to use
- You have total serum calcium and albumin and need albumin-corrected calcium.
- Your labs may be in mg/dL+g/dL or mmol/L+g/L, and you want unit-aware handling.
- You need row-retention rules for missing inputs and optional bounds checks.
Requirements checklist
- Packages: HealthMarkers, dplyr (for display).
- Columns: calcium and albumin; units must be consistent with the
chosen
unitsargument. - Column map: list(calcium = …, albumin = …). Defaults assume Ca/Alb.
- Units: conventional (mg/dL, g/dL), si (mmol/L, g/L), or auto (infers; warns when assuming SI).
- Row policy: na_action = keep (default), omit, error; warn/ignore behave like keep but warn.
- Optional screening: check_extreme + extreme_action (warn/cap/error/ignore/NA); defaults use Ca 4-15 mg/dL and Alb 2-5 g/dL in working units when enabled.
Load packages and example data
Simulated data lack Ca/Alb; we generate illustrative valuesreplace with real labs.
library(HealthMarkers)
library(dplyr)
sim_path <- system.file("extdata", "simulated_hm_data.rds", package = "HealthMarkers")
sim <- readRDS(sim_path)
sim_small <- dplyr::slice_head(sim, n = 30)
set.seed(123)
# Illustrative labs (mg/dL Ca, g/dL Alb)
sim_small$Ca <- pmax(7.5, pmin(11.5, rnorm(nrow(sim_small), 9.2, 0.6)))
sim_small$Alb <- pmax(2.5, pmin(5.0, rnorm(nrow(sim_small), 4.0, 0.4)))Map columns
col_map <- list(calcium = "Ca", albumin = "Alb")Quick start: compute corrected calcium
Defaults keep rows with missing inputs and do not screen extremes.
cc_out <- corrected_calcium(
data = sim_small,
col_map = col_map,
units = "conventional", # use "si" for mmol/L; "auto" to infer
na_action = "keep",
check_extreme = FALSE,
extreme_action = "warn"
)
head(cc_out)
#> # A tibble: 6 × 1
#> corrected_calcium
#> <dbl>
#> 1 8.73
#> 2 9.16
#> 3 9.85
#> 4 8.96
#> 5 9.01
#> 6 10.0Arguments that matter
- col_map: calcium and albumin mappings are required; empty or missing mappings abort.
- units: conventional | si | auto. Auto infers based on medians and warns if assuming SI.
- na_action: keep (rows retained, NA outputs for missing), omit (drop rows with missing inputs), error (abort on missing).
- check_extreme: FALSE by default; TRUE applies bounds in working units (mg/dL, g/dL) before correction.
- extreme_action: warn (default, no change), cap, error, ignore, or NA; override bounds via extreme_rules.
Handling missing and non-numeric inputs
- Non-numeric Ca/Alb are coerced; NA introduced are warned. Non-finite become NA.
- Missing inputs yield NA outputs unless na_action = omit/error.
Compare row policies
demo <- sim_small[1:8, c("Ca", "Alb")]
demo$Ca[3] <- NA
a_keep <- corrected_calcium(demo, col_map, units = "conventional", na_action = "keep")
a_omit <- corrected_calcium(demo, col_map, units = "conventional", na_action = "omit")
list(
keep_rows = nrow(a_keep),
omit_rows = nrow(a_omit),
preview = head(a_keep)
)
#> $keep_rows
#> [1] 8
#>
#> $omit_rows
#> [1] 7
#>
#> $preview
#> # A tibble: 6 × 1
#> corrected_calcium
#> <dbl>
#> 1 8.73
#> 2 9.16
#> 3 NA
#> 4 8.96
#> 5 9.01
#> 6 10.0Extreme-value screening (optional)
Cap or error on implausible inputs before correction.
demo2 <- demo
demo2$Ca[5] <- 2.0 # implausibly low if treated as mg/dL
a_cap <- corrected_calcium(
data = demo2,
col_map = col_map,
units = "conventional",
na_action = "keep",
check_extreme = TRUE,
extreme_action = "cap",
extreme_rules = list(ca_mgdl = c(4, 15), alb_gdl = c(2, 5))
)
head(a_cap)
#> # A tibble: 6 × 1
#> corrected_calcium
#> <dbl>
#> 1 8.73
#> 2 9.16
#> 3 NA
#> 4 8.96
#> 5 3.74
#> 6 10.0Verbose diagnostics
Set verbose = TRUE to emit three structured messages per
call:
- Preparing inputs — start-of-function signal.
-
Column map — confirms which data column each
required key (
calcium,albumin) resolved to. Example:corrected_calcium(): column map: calcium -> 'Ca', albumin -> 'Alb' -
Results summary — shows how many rows computed
successfully (non-NA) per output column. Example:
corrected_calcium(): results: corrected_calcium 28/30, ...
verbose = TRUE emits at the "inform" level;
you also need options(healthmarkers.verbose = "inform")
active. Reset with options(healthmarkers.verbose = NULL) or
options(healthmarkers.verbose = "none").
old_opt <- options(healthmarkers.verbose = "inform")
cc_verbose <- corrected_calcium(
data = sim_small,
col_map = col_map,
units = "conventional",
verbose = TRUE
)
#> corrected_calcium(): preparing inputs
#> corrected_calcium(): column map: calcium -> 'Ca', albumin -> 'Alb'
#> corrected_calcium(): completed 30 rows.
#> corrected_calcium(): results: corrected_calcium 30/30
options(old_opt) # restore original settingOutputs
- corrected_calcium (mg/dL when units = conventional; mmol/L when units = si or auto-inferred SI)
- Rows drop only with na_action = “omit”; na_action = “error” aborts on missing.
Pitfalls and tips
- Prefer explicit units; auto mode may warn and convert if SI is inferred.
- Ensure Ca and Alb units match the chosen mode; mismatched units will mis-scale results.
- Tighten extreme_rules to your lab ranges; use extreme_action = “error” for strict QC.
- Downstream consumers expecting mg/dL should keep units = “conventional” or convert explicitly.
Validation
# Payne formula spot check (conventional): Ca = 9.0, Alb = 3.0 => 9.0 + 0.8*(4-3) = 9.8
v1 <- corrected_calcium(data.frame(Ca = 9.0, Alb = 3.0), col_map, units = "conventional")
stopifnot(abs(v1$corrected_calcium - 9.8) < 1e-10)
# SI round-trip: Ca = 2.25 mmol/L, Alb = 35 g/L
# working: Ca_mg = 2.25*4 = 9.0, Alb_gdl = 35/10 = 3.5
# corrected_mg = 9.0 + 0.8*(4 - 3.5) = 9.4; corrected_mmol = 9.4/4 = 2.35
v2 <- corrected_calcium(data.frame(Ca = 2.25, Alb = 35), col_map, units = "si")
stopifnot(abs(v2$corrected_calcium - 2.35) < 1e-10)
cat("Payne conventional:", v1$corrected_calcium, "mg/dL\n")
#> Payne conventional: 9.8 mg/dL
cat("Payne SI round-trip:", v2$corrected_calcium, "mmol/L\n")
#> Payne SI round-trip: 2.35 mmol/L