Getting started with uroscores

library(uroscores)

An instrument here is data, not code. The registry shows what is installed and whether each definition has been checked against its primary source:

list_instruments()
#>      id
#> 1   bii
#> 2  icpi
#> 3  icsi
#> 4  iiq7
#> 5  ipss
#> 6   isi
#> 7  isi3
#> 8 oabss
#> 9  udi6
#>                                                                    full_name
#> 1                   Benign Prostatic Hyperplasia Impact Index (BII, 4 items)
#> 2                         Interstitial Cystitis Problem Index (O'Leary-Sant)
#> 3                         Interstitial Cystitis Symptom Index (O'Leary-Sant)
#> 4         Incontinence Impact Questionnaire, short form (IIQ-7), 0-100 scale
#> 5 International Prostate Symptom Score / AUA Symptom Index (7 symptom items)
#> 6             Sandvik Incontinence Severity Index, four-level (revised 2000)
#> 7           Sandvik Incontinence Severity Index, three-level (original 1993)
#> 8                                 Overactive Bladder Symptom Score (4 items)
#> 9             Urogenital Distress Inventory, short form (UDI-6), 0-100 scale
#>   n_items     scoring   missing_rule validated
#> 1       4         sum none_published      TRUE
#> 2       4         sum none_published      TRUE
#> 3       4         sum none_published      TRUE
#> 4       7 mean_scaled   prorate_mean      TRUE
#> 5       7         sum none_published      TRUE
#> 6       2     product none_published      TRUE
#> 7       2     product none_published      TRUE
#> 8       4         sum none_published      TRUE
#> 9       6 mean_scaled   prorate_mean      TRUE

One engine scores everything. Wrappers like score_ipss() add instrument-specific conveniences, in this case the separate quality-of-life item and severity classification:

d <- data.frame(
  ipss_q1 = c(1, 3), ipss_q2 = c(2, 4), ipss_q3 = c(3, 5),
  ipss_q4 = c(0, 2), ipss_q5 = c(4, 5), ipss_q6 = c(5, 3),
  ipss_q7 = c(2, 4), qol = c(3, 5)
)
score_ipss(d, qol = "qol", classify = TRUE)
#>   ipss_total ipss_voiding ipss_storage ipss_n_missing ipss_qol ipss_severity
#> 1         17           13            4              0        3      moderate
#> 2         26           16           10              0        5        severe

Missing items are handled by the published rule for the instrument. The IPSS has no published rule, so a missing item gives NA and asking for proration is an error:

d_miss <- d
d_miss$ipss_q3[1] <- NA
score_ipss(d_miss, missing = "prorate")
#> Error: No published prorated-scoring rule exists for "ipss". uroscores will not invent one; handle missing items explicitly upstream (e.g. principled imputation) if you must.

UDI-6 does have a published rule (mean of answered items, at most two missing), so it prorates by default with the published threshold:

u <- data.frame(
  udi6_q1 = c(3, 0), udi6_q2 = c(3, 0), udi6_q3 = c(3, 3),
  udi6_q4 = c(3, 3), udi6_q5 = c(3, NA), udi6_q6 = c(NA, NA)
)
score_instrument(u, "udi6")
#>   udi6_total udi6_n_missing
#> 1        100              1
#> 2         50              2

For responder analyses, look at the published statistics first, then pick a threshold on purpose:

mid_estimates("ipss")[, c("statistic", "value", "subgroup")]
#>     statistic value              subgroup
#> 1 mean change  -3.0          all patients
#> 2 mean change  -5.1          all patients
#> 3 mean change  -8.8          all patients
#> 4 mean change  -1.9  baseline AUA-SI 8-19
#> 5 mean change  -6.1 baseline AUA-SI 20-35
#> 6  ROC cutoff  -3.0          all patients
responder(baseline = c(20, 12), followup = c(14, 11), "ipss", threshold = 3)
#> [1]  TRUE FALSE