Skip to contents

Scope

Compute a simplified, non-validated approximation of FRAX 10-year fracture risk (major osteoporotic and hip). Normalizes sex labels, applies HM-CS missing/extreme policies, and returns placeholder risks; it is not the proprietary FRAX algorithm.

When to use

  • You need a placeholder fracture-risk proxy for demos or pipelines while acknowledging it is not clinical FRAX.
  • You want explicit NA and extreme-value handling (age/BMD bounds) with column mapping flexibility.
  • You plan to swap in real FRAX later; this keeps interfaces stable in the meantime.

Inputs and requirements

  • Required columns (map via col_map): age (years), sex (codes starting with m/f or 1/0).
  • Optional binary risk factors: prior fracture, parent fracture, glucocorticoids, rheumatoid, secondary osteoporosis, current smoker, high alcohol.
  • Optional: femoral neck BMD T-score (bmd).
  • na_action: keep/ignore/warn retain rows with NA outputs; omit drops rows missing required inputs; error aborts.
  • check_extreme: when TRUE, scan age and BMD; default bounds age 40–90, BMD T -6 to 2 (override with extreme_rules).
  • extreme_action: warn (default) leaves values; cap trims into bounds; NA blanks; error aborts; ignore skips messages.

Load packages and data

Illustrative demo data only; replace with your cohort.

library(HealthMarkers)

demo <- tibble::tibble(
  Age = c(65, 72, 80, 68, 74, 60),
  Sex = c("F", "M", "F", "F", "M", "F"),
  prior_fx   = c(1, 0, 1, 0, 0, 0),
  parent_fx  = c(0, 1, 0, 0, 0, 0),
  steroids   = c(0, 1, 0, 0, 1, 0),
  rheumatoid = c(0, 0, 0, 1, 0, 0),
  secondary  = c(0, 0, 0, 0, 0, 0),
  smoker     = c(0, 1, 0, 0, 1, 0),
  alcohol    = c(1, 0, 0, 0, 0, 1),
  bmd        = c(-2.0, -1.0, -2.5, -1.5, -0.8, NA)
)

Column map (required)

Provide your column names; left-hand keys are fixed.

col_map <- list(
  age = "Age",
  sex = "Sex",
  prior_fracture  = "prior_fx",
  parent_fracture = "parent_fx",
  steroids        = "steroids",
  rheumatoid      = "rheumatoid",
  secondary_op    = "secondary",
  smoker          = "smoker",
  alcohol         = "alcohol",
  bmd             = "bmd"
)

What each input represents

  • age: age in years (required).
  • sex: sex code; first letter m/f or 1/0 (required; normalized to male/female).
  • Optional binary risk factors: prior fracture, parent fracture, glucocorticoids, rheumatoid, secondary osteoporosis, current smoker, high alcohol.
  • bmd: femoral neck T-score (optional); placeholder adjustment when present.

Core calculation

Return the placeholder risks and the normalized age/sex/BMD used.

frax_out <- frax_score(
  data = demo,
  col_map = col_map,
  na_action = "keep",
  check_extreme = TRUE,
  extreme_action = "warn",
  verbose = FALSE
)

head(dplyr::select(frax_out, frax_major_percent, frax_hip_percent, frax_sex_norm, frax_age_used, frax_bmd_tscore))
#> # A tibble: 6 × 5
#>   frax_major_percent frax_hip_percent frax_sex_norm frax_age_used
#>                <dbl>            <dbl> <chr>                 <dbl>
#> 1               15.5              8.5 female                   65
#> 2               14.4              8.9 male                     72
#> 3               19               13.3 female                   80
#> 4               10.4              6   female                   68
#> 5               11.8              7.6 male                     74
#> 6                7                3.5 female                   60
#> # ℹ 1 more variable: frax_bmd_tscore <dbl>

Interpretation: frax_major_percent and frax_hip_percent are placeholder probabilities (not clinical). frax_sex_norm shows normalized sex; frax_age_used and frax_bmd_tscore reflect any capping/blanking applied by extreme checks.

