Skip to content

Commit

Permalink
Deprecate 'transpose', 'as.list', 'as.matrix' and 'drop' in 'coords'
Browse files Browse the repository at this point in the history
  • Loading branch information
xrobin committed Jul 7, 2024
1 parent 2f3b8e3 commit f7b8bfd
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 269 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
1.18.9999
* Added "lr_pos" and "lr_neg" to 'coords' (issue #102)
* Deprecated 'transpose', 'as.list', 'as.matrix' and 'drop' in 'coords'

1.18.5 (2023-11-01):
* Fixed formula input when given as variable and combined with `with` (issue #111)
Expand Down
28 changes: 12 additions & 16 deletions R/bootstrap.R
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ stratified.ci.se <- function(n, roc, sp) {
roc$specificities <- perfs$sp * ifelse(roc$percent, 100, 1)
roc$thresholds <- thresholds

return(coords.roc(roc, sp, input = "specificity", ret = "sensitivity", transpose = FALSE, as.matrix = TRUE)[,1])
return(coords.roc(roc, sp, input = "specificity", ret = "sensitivity")[,1])
}

nonstratified.ci.se <- function(n, roc, sp) {
Expand All @@ -466,7 +466,7 @@ nonstratified.ci.se <- function(n, roc, sp) {
roc$specificities <- perfs$sp * ifelse(roc$percent, 100, 1)
roc$thresholds <- thresholds

return(coords.roc(roc, sp, input = "specificity", ret = "sensitivity", transpose = FALSE, as.matrix = TRUE)[, 1])
return(coords.roc(roc, sp, input = "specificity", ret = "sensitivity")[, 1])
}

########## SE of a smooth ROC curve (ci.se) ##########
Expand All @@ -492,7 +492,7 @@ stratified.ci.smooth.se <- function(n, roc, sp, smooth.roc.call) {
smooth.roc <- try(eval(smooth.roc.call), silent=TRUE)
if (methods::is(smooth.roc, "try-error"))
return(NA)
return(coords.smooth.roc(smooth.roc, sp, input = "specificity", ret = "sensitivity", transpose = FALSE, as.matrix = TRUE)[, 1])
return(coords.smooth.roc(smooth.roc, sp, input = "specificity", ret = "sensitivity")[, 1])
}

nonstratified.ci.smooth.se <- function(n, roc, sp, smooth.roc.call) {
Expand Down Expand Up @@ -520,7 +520,7 @@ nonstratified.ci.smooth.se <- function(n, roc, sp, smooth.roc.call) {
smooth.roc <- try(eval(smooth.roc.call), silent=TRUE)
if (methods::is(smooth.roc, "try-error"))
return(NA)
return(coords.smooth.roc(smooth.roc, sp, input = "specificity", ret = "sensitivity", transpose = FALSE, as.matrix = TRUE)[, 1])
return(coords.smooth.roc(smooth.roc, sp, input = "specificity", ret = "sensitivity")[, 1])
}

########## SP of a ROC curve (ci.sp) ##########
Expand All @@ -535,7 +535,7 @@ stratified.ci.sp <- function(n, roc, se) {
roc$specificities <- perfs$sp * ifelse(roc$percent, 100, 1)
roc$thresholds <- thresholds

return(coords.roc(roc, se, input = "sensitivity", ret = "specificity", transpose = FALSE, as.matrix = TRUE)[, 1])
return(coords.roc(roc, se, input = "sensitivity", ret = "specificity")[, 1])
}

nonstratified.ci.sp <- function(n, roc, se) {
Expand All @@ -552,7 +552,7 @@ nonstratified.ci.sp <- function(n, roc, se) {
roc$specificities <- perfs$sp * ifelse(roc$percent, 100, 1)
roc$thresholds <- thresholds

return(coords.roc(roc, se, input = "sensitivity", ret = "specificity", transpose = FALSE, as.matrix = TRUE)[, 1])
return(coords.roc(roc, se, input = "sensitivity", ret = "specificity")[, 1])
}

########## SP of a smooth ROC curve (ci.sp) ##########
Expand All @@ -578,7 +578,7 @@ stratified.ci.smooth.sp <- function(n, roc, se, smooth.roc.call) {
smooth.roc <- try(eval(smooth.roc.call), silent=TRUE)
if (methods::is(smooth.roc, "try-error"))
return(NA)
return(coords.smooth.roc(smooth.roc, se, input = "sensitivity", ret = "specificity", transpose = FALSE, as.matrix = TRUE)[, 1])
return(coords.smooth.roc(smooth.roc, se, input = "sensitivity", ret = "specificity")[, 1])
}

nonstratified.ci.smooth.sp <- function(n, roc, se, smooth.roc.call) {
Expand Down Expand Up @@ -606,7 +606,7 @@ nonstratified.ci.smooth.sp <- function(n, roc, se, smooth.roc.call) {
smooth.roc <- try(eval(smooth.roc.call), silent=TRUE)
if (methods::is(smooth.roc, "try-error"))
return(NA)
return(coords.smooth.roc(smooth.roc, se, input = "sensitivity", ret = "specificity", transpose = FALSE, as.matrix = TRUE)[, 1])
return(coords.smooth.roc(smooth.roc, se, input = "sensitivity", ret = "specificity")[, 1])
}

########## Threshold of a ROC curve (ci.thresholds) ##########
Expand Down Expand Up @@ -649,8 +649,7 @@ stratified.ci.coords <- function(roc, x, input, ret, best.method, best.weights,
roc$thresholds <- thresholds

res <- coords.roc(roc, x = x, input = input, ret = ret,
best.method = best.method, best.weights = best.weights,
drop = FALSE, transpose = FALSE, as.matrix = TRUE)
best.method = best.method, best.weights = best.weights)
# Return a random column with "best"
if (length(x) == 1 && x == "best" && nrow(res) != 1) {
return(enforce.best.policy(res, best.policy))
Expand Down Expand Up @@ -682,8 +681,7 @@ nonstratified.ci.coords <- function(roc, x, input, ret, best.method, best.weight
roc$thresholds <- thresholds

res <- coords.roc(roc, x = x, input = input, ret = ret,
best.method = best.method, best.weights = best.weights,
drop = FALSE, transpose = FALSE, as.matrix = TRUE)
best.method = best.method, best.weights = best.weights)
# Return a random column with "best"
if (length(x) == 1 && x == "best" && nrow(res) != 1) {
return(enforce.best.policy(res, best.policy))
Expand Down Expand Up @@ -718,8 +716,7 @@ stratified.ci.smooth.coords <- function(roc, x, input, ret, best.method, best.we
if (methods::is(smooth.roc, "try-error"))
return(NA)
res <- coords.roc(smooth.roc, x = x, input = input, ret = ret,
best.method = best.method, best.weights = best.weights,
drop = FALSE, transpose = FALSE, as.matrix = TRUE)
best.method = best.method, best.weights = best.weights)
# Return a random column with "best"
if (length(x) == 1 && x == "best" && nrow(res) != 1) {
return(enforce.best.policy(res, best.policy))
Expand Down Expand Up @@ -755,8 +752,7 @@ nonstratified.ci.smooth.coords <- function(roc, x, input, ret, best.method, best
if (methods::is(smooth.roc, "try-error"))
return(NA)
res <- coords.roc(smooth.roc, x = x, input = input, ret = ret,
best.method = best.method, best.weights = best.weights,
drop = FALSE, transpose = FALSE, as.matrix = TRUE)
best.method = best.method, best.weights = best.weights)
# Return a random column with "best"
if (length(x) == 1 && x == "best" && nrow(res) != 1) {
return(enforce.best.policy(res, best.policy))
Expand Down
10 changes: 5 additions & 5 deletions R/ci.coords.R
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ ci.coords.smooth.roc <- function(smooth.roc,
progress <- roc_utils_get_progress_bar(progress, title="Coords confidence interval", label="Bootstrap in progress...", ...)

if (boot.stratified) {
perfs <- raply(boot.n, stratified.ci.smooth.coords(roc, x, input, ret, best.method, best.weights, smooth.roc.call, best.policy), .progress=progress, .drop=FALSE)
perfs <- raply(boot.n, as.matrix(stratified.ci.smooth.coords(roc, x, input, ret, best.method, best.weights, smooth.roc.call, best.policy)), .progress=progress, .drop=FALSE)
}
else {
perfs <- raply(boot.n, nonstratified.ci.smooth.coords(roc, x, input, ret, best.method, best.weights,smooth.roc.call, best.policy), .progress=progress, .drop=FALSE)
perfs <- raply(boot.n, as.matrix(nonstratified.ci.smooth.coords(roc, x, input, ret, best.method, best.weights,smooth.roc.call, best.policy)), .progress=progress, .drop=FALSE)
}

if (any(which.ones <- apply(perfs, 1, function(x) all(is.na(x))))) {
Expand Down Expand Up @@ -164,12 +164,12 @@ ci.coords.roc <- function(roc,
progress <- roc_utils_get_progress_bar(progress, title="Coords confidence interval", label="Bootstrap in progress...", ...)

if (boot.stratified) {
perfs <- raply(boot.n, stratified.ci.coords(roc, x, input, ret, best.method, best.weights, best.policy), .progress=progress, .drop = FALSE)
perfs <- raply(boot.n, as.matrix(stratified.ci.coords(roc, x, input, ret, best.method, best.weights, best.policy)), .progress=progress, .drop = FALSE)
}
else {
perfs <- raply(boot.n, nonstratified.ci.coords(roc, x, input, ret, best.method, best.weights, best.policy), .progress=progress, .drop = FALSE)
perfs <- raply(boot.n, as.matrix(nonstratified.ci.coords(roc, x, input, ret, best.method, best.weights, best.policy)), .progress=progress, .drop = FALSE)
}

if (any(which.ones <- apply(perfs, 1, function(x) all(is.na(x))))) {
if (all(which.ones)) {
warning("All bootstrap iterations produced NA values only.")
Expand Down
2 changes: 1 addition & 1 deletion R/ci.thresholds.R
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ ci.thresholds.roc <- function(roc,
if (length(thresholds) != 1)
stop("'thresholds' of class character must be of length 1.")
thresholds <- match.arg(thresholds, c("all", "best", "local maximas"))
thresholds.num <- coords(roc, x=thresholds, input="threshold", ret="threshold", as.matrix = TRUE, transpose = FALSE, ...)[, 1]
thresholds.num <- coords(roc, x=thresholds, input="threshold", ret="threshold", ...)[, 1]
attr(thresholds.num, "coords") <- thresholds
}
else if (is.logical(thresholds)) {
Expand Down
37 changes: 35 additions & 2 deletions R/coords.R
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ coords.smooth.roc <- function(smooth.roc,
stop("Numeric 'x' has length 0")
}

# Warn about deprecated stuff
if (!missing(drop) && drop) {
warning("'drop' is deprecated and will be removed in a future version.")
}

if (as.matrix) {
warning("'as.matrix' is deprecated and will be removed in a future version.")
}

if (transpose) {
warning("'transpose' is deprecated and will be removed in a future version.")
}

if (as.list) {
warning("'as.list' is deprecated and will be removed in a future version.")
}

# match return
ret <- roc_utils_match_coords_ret_args(ret, threshold = FALSE)

Expand Down Expand Up @@ -177,7 +194,24 @@ coords.roc <- function(roc,
else if (length(x) == 0 && is.numeric(x)) {
stop("Numeric 'x' has length 0")
}


# Warn about deprecated stuff
if (!missing(drop) && drop) {
warning("'drop' is deprecated and will be removed in a future version.")
}

if (as.matrix) {
warning("'as.matrix' is deprecated and will be removed in a future version.")
}

if (transpose) {
warning("'transpose' is deprecated and will be removed in a future version.")
}

if (as.list) {
warning("'as.list' is deprecated and will be removed in a future version.")
}

# match input
input <- roc_utils_match_coords_input_args(input)
# match return
Expand Down Expand Up @@ -384,7 +418,6 @@ coords.roc <- function(roc,
}

if (as.list) {
warning("'as.list' is deprecated and will be removed in a future version.")
list <- apply(t(res)[ret, , drop=FALSE], 2, as.list)
if (drop == TRUE && length(x) == 1) {
return(list[[1]])
Expand Down
67 changes: 24 additions & 43 deletions man/coords.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ as.matrix=FALSE, ...)
\item{as.list}{DEPRECATED. If the returned object must be a list.
Will be removed in a future version.
}
\item{drop}{If \code{TRUE} the result is coerced to the lowest
\item{drop}{DEPRECATED. If \code{TRUE} the result is coerced to the lowest
possible dimension, as per \link{Extract}. By default only drops
if \code{transpose = TRUE} and either \code{ret} or \code{x} is
of length 1.
Expand All @@ -63,12 +63,12 @@ as.matrix=FALSE, ...)
\item{best.weights}{if \code{x="best"}, the weights to determine the
best threshold. See details in the \sQuote{Best thresholds} section.
}
\item{transpose}{whether
\item{transpose}{DEPRECATED. Whether
to return the thresholds in columns (\code{TRUE}) or rows (\code{FALSE}).
Since pROC 1.16 the default value is \code{FALSE}.
See \link{coords_transpose} for more details the change.
}
\item{as.matrix}{if \code{transpose} is \code{FALSE}, whether to return
\item{as.matrix}{DEPRECATED. If \code{transpose} is \code{FALSE}, whether to return
a \code{\link{matrix}} (\code{TRUE}) or a \code{\link{data.frame}}
(\code{FALSE}, the default). A \code{data.frame} is more convenient
and flexible to use, but incurs a slight speed penalty. Consider
Expand Down Expand Up @@ -217,36 +217,17 @@ as.matrix=FALSE, ...)
} % details
\value{
Depending on the length of \code{x} and \code{as.list} argument.
A \code{data.frame} with \code{ret} as columns and as many rows as
given by \code{x}.
\tabular{lll}{
\tab
length(x) == 1 or length(ret) == 1 \tab
length(x) > 1 or length(ret) > 1 or drop == FALSE
\cr
\code{as.list=TRUE} \tab
a list of the length of, in the order of, and named after, \code{ret}. \tab
a list of the length of, and named after, \code{x}. Each element of this list is a list of the length of, in the order of, and named after, \code{ret}. \cr
\code{as.list=FALSE} \tab
a numeric vector of the length of, in the order of, and named after, \code{ret} (if \code{length(x) == 1})
or a numeric vector of the length of, in the order of, and named after, \code{x} (if \code{length(ret) == 1}.\tab
a numeric matrix with one row for each \code{ret} and one column for each \code{x}\cr
}
In all cases if \code{input="specificity"} or \code{input="sensitivity"}
In all cases where \code{input="specificity"} or \code{input="sensitivity"}
and interpolation was required, threshold is returned as \code{NA}.
Note that if giving a character as \code{x} (\dQuote{all},
\dQuote{local maximas} or \dQuote{best}), you cannot predict the
dimension of the return value unless \code{drop=FALSE}. Even
\dQuote{best} may return more than one value (for example if the ROC
curve is below the identity line, both extreme points).
\code{coords} may also return \code{NULL} when there a partial area is
defined but no point of the ROC curve falls within the region.
\dQuote{local maximas} or \dQuote{best}), you cannot predict the dimension of
the return value. Even \dQuote{best} may return more than one value (for
example if the ROC curve is below the identity line, both extreme points).
}
\references{
Expand Down Expand Up @@ -275,34 +256,34 @@ data(aSAH)
roc.s100b <- roc(aSAH$outcome, aSAH$s100b, percent = TRUE)
# Get the coordinates of S100B threshold 0.55
coords(roc.s100b, 0.55, transpose = FALSE)
coords(roc.s100b, 0.55)
# Get the coordinates at 50\% sensitivity
coords(roc=roc.s100b, x=50, input="sensitivity", transpose = FALSE)
coords(roc=roc.s100b, x=50, input="sensitivity")
# Can be abbreviated:
coords(roc.s100b, 50, "se", transpose = FALSE)
coords(roc.s100b, 50, "se")
# Works with smoothed ROC curves
coords(smooth(roc.s100b), 90, "specificity", transpose = FALSE)
coords(smooth(roc.s100b), 90, "specificity")
# Get the sensitivities for all thresholds
cc <- coords(roc.s100b, "all", ret="sensitivity", transpose = FALSE)
cc <- coords(roc.s100b, "all", ret="sensitivity")
print(cc$sensitivity)
# Get the best threshold
coords(roc.s100b, "best", ret="threshold", transpose = FALSE)
coords(roc.s100b, "best", ret="threshold")
# Get the best threshold according to different methods
roc.ndka <- roc(aSAH$outcome, aSAH$ndka, percent=TRUE)
coords(roc.ndka, "best", ret="threshold", transpose = FALSE,
coords(roc.ndka, "best", ret="threshold",
best.method="youden") # default
coords(roc.ndka, "best", ret="threshold", transpose = FALSE,
coords(roc.ndka, "best", ret="threshold",
best.method="closest.topleft")
# and with different weights
coords(roc.ndka, "best", ret="threshold", transpose = FALSE,
coords(roc.ndka, "best", ret="threshold",
best.method="youden", best.weights=c(50, 0.2))
coords(roc.ndka, "best", ret="threshold", transpose = FALSE,
coords(roc.ndka, "best", ret="threshold",
best.method="closest.topleft", best.weights=c(5, 0.2))
# This is available with the plot.roc function too:
Expand All @@ -311,26 +292,26 @@ plot(roc.ndka, print.thres="best", print.thres.best.method="youden",
# Return more values:
coords(roc.s100b, "best", ret=c("threshold", "specificity", "sensitivity", "accuracy",
"precision", "recall"), transpose = FALSE)
"precision", "recall"))
# Return all values
coords(roc.s100b, "best", ret = "all", transpose = FALSE)
coords(roc.s100b, "best", ret = "all")
# You can use coords to plot for instance a sensitivity + specificity vs. cut-off diagram
plot(specificity + sensitivity ~ threshold,
coords(roc.ndka, "all", transpose = FALSE),
coords(roc.ndka, "all"),
type = "l", log="x",
subset = is.finite(threshold))
# Plot the Precision-Recall curve
plot(precision ~ recall,
coords(roc.ndka, "all", ret = c("recall", "precision"), transpose = FALSE),
coords(roc.ndka, "all", ret = c("recall", "precision")),
type="l", ylim = c(0, 100))
# Alternatively plot the curve with TPR and FPR instead of SE/SP
# (identical curve, only the axis change)
plot(tpr ~ fpr,
coords(roc.ndka, "all", ret = c("tpr", "fpr"), transpose = FALSE),
coords(roc.ndka, "all", ret = c("tpr", "fpr")),
type="l")
}
Expand Down
Loading

0 comments on commit f7b8bfd

Please sign in to comment.