tabdiag: Comprehensive Diagnostic Accuracy and ROC Analysis

View source: R/tabdiag.R

tabdiagR Documentation

Comprehensive Diagnostic Accuracy and ROC Analysis

Description

tabdiag() is the comprehensive diagnostic-accuracy command in R4VN. It is designed for binary diagnostic tests, continuous or ordinal biomarkers, prediction scores, and comparisons of paired ROC curves. The first variable is the binary reference standard (gold standard); diagnostic tests can be supplied directly in ... and/or through vars().

The default output follows the R4VN philosophy: a compact publication-ready Viewer table is shown, while the returned object retains the complete set of diagnostic measures and ROC information for later inspection or export. Interpretation is opt-in (interpretation = FALSE by default).

Usage

tabdiag(
  outcome,
  ...,
  data = NULL,
  vars = NULL,
  event = NULL,
  positive = NULL,
  direction = c("auto", "<", ">"),
  best = "youden",
  cuts = NULL,
  target = 0.95,
  ci = TRUE,
  ci_level = 0.95,
  ci_method = c("auto", "wilson", "exact"),
  cut_ci = FALSE,
  boot = 2000,
  prevalence = NULL,
  roc = TRUE,
  partial_auc = NULL,
  partial_focus = c("specificity", "sensitivity"),
  partial_correct = FALSE,
  compare = FALSE,
  compare_method = c("delong", "bootstrap"),
  adjust = "none",
  all_cuts = FALSE,
  missing = FALSE,
  show = TRUE,
  measures = "core",
  hide = NULL,
  plot = FALSE,
  plot_args = list(),
  interpretation = FALSE,
  digit = 2,
  p_digit = 3,
  zero_correction = 0.5,
  title = NULL,
  console = FALSE,
  print = NULL,
  export = NULL,
  file = NULL,
  open = FALSE
)

Arguments

outcome

Binary reference-standard outcome. Supply an unquoted variable name or a single character variable name.

...

One or more diagnostic tests/markers. Binary variables are analyzed as 2 x 2 tests. Numeric variables, dates/times, and ordered factors are analyzed by ROC.

data

Optional data frame. If omitted, the active R4VN data selected by usedf() or opendata(..., active = TRUE) is used.

vars

Optional diagnostic-marker selection created by vars(). This may be used instead of, or together with, markers supplied in ....

event

Positive level of the reference-standard outcome. If omitted, R4VN recognizes common positive encodings such as 1, TRUE, Yes, Positive, Case, or Co; otherwise it uses the second factor/observed level and reports the selected positive outcome explicitly.

positive

Positive level for binary diagnostic tests. It may be a single value used for all binary tests, an unnamed vector in test order, or a named vector such as positive = c(rapid = "Positive", ct = "Abnormal").

direction

ROC direction: "auto" (default), "<", or ">". With R4VN conventions, "<" means higher marker values indicate the positive outcome and R4VN reports cutoffs as >=; ">" means lower values indicate the positive outcome and R4VN reports cutoffs as <=.

best

Optimal-cutoff methods. Default "youden". May contain one or more of "youden", "closest", "ruleout", and "rulein". "ruleout" finds the threshold with the highest specificity while sensitivity is at least target; "rulein" finds the threshold with the highest sensitivity while specificity is at least target. Set best = NULL to suppress automatically selected cutoffs.

cuts

Optional numeric cutoff(s) requested by the user. Every cutoff becomes a separate diagnostic-performance row for each quantitative marker. This is useful for clinically predefined thresholds.

target

Target sensitivity/specificity for "ruleout"/"rulein", default 0.95.

ci

TRUE (default), FALSE, or a character vector of measures for which confidence intervals are required. Examples include c("sens", "spec"), c("sens","spec","ppv","npv"), and c("auc","lr+","lr-","dor"). TRUE requests every currently implemented stable interval.

ci_level

Confidence level, default 0.95.

ci_method

Binomial confidence-interval method for 2 x 2 proportions: "auto"/"wilson" or "exact". AUC uses an internally implemented DeLong CI for an ordinary empirical ROC and stratified bootstrap CI for partial AUC.

cut_ci

