| name | diagnostic-accuracy |
| description | Diagnostic accuracy analysis in R, including sensitivity, specificity, ROC curves, likelihood ratios, and decision curves. |
Diagnostic Accuracy Analysis in R
Overview
Comprehensive diagnostic test accuracy analysis covering ROC curve analysis, optimal cutpoint determination, sensitivity and specificity estimation, likelihood ratios, decision curve analysis, inter-rater reliability measures, and diagnostic meta-analysis.
Basic Diagnostic Measures
2x2 Table Analysis
library(epiR)
diag_table <- matrix(c(85, 15,
20, 180),
nrow = 2, byrow = TRUE,
dimnames = list(
Test = c("Positive", "Negative"),
Disease = c("Present", "Absent")
))
results <- epi.tests(as.table(diag_table), method = "exact")
print(results)
results$detail
Manual Calculations
TP <- 85; FN <- 15; FP <- 20; TN <- 180
sensitivity <- TP / (TP + FN)
specificity <- TN / (TN + FP)
ppv <- TP / (TP + FP)
npv <- TN / (TN + FN)
lr_pos <- sensitivity / (1 - specificity)
lr_neg <- (1 - sensitivity) / specificity
dor <- (TP * TN) / (FP * FN)
youden <- sensitivity + specificity - 1
accuracy <- (TP + TN) / (TP + TN + FP + FN)
library(binom)
se_ci <- binom.confint(TP, TP + FN, method = "wilson")
sp_ci <- binom.confint(TN, TN + FP, method = "wilson")
ROC Curve Analysis
Basic ROC Curve
library(pROC)
roc_obj <- roc(
response = df$disease,
predictor = df$biomarker,
levels = c(0, 1),
direction = "<"
)
print(roc_obj)
auc(roc_obj)
ci.auc(roc_obj, method = "delong")
ci.auc(roc_obj, method = "bootstrap", boot.n = 2000)
ROC Curve Plotting
library(pROC)
plot(roc_obj, print.auc = TRUE, print.thres = TRUE)
ggroc(roc_obj) +
geom_abline(intercept = 1, slope = 1, linetype = "dashed", color = "gray") +
annotate("text", x = 0.25, y = 0.25,
label = paste0("AUC = ", round(auc(roc_obj), 3))) +
theme_bw() +
labs(x = "Specificity", y = "Sensitivity",
title = "ROC Curve for Biomarker X")
roc1 <- roc(df$disease, df$biomarker1)
roc2 <- roc(df$disease, df$biomarker2)
roc3 <- roc(df$disease, df$biomarker3)
ggroc(list(Biomarker1 = roc1, Biomarker2 = roc2, Biomarker3 = roc3)) +
geom_abline(intercept = 1, slope = 1, linetype = "dashed") +
theme_bw() +
scale_color_brewer(palette = "Set1")
Comparing ROC Curves
library(pROC)
roc_test <- roc.test(roc1, roc2, method = "delong")
print(roc_test)
roc_test_boot <- roc.test(roc1, roc2, method = "bootstrap", boot.n = 2000)
roc_test_venk <- roc.test(roc1, roc2, method = "venkatraman")
comparison <- data.frame(
Marker = c("Biomarker1", "Biomarker2", "Biomarker3"),
AUC = c(auc(roc1), auc(roc2), auc(roc3)),
CI_Lower = c(ci.auc(roc1)[1], ci.auc(roc2)[1], ci.auc(roc3)[1]),
CI_Upper = c(ci.auc(roc1)[3], ci.auc(roc2)[3], ci.auc(roc3)[3])
)
Partial AUC
library(pROC)
pauc_spec <- auc(roc_obj, partial.auc = c(1, 0.9),
partial.auc.focus = "specificity")
pauc_sens <- auc(roc_obj, partial.auc = c(0.9, 1),
partial.auc.focus = "sensitivity")
pauc_std <- auc(roc_obj, partial.auc = c(1, 0.9),
partial.auc.focus = "specificity",
partial.auc.correct = TRUE)
ci.auc(roc_obj, partial.auc = c(1, 0.9),
partial.auc.focus = "specificity")
Optimal Cutpoint Selection
Using cutpointr Package
library(cutpointr)
cp_youden <- cutpointr(
data = df,
x = biomarker,
class = disease,
method = maximize_metric,
metric = youden
)
summary(cp_youden)
plot(cp_youden)
cp_constrained <- cutpointr(
df, biomarker, disease,
method = maximize_metric,
metric = sensitivity,
tol_metric = specificity,
tol_threshold = 0.9
)
cp_cost <- cutpointr(
df, biomarker, disease,
method = minimize_metric,
metric = misclassification_cost,
cost_fp = 1,
cost_fn = 5
)
cp_multi <- multi_cutpointr(
df, biomarker, disease,
method = maximize_metric,
metric = youden,
boot_cut = 1000
)
Using OptimalCutpoints Package
library(OptimalCutpoints)
opt_cut <- optimal.cutpoints(
X = "biomarker",
status = "disease",
methods = c("Youden", "MaxSpSe", "MaxProdSpSe", "ROC01",
"MinValueSp", "MinValueSe", "MaxEfficiency"),
data = df,
tag.healthy = 0
)
summary(opt_cut)
opt_cost <- optimal.cutpoints(
X = "biomarker",
status = "disease",
methods = "CB",
data = df,
tag.healthy = 0,
costs.ratio = 5,
prevalence = 0.10
)
Manual Cutpoint Selection
library(pROC)
coords_youden <- coords(roc_obj, "best", best.method = "youden")
print(coords_youden)
coords_closest <- coords(roc_obj, "best", best.method = "closest.topleft")
coords_se90 <- coords(roc_obj, x = 0.90, input = "sensitivity",
ret = c("threshold", "sensitivity", "specificity"))
all_coords <- coords(roc_obj, x = "all",
ret = c("threshold", "sensitivity", "specificity",
"ppv", "npv", "accuracy"))
Decision Curve Analysis
Using dcurves Package
library(dcurves)
model1 <- glm(cancer ~ age + psa, data = df, family = binomial)
model2 <- glm(cancer ~ age + psa + dre, data = df, family = binomial)
df$pred1 <- predict(model1, type = "response")
df$pred2 <- predict(model2, type = "response")
dca_result <- dca(
cancer ~ pred1 + pred2,
data = df,
thresholds = seq(0, 0.5, by = 0.01),
label = list(pred1 = "PSA Model", pred2 = "PSA + DRE Model")
)
plot(dca_result)
plot(dca_result, smooth = TRUE) +
ggplot2::coord_cartesian(ylim = c(-0.05, 0.2)) +
ggplot2::labs(x = "Threshold Probability",
y = "Net Benefit",
title = "Decision Curve Analysis")
Net Benefit Calculation
library(dcurves)
nb_data <- as_tibble(dca_result)
net_intervention_avoided(dca_result)
standardized_net_benefit <- function(nb, prevalence, threshold) {
max_nb <- prevalence - (1 - prevalence) * threshold / (1 - threshold)
nb / max_nb
}
Clinical Utility Visualization
library(dcurves)
dca_result |>
plot(type = "clinical_impact")
dca_boot <- dca(
cancer ~ pred1,
data = df,
thresholds = seq(0, 0.5, by = 0.05)
)
Inter-Rater Reliability
Cohen's Kappa (Two Raters)
library(irr)
ratings <- data.frame(
rater1 = c(1, 2, 3, 1, 2, 3, 1, 2, 3, 1),
rater2 = c(1, 2, 3, 1, 2, 2, 1, 3, 3, 2)
)
kappa_unweighted <- kappa2(ratings, weight = "unweighted")
print(kappa_unweighted)
kappa_linear <- kappa2(ratings, weight = "equal")
kappa_quadratic <- kappa2(ratings, weight = "squared")
Fleiss' Kappa (Multiple Raters)
library(irr)
ratings_multi <- matrix(c(
1, 1, 1, 2,
2, 2, 2, 2,
3, 3, 2, 3,
1, 1, 1, 1,
2, 3, 2, 2
), nrow = 5, byrow = TRUE)
fleiss_k <- kappam.fleiss(ratings_multi)
print(fleiss_k)
light_k <- kappam.light(ratings_multi)
Intraclass Correlation Coefficient (ICC)
library(irr)
measurements <- data.frame(
rater1 = c(2.5, 3.1, 4.2, 2.8, 3.5),
rater2 = c(2.4, 3.3, 4.0, 2.9, 3.4),
rater3 = c(2.6, 3.0, 4.1, 2.7, 3.6)
)
icc_result <- icc(measurements, model = "twoway", type = "agreement", unit = "single")
print(icc_result)
icc_avg <- icc(measurements, model = "twoway", type = "consistency", unit = "average")
Agreement for Continuous Data
library(BlandAltmanLeh)
ba <- bland.altman.stats(
method1 = df$measurement1,
method2 = df$measurement2
)
bland.altman.plot(
method1 = df$measurement1,
method2 = df$measurement2,
main = "Bland-Altman Plot",
xlab = "Mean of Methods",