Scope
Compute atherogenic risk ratios from a lipid panel: - AIP = log10(TG / HDL_c) - Castelli Risk Index I (CRI_I) = TC / HDL_c - Castelli Risk Index II (CRI_II) = LDL_c / HDL_c
All ratios are unitless; use consistent lipid units (mg/dL or mmol/L).
When to use
- You have triglycerides and HDL cholesterol and want AIP.
- You also have total and/or LDL cholesterol and want Castelli indices.
- You need strict missing-data handling.
Requirements checklist
- Packages: HealthMarkers, dplyr (for display).
- Data frame with lipid columns; required keys: TG and HDL_c. Optional: TC and LDL_c.
- Numeric lipids; non-numeric are coerced with warnings; non-finite become NA.
- Decide row policy: na_action = keep (default), omit, or error.
Load packages and example data
Replace the example slice with your data frame.
library(HealthMarkers)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
sim_path <- system.file("extdata", "simulated_hm_data.rds", package = "HealthMarkers")
sim <- readRDS(sim_path)
sim_small <- dplyr::slice_head(sim, n = 30)Map lipid columns
TG and HDL_c are required. TC and LDL_c are optional but needed for Castelli indices.
col_map <- list(
TG = "TG",
HDL_c = "HDL_c",
TC = "TC",
LDL_c = "LDL_c"
)Quick start: compute indices
Defaults keep rows with missing lipids and return NA for their ratios.
aip_out <- atherogenic_indices(
data = sim_small,
col_map = col_map,
na_action = "keep",
verbose = FALSE
)
new_cols <- setdiff(names(aip_out), names(sim_small))
head(select(aip_out, all_of(new_cols)))
#> # A tibble: 6 × 3
#> AIP CRI_I CRI_II
#> <dbl> <dbl> <dbl>
#> 1 0.384 4.95 2.85
#> 2 -0.229 4.75 3.48
#> 3 -0.0710 5.52 4.13
#> 4 -0.204 3.02 1.73
#> 5 0.0134 2.96 1.49
#> 6 -0.118 3.38 2.04Arguments that matter
- col_map: named list for TG, HDL_c (required), and optional TC, LDL_c. Missing required keys error.
- na_action: keep (default, rows retained; ratios become NA), omit (drop rows with NA in used lipids), error (abort on NA).
- verbose: emit step messages.
Handling missing and zero denominators
- Non-numeric lipids are coerced to numeric; NA introduced are warned. Non-finite become NA.
- Zero HDL_c denominators yield NA ratios and a warning; Castelli indices use HDL_c as the divisor.
- Row policy: keep vs omit vs error via na_action.
Compare row policies
demo <- sim_small
demo$HDL_c[c(2, 5)] <- NA
aip_keep <- atherogenic_indices(demo, col_map, na_action = "keep")
#> atherogenic_indices(): reading input 'demo' — 30 rows × 519 variables
#> atherogenic_indices(): col_map (4 columns — 4 specified)
#> TG -> 'TG'
#> HDL_c -> 'HDL_c'
#> TC -> 'TC'
#> LDL_c -> 'LDL_c'
#> atherogenic_indices(): computing markers:
#> AIP [log10(TG / HDL_c)]
#> CRI_I [TC / HDL_c [if TC available]]
#> CRI_II [LDL_c / HDL_c [if LDL_c available]]
#> atherogenic_indices(): results: id 30/30, AIP 28/30, CRI_I 28/30, CRI_II 28/30
aip_omit <- atherogenic_indices(demo, col_map, na_action = "omit")
#> atherogenic_indices(): reading input 'demo' — 30 rows × 519 variables
#> atherogenic_indices(): col_map (4 columns — 4 specified)
#> TG -> 'TG'
#> HDL_c -> 'HDL_c'
#> TC -> 'TC'
#> LDL_c -> 'LDL_c'
#> atherogenic_indices(): computing markers:
#> AIP [log10(TG / HDL_c)]
#> CRI_I [TC / HDL_c [if TC available]]
#> CRI_II [LDL_c / HDL_c [if LDL_c available]]
#> atherogenic_indices(): results: id 28/28, AIP 28/28, CRI_I 28/28, CRI_II 28/28
list(
keep_rows = nrow(aip_keep),
omit_rows = nrow(aip_omit),
sample_ratios = head(select(aip_keep, AIP, CRI_I, CRI_II))
)
#> $keep_rows
#> [1] 30
#>
#> $omit_rows
#> [1] 28
#>
#> $sample_ratios
#> # A tibble: 6 × 3
#> AIP CRI_I CRI_II
#> <dbl> <dbl> <dbl>
#> 1 0.384 4.95 2.85
#> 2 NA NA NA
#> 3 -0.0710 5.52 4.13
#> 4 -0.204 3.02 1.73
#> 5 NA NA NA
#> 6 -0.118 3.38 2.04Extreme-value screening (optional)
Zero or negative HDL_c denominators yield NA ratios and a warning. Filter or remove such values before calling.
demo2 <- sim_small
demo2$TG[5] <- 12000 # extreme TG
# Note: extreme values will produce extreme ratio outputs; pre-filter if needed
head(select(atherogenic_indices(demo2, col_map = col_map), AIP, CRI_I, CRI_II))
#> atherogenic_indices(): reading input 'demo2' — 30 rows × 519 variables
#> atherogenic_indices(): col_map (4 columns — 4 specified)
#> TG -> 'TG'
#> HDL_c -> 'HDL_c'
#> TC -> 'TC'
#> LDL_c -> 'LDL_c'
#> atherogenic_indices(): computing markers:
#> AIP [log10(TG / HDL_c)]
#> CRI_I [TC / HDL_c [if TC available]]
#> CRI_II [LDL_c / HDL_c [if LDL_c available]]
#> atherogenic_indices(): results: id 30/30, AIP 30/30, CRI_I 30/30, CRI_II 30/30
#> # A tibble: 6 × 3
#> AIP CRI_I CRI_II
#> <dbl> <dbl> <dbl>
#> 1 0.384 4.95 2.85
#> 2 -0.229 4.75 3.48
#> 3 -0.0710 5.52 4.13
#> 4 -0.204 3.02 1.73
#> 5 3.88 2.96 1.49
#> 6 -0.118 3.38 2.04Outputs
- AIP: log10(TG / HDL_c)
- CRI_I: TC / HDL_c (NA if TC not mapped/present)
- CRI_II: LDL_c / HDL_c (NA if LDL_c not mapped/present) Rows only drop when na_action = “omit” or when na_action = “error” encounters missing.
Pitfalls and tips
- Keep lipid units consistent; ratios are unitless but inputs must share units.
- Mapping omissions for TG or HDL_c throw errors; missing TC/LDL_c just yield NA Castelli indices.
- Zero HDL_c produces NA ratios and a warning; address true zeros before computing.
- Use na_action = “error” for pipelines that must fail fast on missing lipids.
Validation ideas
- Spot-check AIP: if TG = 150 and HDL_c = 50, expected AIP = log10(3) ~ 0.477.
- CRI_I should decrease if HDL_c rises with other lipids unchanged; similar for CRI_II.
- Confirm that rows with NA in TG/HDL_c are dropped only when na_action = “omit”.
Verbose diagnostics
Set verbose = TRUE to emit three structured messages per
call:
- Preparing inputs — start-of-function signal.
-
Column map — confirms which data column each lipid
key resolved to. Example:
atherogenic_indices(): column map: TG -> 'TG', HDL_c -> 'HDL_c', TC -> 'TC', LDL_c -> 'LDL_c' -
Results summary — shows how many rows computed
successfully (non-NA) per output column. Example:
atherogenic_indices(): results: AIP 28/30, CRI_I 30/30, CRI_II 30/30, ...
verbose = TRUE emits at the "inform" level;
you also need options(healthmarkers.verbose = "inform")
active:
old_opt <- options(healthmarkers.verbose = "inform")
df_v <- tibble::tibble(
TG = c(150, 200),
HDL_c = c(50, 40),
TC = c(200, 220),
LDL_c = c(120, 150)
)
atherogenic_indices(
df_v,
col_map = list(TG = "TG", HDL_c = "HDL_c", TC = "TC", LDL_c = "LDL_c"),
verbose = TRUE
)
#> atherogenic_indices(): reading input 'df_v' — 2 rows × 4 variables
#> atherogenic_indices(): col_map (4 columns — 4 specified)
#> TG -> 'TG'
#> HDL_c -> 'HDL_c'
#> TC -> 'TC'
#> LDL_c -> 'LDL_c'
#> atherogenic_indices(): computing markers:
#> AIP [log10(TG / HDL_c)]
#> CRI_I [TC / HDL_c [if TC available]]
#> CRI_II [LDL_c / HDL_c [if LDL_c available]]
#> atherogenic_indices(): results: AIP 2/2, CRI_I 2/2, CRI_II 2/2
#> # A tibble: 2 × 3
#> AIP CRI_I CRI_II
#> <dbl> <dbl> <dbl>
#> 1 0.477 4 2.4
#> 2 0.699 5.5 3.75
options(old_opt)Reset with options(healthmarkers.verbose = NULL) or
"none".
Column recognition
Run hm_col_report(your_data) to check which
lipid/analyte columns are auto-detected before building your
col_map. See the Multi-Biobank
Compatibility article for recognised synonyms.
hm_col_report(your_data)