Skip to contents

Not yet released in meddecide. This article documents decisionpanel, which is not part of the meddecide 1.0.4 release. It is documented here ahead of a future release, so the options, defaults and output described below are subject to change and the analysis will not appear in your jamovi menu yet. For the analyses that ship today see the meddecide articles index.

Introduction

The Decision Panel Optimization module in the meddecide package provides a comprehensive framework for optimizing diagnostic test combinations in medical decision-making. This vignette introduces the basic concepts and demonstrates core functionality.

Key Concepts

Testing Strategies

When multiple diagnostic tests are available, they can be combined in different ways:

  1. Single Testing: Use individual tests independently
  2. Parallel Testing: Perform multiple tests simultaneously
    • ANY rule (OR): Positive if any test is positive
    • ALL rule (AND): Positive only if all tests are positive
    • MAJORITY rule: Positive if majority of tests are positive
  3. Sequential Testing: Perform tests in sequence based on previous results
    • Stop on first positive
    • Confirmatory (require multiple positives)
    • Exclusion (require multiple negatives)

Optimization Criteria

The module can optimize test panels based on various criteria:

  • Accuracy: Overall correct classification rate
  • Sensitivity: Ability to detect disease (minimize false negatives)
  • Specificity: Ability to rule out disease (minimize false positives)
  • Predictive Values: PPV and NPV
  • Cost-Effectiveness: Balance performance with resource utilization
  • Utility: Custom utility functions incorporating costs of errors

Installation and Loading

# Install meddecide package
install.packages("meddecide")

# Or install from GitHub
devtools::install_github("ClinicoPath/meddecide")

Basic Example: COVID-19 Screening

Let’s start with a simple example using COVID-19 screening data:

# Examine the data structure
str(covid_screening_data)

# Check disease prevalence
table(covid_screening_data$covid_status)
prop.table(table(covid_screening_data$covid_status))

Running Basic Analysis

# Basic decision panel analysis
covid_panel <- decisionpanel(
  data = covid_screening_data,
  tests = c("rapid_antigen", "pcr", "chest_ct"),
  testLevels = c("Positive", "Positive", "Abnormal"),
  gold = "covid_status",
  goldPositive = "Positive",
  strategies = "all",
  optimizationCriteria = "accuracy"
)

Interpreting Results

The analysis provides several key outputs:

  1. Individual Test Performance: How each test performs alone
  2. Optimal Panel: The best combination of tests
  3. Strategy Comparison: Performance of different testing approaches
  4. Decision Tree: Optimal sequence for testing

Understanding Testing Strategies

Parallel Testing Example

# Simulate parallel testing with ANY rule
# Positive if rapid_antigen OR pcr is positive
parallel_any <- with(
  covid_screening_data,
  rapid_antigen == "Positive" | pcr == "Positive"
)

# Create confusion matrix
conf_matrix_any <- table(
  Predicted = parallel_any,
  Actual = covid_screening_data$covid_status == "Positive"
)

print(conf_matrix_any)

# Calculate metrics
sensitivity_any <- conf_matrix_any[2, 2] / sum(conf_matrix_any[, 2])
specificity_any <- conf_matrix_any[1, 1] / sum(conf_matrix_any[, 1])

cat("Parallel ANY Rule:\n")
cat(sprintf("Sensitivity: %.1f%%\n", sensitivity_any * 100))
cat(sprintf("Specificity: %.1f%%\n", specificity_any * 100))

Sequential Testing Example

# Simulate sequential testing
# Start with rapid test, only do PCR if rapid is positive
sequential_result <- rep("Negative", nrow(covid_screening_data))

# Those with positive rapid test
rapid_pos_idx <- which(covid_screening_data$rapid_antigen == "Positive")

# Among those, check PCR
sequential_result[rapid_pos_idx] <-
  ifelse(covid_screening_data$pcr[rapid_pos_idx] == "Positive",
    "Positive", "Negative"
  )

# Create confusion matrix
conf_matrix_seq <- table(
  Predicted = sequential_result == "Positive",
  Actual = covid_screening_data$covid_status == "Positive"
)

print(conf_matrix_seq)

# Calculate metrics
sensitivity_seq <- conf_matrix_seq[2, 2] / sum(conf_matrix_seq[, 2])
specificity_seq <- conf_matrix_seq[1, 1] / sum(conf_matrix_seq[, 1])

cat("\nSequential Testing:\n")
cat(sprintf("Sensitivity: %.1f%%\n", sensitivity_seq * 100))
cat(sprintf("Specificity: %.1f%%\n", specificity_seq * 100))

