Breaking changes for ordered predictors in upcoming pROC 1.20.0
pROC has always accepted ordered factors as predictors (for example aSAH$wfns,
the WFNS neurological grade in the aSAH dataset, with levels 1 to 5).
Internally it simply called as.numeric() on them and computed thresholds as the midpoints
between consecutive numeric codes (1.5, 2.5, and so on).
That's reasonable when the levels happen to be the numbers 1:n, but it is
not appropriate for an ordered factor whose levels are textual, for example "very low",
"low", "medium", "high", "very high". The thresholds
were still -Inf, 1.5, 2.5, 3.5, 4.5, Inf, numbers that do not correspond to anything in
the data and are meaningless to show. This was reported as
issue #63, and a fix for it was just merged into
the develop branch.
In practice, this could potentially break some of your code. Before it reaches CRAN, I'd like testers
who use ordered predictors to try the develop version on their own data and tell me if
anything looks wrong.
What changes
On develop, thresholds of an ordered ROC curve are now the predictor's own levels, plus
one infinite sentinel for the all-positive or all-negative cut-off. Sensitivities, specificities
and AUC are exactly the same as before, only the threshold labels change, from numeric
midpoints to the levels you actually gave it:
library(pROC)
data(aSAH)
wfns.txt <- aSAH$wfns
levels(wfns.txt) <- c("very low", "low", "medium", "high", "very high")
r <- roc(aSAH$outcome, wfns.txt, quiet = TRUE)
r$thresholds
# very low, low, medium, high, very high, Inf
# Levels: very low < low < medium < high < very high < Inf
coords(r, "medium")
# threshold specificity sensitivity
# 1 medium 0.7916667 0.6585365
This is a breaking change for any code that reads roc$thresholds as numbers,
or passes a numeric cut-off into coords(), ci.thresholds(), ci.coords(),
or plot.roc(print.thres = ...) on an ordered ROC curve. Numeric predictors are entirely
unaffected by this change. Where you used to pass a midpoint, pass the level label instead:
# Incorrect (pROC <= 1.19.1 converted wfns to 1:5, thresholds were midpoints): coords(roc(aSAH$outcome, aSAH$wfns), 2.5) # Correct: pass the level label coords(roc(aSAH$outcome, aSAH$wfns, quiet = TRUE), "3")
The mapping from old midpoints to new labels is straightforward (for direction = "<",
the default): the old -Inf becomes the lowest level, the old midpoint k + 0.5
becomes the level in position k + 1, and the old Inf becomes the level literally
named "Inf".
As a side effect of the same fix, ci.coords(..., x = "best", best.policy = "omit") now
correctly drops a bootstrap replicate that has several equally-good "best" points, instead of
silently keeping the first one.
How to test it
This is not released yet: it is only on the develop branch, ahead of the
next CRAN release, planned for early 2027. If you use ordered predictors with roc(), please
install the development version and run your own code against it:
if (! requireNamespace("devtools")) install.packages("devtools")
devtools::install_github("xrobin/pROC@develop")
Recommendations
If you have scripts or packages that build ROC curves from ordered predictors, please run them against
the develop version and check the following:
- Any place you hard-code a numeric threshold for an ordered predictor (in
coords(),ci.thresholds(),ci.coords(), orprint.thres) will need to switch to the level label. roc()now requirescasesandcontrolsbuilt from ordered vectors to have identical levels; it errors instead of silently coercing when they differ (for example after an unbalanceddroplevels()).- Smoothing an ordered ROC curve now only supports
method = "binormal"; other smoothing methods error instead of silently falling back to a numeric conversion. - AUC, DeLong and bootstrap CIs,
roc.test(),cov()/var(), andpower.roc.test()all still match what you got before. Only the thresholds changed, not the statistics.
If anything looks off, please comment on issue #63 or open a new issue on GitHub before this reaches CRAN. Thanks in advance for testing!
Xavier Robin
Publié le mardi 6 octobre 2026 à 14:26 CEST
Lien permanent : /blog/2026/10/06/breaking-changes-for-ordered-predictors-in-upcoming-proc-1.20.0
Tags :
pROC
Commentaires : 0
Chercher
Tags
Billets récents
Calendrier
Syndication