Logical; calculate a bootstrap confidence interval for an automatically selected Youden/closest cutoff. Default FALSE because this can be computationally expensive.

boot

Number of bootstrap replicates for cutoff CI, partial-AUC CI, and bootstrap ROC comparison. Default 2000.

prevalence

Optional target disease prevalence, strictly between 0 and

  1. When supplied, PPV and NPV are recalculated for that prevalence.

roc

Logical; calculate ROC analysis for eligible non-binary tests. Default TRUE.

partial_auc

Optional numeric vector of length two defining a partial AUC range, for example c(1, 0.80) for specificity 100%-80% when partial_focus = "specificity".

partial_focus

"specificity" (default) or "sensitivity".

partial_correct

Logical; request corrected partial AUC where supported by the R4VN internal ROC engine.

compare

Logical; for two or more ROC-eligible markers, calculate pairwise comparisons of AUC. Default FALSE.

compare_method

ROC comparison method: "delong" (default) or "bootstrap". Both are implemented internally by R4VN.

adjust

Multiplicity adjustment for pairwise comparison p-values, passed to stats::p.adjust(). Examples: "none", "holm", "BH".

all_cuts

Logical; if TRUE, calculate full 2 x 2 diagnostic properties for every finite empirical ROC threshold and store them in ⁠$all_cutoffs⁠. FALSE by default because this table can be very large.

missing

Logical; include the marker-specific analysis sample table in printed/Viewer output. The sample table is always retained in ⁠$descriptive⁠. Default FALSE.

show

Logical; open the formatted result in the Viewer. Default TRUE. For backward compatibility, a character vector supplied to show is interpreted as the old diagnostic-column selection syntax.

measures

Diagnostic measures displayed in the selected-threshold table. Default "core" gives N, sensitivity, specificity, PPV, NPV, LR+, LR-, and accuracy. Other convenient profiles are "minimal", "publication", "counts", and "all"; or supply an explicit vector such as c("sens","spec","ppv","npv","lr+","lr-","dor"). Cell counts are presented as a conventional 2 x 2 diagnostic classification table rather than as separate TP/FP/FN/TN measures. The returned object always retains all calculated measures.

hide

Optional diagnostic columns to remove from the displayed cutoff table, for example hide = c("n","accuracy").

plot

FALSE (default), TRUE, "roc", "cutoff", or "both". TRUE is equivalent to "roc". Requested figures are saved internally, embedded in the Viewer report, and also sent to the RStudio Plot pane when running interactively.

plot_args

Named list of graphical arguments passed to plot.r4vn_diag(). Useful options include color, lty, line_width, legend, legend_position, auc, diagonal, grid, xlim, ylim, xlab, ylab, main, and cutoff-plot controls. ROC axes are constrained to 0-1 by default.

interpretation

Logical; add a cautious deterministic interpretation table. Default FALSE. Interpretation describes discrimination and selected thresholds but does not claim clinical utility or causality.

digit

Number of digits displayed for estimates. Default 2.

p_digit

Number of digits displayed for p-values. Default 3.

zero_correction

Continuity correction used only for a requested DOR confidence interval when a zero cell is present. Default 0.5.

title

Optional title printed above the output.

console

Logical; also print the traditional Console result. Default FALSE.

print

Deprecated compatibility argument. When supplied, it overrides console.

export

Optional export format accepted by tabexport(), such as "html", "docx", "xlsx", "pdf", or "png". Multiple formats may be requested in one call.

file

Optional export filename. Its extension may also determine the export format.

open

Logical; open the exported file when supported.

Details

Binary diagnostic tests

A test with exactly two observed non-missing values is analyzed directly as a 2 x 2 table. tabdiag() and tabdiagi() use the same internal engine, so identical TP/FP/FN/TN counts give identical sensitivity, specificity, predictive values, likelihood ratios, DOR, accuracy, balanced accuracy, Youden index, F1 score, MCC, kappa, FPR, FNR, FDR, FOR, prevalence, detection rate, and detection prevalence.

Quantitative and ordered diagnostic markers

ROC analysis is implemented inside R4VN using base R. tabdiag() therefore does not require pROC, ggplot2, or another ROC package for routine ROC, AUC, cutoff selection, DeLong inference, partial AUC, or paired AUC comparison. This keeps installation light while preserving a complete analysis object.