Inputs and reminders

  • Required: age (years) and sex codes mapped in col_map.
  • Optional: binary risk factors and BMD T-score. Non-numeric inputs are coerced; non-finite values become NA.
  • na_action: keep/ignore/warn retain rows with NA outputs; omit drops rows missing required inputs; error aborts.
  • check_extreme: when TRUE, scan age and BMD; defaults bounds age 40–90, BMD T -6 to 2 (override with extreme_rules).
  • extreme_action: warn (default) leaves values; cap trims into bounds; NA blanks; error aborts; ignore skips messages.

Missing data and extremes

Show row handling, capping, and dropping on a small slice.

demo_miss <- demo
demo_miss$Age[3] <- NA      # missing age
demo_miss$Sex[4] <- "X"    # unknown sex -> NA
demo_miss$Age[5] <- 105     # extreme age
demo_miss$bmd[2] <- -7      # extreme BMD

keep_cap <- frax_score(
  data = demo_miss,
  col_map = col_map,
  na_action = "keep",
  check_extreme = TRUE,
  extreme_action = "cap",
  verbose = FALSE
)

omit_cap <- frax_score(
  data = demo_miss,
  col_map = col_map,
  na_action = "omit",
  check_extreme = TRUE,
  extreme_action = "cap",
  verbose = FALSE
)

list(
  keep_rows = nrow(keep_cap),
  omit_rows = nrow(omit_cap),
  capped_preview = head(dplyr::select(keep_cap, frax_age_used, frax_bmd_tscore, frax_major_percent, frax_hip_percent))
)
#> $keep_rows
#> [1] 6
#> 
#> $omit_rows
#> [1] 4
#> 
#> $capped_preview
#> # A tibble: 6 × 4
#>   frax_age_used frax_bmd_tscore frax_major_percent frax_hip_percent
#>           <dbl>           <dbl>              <dbl>            <dbl>
#> 1            65            -2                 15.5              8.5
#> 2            72            -6                 14.4              8.9
#> 3            NA            -2.5               NA               NA  
#> 4            68            -1.5               NA               NA  
#> 5            90            -0.8               15               12  
#> 6            60            NA                  7                3.5

Here na_action = "keep" retains rows even with missing/unknown sex or age (outputs become NA). na_action = "omit" drops rows missing required inputs. extreme_action = "cap" trims age/BMD into allowed ranges before risk calculation.

Expectations

  • Placeholder approximation only; not a validated FRAX calculator and not for clinical use.
  • You must map age and sex; missing mappings or columns abort.
  • Choose na_action to enforce or relax completeness; error is strict, omit drops, keep retains.
  • Use check_extreme/extreme_action to cap, blank, or reject out-of-range age/BMD (defaults age 40–90, BMD T -6 to 2).
  • verbose = TRUE surfaces progress and QA messages.

Verbose diagnostics

Set verbose = TRUE (and healthmarkers.verbose = "inform") to surface three structured messages on each call: preparing inputs, the column map, and a results summary.

old_opt <- options(healthmarkers.verbose = "inform")
df_v <- data.frame(Age = c(65, 72), Sex = c("F", "M"),
                   bmd = c(-2.0, -1.0))
frax_score(
  df_v,
  col_map = list(age = "Age", sex = "Sex", bmd = "bmd"),
  verbose = TRUE
)
#> frax_score(): preparing inputs
#> frax_score(): column map: age -> 'Age', sex -> 'Sex'
#> frax_score(): results: frax_major_percent 2/2, frax_hip_percent 2/2, frax_sex_norm 2/2, frax_age_used 2/2, frax_bmd_tscore 2/2
#> # A tibble: 2 × 5
#>   frax_major_percent frax_hip_percent frax_sex_norm frax_age_used
#>                <dbl>            <dbl> <chr>                 <dbl>
#> 1                8.5              4.7 female                   65
#> 2                5.4              3.3 male                     72
#> # ℹ 1 more variable: frax_bmd_tscore <dbl>
options(old_opt)

Tips

  • Normalize sex coding to M/F or 1/0 before calling to avoid NA normalization.
  • Adjust extreme_rules if your bounds differ; set check_extreme = FALSE to skip scanning.
  • Use na_action = "error" for strict pipelines; omit to filter incomplete rows; keep/warn for exploratory use.
  • Outputs are illustrative only; replace with a validated FRAX implementation for clinical workflows.