# Calculate cost savings
pcr_tests_saved <- sum(covid_screening_data$rapid_antigen == "Negative")
cat(sprintf(
  "PCR tests saved: %d (%.1f%%)\n",
  pcr_tests_saved,
  pcr_tests_saved / nrow(covid_screening_data) * 100
))

Cost-Effectiveness Analysis

When costs are considered, the optimal strategy may change:

# Analysis with costs
covid_panel_cost <- decisionpanel(
  data = covid_screening_data,
  tests = c("rapid_antigen", "pcr", "chest_ct"),
  testLevels = c("Positive", "Positive", "Abnormal"),
  gold = "covid_status",
  goldPositive = "Positive",
  strategies = "all",
  optimizationCriteria = "utility",
  useCosts = TRUE,
  testCosts = "5,50,200", # Costs for each test
  fpCost = 500, # Cost of false positive
  fnCost = 5000 # Cost of false negative
)

Visualization

Performance Comparison Plot

# Create performance comparison data
strategies <- data.frame(
  Strategy = c("Rapid Only", "PCR Only", "Parallel ANY", "Sequential"),
  Sensitivity = c(65, 95, 98, 62),
  Specificity = c(98, 99, 97, 99.9),
  Cost = c(5, 50, 55, 15)
)

# Plot sensitivity vs specificity
ggplot(strategies, aes(x = 100 - Specificity, y = Sensitivity)) +
  geom_point(aes(size = Cost), alpha = 0.6) +
  geom_text(aes(label = Strategy), vjust = -1) +
  scale_size_continuous(range = c(3, 10)) +
  xlim(0, 5) +
  ylim(60, 100) +
  labs(
    title = "Testing Strategy Comparison",
    x = "False Positive Rate (%)",
    y = "Sensitivity (%)",
    size = "Cost ($)"
  ) +
  theme_minimal()

Decision Trees

Decision trees provide clear algorithms for clinical use:

# Generate decision tree
covid_tree <- decisionpanel(
  data = covid_screening_data,
  tests = c("rapid_antigen", "pcr", "chest_ct", "symptom_score"),
  testLevels = c("Positive", "Positive", "Abnormal", ">5"),
  gold = "covid_status",
  goldPositive = "Positive",
  createTree = TRUE,
  treeMethod = "cart",
  maxDepth = 3
)

Interpreting the Tree

A typical decision tree output might look like:

1. Start with Rapid Antigen Test
   ├─ If Positive (2% of patients)
   │  └─ Confirm with PCR
   │     ├─ If Positive → COVID Positive (PPV: 95%)
   │     └─ If Negative → COVID Negative (NPV: 98%)
   └─ If Negative (98% of patients)
      ├─ If Symptoms > 5
      │  └─ Perform Chest CT
      │     ├─ If Abnormal → Perform PCR
      │     └─ If Normal → COVID Negative
      └─ If Symptoms <= 5 → COVID Negative

Advanced Features

Cross-Validation

Validate panel performance using k-fold cross-validation:

# Run with cross-validation
covid_panel_cv <- decisionpanel(
  data = covid_screening_data,
  tests = c("rapid_antigen", "pcr", "chest_ct"),
  testLevels = c("Positive", "Positive", "Abnormal"),
  gold = "covid_status",
  goldPositive = "Positive",
  crossValidate = TRUE,
  nFolds = 5,
  seed = 123
)

Bootstrap Confidence Intervals

Get uncertainty estimates for performance metrics:

# Run with bootstrap
covid_panel_boot <- decisionpanel(
  data = covid_screening_data,
  tests = c("rapid_antigen", "pcr", "chest_ct"),
  testLevels = c("Positive", "Positive", "Abnormal"),
  gold = "covid_status",
  goldPositive = "Positive",
  bootstrap = TRUE,
  bootReps = 1000,
  seed = 123
)

Best Practices

  1. Start Simple: Begin with individual test performance before combinations
  2. Consider Context: Screening vs. diagnosis requires different strategies
  3. Validate Results: Use cross-validation or separate test sets
  4. Include Costs: Real-world decisions must consider resources
  5. Think Sequentially: Often more efficient than parallel testing
  6. Set Constraints: Define minimum acceptable performance
  7. Interpret Clinically: Statistical optimality isn’t everything

Conclusion

The Decision Panel Optimization module provides a systematic approach to combining diagnostic tests. By considering various strategies, costs, and constraints, it helps identify practical testing algorithms that balance performance with resource utilization.

Next Steps

  • See the “Clinical Applications” vignette for disease-specific examples
  • Review “Advanced Optimization” for complex scenarios
  • Check “Implementation Guide” for deploying algorithms in practice

Session Information