| Title: | Optimal Diagnostic Cutoff Selection under Scale Mixtures of Skew-Normal Distributions |
|---|---|
| Description: | Implements a parametric decision-theoretic framework for optimal diagnostic cutoff selection under the family of scale mixtures of skew-normal (SMSN) distributions, including the skew-normal (SN) and skew-t (ST) models as special cases. The optimal cutoff is defined by minimising a weighted misclassification risk that incorporates disease prevalence and asymmetric costs, leading to a likelihood-ratio equation that generalises the Youden criterion. Under a monotone likelihood ratio condition, existence, uniqueness, and global optimality of the cutoff are established. Asymptotic normality and a closed-form plug-in variance estimator are provided via the implicit function theorem and the multivariate delta method. Tools for model fitting, cutoff estimation, confidence intervals, the local identifiability diagnostic, and Monte Carlo simulation are included. The methodology is described in de Paula, Mouriño, and Dias Domingues (2026) <doi:10.48550/arXiv.2605.07829>. |
| Authors: | Renato de Paula [aut, cre] (ORCID: <https://orcid.org/0000-0002-5835-5918>), Helena Mouriño [aut], Tiago Dias Domingues [aut] |
| Maintainer: | Renato de Paula <[email protected]> |
| License: | GPL-3 |
| Version: | 0.1.0 |
| Built: | 2026-05-19 14:14:04 UTC |
| Source: | https://github.com/cran/smsncut |
Builds a compact admissible interval for cutoff optimisation from the
quantiles of the fitted SMSN distributions
(method "effective_support") or from extreme quantiles of the pooled
observed sample (method "pooled_quantile").
cutoff_admissible_interval( theta0, theta1, family0, family1, method = c("effective_support", "pooled_quantile"), alpha = 0.01, x0 = NULL, x1 = NULL, probs = c(0.005, 0.995) )cutoff_admissible_interval( theta0, theta1, family0, family1, method = c("effective_support", "pooled_quantile"), alpha = 0.01, x0 = NULL, x1 = NULL, probs = c(0.005, 0.995) )
theta0 |
Numeric vector. Parameters for group 0. |
theta1 |
Numeric vector. Parameters for group 1. |
family0 |
Character. Family for group 0. |
family1 |
Character. Family for group 1. |
method |
Character. |
alpha |
Numeric in |
x0 |
Numeric vector. Group-0 data (required for
|
x1 |
Numeric vector. Group-1 data (required for
|
probs |
Numeric vector of length 2. Quantile probabilities for
|
Named numeric vector c(a = ..., b = ...).
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) cutoff_admissible_interval(theta0, theta1, "SN", "SN")theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) cutoff_admissible_interval(theta0, theta1, "SN", "SN")
Checks whether and
, which guarantees that the estimating equation
has a root inside .
cutoff_boundary_check( a, b, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1 )cutoff_boundary_check( a, b, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1 )
a |
Numeric. Left endpoint of the admissible interval. |
b |
Numeric. Right endpoint. |
theta0 |
Numeric vector. Unconstrained parameters for group 0. |
theta1 |
Numeric vector. Unconstrained parameters for group 1. |
family0 |
Character. Family for group 0 ( |
family1 |
Character. Family for group 1 ( |
lambda0 |
Positive numeric. Cost of a false positive. |
lambda1 |
Positive numeric. Cost of a false negative. |
pi0 |
Numeric in |
pi1 |
Numeric in |
A list with components phi_a, phi_b,
target_ratio, cond_a, cond_b, satisfied.
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) ab <- cutoff_admissible_interval(theta0, theta1, "SN", "SN") cutoff_boundary_check(ab["a"], ab["b"], theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) ab <- cutoff_admissible_interval(theta0, theta1, "SN", "SN") cutoff_boundary_check(ab["a"], ab["b"], theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)
Constructs an interval
where is the 0.5th–99.5th percentile effective support of the
two fitted distributions and is its width. This mirrors the
interval construction in the Monte Carlo study of de Paula et al. (2026).
cutoff_build_interval(theta, family0, family1, pad = 0.5)cutoff_build_interval(theta, family0, family1, pad = 0.5)
theta |
Numeric vector: |
family0 |
Character. Family for group 0. |
family1 |
Character. Family for group 1. |
pad |
Non-negative numeric. Relative expansion factor |
Named numeric vector c(a = ..., b = ...), or
c(a = NA, b = NA) if quantiles are non-finite.
theta <- c(0, log(1), 1, 2, log(1), 1.5) # theta0 then theta1, both SN cutoff_build_interval(theta, "SN", "SN") cutoff_build_interval(theta, "SN", "SN", pad = 0.5)theta <- c(0, log(1), 1, 2, log(1), 1.5) # theta0 then theta1, both SN cutoff_build_interval(theta, "SN", "SN") cutoff_build_interval(theta, "SN", "SN", pad = 0.5)
Constructs a Wald-type confidence interval for
(equation (31) of de Paula et al., 2026):
cutoff_ci(c_star, V, n, conf_level = 0.95)cutoff_ci(c_star, V, n, conf_level = 0.95)
c_star |
Numeric. The plug-in optimal cutoff. |
V |
Numeric. Asymptotic variance from |
n |
Integer. Total sample size |
conf_level |
Numeric in |
Named numeric vector c(lower, upper, length).
cutoff_ci(c_star = 1.6, V = 0.81, n = 400, conf_level = 0.95)cutoff_ci(c_star = 1.6, V = 0.81, n = 400, conf_level = 0.95)
Implements the additive interval-expansion strategy of de Paula et al.
(2026, Section 6.1.1): starting from pad = pad_init, the padding
is incremented by pad_step at each iteration until the boundary
conditions and are satisfied, or
max_iter attempts are exhausted.
cutoff_expand_interval_until_valid( theta, family0, family1, lambda0, lambda1, pi0, pi1, pad_init = 0, pad_step = 0.2, max_iter = 20L )cutoff_expand_interval_until_valid( theta, family0, family1, lambda0, lambda1, pi0, pi1, pad_init = 0, pad_step = 0.2, max_iter = 20L )
theta |
Numeric vector: |
family0 |
Character. Family for group 0. |
family1 |
Character. Family for group 1. |
lambda0 |
Positive numeric. Cost of a false positive. |
lambda1 |
Positive numeric. Cost of a false negative. |
pi0 |
Numeric in |
pi1 |
Numeric in |
pad_init |
Initial padding. Default |
pad_step |
Additive padding increment. Default |
max_iter |
Maximum expansion attempts. Default |
The output of cutoff_boundary_check for the final
interval, augmented with a, b, ok, pad,
and iter.
theta <- c(0, log(1), 1, 2, log(1), 1.5) cutoff_expand_interval_until_valid(theta, "SN", "SN", lambda0 = 1, lambda1 = 1, pi0 = 0.5, pi1 = 0.5)theta <- c(0, log(1), 1, 2, log(1), 1.5) cutoff_expand_interval_until_valid(theta, "SN", "SN", lambda0 = 1, lambda1 = 1, pi0 = 0.5, pi1 = 0.5)
Computes at the optimal cutoff
(Remark 5.14 of de Paula et al., 2026). Small values indicate a locally
flat estimating equation, inflated asymptotic variance, and suggest using
bootstrap confidence intervals instead of Wald intervals.
cutoff_identifiability( c_star, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1 )cutoff_identifiability( c_star, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1 )
c_star |
Numeric. The optimal cutoff. |
theta0 |
Numeric vector. Unconstrained parameters for group 0. |
theta1 |
Numeric vector. Unconstrained parameters for group 1. |
family0 |
Character. Family for group 0 ( |
family1 |
Character. Family for group 1 ( |
lambda0 |
Positive numeric. Cost of a false positive. |
lambda1 |
Positive numeric. Cost of a false negative. |
pi0 |
Numeric in |
pi1 |
Numeric in |
Positive numeric scalar.
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) c_star <- cutoff_optimal(theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)$cutoff cutoff_identifiability(c_star, theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) c_star <- cutoff_optimal(theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)$cutoff cutoff_identifiability(c_star, theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)
Estimates the decision-theoretic optimal cutoff as the minimiser
of the weighted misclassification risk over the admissible
interval (Theorem 5.13 of de Paula et al., 2026). Under the
monotone likelihood ratio condition, is the unique root of
, found by grid search followed by
uniroot. If no root is found, optimize on is used
as fallback.
cutoff_optimal( theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1, interval = NULL, grid_length = 5000L )cutoff_optimal( theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1, interval = NULL, grid_length = 5000L )
theta0 |
Numeric vector. MLE for group 0. |
theta1 |
Numeric vector. MLE for group 1. |
family0 |
Character. Family for group 0. |
family1 |
Character. Family for group 1. |
lambda0 |
Positive numeric. Cost of a false positive. |
lambda1 |
Positive numeric. Cost of a false negative. |
pi0 |
Numeric in |
pi1 |
Numeric in |
interval |
Numeric vector |
grid_length |
Integer. Grid resolution for sign-change search.
Default |
A list with cutoff, risk, interval,
and boundary.
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) res <- cutoff_optimal(theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5) res$cutofftheta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) res <- cutoff_optimal(theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5) res$cutoff
Computes the derivative of the weighted misclassification risk with respect to the threshold (equation (15) of de Paula et al., 2026):
An interior minimiser satisfies , equivalently
.
cutoff_phi(c, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1)cutoff_phi(c, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1)
c |
Numeric scalar. Threshold value. |
theta0 |
Numeric vector. Unconstrained parameters for group 0. |
theta1 |
Numeric vector. Unconstrained parameters for group 1. |
family0 |
Character. Family for group 0 ( |
family1 |
Character. Family for group 1 ( |
lambda0 |
Positive numeric. Cost of a false positive. |
lambda1 |
Positive numeric. Cost of a false negative. |
pi0 |
Numeric in |
pi1 |
Numeric in |
Numeric scalar.
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) cutoff_phi(1.6, theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) cutoff_phi(1.6, theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)
Computes the expected weighted misclassification risk at threshold
(equation (13) of de Paula et al., 2026):
cutoff_risk(c, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1)cutoff_risk(c, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1)
c |
Numeric scalar. Threshold value. |
theta0 |
Numeric vector. Unconstrained parameters for group 0. |
theta1 |
Numeric vector. Unconstrained parameters for group 1. |
family0 |
Character. Family for group 0 ( |
family1 |
Character. Family for group 1 ( |
lambda0 |
Positive numeric. Cost of a false positive. |
lambda1 |
Positive numeric. Cost of a false negative. |
pi0 |
Numeric in |
pi1 |
Numeric in |
Numeric scalar.
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) cutoff_risk(1.5, theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) cutoff_risk(1.5, theta0, theta1, "SN", "SN", 1, 1, 0.5, 0.5)
Computes the closed-form plug-in asymptotic variance of
under separate SMSN maximum-likelihood fits (Theorem 5.13 and
Proposition 5.15 of de Paula et al., 2026):
where and is the Hessian
of the negative log-likelihood for group (equation (26)).
cutoff_variance( c_star, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1, hess0, hess1, n0, n1, ridge = 1e-06 )cutoff_variance( c_star, theta0, theta1, family0, family1, lambda0, lambda1, pi0, pi1, hess0, hess1, n0, n1, ridge = 1e-06 )
c_star |
Numeric. The plug-in optimal cutoff. |
theta0 |
Numeric vector. MLE for group 0. |
theta1 |
Numeric vector. MLE for group 1. |
family0 |
Character. Family for group 0. |
family1 |
Character. Family for group 1. |
lambda0 |
Positive numeric. Cost of a false positive. |
lambda1 |
Positive numeric. Cost of a false negative. |
pi0 |
Numeric in |
pi1 |
Numeric in |
hess0 |
Numeric matrix. Hessian of the negative log-likelihood for
group 0 at the MLE (from |
hess1 |
Numeric matrix. Hessian for group 1. |
n0 |
Integer. Group-0 sample size. |
n1 |
Integer. Group-1 sample size. |
ridge |
Numeric. Ridge added to each Hessian diagonal for numerical
stability. Default |
Numeric scalar. The asymptotic variance .
Estimates the Youden cutoff as the maximiser of
over the admissible
interval (equation (9) of de Paula et al., 2026), implemented via
optimize on directly.
cutoff_youden( theta0, theta1, family0, family1, interval = NULL, grid_length = 5000L )cutoff_youden( theta0, theta1, family0, family1, interval = NULL, grid_length = 5000L )
theta0 |
Numeric vector. Parameters for group 0. |
theta1 |
Numeric vector. Parameters for group 1. |
family0 |
Character. Family for group 0. |
family1 |
Character. Family for group 1. |
interval |
Numeric vector of length 2. If |
grid_length |
Number of grid points used in the search. |
Numeric scalar. The estimated Youden cutoff.
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) cutoff_youden(theta0, theta1, "SN", "SN")theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) cutoff_youden(theta0, theta1, "SN", "SN")
Run one Monte Carlo replication
mc_one_rep( theta0_true, theta1_true, family0, family1, c_true, lambda0, lambda1, pi0, pi1, n0, n1, flat_tol = 0.001, conf_level = 0.95 )mc_one_rep( theta0_true, theta1_true, family0, family1, c_true, lambda0, lambda1, pi0, pi1, n0, n1, flat_tol = 0.001, conf_level = 0.95 )
theta0_true |
True parameter vector for group 0. |
theta1_true |
True parameter vector for group 1. |
family0 |
Family for group 0. |
family1 |
Family for group 1. |
c_true |
True optimal cutoff. |
lambda0 |
Cost of a false positive. |
lambda1 |
Cost of a false negative. |
pi0 |
Prevalence of group 0. |
pi1 |
Prevalence of group 1. |
n0 |
Sample size for group 0. |
n1 |
Sample size for group 1. |
flat_tol |
Minimum admissible value of the identifiability diagnostic. |
conf_level |
Confidence level. |
A list describing the replication outcome.
Run all scenarios
mc_run_all( scenarios, n_vec = c(200L, 400L, 1000L), B = 2000L, flat_tol = 0.001, conf_level = 0.95, seed = NULL )mc_run_all( scenarios, n_vec = c(200L, 400L, 1000L), B = 2000L, flat_tol = 0.001, conf_level = 0.95, seed = NULL )
scenarios |
Named list of scenarios. |
n_vec |
Vector of total sample sizes. |
B |
Number of replications. |
flat_tol |
Identifiability tolerance. |
conf_level |
Confidence level. |
seed |
Optional random seed. |
Named list of simulation outputs.
Monte Carlo simulation for one scenario
mc_simulate_scenario( theta0_true, theta1_true, family0, family1, c_true, lambda0, lambda1, pi0, pi1, n0, n1, B = 2000L, flat_tol = 0.001, conf_level = 0.95, seed = NULL )mc_simulate_scenario( theta0_true, theta1_true, family0, family1, c_true, lambda0, lambda1, pi0, pi1, n0, n1, B = 2000L, flat_tol = 0.001, conf_level = 0.95, seed = NULL )
theta0_true |
True parameter vector for group 0. |
theta1_true |
True parameter vector for group 1. |
family0 |
Family for group 0. |
family1 |
Family for group 1. |
c_true |
True optimal cutoff. |
lambda0 |
Cost of a false positive. |
lambda1 |
Cost of a false negative. |
pi0 |
Prevalence of group 0. |
pi1 |
Prevalence of group 1. |
n0 |
Sample size for group 0. |
n1 |
Sample size for group 1. |
B |
Number of replications. |
flat_tol |
Minimum admissible value of the identifiability diagnostic. |
conf_level |
Confidence level. |
seed |
Optional random seed. |
A list of Monte Carlo summary statistics.
Approximates the AUC by the trapezoidal rule with h grid points
(equation (13) of de Paula et al., 2026). The approximation error is
; the default gives error .
smsn_auc(theta0, theta1, family0, family1, h = 1000L)smsn_auc(theta0, theta1, family0, family1, h = 1000L)
theta0 |
Numeric vector. Parameters for group 0. |
theta1 |
Numeric vector. Parameters for group 1. |
family0 |
Character. Family for group 0. |
family1 |
Character. Family for group 1. |
h |
Integer. Number of grid points. Default |
Numeric scalar in .
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) smsn_auc(theta0, theta1, "SN", "SN")theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) smsn_auc(theta0, theta1, "SN", "SN")
Evaluates the cumulative distribution function of a skew-normal (SN) or
skew-t (ST) distribution at the unconstrained parametrisation
for SN, or
for ST.
smsn_cdf(x, theta, family = c("SN", "ST"))smsn_cdf(x, theta, family = c("SN", "ST"))
x |
Numeric vector of evaluation points. |
theta |
Numeric vector of unconstrained parameters. Length 3 for SN, length 4 for ST. |
family |
Character string, either |
Numeric vector of CDF values in .
theta_sn <- c(0, log(1), 1.5) smsn_cdf(c(-1, 0, 1, 2), theta_sn, "SN")theta_sn <- c(0, log(1), 1.5) smsn_cdf(c(-1, 0, 1, 2), theta_sn, "SN")
Evaluates the density of a skew-normal (SN) or skew-t (ST) distribution
at the unconstrained parametrisation
for SN, or for ST.
smsn_density(x, theta, family = c("SN", "ST"))smsn_density(x, theta, family = c("SN", "ST"))
x |
Numeric vector of evaluation points. |
theta |
Numeric vector of unconstrained parameters. Length 3 for SN, length 4 for ST. |
family |
Character string, either |
Numeric vector of density values.
theta_sn <- c(0, log(1), 1.5) smsn_density(c(-1, 0, 1, 2), theta_sn, "SN") theta_st <- c(0, log(1), 1.5, log(8 - 2)) smsn_density(c(-1, 0, 1, 2), theta_st, "ST")theta_sn <- c(0, log(1), 1.5) smsn_density(c(-1, 0, 1, 2), theta_sn, "SN") theta_st <- c(0, log(1), 1.5, log(8 - 2)) smsn_density(c(-1, 0, 1, 2), theta_st, "ST")
Fits a skew-normal (SN) or skew-t (ST) distribution to a univariate
sample by maximising the log-likelihood, using the unconstrained
parametrisation for SN or
for ST.
smsn_fit(x, family = c("SN", "ST"), maxit = 4000L, reltol = 1e-10)smsn_fit(x, family = c("SN", "ST"), maxit = 4000L, reltol = 1e-10)
x |
Numeric vector of observations. Non-finite values are removed. |
family |
Character string, either |
maxit |
Integer. Maximum number of iterations for |
reltol |
Numeric. Relative convergence tolerance. Default |
The observed Fisher information matrix is computed via Richardson
extrapolation as implemented in numDeriv::hessian(), which is
critical for the reliability of the plug-in variance estimator
in heavy-tailed models (de Paula et al., 2026, Section 6.2).
A list with components:
par |
MLE in unconstrained parametrisation |
hessian |
Hessian of the negative log-likelihood at the MLE, computed by Richardson extrapolation. |
logLik |
Maximised log-likelihood. |
family |
Fitted family ( |
convergence |
Convergence code from |
de Paula, R., Mouriño, H., and Dias Domingues, T. (2026). Parametric ROC analysis and optimal cutoff selection under scale mixtures of skew-normal distributions: a decision-theoretic framework with asymptotic inference. arXiv:2605.07829. doi:10.48550/arXiv.2605.07829.
set.seed(1) x <- sn::rsn(200, xi = 0, omega = 1, alpha = 2) fit <- smsn_fit(x, family = "SN") fit$par fit$convergenceset.seed(1) x <- sn::rsn(200, xi = 0, omega = 1, alpha = 2) fit <- smsn_fit(x, family = "SN") fit$par fit$convergence
Returns quantiles of a skew-normal (SN) or skew-t (ST) distribution at
the unconstrained parametrisation
for SN, or for ST.
smsn_quantile(prob, theta, family = c("SN", "ST"))smsn_quantile(prob, theta, family = c("SN", "ST"))
prob |
Numeric vector of probabilities in |
theta |
Numeric vector of unconstrained parameters. Length 3 for SN, length 4 for ST. |
family |
Character string, either |
Numeric vector of quantiles.
theta_sn <- c(0, log(1), 1.5) smsn_quantile(c(0.025, 0.5, 0.975), theta_sn, "SN")theta_sn <- c(0, log(1), 1.5) smsn_quantile(c(0.025, 0.5, 0.975), theta_sn, "SN")
Generates random observations from a skew-normal (SN) or skew-t (ST)
distribution under the unconstrained parametrisation
for SN, or
for ST.
smsn_rdist(n, theta, family = c("SN", "ST"))smsn_rdist(n, theta, family = c("SN", "ST"))
n |
Integer. Number of observations. |
theta |
Numeric vector of unconstrained parameters. Length 3 for SN, length 4 for ST. |
family |
Character string, either |
Numeric vector of length n.
set.seed(1) theta_sn <- c(0, log(1), 1.5) x <- smsn_rdist(100L, theta_sn, "SN") hist(x, breaks = 20, main = "SN sample")set.seed(1) theta_sn <- c(0, log(1), 1.5) x <- smsn_rdist(100L, theta_sn, "SN") hist(x, breaks = 20, main = "SN sample")
Evaluates the parametric ROC curve at false positive rates
(equation (6) of de Paula et al., 2026):
smsn_roc(r, theta0, theta1, family0, family1)smsn_roc(r, theta0, theta1, family0, family1)
r |
Numeric vector of false positive rates in |
theta0 |
Numeric vector. Parameters for group 0. |
theta1 |
Numeric vector. Parameters for group 1. |
family0 |
Character. Family for group 0. |
family1 |
Character. Family for group 1. |
Numeric vector of true positive rates.
theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) r <- seq(0.01, 0.99, by = 0.01) plot(r, smsn_roc(r, theta0, theta1, "SN", "SN"), type = "l", xlab = "FPR", ylab = "TPR") abline(0, 1, lty = 2)theta0 <- c(0, log(1), 1) theta1 <- c(2, log(1), 1.5) r <- seq(0.01, 0.99, by = 0.01) plot(r, smsn_roc(r, theta0, theta1, "SN", "SN"), type = "l", xlab = "FPR", ylab = "TPR") abline(0, 1, lty = 2)