pretestcad

An R package for Pretest Probability
for Coronary Artery Disease


Jeremy Selva

@JauntyJJS
https://jeremy-selva.netlify.app

For useR! 2025

useR! 2025 Logo.

10 August 2025

Hex Sticker of pretestcad.

What is Coronary Artery Disease (CAD) ?

Coronary Artery Disease (CAD) happens when the coronary arteries from the heart become narrow or blocked by fatty deposits called plaque.

This can lead to chest pain, shortness of breath or even heart attack.

Picture showing coronary arteries from the heart become narrow or blocked by fatty deposits called plaque.

Coronary Artery Disease for health education Infographic designed by brgfx from Freepik.

Diagnosis of CAD

Patient needs to go for many invasive, time consuming and expensive tests for diagnosis of CAD.

Picture showing different tests for diagnosis of Coronary Artery Disease such as troponon blood test, electrocardiogram, echocardiogram, stress test, coronary calcium scan, computed tomography coronary angiogram and cardiac catherisation

Images from Pokharel, MD, B., & Dhakal, MD, B The American Journal of Patient Health Info 2024; 1(01) doi: 10.69512/ajphi.v1i01.37.

What is Pretest Probability ?

Pretest probability is the estimated probability, given a set of risk factors (e.g age, sex, etc.), that a patient has a specific disease or condition before any diagnostic tests are performed.

It helps clinicians and doctors makes informed decision on which patients go for which diagnostic tests.

European Society of Cardiology (ESC) 2024 guidelines of first-line testing in symptomatic individuals with suspected chronic coronary syndrome.

European Society of Cardiology (ESC) 2024 guidelines of first-line testing in symptomatic individuals with suspected chronic coronary syndrome.

Pretest Probability Calculation

Pretest probability of CAD may be presented in the form of a table or an online calculator where we calculate the probability for each patient.

European Society of Cardiology (ESC) 2024 pretest probability for CAD table.

Workflow from Vrints et al. European Heart journal 2024; 45(36):3415-3537 doi: 10.1093/eurheartj/ehae177.

Online calculator to calculate pretest probability for CAD using the CAD Consortium cohort.

Motivation

As pretest models changes with time, it becomes tedious to calculate and update pretest probability one patient at a time.

Motivation

There are many R packages for calculating risk for cardiovascular disease (CVD).

Why not create one for CAD ?

R packages preventr, RiskScorecvd and globorisk to calculate risk of cardiovascular disease.

R packages preventr, RiskScorecvd and globorisk to calculate risk of CVD.

pretestcad

https://jauntyjjs.github.io/pretestcad/

Usage Example

# 30 female with symptom score of 3 and 0 risk factors
calculate_esc_2024_fig_4_ptp(
  age = 30,
  sex = "female",
  chest_pain_type = "typical",
  have_dyspnoea = "no",
  have_family_history = "no",
  have_smoking_history = "no",
  have_dyslipidemia = "no",
  have_hypertension = "no",
  have_diabetes = "no",
  output = "percentage"
)
[1] "2%"
ESC 2024 pretest probability of CAD table.

ESC 2024 guidelines from Vrints et al. European Heart journal 2024; 45(36):3415-3537 doi: 10.1093/eurheartj/ehae177.

Robust to missing clinical data

# 55 male with symptom score of 3 and 3 risk factors and 2 NA
calculate_esc_2024_fig_4_ptp(
  age = 55,
  sex = "male",
  chest_pain_type = "typical",
  have_dyspnoea = "no",
  have_family_history = NA,
  have_smoking_history = NA,
  have_dyslipidemia = "yes",
  have_hypertension = "yes",
  have_diabetes = "yes",
  max_na_num_of_rf = 2,
  output = "percentage"
)
[1] "27%"
ESC 2024 pretest probability of CAD table.

ESC 2024 guidelines from Vrints et al. European Heart journal 2024; 45(36):3415-3537 doi: 10.1093/eurheartj/ehae177.

Helpful error messages

Applied this talk from useR! 2024 to write better error messages.

Helpful error messages

calculate_esc_2024_fig_4_ptp(
  age = "Something Else",
  sex = "male",
  chest_pain_type = "typical",
  have_dyspnoea = "no",
  have_family_history = "no",
  have_smoking_history = "no",
  have_dyslipidemia = "yes",
  have_hypertension = "yes",
  have_diabetes = "yes",
  output = "percentage"
)
Error in `calculate_esc_2024_fig_4_ptp()`:
! Provided input `age`, must be <numeric>, `NA` or `NaN`. It is
  currently "Something Else" of type <character>
calculate_esc_2024_fig_4_ptp(
  age = 0,
  sex = "male",
  chest_pain_type = "typical",
  have_dyspnoea = "no",
  have_family_history = "no",
  have_smoking_history = "no",
  have_dyslipidemia = "yes",
  have_hypertension = "yes",
  have_diabetes = "yes",
  output = "percentage"
)
Error in `calculate_esc_2024_fig_4_ptp()`:
! `age` must be positive, not 0
calculate_esc_2024_fig_4_ptp(
  age = 50,
  sex = "Something Else",
  chest_pain_type = "typical",
  have_dyspnoea = "no",
  have_family_history = "no",
  have_smoking_history = "no",
  have_dyslipidemia = "yes",
  have_hypertension = "yes",
  have_diabetes = "yes",
  output = "percentage"
)
Error in `calculate_esc_2024_fig_4_ptp()`:
! `sex` must be one of "male", "female", "NA", or "NaN", not "Something
  Else".
