Metabolic Risk Features
Source:vignettes/articles/metabolic_risk_features.Rmd
metabolic_risk_features.RmdScope
Create pediatric-friendly binary risk flags (dyslipidemia, insulin resistance, hyperglycemia, hypertension) from routine labs and BP z-scores with configurable NA and extreme handling.
When to use
- You have lipid panel, glucose/HbA1c, BP z-scores, age, and z-scored HOMA-IR and want quick binary risk features.
- You need NA policies, high-missingness warnings, and optional extreme-value screening/capping built in.
- You are working in pediatrics/adolescents where triglyceride thresholds differ by age and BP is already standardized.
Inputs
-
data: data.frame/tibble with required numeric inputs. -
col_map: named list mapping required keys (defaults to same names):chol_total,chol_ldl,chol_hdl,triglycerides,age_year,z_HOMA,glucose,HbA1c,bp_sys_z,bp_dia_z. - Units assumed: lipids mmol/L; glucose mmol/L; HbA1c mmol/mol (IFCC);
BP inputs already z-scores; age in years;
z_HOMAis a z-scored HOMA-IR. -
na_action:keep(default) propagates NA;omitdrops rows missing required inputs;erroraborts;ignoreacts like keep;warnacts like keep plus high-missingness warnings viana_warn_prop(default 0.2).
Preparing derived inputs
Two required inputs are derived, not raw lab values:
z_HOMA (standardised HOMA-IR)
Compute HOMA-IR from fasting glucose (mmol/L) and fasting insulin (µIU/mL), then standardise to a z-score relative to a healthy reference population:
# Step 1: compute HOMA-IR
data$HOMA_IR <- (data$fasting_glucose_mmol * data$fasting_insulin_uIU) / 22.5
# Step 2: standardise within cohort (or use external reference parameters)
data$z_HOMA <- normalize_vec(data$HOMA_IR, method = "z")Published paediatric reference values: Keskin et al. (2005, Pediatr Diabetes); for adults, a 90th-percentile cutpoint (z ≈ 1.28) is commonly applied.
BP z-scores (bp_sys_z / bp_dia_z)
BP z-scores must be derived using sex- and age-specific normative
tables (AAP 2017 Fourth Report or WHO paediatric reference). The
calc_sds() function can apply stratified reference
parameters if you have them; alternatively the childsds
package (CRAN) provides ready-to-use paediatric normative tables.
# Manual z-score: (observed − reference_mean) / reference_sd
data$bp_sys_z <- (data$sbp - ref_sbp_mean) / ref_sbp_sdQuick start
library(HealthMarkers)
library(tibble)
# Six children/adolescents spanning multiple metabolic risk combinations
peds <- tibble::tibble(
chol_total = c(5.4, 4.8, 3.9, 6.2, 5.1, 4.5),
chol_ldl = c(3.6, 2.9, 2.4, 4.1, 3.2, 2.8),
chol_hdl = c(0.9, 1.2, 1.4, 0.8, 1.0, 1.3),
triglycerides = c(1.6, 1.0, 0.8, 2.0, 1.3, 0.9),
age_year = c(15, 9, 12, 17, 11, 8 ),
z_HOMA = c(1.5, 0.8, 0.2, 2.1, 1.0, -0.3),
glucose = c(5.8, 5.1, 4.9, 6.5, 5.4, 4.8),
HbA1c = c(40, 35, 32, 46, 38, 30 ),
bp_sys_z = c(1.2, 1.8, 0.3, 2.1, 0.8, -0.2),
bp_dia_z = c(0.5, 1.7, 0.2, 1.9, 0.6, 0.1)
)
metabolic_risk_features(
data = peds,
col_map = list(
chol_total = "chol_total", chol_ldl = "chol_ldl", chol_hdl = "chol_hdl", triglycerides = "triglycerides",
age_year = "age_year", z_HOMA = "z_HOMA", glucose = "glucose", HbA1c = "HbA1c",
bp_sys_z = "bp_sys_z", bp_dia_z = "bp_dia_z"
),
na_action = "keep"
)
#> # A tibble: 6 × 4
#> dyslipidemia insulin_resistance hyperglycemia hypertension
#> <fct> <fct> <fct> <fct>
#> 1 1 1 1 0
#> 2 0 0 0 1
#> 3 0 0 0 0
#> 4 1 1 1 1
#> 5 0 0 0 0
#> 6 0 0 0 0Extreme values
Extreme inputs will produce extreme or incorrect flags. Pre-filter implausible values before calling.
extreme <- peds
extreme$triglycerides[1] <- 40 # extreme; pre-filter
extreme$bp_sys_z[2] <- 9 # out of expected range; pre-filter
extreme$triglycerides[extreme$triglycerides > 30] <- NA
extreme$bp_sys_z[abs(extreme$bp_sys_z) > 5] <- NA
metabolic_risk_features(
data = extreme,
col_map = list(
chol_total = "chol_total", chol_ldl = "chol_ldl", chol_hdl = "chol_hdl", triglycerides = "triglycerides",
age_year = "age_year", z_HOMA = "z_HOMA", glucose = "glucose", HbA1c = "HbA1c",
bp_sys_z = "bp_sys_z", bp_dia_z = "bp_dia_z"
),
na_action = "warn",
verbose = TRUE
)
#> # A tibble: 6 × 4
#> dyslipidemia insulin_resistance hyperglycemia hypertension
#> <fct> <fct> <fct> <fct>
#> 1 1 1 1 0
#> 2 0 0 0 1
#> 3 0 0 0 0
#> 4 1 1 1 1
#> 5 0 0 0 0
#> 6 0 0 0 0Missing data policy
missing <- peds
missing$glucose[2] <- NA
metabolic_risk_features(
data = missing,
col_map = list(chol_total = "chol_total", chol_ldl = "chol_ldl", chol_hdl = "chol_hdl", triglycerides = "triglycerides",
age_year = "age_year", z_HOMA = "z_HOMA", glucose = "glucose", HbA1c = "HbA1c",
bp_sys_z = "bp_sys_z", bp_dia_z = "bp_dia_z"),
na_action = "omit"
)
#> # A tibble: 5 × 4
#> dyslipidemia insulin_resistance hyperglycemia hypertension
#> <fct> <fct> <fct> <fct>
#> 1 1 1 1 0
#> 2 0 0 0 0
#> 3 1 1 1 1
#> 4 0 0 0 0
#> 5 0 0 0 0
metabolic_risk_features(
data = missing,
col_map = list(chol_total = "chol_total", chol_ldl = "chol_ldl", chol_hdl = "chol_hdl", triglycerides = "triglycerides",
age_year = "age_year", z_HOMA = "z_HOMA", glucose = "glucose", HbA1c = "HbA1c",
bp_sys_z = "bp_sys_z", bp_dia_z = "bp_dia_z"),
na_action = "warn"
)
#> # A tibble: 6 × 4
#> dyslipidemia insulin_resistance hyperglycemia hypertension
#> <fct> <fct> <fct> <fct>
#> 1 1 1 1 0
#> 2 0 0 NA 1
#> 3 0 0 0 0
#> 4 1 1 1 1
#> 5 0 0 0 0
#> 6 0 0 0 0Expanded example: drop incomplete rows
ext_cap <- peds
ext_cap$triglycerides[1] <- 40 # extreme high
ext_cap$bp_sys_z[2] <- 9 # extreme high
ext_cap$HbA1c[2] <- NA # missing HbA1c to trigger drop
out_cap <- metabolic_risk_features(
data = ext_cap,
col_map = list(
chol_total = "chol_total", chol_ldl = "chol_ldl", chol_hdl = "chol_hdl", triglycerides = "triglycerides",
age_year = "age_year", z_HOMA = "z_HOMA", glucose = "glucose", HbA1c = "HbA1c",
bp_sys_z = "bp_sys_z", bp_dia_z = "bp_dia_z"
),
na_action = "omit",
verbose = TRUE
)
list(rows_returned = nrow(out_cap), flags = out_cap)
#> $rows_returned
#> [1] 5
#>
#> $flags
#> # A tibble: 5 × 4
#> dyslipidemia insulin_resistance hyperglycemia hypertension
#> <fct> <fct> <fct> <fct>
#> 1 1 1 1 0
#> 2 0 0 0 0
#> 3 1 1 1 1
#> 4 0 0 0 0
#> 5 0 0 0 0Threshold quick-reference
| Flag | Logic (values in mmol/L unless noted) |
|---|---|
| Dyslipidemia | CT > 5.2 OR LDL > 3.4 OR HDL < 1.0 OR TG > 1.1 (age 0–9) OR TG > 1.5 (age 10–19) |
| Insulin resistance | z_HOMA > 1.28 (≈90th percentile) |
| Hyperglycemia | Glucose 5.6–6.9 OR HbA1c 39–47 mmol/mol |
| Hypertension | BP z-score > 1.64 (systolic or diastolic) |
Handling and outputs
- Missingness:
keep/ignorepropagate NA;omitdrops rows with missing required inputs;warnlogs high-missingness;erroraborts on missing required inputs. - Outputs: tibble with factor columns
dyslipidemia,insulin_resistance,hyperglycemia,hypertension(levels0/1). Age-based TG cutoffs: >1.1 mmol/L (0–9 y) or >1.5 mmol/L (10–19 y) for dyslipidemia; BP z > 1.64 for hypertension; z_HOMA > 1.28 for insulin resistance; glucose 5.6–6.9 or HbA1c 39–47 for hyperglycemia.
Verbose diagnostics
old_opt <- options(healthmarkers.verbose = "inform")
metabolic_risk_features(
data = peds,
col_map = list(
chol_total = "chol_total", chol_ldl = "chol_ldl", chol_hdl = "chol_hdl",
triglycerides = "triglycerides", age_year = "age_year", z_HOMA = "z_HOMA",
glucose = "glucose", HbA1c = "HbA1c", bp_sys_z = "bp_sys_z", bp_dia_z = "bp_dia_z"
),
verbose = TRUE
)
#> metabolic_risk_features(): reading input 'peds' — 6 rows × 10 variables
#> metabolic_risk_features(): col_map (10 columns — 10 specified)
#> chol_total -> 'chol_total'
#> chol_ldl -> 'chol_ldl'
#> chol_hdl -> 'chol_hdl'
#> triglycerides -> 'triglycerides'
#> age_year -> 'age_year'
#> z_HOMA -> 'z_HOMA'
#> glucose -> 'glucose'
#> HbA1c -> 'HbA1c'
#> bp_sys_z -> 'bp_sys_z'
#> bp_dia_z -> 'bp_dia_z'
#> metabolic_risk_features(): computing markers:
#> dyslipidemia [chol_total, chol_ldl, chol_hdl, triglycerides, age_year]
#> insulin_resistance [z_HOMA]
#> hyperglycemia [glucose, HbA1c]
#> hypertension [bp_sys_z, bp_dia_z]
#> metabolic_risk_features(): results: dyslipidemia 6/6, insulin_resistance 6/6, hyperglycemia 6/6, hypertension 6/6
#> # A tibble: 6 × 4
#> dyslipidemia insulin_resistance hyperglycemia hypertension
#> <fct> <fct> <fct> <fct>
#> 1 1 1 1 0
#> 2 0 0 0 1
#> 3 0 0 0 0
#> 4 1 1 1 1
#> 5 0 0 0 0
#> 6 0 0 0 0
options(old_opt)Tips
- Keep lipids/glucose in mmol/L and HbA1c in mmol/mol; BP inputs must already be z-scores.
- Use
na_action = "omit"for analysis-ready flags; keep/warn during QA to inspect missingness. - Watch for HbA1c reported in % (values <=14 suggest percent) and lipids/glucose in mg/dL (would appear ~×38.7 or ×18 higher); standardize units before calling.
- BP inputs must already be z-scores; large magnitude (|z| > 5) usually indicates scaling issues.