Numeric markers, Date/POSIX values, and ordered factors with more than two levels are analyzed by ROC. Unordered factors with more than two levels are rejected because a clinically meaningful ordering cannot safely be inferred. A marker that becomes constant after removal of missing values is retained in the sample description and omitted from ROC analysis with an explanatory note instead of crashing the whole report.

Cutoffs

best = "youden" maximizes sensitivity + specificity. "closest" minimizes distance to the upper-left ROC corner. "ruleout" and "rulein" choose thresholds meeting a target sensitivity or specificity. Tied optimal thresholds are deliberately retained rather than silently choosing one. User-defined cuts are added as separate rows rather than replacing the automatic cutoff.

Confidence intervals

Sensitivity, specificity, PPV, NPV, and accuracy use Wilson or exact binomial intervals. LR+/LR- and DOR use conventional log-scale intervals. Ordinary AUC uses DeLong CI. Partial-AUC CI and optimal-cutoff CI use stratified bootstrap. If prevalence-adjusted PPV/NPV are requested, simple binomial CIs are not attached to those adjusted predictive values because they would not correspond to the target-prevalence estimates.

AUC inference and paired ROC comparison

For each ordinary empirical ROC, R4VN stores DeLong AUC variance, standard error, z statistic, and a two-sided large-sample p-value for H0: AUC = 0.5. When compare = TRUE, every pair of eligible markers is compared on its own pairwise complete-case sample. This keeps the comparison paired and makes the reported N explicit when marker missingness differs.

Missing values

Each marker is analyzed on its own complete cases with the outcome. Pairwise ROC comparison uses complete cases for both markers and the outcome. Therefore N may differ between marker-specific AUCs and pairwise comparisons. Use missing = TRUE to show the sample accounting table; it is always available as ⁠$descriptive⁠.

Returned result contract

Backward-compatible components (⁠$summary⁠, ⁠$thresholds⁠, ⁠$coordinates⁠, ⁠$comparison⁠, ⁠$all_cutoffs⁠, ⁠$roc⁠) are retained. In addition, the object follows the common R4VN reporting contract with ⁠$descriptive⁠, ⁠$estimates⁠, ⁠$tests⁠, ⁠$diagnostics⁠, ⁠$interpretation⁠, ⁠$tables⁠, ⁠$plots⁠, ⁠$models⁠, ⁠$metadata⁠, and ⁠$call⁠.

Publication-oriented display

The default measures = "core" keeps the Viewer readable. Use measures = "publication" to add DOR, measures = "all" for the full diagnostic panel, or measures = "counts" for the 2 x 2 diagnostic classification table only. Cell counts are never shown as a vertical TP/FP/FN/TN measure list. Calculations are never discarded by this display choice. When a full performance table contains only a few rows but many columns, the Viewer automatically presents measures vertically for easier reading.

ROC plotting

ROC plots are drawn from false-positive rate (1-specificity) and sensitivity coordinates using ordinary base graphics. The default axes are exactly 0 to 1 (xaxs = "i", yaxs = "i"), preventing the small negative/>1 axis extensions that can otherwise appear in automatic plotting systems.

Value

An object of class r4vn_diag. Important components include:

  • ⁠$summary⁠: marker-specific AUC results.

  • ⁠$thresholds⁠: full diagnostic metrics at binary tests and selected cutoffs.

  • ⁠$comparison⁠: pairwise AUC comparisons.

  • ⁠$descriptive⁠: marker-specific N/missing/case/control accounting.

  • ⁠$estimates⁠: structured AUC and threshold estimates.

  • ⁠$tests⁠: AUC-vs-0.5 and pairwise ROC tests.

  • ⁠$diagnostics⁠: ROC coordinates and optional all-cutoff table.

  • ⁠$tables⁠: publication-ready display tables used by Viewer/export, including ⁠$tables$Confusion_matrix⁠ for selected tests/cutoffs.

  • ⁠$interpretation⁠: NULL by default, or an interpretation table when requested.

  • ⁠$roc⁠ / ⁠$models$roc⁠: lightweight internal R4VN ROC objects.

