
smooth(...)
## S3 method for class 'default':
smooth(...)
## S3 method for class 'roc':
smooth(roc,
method=c("binormal", "density", "fitdistr"), n=512, bw = "nrd0",
density=NULL, density.controls=density, density.cases=density,
start=NULL, start.controls=start, start.cases=start,
reuse.auc=TRUE, reuse.ci=FALSE, ...)
## S3 method for class 'smooth.roc':
smooth(smooth.roc, ...)
roc
function, or a smooth.roc
function.method="density"
and density.controls
and
density.cases
are not provided, bw
is passed to
density
to determine the bandwidth of the
densitymethod="density"
, a numeric value of density (over the y
axis) or a function returning a density (such as
density
. If method="fitdistr"
, a densfun
amethod="fitdistr"
, optionnal start
arguments for . start.controls
and start.cases
allows to specify different distributions for
controls and cases.TRUE
(default for reuse.auc) and the auc
or
density
(only cut
, adjust
,
and kernel
, plus window
for compatibility with S+)match.call
for
more details.fitdistr
function for controls and cases, with an
additional roc
object is stored as a
method
is a function, the return values will be checked
thoroughly for validity (list with two numeric elements of the same
length named The message density
function did not return a valid output. The message
Binormal smoothing cannot smooth ROC curve defined by only one
point. Any such attempt will fail with the error
If the smooth ROC curve was generated by roc
with
density.controls
and density.cases
numeric arguments, it
cannot be smoothed and the error fitdistr
and density
smoothing methods require a
numeric predictor
. If the ROC curve to smooth was
generated with an ordered factor only binormal smoothing can be
applied and the message
method="binormal"
, a linear model is fitted to the quantiles of
the sensitivities and specificities. Smoothed sensitivities and
specificities are then generated from this model on n
points.
This simple approach was found to work well for most ROC curves, but
it may produce hooked smooths in some situations (see in Hanley (1988)). With method="density"
, the density
function is employed to generate a smooth kernel
density of the control and case observations as described by Zhou
et al. (1997), unless
density.controls
or density.cases
are provided
directly. bw
can be given to
specify a bandwidth to use with density
. It can be a
numeric value or a character string (kernel
argument for density
. By default,
roc
), for example with quantile
normalization:
norm.x <- qnorm(rank(x)/(length(x)+1))
smooth(roc(response, norm.x, ...), ...)
Additionally, density
can be a function which must return
either a numeric vector of densities over the y axis or a list
with a density
function. It
must accept the following input:
density.fun(x, n, from, to, bw, kernel, ...)
It is important to honour n
, from
and to
in order
to have the densities evaluated on the same points for controls and
cases. Failing to do so and returning densities of different length
will produce an error. It is also a good idea to use a constant
smoothing parameter (such as bw
) especially when controls and
cases have a different number of observations, to avoid producing
smoother or rougher densities.
If method="fitdistr"
, the fitdistr
function from the density
with optionnal start parameters
start
. The density function are fitted
separately in control (density.controls
, start.controls
)
and case observations (density.cases
,
start.cases
). density
can be one of the character values
allowed by fitdistr
or a density function (such
as dnorm
, dweibull
, ...).
Finally, method
can also be a function. It must
return a list with exactly 2 elements named percent
argument to
roc
). It is passed all the arguments to the
smooth
function.
smooth.default
forces the usage of the
smooth
function in the
Smoothed ROC curves can be passed to smooth again. In this case, the
smoothing is not re-applied on the smoothed ROC curve but the
original
roc
data(aSAH)
## Basic example
rocobj <- roc(aSAH$outcome, aSAH$s100b)
smooth(rocobj)
# or directly with roc()
roc(aSAH$outcome, aSAH$s100b, smooth=TRUE)
# plotting
plot(rocobj)
rs <- smooth(rocobj, method="binormal")
plot(rs, add=TRUE, col="green")
rs2 <- smooth(rocobj, method="density")
plot(rs2, add=TRUE, col="blue")
rs3 <- smooth(rocobj, method="fitdistr", density="lognormal")
plot(rs3, add=TRUE, col="magenta")
legend("bottomright", legend=c("Empirical", "Binormal", "Density", "Log-normal"),
col=c("black", "green", "blue", "magenta"), lwd=2)
## Advanced smoothing
# if we know the distributions are normal with sd=0.1 and an unknown mean:
smooth(rocobj, method="fitdistr", density=dnorm, start=list(mean=1), sd=.1)
# different distibutions for controls and cases:
smooth(rocobj, method="fitdistr", density.controls="normal", density.cases="lognormal")
# with densities
bw <- bw.nrd0(rocobj$predictor)
density.controls <- density(rocobj$controls, from=min(rocobj$predictor) - 3 * bw,
to=max(rocobj$predictor) + 3*bw, bw=bw, kernel="gaussian")
density.cases <- density(rocobj$cases, from=min(rocobj$predictor) - 3 * bw,
to=max(rocobj$predictor) + 3*bw, bw=bw, kernel="gaussian")
smooth(rocobj, method="density", density.controls=density.controls$y,
density.cases=density.cases$y)
# which is roughly what is done by a simple:
smooth(rocobj, method="density")
## Smoothing artificial ROC curves
rand.unif <- runif(1000, -1, 1)
rand.exp <- rexp(1000)
rand.norm <-
rnorm(1000)
# two normals
roc.norm <- roc(controls=rnorm(1000), cases=rnorm(1000)+1, plot=TRUE)
plot(smooth(roc.norm), col="green", lwd=1, add=TRUE)
plot(smooth(roc.norm, method="density"), col="red", lwd=1, add=TRUE)
plot(smooth(roc.norm, method="fitdistr"), col="blue", lwd=1, add=TRUE)
legend("bottomright", legend=c("empirical", "binormal", "density", "fitdistr"),
col=c(par("fg"), "green", "red", "blue"), lwd=c(2, 1, 1, 1))
# deviation from the normality
roc.norm.exp <- roc(controls=rnorm(1000), cases=rexp(1000), plot=TRUE)
plot(smooth(roc.norm.exp), col="green", lwd=1, add=TRUE)
plot(smooth(roc.norm.exp, method="density"), col="red", lwd=1, add=TRUE)
# Wrong fitdistr: normality assumed by default
plot(smooth(roc.norm.exp, method="fitdistr"), col="blue", lwd=1, add=TRUE)
# Correct fitdistr
plot(smooth(roc.norm.exp, method="fitdistr", density.controls="normal",
density.cases="exponential"), col="purple", lwd=1, add=TRUE)
legend("bottomright", legend=c("empirical", "binormal", "density",
"wrong fitdistr", "correct fitdistr"),
col=c(par("fg"), "green", "red", "blue", "purple"), lwd=c(2, 1, 1, 1, 1))
# large deviation from the normality
roc.unif.exp <- roc(controls=runif(1000, 2, 3), cases=rexp(1000)+2, plot=TRUE)
plot(smooth(roc.unif.exp), col="green", lwd=1, add=TRUE)
plot(smooth(roc.unif.exp, method="density"), col="red", lwd=1, add=TRUE)
plot(smooth(roc.unif.exp, method="density", bw="ucv"), col="magenta", lwd=1, add=TRUE)
# Wrong fitdistr: normality assumed by default (uniform distributions not handled)
plot(smooth(roc.unif.exp, method="fitdistr"), col="blue", lwd=1, add=TRUE)
legend("bottomright", legend=c("empirical", "binormal", "density",
"density ucv", "wrong fitdistr"),
col=c(par("fg"), "green", "red", "magenta", "blue"), lwd=c(2, 1, 1, 1, 1))
# 2 uniform distributions with a custom density function
unif.density <- function(x, n, from, to, bw, kernel, ...) {
smooth.x <- seq(from=from, to=to, length.out=n)
smooth.y <- dunif(smooth.x, min=min(x), max=max(x))
return(smooth.y)
}
roc.unif <- roc(controls=runif(1000, -1, 1), cases=runif(1000, 0, 2), plot=TRUE)
s <- smooth(roc.unif, method="density", density=unif.density)
plot(roc.unif)
plot(s, add=TRUE, col="grey")
# you can bootstrap a ROC curve smoothed with a density function:
ci(s, boot.n=100)
Run the code above in your browser using DataLab