calculate_esc_2024_fig_4_ptp(
  age = 50,
  sex = "ale",
  chest_pain_type = "typical",
  have_dyspnoea = "no",
  have_family_history = "no",
  have_smoking_history = "no",
  have_dyslipidemia = "yes",
  have_hypertension = "yes",
  have_diabetes = "yes",
  output = "percentage"
)
Error in `calculate_esc_2024_fig_4_ptp()`:
! `sex` must be one of "male", "female", "NA", or "NaN", not "ale".
ℹ Did you mean "male"?

Use of purrr

patient_data <- tibble::tribble(
    ~unique_id,
    ~age,     ~sex, 
    ~chest_pain_type, ~have_dyspnoea, 
    ~have_family_history, ~have_smoking_history, ~have_dyslipidemia, ~have_hypertension, ~have_diabetes,
    "45 year old male with typical chest pain, no dyspnoea, hypertension and diabetes",
    45, "male", 
    "typical", "no",  
    "no", "no", "no", "yes", "yes",
    "70 year old female with no chest pain, dyspnoea, have smoking history (past or current smoker) and dyslipidemia",
    70, "female", 
    "no chest pain", "yes",  
    "no", "yes", "yes", "no", "no"
)

risk_data <- patient_data |>
    dplyr::mutate(
      esc_2024_ptp_percent = purrr::pmap_chr(
        .l = list(
          age = .data[["age"]],
          sex = .data[["sex"]],
          chest_pain_type = .data[["chest_pain_type"]],
          have_dyspnoea = .data[["have_dyspnoea"]],
          have_family_history = .data[["have_family_history"]],
          have_smoking_history = .data[["have_smoking_history"]],
          have_dyslipidemia = .data[["have_dyslipidemia"]],
          have_hypertension = .data[["have_hypertension"]],
          have_diabetes = .data[["have_diabetes"]],
          output = "percentage"
        ),
        .f = pretestcad::calculate_esc_2024_fig_4_ptp
      )
    ) |> 
   dplyr::select(
      c("unique_id", "esc_2024_ptp_percent")
   )

print(risk_data)
# A tibble: 2 × 2
  unique_id                                                 esc_2024_ptp_percent
  <chr>                                                     <chr>               
1 45 year old male with typical chest pain, no dyspnoea, h… 20%                 
2 70 year old female with no chest pain, dyspnoea, have sm… 10%                 

Flexible Labeling (Development Version)

Applied this talk from R/Medicine 2025 to include flexible labelling.

Flexible Labeling (Development Version)

Use of parameters label_sex_female for flexible labelling of female.

Flexible Labeling (Development Version)

calculate_esc_2024_fig_4_ptp(
  age = 50,
  sex = "男性",
  chest_pain_type = "typical",
  have_dyspnoea = "tidak",
  have_family_history = "no",
  have_smoking_history = "no",
  have_dyslipidemia = "yes",
  have_hypertension = "yes",
  have_diabetes = "ஆம்",
  output = "percentage",
  label_sex_male = c("male", "男性", "男人"),
  label_have_dyspnoea_no = c("no", "tidak"),
  label_have_diabetes_yes = c("yes", "ஆம்", "ஆமாம்") 
)
[1] "27%"
calculate_esc_2024_fig_4_ptp(
  age = 50,
  sex = "女性性",
  chest_pain_type = "typical",
  have_dyspnoea = "no",
  have_family_history = "no",
  have_smoking_history = "no",
  have_dyslipidemia = "yes",
  have_hypertension = "yes",
  have_diabetes = "no",
  output = "percentage",
  label_sex_male = c("male", "男性", "男人"),
  label_sex_female = c("female", "女性", "女人"),
  label_sex_unknown = c("NIL", NA, NaN)
)
Error in `calculate_esc_2024_fig_4_ptp()`:
! `sex` must be one of "male", "男性", "男人", "female", "女性", "女人",
  "NIL", "NA", or "NaN", not "女性性".
ℹ Did you mean "女性"?
calculate_esc_2024_fig_4_ptp(
  age = 50,
  sex = "male",
  chest_pain_type = "typical",
  have_dyspnoea = "no",
  have_family_history = "no",
  have_smoking_history = "no",
  have_dyslipidemia = "yes",
  have_hypertension = "yes",
  have_diabetes = "no",
  output = "percentage",
  label_sex_male = c("Anything", "male", "男性", "男人", "随便"),
  label_sex_female = c("Anything", "male", "女性", "女人"),
  label_sex_unknown = c("Anything", "NIL", NA, NaN, "随便")
)
Error in `calculate_esc_2024_fig_4_ptp()`:
! `label_sex_male`, `label_sex_female` and `label_sex_unknown` must be
  mutually exclusive.
Common values found in `label_sex_male` and `label_sex_female`: "Anything" and
"male".
Common values found in `label_sex_male` and `label_sex_unknown`: "Anything" and
"随便".
Common values found in `label_sex_female` and `label_sex_unknown`: "Anything".
Please ensure `label_sex_male`, `label_sex_female` and `label_sex_unknown` do
not hold common values.

Thank you

Available on CRAN, RUniverse and Github.

Comic howing too many bugs to fix during first day of release.

Great Expectations from MonkeyUser.com