Examples - 1. Simplest binary test

d_bin <- data.frame(
  disease = c(1,1,1,1,0,0,0,0),
  rapid   = c(1,1,1,0,1,0,0,0)
)
tabdiag(disease, rapid, data = d_bin)

Examples - 2. Explicit positive outcome/test level

d_txt <- data.frame(
  truth = factor(c("No","Yes","Yes","No","Yes","No")),
  test  = factor(c("Neg","Pos","Pos","Neg","Neg","Pos"))
)
tabdiag(truth, test, data = d_txt, event = "Yes", positive = "Pos")

Examples - 3. Several binary tests with named positive levels

d_multi_bin <- data.frame(
  disease = c(1,1,1,0,0,0,1,0),
  rapid = c("Positive","Positive","Negative","Negative","Positive","Negative","Positive","Negative"),
  ct = c("Abnormal","Normal","Abnormal","Normal","Normal","Normal","Abnormal","Normal")
)
tabdiag(
  disease, rapid, ct, data = d_multi_bin,
  positive = c(rapid = "Positive", ct = "Abnormal")
)

Examples - 4. One continuous biomarker

  set.seed(101)
  d <- data.frame(
    disease = rep(c(0,1), each = 100),
    crp = c(rnorm(100, 5, 2), rnorm(100, 10, 3))
  )
  z <- tabdiag(disease, crp, data = d, show = FALSE)
  z$summary
  z$thresholds

Examples - 5. Select markers with vars()

  tabdiag(disease, data = d, vars = vars(crp), show = FALSE)

Examples - 6. Automatic and clinically specified cutoffs

  tabdiag(
    disease, crp, data = d,
    best = "youden", cuts = c(5, 7.5, 10), show = FALSE
  )

Examples - 7. Several optimal-cutoff definitions

  tabdiag(
    disease, crp, data = d,
    best = c("youden", "closest", "ruleout", "rulein"),
    target = 0.90, show = FALSE
  )

Examples - 8. Disable automatic cutoffs and use only clinical cutoffs

  tabdiag(disease, crp, data = d, best = NULL, cuts = c(6, 8, 10), show = FALSE)

Examples - 9. Confidence-interval control

  tabdiag(disease, crp, data = d, ci = c("auc", "sens", "spec"), show = FALSE)
  tabdiag(disease, crp, data = d, ci = TRUE, show = FALSE)
  tabdiag(disease, crp, data = d, ci = FALSE, show = FALSE)

Examples - 10. Exact binomial CI for diagnostic proportions

tabdiag(disease, rapid, data = d_bin, ci = TRUE, ci_method = "exact", show = FALSE)

Examples - 11. Bootstrap CI for the optimal cutoff

  tabdiag(
    disease, crp, data = d, cut_ci = TRUE,
    boot = 200, show = FALSE
  )

Examples - 12. Predictive values at a target prevalence

  tabdiag(disease, crp, data = d, prevalence = 0.10, show = FALSE)

Examples - 13. Pairwise DeLong comparison

  set.seed(102)
  d$pct <- c(rnorm(100, 1, 0.8), rnorm(100, 3, 1.2))
  tabdiag(disease, crp, pct, data = d, compare = TRUE, show = FALSE)

Examples - 14. Multiple-comparison adjustment

  d$score3 <- d$crp + rnorm(nrow(d), 0, 2)
  tabdiag(
    disease, crp, pct, score3, data = d,
    compare = TRUE, adjust = "holm", show = FALSE
  )

Examples - 15. Bootstrap ROC comparison

  tabdiag(
    disease, crp, pct, data = d,
    compare = TRUE, compare_method = "bootstrap", boot = 200,
    show = FALSE
  )

Examples - 16. Partial AUC

  tabdiag(
    disease, crp, data = d,
    partial_auc = c(1, 0.80), partial_focus = "specificity",
    partial_correct = TRUE, show = FALSE
  )

Examples - 17. Ordered diagnostic score

  d_ord <- data.frame(
    disease = c(0,0,0,0,1,1,1,1,1,0),
    score = ordered(c("Low","Low","Medium","Low","Medium","High","High","Medium","High","Medium"),
                    levels = c("Low","Medium","High"))
  )
  tabdiag(disease, score, data = d_ord, show = FALSE)

Examples - 18. Lower values indicate disease

  d$low_marker <- -d$crp
  tabdiag(disease, low_marker, data = d, direction = ">", show = FALSE)

Examples - 19. Marker-specific missing values

  d_missing <- d
  d_missing$crp[1:12] <- NA
  d_missing$pct[21:35] <- NA
  zmis <- tabdiag(
    disease, crp, pct, data = d_missing,
    compare = TRUE, missing = TRUE, show = FALSE
  )
  zmis$descriptive
  zmis$comparison

Examples - 20. Compact, publication, counts, and full displays

  tabdiag(disease, crp, data = d, measures = "minimal", show = FALSE)
  tabdiag(disease, crp, data = d, measures = "publication", show = FALSE)
  tabdiag(disease, crp, data = d, measures = "counts", show = FALSE)
  tabdiag(disease, crp, data = d, measures = "all", show = FALSE)

Examples - 21. Explicit measure selection and hiding columns

  tabdiag(
    disease, crp, data = d,
    measures = c("sens","spec","ppv","npv","lr+","lr-","dor"),
    hide = "npv", show = FALSE
  )

Examples - 22. Full metrics at every empirical ROC threshold

  zall <- tabdiag(disease, crp, data = d, all_cuts = TRUE, show = FALSE)
  head(zall$all_cutoffs)

Examples - 23. Interpretation is explicit opt-in

  zint <- tabdiag(disease, crp, data = d, interpretation = TRUE, show = FALSE)
  zint$interpretation

Examples - 24. ROC graph with publication controls

  tabdiag(
    disease, crp, pct, data = d, plot = "roc",
    plot_args = list(
      line_width = 2, diagonal = TRUE, grid = TRUE,
      legend_position = "bottomright",
      xlab = "1 - Specificity", ylab = "Sensitivity",
      main = "ROC curves"
    )
  )

Examples - 25. Sensitivity/specificity across cutoffs

  tabdiag(
    disease, crp, data = d, plot = "cutoff",
    plot_args = list(cutoff_mark = TRUE)
  )

Examples - 26. Draw both graph families from a saved object

  z <- tabdiag(disease, crp, pct, data = d, plot = FALSE, show = FALSE)
  plot(z, what = "roc")
  plot(z, what = "cutoff")

Examples - 27. Active R4VN data

\donttest{
  usedf(d)
  tabdiag(disease, crp, show = FALSE)
}

Examples - 28. Export publication tables

\donttest{
  tabdiag(
    disease, crp, data = d, show = FALSE,
    export = "xlsx", file = tempfile(fileext = ".xlsx")
  )
}

Examples - 29. Inspect the stable R4VN result contract

  z <- tabdiag(disease, crp, data = d, show = FALSE)
  names(z$tables)
  z$estimates$auc
  z$tests$auc_vs_0.5
  z$diagnostics$coordinates
  z$tables$Confusion_matrix
  z$metadata

Examples - 30. Full confusion matrix and full performance panel

  zfull <- tabdiag(
    disease, crp, data = d,
    measures = "all", missing = TRUE, show = FALSE
  )
  zfull$tables$Diagnostic_performance
  zfull$tables$Confusion_matrix

Examples - 31. Save publication-ready ROC and cutoff plots

\donttest{
  zplot <- tabdiag(disease, crp, data = d, show = FALSE)
  plot(
    zplot, what = "roc",
    file = tempfile(fileext = ".png"),
    width = 7, height = 7, res = 300,
    line_width = 2, grid = TRUE
  )
  plot(
    zplot, what = "cutoff",
    file = tempfile(fileext = ".pdf"),
    width = 7, height = 7, cutoff_mark = TRUE
  )
}

Examples

  set.seed(123)
  dd <- data.frame(
    disease = rep(c(0, 1), each = 40),
    marker = c(rnorm(40, 0, 1), rnorm(40, 1.5, 1))
  )
  z <- tabdiag(disease, marker, data = dd, show = FALSE)
  z$summary


R4VN documentation built on Sept. 30, 2026, 5:13 p.m.