Statistical Supplement: A Priori Power Analysis for Urogynecology Wait Times

Introduction

This supplement provides a detailed description of our a priori power analysis for examining wait times across three different urogynecological diagnoses (Urinary incontinence, Prolapse, and Interstitial Cystitis), comparing patients with Private and Medicaid insurance. Our primary outcome is the wait time (in business days) from initial contact to the first available appointment.

Based on clinical experience and preliminary data, we expect diagnoses Urinary incontinence and Prolapse to have similar wait times, while Interstitial Cystitis is expected to have substantially longer wait times. This pattern informs our power analysis and study design.

# Initialize logger
logger::log_info("Starting robust power analysis")

# Set seed for reproducibility
set.seed(12345)
logger::log_info("Random seed set to 12345")

# Define wait time parameters (days)
mean_wait_A_BCBS <- 14
mean_wait_B_BCBS <- 14
mean_wait_IC_BCBS <- 28
insurance_effect <- 1.3

# Define variance components
between_physician_sd <- 3
residual_sd <- 5
total_sd <- sqrt(between_physician_sd^2 + residual_sd^2)

Effect Size Calculations

We calculated Cohen’s d using the following formula:

\[ d = \frac{\mu_1 - \mu_2}{\sqrt{\sigma_{\text{between}}^2 + \sigma_{\text{residual}}^2}} \]

Where: - \(\mu_1\) and \(\mu_2\) are the mean wait times for two groups - \(\sigma_{\text{between}} = 3\) is the standard deviation between physicians - \(\sigma_{\text{residual}} = 5\) is the within-physician residual standard deviation

This gives a total standard deviation:

\[ \sigma_{\text{total}} = \sqrt{3^2 + 5^2} = \sqrt{34} \approx 5.83 \]

We then translated each Cohen’s d value into wait time differences (in days) using:

\[ \text{Day Difference} = d \times \sigma_{\text{total}} \]

For example: - \(d = 0.2 \Rightarrow 0.2 \times 5.83 = 1.2 \text{ days}\) - \(d = 0.5 \Rightarrow 0.5 \times 5.83 = 2.9 \text{ days}\) - \(d = 0.8 \Rightarrow 0.8 \times 5.83 = 4.7 \text{ days}\)

Effect Size Calculations

We calculated Cohen’s d using the following formula:

\[ d = \frac{\mu_1 - \mu_2}{\sqrt{\sigma_{\text{between}}^2 + \sigma_{\text{residual}}^2}} \]

Where: - \(\mu_1 = 28\) (mean wait time for Interstitial Cystitis) - \(\mu_2 = 14\) (mean wait time for diagnoses Urinary incontinence or Prolapse) - \(\sigma_{\text{between}} = 3\) - \(\sigma_{\text{residual}} = 5\)

So:

\[ \sigma_{\text{total}} = \sqrt{3^2 + 5^2} = \sqrt{9 + 25} = \sqrt{34} \approx 5.83 \]

Then:

\[ d = \frac{28 - 14}{5.83} = \frac{14}{5.83} \approx 2.40 \]

This is a very large effect size, indicating that wait times for Interstitial Cystitis are much longer than for Urinary incontinence or Prolapse.

We also calculated translated day differences for other typical effect sizes:

  • \(d = 0.2 \Rightarrow 0.2 \times 5.83 \approx 1.2\) days
  • \(d = 0.5 \Rightarrow 0.5 \times 5.83 \approx 2.9\) days
  • \(d = 0.8 \Rightarrow 0.8 \times 5.83 \approx 4.7\) days
# Define effect sizes and convert to days
effect_sizes <- c(0.2, 0.5, 0.8)
effect_labels <- paste0(effect_sizes, " (~", round(effect_sizes * total_sd, 1), " days)")
names(effect_labels) <- effect_sizes

# Range of physician counts
physician_range <- seq(10, 300, by = 10)
power_df <- expand.grid(n_physicians = physician_range, effect_size = effect_sizes)

# Calculate power
power_df <- power_df %>%
  rowwise() %>%
  mutate(power = tryCatch({
    pwr::pwr.t.test(d = effect_size, n = n_physicians, sig.level = 0.05, 
                   type = "two.sample", alternative = "two.sided")$power
  }, error = function(e) NA_real_)) %>%
  ungroup()

Power Curves for Detectable Differences

# Plot power curves by effect size
power_plot <- ggplot(power_df, aes(x = n_physicians, y = power, 
                                   color = factor(effect_size), 
                                   linetype = factor(effect_size))) +
  geom_line(linewidth = 1.2) +
  geom_hline(yintercept = 0.8, linetype = "dashed", color = "red") +
  scale_x_continuous(breaks = seq(0, 300, 50)) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1), limits = c(0, 1)) +
  scale_color_brewer(palette = "Set1", labels = effect_labels, name = "Effect Size") +
  scale_linetype_manual(values = c("solid", "dotted", "dotdash"), labels = effect_labels, name = "Effect Size") +
  labs(title = "Power Analysis for Various Effect Sizes",
       subtitle = "Each curve shows the power to detect a given difference in wait time",
       x = "Number of Physicians",
       y = "Statistical Power") +
  theme_minimal() +
  theme(legend.position = "right", plot.subtitle = element_text(face = "italic"))

print(power_plot)

Summary and Recommendation

This power analysis used a fixed-effects approach to evaluate the sample size needed to detect clinically meaningful differences in appointment wait times. Multiple effect sizes were examined:

  • 0.2 effect size = ~1.2 day difference
  • 0.5 effect size = ~2.9 day difference
  • 0.8 effect size = ~4.7 day difference

These values are consistent with real-world differences observed in pilot data. We intentionally avoided fragile simulation-based packages (e.g., simr) and instead used analytic methods (pwr) for reliable and interpretable power estimates.

✅ Final Recommendation

To achieve ≥80% power to detect a wait time difference of approximately 2.9 days or more (effect size 0.5), we recommend calling at least 70 physicians per group.

This estimate provides a solid balance between statistical power and practical feasibility for a mystery caller study.

logger::log_info("Power analysis complete")
# Setup logging to capture analysis steps
log_threshold(INFO)
log_info("Starting a priori power analysis for urogynecology wait time study")

Study Design

We will conduct a prospective observational study comparing wait times across three urogynecological diagnoses (A, B, and Interstitial Cystitis) while also examining the impact of insurance type (Private vs. Medicaid). This design allows us to address two key research questions:

  1. Do wait times differ significantly across the three diagnoses, with a particular focus on how Interstitial Cystitis differs from diagnoses Urinary incontinence and Prolapse?
  2. Does insurance type influence wait times within each diagnostic category?
# Create a table outlining the study design
design_table <- data.frame(
  Diagnosis = rep(c("Urinary incontinence", "Prolapse", "Interstitial Cystitis"), each = 2),
  Insurance = rep(c("Private", "Medicaid"), times = 3),
  Expected_Difference = c("Reference", "+4-6 days", 
                         "Similar to Urinary incontinence", "+4-6 days", 
                         "+10-15 days", "+18-25 days")
)

kable(design_table, 
     caption = "Study Design: Expected Wait Time Differences by Diagnosis and Insurance") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
               full_width = FALSE) %>%
  pack_rows("Urinary incontinence", 1, 2) %>%
  pack_rows("Prolapse", 3, 4) %>%
  pack_rows("Interstitial Cystitis", 5, 6)
Study Design: Expected Wait Time Differences by Diagnosis and Insurance
Diagnosis Insurance Expected_Difference
Urinary incontinence
Urinary incontinence Private Reference
Urinary incontinence Medicaid +4-6 days
Prolapse
Prolapse Private Similar to Urinary incontinence
Prolapse Medicaid +4-6 days
Interstitial Cystitis
Interstitial Cystitis Private +10-15 days
Interstitial Cystitis Medicaid +18-25 days

A Priori Power Analysis

For our power analysis, we need to take into account the expected pattern of results (Urinary incontinence ≈ Prolapse < Interstitial Cystitis). This pattern suggests a modified approach to our analysis.

Planned Contrasts for Diagnosis Comparison

Given our expected pattern of wait times, we will use planned contrasts rather than omnibus ANOVA to maximize our power to detect the specific differences we hypothesize.

Specifically, we will use the following contrasts: 1. Urinary incontinence + Prolapse vs. Interstitial Cystitis (primary contrast of interest) 2. Urinary incontinence vs. Prolapse (to confirm our expectation of similar wait times)

# Define parameters for planned contrast analysis
log_info("Performing power analysis for planned contrasts")

# Parameters
alpha <- 0.05          # Type I error rate
power_target <- 0.80   # Desired power
groups <- 3            # Number of diagnosis groups
contrast_effect <- 0.40  # Expected effect size for contrast (A+B vs. IC) - large effect

# Calculate required sample size for detecting the contrast
contrast_power_result <- pwr::pwr.t.test(
  d = contrast_effect,   # Effect size (Cohen's d)
  sig.level = alpha,     # Significance level
  power = power_target,  # Target power
  type = "two.sample",   # Two-sample test (comparing two groups)
  alternative = "two.sided"  # Two-sided test
)

# Sample size per group (A+B combined vs. IC)
n_per_group_contrast <- ceiling(contrast_power_result$n)
n_A_B_combined <- n_per_group_contrast
n_IC <- n_per_group_contrast
n_per_diagnosis <- ceiling(n_A_B_combined / 2) # Split equally between A and B
total_n_contrast <- n_A_B_combined + n_IC

log_info(paste("Planned contrast power analysis with effect size d =", contrast_effect))
log_info(paste("Required sample size for Urinary incontinence + Prolapse combined:", n_A_B_combined))
log_info(paste("Required sample size for Interstitial Cystitis:", n_IC))
log_info(paste("Required sample size per diagnosis (Urinary incontinence and Prolapse):", n_per_diagnosis))
log_info(paste("Total required sample size for contrast analysis:", total_n_contrast))
# Create a table of the planned contrast power analysis results
contrast_power_table <- data.frame(
  Parameter = c("Effect size (Cohen's d) for Urinary incontinence + Prolapse vs. Interstitial Cystitis", "Type I error (α)", 
               "Power (1 - β)", "Required sample size for A+B combined", 
               "Required sample size for Interstitial Cystitis", "Required sample size per diagnosis (Urinary incontinence and Prolapse)", 
               "Total required sample size"),
  Value = c(contrast_effect, alpha, power_target, n_A_B_combined, n_IC, 
           n_per_diagnosis, total_n_contrast)
)

kable(contrast_power_table, 
     caption = "Power Analysis for Planned Contrasts") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
               full_width = FALSE)
Power Analysis for Planned Contrasts
Parameter Value
Effect size (Cohen’s d) for Urinary incontinence + Prolapse vs. Interstitial Cystitis 4e-01
Type I error (α) 5e-02
Power (1 - β) 8e-01
Required sample size for A+B combined 1e+02
Required sample size for Interstitial Cystitis 1e+02
Required sample size per diagnosis (Urinary incontinence and Prolapse) 5e+01
Total required sample size 2e+02

Insurance Effect Analysis

We also need to determine the sample size required to detect differences between insurance types within each diagnosis group.

# Define parameters for insurance effect analysis
log_info("Performing power analysis for insurance effect")

# Parameters
insurance_effect_A_B <- 0.30  # Medium effect size for insurance in A and B
insurance_effect_IC <- 0.50   # Large effect size for insurance in IC

# Calculate required sample size for insurance effect in A and B
insurance_power_A_B <- pwr::pwr.t.test(
  d = insurance_effect_A_B,  # Effect size (Cohen's d)
  sig.level = alpha,         # Significance level
  power = power_target,      # Target power
  type = "two.sample",       # Two-sample test (comparing two insurance types)
  alternative = "two.sided"  # Two-sided test
)

# Calculate required sample size for insurance effect in IC
insurance_power_IC <- pwr::pwr.t.test(
  d = insurance_effect_IC,   # Effect size (Cohen's d)
  sig.level = alpha,         # Significance level
  power = power_target,      # Target power
  type = "two.sample",       # Two-sample test
  alternative = "two.sided"  # Two-sided test
)

n_per_insurance_A_B <- ceiling(insurance_power_A_B$n)
n_per_insurance_IC <- ceiling(insurance_power_IC$n)

log_info(paste("Insurance effect analysis for Urinary incontinence and Prolapse with effect size d =", insurance_effect_A_B))
log_info(paste("Required sample size per insurance type in Urinary incontinence and Prolapse:", n_per_insurance_A_B))
log_info(paste("Insurance effect analysis for Interstitial Cystitis with effect size d =", insurance_effect_IC))
log_info(paste("Required sample size per insurance type in Interstitial Cystitis:", n_per_insurance_IC))
# Create a table of the insurance effect power analysis results
insurance_power_table <- data.frame(
  Diagnosis = c("Urinary incontinence AND Prolapse", "Interstitial Cystitis"),
  Effect_Size = c(insurance_effect_A_B, insurance_effect_IC),
  Required_Sample_Size_Per_Insurance = c(n_per_insurance_A_B, n_per_insurance_IC)
)

kable(insurance_power_table, 
     caption = "Power Analysis for Insurance Effect by Diagnosis",
     col.names = c("Diagnosis", "Effect Size (Cohen's d)", "Required Sample Size per Insurance Type")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
               full_width = FALSE)
Power Analysis for Insurance Effect by Diagnosis
Diagnosis Effect Size (Cohen’s d) Required Sample Size per Insurance Type
Urinary incontinence AND Prolapse 0.3 176
Interstitial Cystitis 0.5 64

Multiple Regression Analysis

We will also conduct regression analyses to quantify the effects of diagnosis and insurance while controlling for potential covariates.

# Define regression power analysis parameters
log_info("Performing regression-based a priori power analysis")

# Parameters
alpha <- 0.05           # Type I error rate
power_target <- 0.85    # Desired power (slightly higher for regression model)
predictors <- 6         # Number of predictors (2 insurance, 2 diagnosis, 2 covariates)
f2 <- 0.15              # Effect size (Cohen's f²) - medium effect

# Calculate required sample size
reg_power_result <- pwr::pwr.f2.test(
  u = predictors,      # Number of predictors
  v = NULL,            # Degrees of freedom (what we're solving for)
  f2 = f2,             # Effect size
  sig.level = alpha,   # Significance level
  power = power_target # Target power
)

# Total sample size
reg_total_n <- ceiling(reg_power_result$v + predictors + 1)

log_info(paste("Regression-based power analysis with effect size f² =", f2))
log_info(paste("Number of predictors:", predictors))
log_info(paste("Total required sample size for regression:", reg_total_n))

Sample Size Determination

Based on our power analyses, we need to determine the final sample size for our study. For a balanced design that allows for robust analysis of all effects, we’ll use the most conservative estimates.

# Determine required sample size per cell
n_per_cell_A_B <- max(n_per_diagnosis, n_per_insurance_A_B)
n_per_cell_IC <- max(n_IC, n_per_insurance_IC)

# Total required sample size (balanced design)
n_per_cell_all <- max(n_per_cell_A_B, n_per_cell_IC)
total_n_balanced <- n_per_cell_all * 6  # 3 diagnoses × 2 insurance types

# We can also consider an unbalanced design if more efficient
total_n_unbalanced <- (n_per_cell_A_B * 4) + (n_per_cell_IC * 2)

# Check if regression requires larger sample size
total_n_final <- max(total_n_balanced, reg_total_n)

# Additional margin for potential dropouts or incomplete data (10%)
dropout_rate <- 0.10
adjusted_n <- ceiling(total_n_final / (1 - dropout_rate))
adjusted_n_per_cell <- ceiling(adjusted_n / 6)  # 6 cells total

log_info(paste("Final determined sample size (balanced):", total_n_balanced))
log_info(paste("Per cell sample size:", n_per_cell_all))
log_info(paste("Adjusted for 10% missing data:", adjusted_n))
log_info(paste("Adjusted per cell sample size:", adjusted_n_per_cell))
# Create a table of the final sample size determination
final_sample_table <- data.frame(
  Analysis = c("Diagnosis contrast (Urinary incontinence+Prolapse vs. IC)", "Insurance effect in A and B", 
              "Insurance effect in IC", "Multiple regression", 
              "Final balanced design", "Adjusted for excluded physicians (10%)"),
  Sample_Size = c(total_n_contrast, n_per_insurance_A_B * 2, 
                 n_per_insurance_IC * 2, reg_total_n, 
                 total_n_balanced, adjusted_n)
)

kable(final_sample_table, 
     caption = "Final Sample Size Determination",
     col.names = c("Analysis", "Required Sample Size")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
               full_width = FALSE)
Final Sample Size Determination
Analysis Required Sample Size
Diagnosis contrast (Urinary incontinence+Prolapse vs. IC) 200
Insurance effect in A and B 352
Insurance effect in IC 128
Multiple regression 109
Final balanced design 1056
Adjusted for excluded physicians (10%) 1174
# Create a table showing sample allocation across groups
allocation_table <- data.frame(
  Diagnosis = rep(c("Urinary incontinence", "Prolapse", "Interstitial Cystitis"), each = 2),
  Insurance = rep(c("Private", "Medicaid"), times = 3),
  Participants = rep(adjusted_n_per_cell, 6),
  Proportion = rep(adjusted_n_per_cell/adjusted_n * 100, 6)
)

kable(allocation_table, 
     caption = "Planned Sample Allocation",
     col.names = c("Diagnosis", "Insurance", "Participants (n)", "Proportion (%)"),
     digits = c(0, 0, 0, 1)) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
               full_width = FALSE)
Planned Sample Allocation
Diagnosis Insurance Participants (n) Proportion (%)
Urinary incontinence Private 196 16.7
Urinary incontinence Medicaid 196 16.7
Prolapse Private 196 16.7
Prolapse Medicaid 196 16.7
Interstitial Cystitis Private 196 16.7
Interstitial Cystitis Medicaid 196 16.7

Expected Clinical Differences

To provide context for our power analysis, we evaluated the clinical significance of expected wait time differences across diagnoses. Based on clinical experience and preliminary data, we established the following expectations:

# Define expected wait time differences between diagnoses and insurance types
expected_diff <- data.frame(
  Comparison = c(
    "B vs. A",
    "Interstitial Cystitis vs. Urinary incontinence",
    "Interstitial Cystitis vs. Prolapse",
    "Medicaid vs. Private (A)",
    "Medicaid vs. Private (B)",
    "Medicaid vs. Private (Interstitial Cystitis)"
  ),
  Expected_Difference = c(
    "0-2 days (similar)",
    "10-15 days longer",
    "10-15 days longer",
    "4-6 days longer",
    "4-6 days longer",
    "8-10 days longer"
  ),
  Clinical_Significance = c(
    "Low",
    "High",
    "High",
    "Moderate",
    "Moderate",
    "High"
  )
)

kable(expected_diff, 
     caption = "Expected Wait Time Differences with Clinical Significance") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
               full_width = FALSE)
Expected Wait Time Differences with Clinical Significance
Comparison Expected_Difference Clinical_Significance
B vs. A 0-2 days (similar) Low
Interstitial Cystitis vs. Urinary incontinence 10-15 days longer High
Interstitial Cystitis vs. Prolapse 10-15 days longer High
Medicaid vs. Private (A) 4-6 days longer Moderate
Medicaid vs. Private (B) 4-6 days longer Moderate
Medicaid vs. Private (Interstitial Cystitis) 8-10 days longer High

Power for Detecting Specific Wait Time Differences

We calculated the power of our planned sample size to detect specific wait time differences across diagnoses. Given our expectation of large differences between Interstitial Cystitis and the other two diagnoses, we focus particularly on this contrast.

# Calculate power for expected wait time differences with our planned sample size
wait_time_sd <- 10  # Assumed standard deviation in wait times

# Define specific differences in wait times to detect (days)
diff_days <- c(2, 5, 10, 15, 20)

# Calculate power for each effect size for A+B vs. IC comparison
powers_contrast <- numeric(length(diff_days))
for (i in seq_along(diff_days)) {
  effect_size <- diff_days[i] / wait_time_sd  # Convert to Cohen's d
  powers_contrast[i] <- pwr::pwr.t.test(
    n = adjusted_n_per_cell * 2,  # A+B sample size per insurance type
    d = effect_size,
    sig.level = alpha,
    type = "two.sample"
  )$power
}

# Calculate power for each effect size for A vs. B comparison
powers_ab <- numeric(length(diff_days))
for (i in seq_along(diff_days)) {
  effect_size <- diff_days[i] / wait_time_sd  # Convert to Cohen's d
  powers_ab[i] <- pwr::pwr.t.test(
    n = adjusted_n_per_cell,  # Sample size per diagnosis-insurance cell
    d = effect_size,
    sig.level = alpha,
    type = "two.sample"
  )$power
}

# Calculate power for each effect size for insurance comparison within each diagnosis
powers_ins <- numeric(length(diff_days))
for (i in seq_along(diff_days)) {
  effect_size <- diff_days[i] / wait_time_sd  # Convert to Cohen's d
  powers_ins[i] <- pwr::pwr.t.test(
    n = adjusted_n_per_cell,
    d = effect_size,
    sig.level = alpha,
    type = "two.sample"
  )$power
}

# Combine results
power_by_diff <- data.frame(
  Wait_Time_Difference = diff_days,
  Power_A_Plus_B_vs_IC = powers_contrast,
  Power_A_vs_B = powers_ab,
  Power_Insurance_Comparison = powers_ins
)
kable(power_by_diff, 
     caption = "Power to Detect Specific Wait Time Differences with Planned Sample Size",
     col.names = c("Wait Time Difference (days)", 
                  "Power: Urinary incontinence+Prolapse vs. IC", 
                  "Power: Urinary incontinence vs. Prolapse",
                  "Power: Insurance Comparison"),
     digits = c(0, 3, 3, 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
               full_width = FALSE) %>%
  row_spec(which(power_by_diff$Power_A_Plus_B_vs_IC >= 0.8), 
          background = "#e6f0ff")
Power to Detect Specific Wait Time Differences with Planned Sample Size
Wait Time Difference (days) Power: Urinary incontinence+Prolapse vs. IC Power: Urinary incontinence vs. Prolapse Power: Insurance Comparison
2 0.799 0.506 0.506
5 1.000 0.999 0.999
10 1.000 1.000 1.000
15 1.000 1.000 1.000
20 1.000 1.000 1.000
# Create long format data for plotting
plot_data <- rbind(
  data.frame(
    Difference = diff_days,
    Power = powers_contrast,
    Comparison = "A+B vs. IC"
  ),
  data.frame(
    Difference = diff_days,
    Power = powers_ab,
    Comparison = "A vs. B"
  ),
  data.frame(
    Difference = diff_days,
    Power = powers_ins,
    Comparison = "Insurance Comparison"
  )
)

# Plot the power curves
ggplot(plot_data, aes(x = Difference, y = Power, color = Comparison, group = Comparison)) +
  geom_line(size = 1) +
  geom_point(size = 3) +
  geom_hline(yintercept = 0.8, linetype = "dashed", color = "red") +
  theme_minimal() +
  labs(
    title = "Power vs. Wait Time Difference for Planned Sample Size",
    x = "Wait Time Difference (days)",
    y = "Statistical Power",
    subtitle = paste("Sample size per cell =", adjusted_n_per_cell)
  ) +
  scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.1)) +
  scale_x_continuous(breaks = diff_days) +
  theme(
    panel.grid.minor = element_blank(),
    plot.title = element_text(face = "bold"),
    axis.title = element_text(face = "bold")
  )

Discussion

Interpretation of Power Analysis

Our power analysis is tailored to the specific expected pattern of results, where diagnoses A and B have similar wait times, while Interstitial Cystitis has substantially longer wait times. This approach allows us to maximize statistical power for detecting the specific differences we hypothesize.

Based on our a priori power analysis, we determined that a total sample size of 1174 patients (196 per diagnosis-insurance combination) would provide sufficient power (80%) to detect:

  1. Wait time differences of 5 days or greater between Interstitial Cystitis and the other diagnoses (Urinary incontinence AND Prolapse)
  2. Wait time differences of 5 days or greater between insurance types within each diagnosis

These detectable differences align well with our expected clinical differences, particularly for the comparisons between Interstitial Cystitis and diagnoses Urinary incontinence and Prolapse, where we expect differences of 10-15 days.

Planned Analytical Approach

Our primary analytical approach will involve planned contrasts rather than omnibus ANOVA, as this provides greater statistical power for testing our specific hypotheses. We will:

  1. Test the contrast between Urinary incontinence+Prolapse combined vs. Interstitial Cystitis (primary hypothesis)
  2. Test the contrast between Urinary incontinence vs. Prolapse (to confirm our expectation of similar wait times)
  3. Test the effect of insurance type within each diagnosis category

This approach is more efficient than an omnibus ANOVA followed by post-hoc tests, as it directly addresses our research questions and maximizes statistical power.

Clinical Significance

From a clinical perspective, our power analysis ensures that we can detect wait time differences that have meaningful impact on patient care. The expected 10-15 day longer wait times for Interstitial Cystitis patients is clinically significant, as delayed treatment can lead to prolonged patient discomfort and potentially poorer outcomes.

Similarly, the expected insurance-based disparities (particularly for Interstitial Cystitis patients) have important implications for healthcare access, patient outcomes, and health policy. Our study design will allow us to quantify these disparities with adequate statistical power.

Conclusions

Our a priori power analysis indicates that a total sample size of 1174 patients, stratified across three diagnoses and two insurance types (196 per cell), will provide sufficient statistical power to detect clinically meaningful differences in wait times for urogynecology appointments. This sample size accounts for potential missing data and allows for robust analysis of the specific pattern of wait times we expect to observe (Urinary incontinence ≈ Prolapse < Interstitial Cystitis).

The results of this power analysis will guide our recruitment efforts and provide a foundation for interpreting the statistical and clinical significance of our findings once the study is completed.

# Create a boxplot visualization of expected wait times by diagnosis and insurance
wait_time_data <- data.frame(
  Diagnosis = rep(c("Urinary incontinence", "Prolapse", "Interstitial Cystitis"), each = 2),
  Insurance = rep(c("Private", "Medicaid"), times = 3),
  Mean_Wait = c(mean_wait_A_BCBS, mean_wait_A_BCBS * insurance_effect,
                mean_wait_B_BCBS, mean_wait_B_BCBS * insurance_effect,
                mean_wait_IC_BCBS, mean_wait_IC_BCBS * insurance_effect)
)

# Plot expected wait times by diagnosis and insurance
wait_time_plot <- ggplot(wait_time_data, 
                         aes(x = Diagnosis, y = Mean_Wait, fill = Insurance)) +
  geom_bar(stat = "identity", position = position_dodge(width = 0.9), width = 0.8) +
  geom_text(aes(label = round(Mean_Wait, 1)), 
            position = position_dodge(width = 0.9), 
            vjust = -0.5, size = 3.5) +
  scale_fill_brewer(palette = "Set2") +
  labs(title = "Expected Wait Times by Diagnosis and Insurance Type",
       subtitle = "Based on model parameters",
       x = "Diagnosis",
       y = "Wait Time (Business Days)") +
  theme_minimal() +
  theme(legend.position = "right",
        axis.text = element_text(size = 10),
        plot.title = element_text(face = "bold"),
        panel.grid.major.x = element_blank())

print(wait_time_plot)

# Create a heatmap of required sample sizes by effect size and power
power_levels <- seq(0.7, 0.95, by = 0.05)
effect_sizes_heatmap <- seq(0.2, 1.0, by = 0.1)
sample_size_matrix <- matrix(NA, nrow = length(effect_sizes_heatmap), 
                            ncol = length(power_levels))

# Calculate sample sizes
for (i in seq_along(effect_sizes_heatmap)) {
  for (j in seq_along(power_levels)) {
    sample_size_matrix[i, j] <- ceiling(pwr::pwr.t.test(
      d = effect_sizes_heatmap[i],
      power = power_levels[j],
      sig.level = 0.05,
      type = "two.sample"
    )$n)
  }
}

# Create data frame for heatmap
heatmap_data <- expand.grid(
  Effect_Size = effect_sizes_heatmap,
  Power = power_levels
)
heatmap_data$Sample_Size <- as.vector(sample_size_matrix)

# Plot heatmap
sample_size_heatmap <- ggplot(heatmap_data, 
                             aes(x = factor(Power), y = factor(Effect_Size), 
                                 fill = Sample_Size)) +
  geom_tile() +
  geom_text(aes(label = Sample_Size), color = "white", size = 3) +
  scale_fill_gradient2(low = "darkblue", mid = "purple", high = "red", 
                      midpoint = median(heatmap_data$Sample_Size),
                      name = "Sample Size\nRequired") +
  labs(title = "Required Sample Size by Effect Size and Power",
       subtitle = "For two-sample t-test with α = 0.05",
       x = "Statistical Power",
       y = "Effect Size (Cohen's d)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 0),
        plot.title = element_text(face = "bold"))

print(sample_size_heatmap)

# Install and load required packages
if (!requireNamespace("DiagrammeR", quietly = TRUE)) {
  install.packages("DiagrammeR")
}
library(DiagrammeR)
# Create an enhanced flowchart with statistical details
enhanced_flowchart <- DiagrammeR::grViz("
digraph enhanced_urogyn_flowchart {
  # Graph settings
  graph [rankdir = TB, fontsize = 14, fontname = 'Helvetica', splines = true, 
         nodesep = 0.8, ranksep = 0.9, bgcolor = '#F9F9F9', 
         label = '\\n\\nUrogynecology Wait Time Study Statistical Design\\nStatistical Power Analysis for Effect Size = 0.4', 
         labelloc = 't', labeljust = 'c', fontsize = 20, fontname = 'Helvetica-Bold']
  
  # Node defaults
  node [shape = box, style = 'filled,rounded', fontname = 'Helvetica', 
        fontsize = 12, margin = '0.25,0.2', penwidth = 1.5]
  edge [color = '#555555', penwidth = 1.3, arrowhead = vee]
  
  # Main nodes
  start [label = 'Study Design:\\nEffect Size = 0.4, Power = 0.8\\nCohen\\'s d = 0.4 → 2.3 day difference\\nSignificance level α = 0.05', 
         fillcolor = '#E6F3FF', shape = 'oval', width = 3.5, height = 1.5, penwidth = 2,
         color = '#4682B4']
         
  totalSample [label = 'Total Required Sample Size:\\n100 subjects per group\\nDetection Sensitivity: 80% power to detect\\na 2.3+ day difference in wait times\\nTotal N = 600 across all groups', 
               fillcolor = '#F0F8FF', width = 4, color = '#4682B4', penwidth = 2]
  
  # Scenario nodes with statistical info
  diagA [label = 'Scenario A\\n↓\\nBaseline diagnosis\\nEstimated variance: σ² = 34', 
         fillcolor = '#FFD6D6', fontsize = 14, color = '#CD5C5C', penwidth = 2]
         
  diagB [label = 'Scenario B\\n↓\\nSimilar to diagnosis A\\n(Expected Δ < 2 days, Cohen\\'s d < 0.2)', 
         fillcolor = '#FFD6D6', fontsize = 14, color = '#CD5C5C', penwidth = 2]
         
  diagIC [label = 'Scenario Interstitial Cystitis\\n↓\\nSubstantially longer wait times\\n(Expected Δ = 11-14 days, Cohen\\'s d ≈ 2.0)', 
          fillcolor = '#FFD6D6', fontsize = 14, color = '#CD5C5C', 
          width = 4, penwidth = 2]
  
  # Insurance nodes with statistical info
  a1 [label = 'Private Insurance\\n50 subjects\\nExpected Wait: 14 days\\nReference group', 
      fillcolor = '#D6F5F5', color = '#20B2AA']
      
  a2 [label = 'Medicaid\\n50 subjects\\nExpected Wait: 18.2 days\\nInsurance effect: +4.2 days (Cohen\\'s d ≈ 0.7)', 
      fillcolor = '#D6F5F5', color = '#20B2AA']
  
  b1 [label = 'Private Insurance\\n50 subjects\\nExpected Wait: 14 days\\nReference group', 
      fillcolor = '#D6F5F5', color = '#20B2AA']
      
  b2 [label = 'Medicaid\\n50 subjects\\nExpected Wait: 18.2 days\\nInsurance effect: +4.2 days (Cohen\\'s d ≈ 0.7)', 
      fillcolor = '#D6F5F5', color = '#20B2AA']
  
  c1 [label = 'Private Insurance\\n50 subjects\\nExpected Wait: 25 days\\nReference group', 
      fillcolor = '#D6F5F5', color = '#20B2AA']
      
  c2 [label = 'Medicaid\\n50 subjects\\nExpected Wait: 32.5 days\\nInsurance effect: +7.5 days (Cohen\\'s d ≈ 1.3)', 
      fillcolor = '#D6F5F5', color = '#20B2AA']
  
  # Analysis nodes with statistical details
  analysis [label = 'Primary Analysis:\\nPlanned Contrasts\\n\\nModeled as mixed-effects regression\\nwith physician random effects (SD = 3)\\nand residual SD = 5', 
            fillcolor = '#FFFFDD', fontsize = 14, color = '#B8860B', width = 4.5, penwidth = 2]
            
  contrast1 [label = 'Contrast 1:\\nUrinary incontinence+Prolapse vs. Interstitial Cystitis\\n\\nPrimary hypothesis\\nPower > 99% to detect expected difference\\np-value adjustment: Bonferroni', 
             fillcolor = '#FFFFEE', color = '#B8860B']
             
  contrast2 [label = 'Contrast 2:\\nUrinary incontinence vs. Prolapse\\n\\nExpected to be non-significant\\nPower = 15% for Cohen\\'s d = 0.2\\nEquivalence bounds: ±2 days', 
             fillcolor = '#FFFFEE', color = '#B8860B']
             
  contrast3 [label = 'Contrast 3:\\nPrivate vs. Medicaid\\n\\nExpected to be significant\\nPower = 90% for Cohen\\'s d = 0.5\\nEstimated effect: 30% longer waits', 
             fillcolor = '#FFFFEE', color = '#B8860B']
  
  # Add sample size calculation node with expanded z-score notation
  sample_calc [label = 'Sample Size Calculation Details\\n\\nFormula: n = 2 * ((z₁₋ₐ/₂ + z₁₋ᵦ)²) / (Cohen\\'s d)²\\nWhere:\\nz₁₋ₐ/₂ = standard normal value at (1-α/2) = 1.96 for α = 0.05\\nz₁₋ᵦ = standard normal value at (1-β) = 0.84 for power = 0.8\\nCohen\\'s d = 0.4, α = 0.05, 1-β = 0.8\\nResult: n = 100 per group\\nAdjusted for potential 10% missing data', 
               fillcolor = '#E6E6FA', shape = 'box', style = 'filled,rounded', 
               fontsize = 12, width = 5, color = '#483D8B', penwidth = 1.5]
  
  # Add reference to statistical heatmap          
  heatmap_ref [label = 'Reference: Sample Size Heatmap\\nn = 100 is based on power analysis heatmap\\nwith effect size = 0.4 and power = 0.8\\nIncreasing power to 0.95 would require n = 164', 
               fillcolor = '#ECD9FF', shape = 'box', style = 'filled,rounded', 
               fontsize = 12, width = 4.5, color = '#9370DB', penwidth = 1.5]
  
  # Edges/connections
  start -> totalSample [color = '#4682B4', penwidth = 2]
  
  totalSample -> diagA [color = '#CD5C5C', penwidth = 1.5]
  totalSample -> diagB [color = '#CD5C5C', penwidth = 1.5]
  totalSample -> diagIC [color = '#CD5C5C', penwidth = 1.5]
  
  diagA -> a1 [color = '#20B2AA', penwidth = 1.2]
  diagA -> a2 [color = '#20B2AA', penwidth = 1.2]
  
  diagB -> b1 [color = '#20B2AA', penwidth = 1.2]
  diagB -> b2 [color = '#20B2AA', penwidth = 1.2]
  
  diagIC -> c1 [color = '#20B2AA', penwidth = 1.2]
  diagIC -> c2 [color = '#20B2AA', penwidth = 1.2]
  
  {a1 a2 b1 b2 c1 c2} -> analysis [color = '#B8860B', penwidth = 1.5]
  
  analysis -> contrast1 [color = '#B8860B', penwidth = 1.5]
  analysis -> contrast2 [color = '#B8860B', penwidth = 1.5]
  analysis -> contrast3 [color = '#B8860B', penwidth = 1.5]
  
  # Connect new information nodes
  totalSample -> sample_calc [dir = 'back', style = 'dashed', color = '#483D8B']
  totalSample -> heatmap_ref [dir = 'back', style = 'dashed', color = '#9370DB']
  
  # Invisible edges for layout
  {rank = same; diagA; diagB; diagIC}
  {rank = same; a1; b1; c1}
  {rank = same; a2; b2; c2}
  {rank = same; contrast1; contrast2; contrast3}
  {rank = same; sample_calc; heatmap_ref}
}
")
# Display the flowchart
enhanced_flowchart
# Save the flowchart using DiagrammeRsvg and rsvg
if (!requireNamespace("DiagrammeRsvg", quietly = TRUE)) {
  install.packages("DiagrammeRsvg")
}
if (!requireNamespace("rsvg", quietly = TRUE)) {
  install.packages("rsvg")
}
library(DiagrammeRsvg)
library(rsvg)

# Export as SVG, then convert to PNG
svg_content <- DiagrammeRsvg::export_svg(enhanced_flowchart)
rsvg::rsvg_png(charToRaw(svg_content), "enhanced_urogynecology_flowchart.png", 
               width = 1200, height = 1000)

# Optionally save as PDF for higher quality printing
rsvg::rsvg_pdf(charToRaw(svg_content), "enhanced_urogynecology_flowchart.pdf", 
               width = 10, height = 8.3)

# Message to confirm file creation
cat("Enhanced flowchart saved as PNG and PDF files\n")
## Enhanced flowchart saved as PNG and PDF files

I’ll explain the sample size considerations for this urogynecology wait time study in detail, focusing on both the primary outcome (comparing clinical scenarios) and secondary outcome (comparing insurance types).

Primary Outcome: Wait Time Differences Between Clinical Scenarios

The study is designed to detect meaningful differences in wait times between three clinical scenarios (Urinary incontinence, Prolapse, and Interstitial Cystitis), with the main comparison being between Urinary incontinence+Prolapse (combined) versus Interstitial Cystitis.

Sample Size Calculation

The sample size is calculated using the formula for a two-sample t-test:

\[n = 2 \times \frac{(z_{1-\alpha/2} + z_{1-\beta})^2}{(\text{Cohen's }d)^2}\]

Where: - \(z_{1-\alpha/2}\) is the standard normal value at confidence level \(1-\alpha/2\) (1.96 for α = 0.05) - \(z_{1-\beta}\) is the standard normal value for power \(1-\beta\) (0.84 for power = 0.8) - Cohen’s d is the standardized effect size (0.4 in this study)

Substituting these values: \[n = 2 \times \frac{(1.96 + 0.84)^2}{(0.4)^2} = 2 \times \frac{7.84}{0.16} = 2 \times 49 = 98 \approx 100\]

This calculation indicates the need for 100 subjects per group to achieve 80% power to detect an effect size of 0.4 (equivalent to approximately 2.3 days difference) at a significance level of 0.05.

Effect Size Translation

The Cohen’s d value of 0.4 is translated into actual wait time differences using:

\[\Delta\text{ days} = d \times \sigma_{\text{total}}\]

Where \(\sigma_{\text{total}} = \sqrt{\sigma_{\text{between}}^2 + \sigma_{\text{residual}}^2} = \sqrt{3^2 + 5^2} = \sqrt{34} \approx 5.83\)

Therefore, an effect size of 0.4 corresponds to a difference of: \[\Delta\text{ days} = 0.4 \times 5.83 \approx 2.3\text{ days}\]

This means the study is powered to detect differences of at least 2.3 days between groups.

Secondary Outcome: Wait Time Differences Between Insurance Types

For comparing Private vs. Medicaid insurance within each diagnosis, the study used the same approach but with different effect sizes:

  • For diagnoses Urinary incontinence and Prolapse: Expected effect size Cohen’s d ≈ 0.7 (corresponding to +4.2 days)
  • For Interstitial Cystitis: Expected effect size Cohen’s d ≈ 1.3 (corresponding to +7.5 days)

These larger effect sizes for the insurance comparisons mean that the same sample size of 50 subjects per insurance-diagnosis combination provides even greater power for these comparisons.

Sample Allocation

The total sample of 600 participants is allocated as follows: - 6 groups (3 diagnoses × 2 insurance types) - 100 subjects per group (split as 50 subjects per diagnosis-insurance combination) - This balanced design maximizes statistical power for all comparisons

Adjustment for Missing Data

The final sample size was adjusted for potential missing data using:

\[n_{\text{adjusted}} = \frac{n_{\text{calculated}}}{1 - \text{excluded physicians}}\]

With a 10% excluded physician rate, this gives: \[n_{\text{adjusted}} = \frac{100}{1 - 0.1} \approx 111\]

However, the flowchart appears to maintain 100 subjects per group, suggesting they may have decided the original calculation provided sufficient margin.

Statistical Approach

The primary analysis uses planned contrasts rather than omnibus ANOVA, focusing on: 1. Urinary incontinence+Prolapse vs. Interstitial Cystitis (primary hypothesis) 2. Urinary incontinence vs. Prolapse (expected to be non-significant) 3. Private vs. Medicaid (insurance effect)

This approach provides greater statistical power for testing specific hypotheses than a post-hoc approach following ANOVA.

The mixed-effects regression model accounts for physician clustering (random effects with SD = 3) and within-physician variability (residual SD = 5), which is essential for accurate power calculations for this study design.

# Create a visualization of wait time differences translated from effect sizes
effect_size_range <- seq(0.1, 2.5, by = 0.1)
day_differences <- effect_size_range * total_sd

effect_size_translation <- data.frame(
  Effect_Size = effect_size_range,
  Day_Difference = day_differences
)

# Add common effect size labels
label_points <- data.frame(
  Effect_Size = c(0.2, 0.5, 0.8, 2.4),
  Day_Difference = c(0.2, 0.5, 0.8, 2.4) * total_sd,
  Label = c("Small\n(0.2)", "Medium\n(0.5)", "Large\n(0.8)", "IC vs. A/B\n(2.4)")
)

# Plot effect size translation
effect_size_plot <- ggplot(effect_size_translation, 
                          aes(x = Effect_Size, y = Day_Difference)) +
  geom_line(color = "blue", size = 1.2) +
  geom_point(data = label_points, aes(x = Effect_Size, y = Day_Difference), 
             size = 3, color = "red") +
  geom_text(data = label_points, 
            aes(label = Label), 
            vjust = -0.8, size = 3.5) +
  geom_hline(yintercept = c(5, 10, 15), 
             linetype = "dashed", color = "gray50", alpha = 0.7) +
  scale_x_continuous(breaks = seq(0, 2.5, by = 0.2)) +
  scale_y_continuous(breaks = seq(0, 15, by = 1)) +
  labs(title = "Translation Between Effect Size and Wait Time Differences",
       subtitle = paste("Based on estimated total SD =", round(total_sd, 2), "days"),
       x = "Effect Size (Cohen's d)",
       y = "Equivalent Wait Time Difference (Days)") +
  theme_minimal() +
  theme(panel.grid.minor = element_blank(),
        plot.title = element_text(face = "bold"))

print(effect_size_plot)

# Create a minimal detectable effect plot across sample sizes
n_range <- seq(10, 300, by = 10)
alpha_levels <- c(0.01, 0.05, 0.10)
power_fixed <- 0.80

mde_data <- expand.grid(
  Sample_Size = n_range,
  Alpha = alpha_levels
)

# Calculate minimal detectable effect sizes
mde_data$MDE <- NA
for (i in 1:nrow(mde_data)) {
  mde_data$MDE[i] <- pwr::pwr.t.test(
    n = mde_data$Sample_Size[i],
    sig.level = mde_data$Alpha[i],
    power = power_fixed,
    type = "two.sample"
  )$d
}

# Convert to days
mde_data$Days <- mde_data$MDE * total_sd

# Plot minimal detectable effect
mde_plot <- ggplot(mde_data, aes(x = Sample_Size, y = Days, color = factor(Alpha))) +
  geom_line(size = 1.2) +
  scale_color_brewer(palette = "Set1", 
                    name = "Significance\nLevel (α)", 
                    labels = c("0.01", "0.05", "0.10")) +
  geom_hline(yintercept = c(1, 3, 5), 
             linetype = "dotted", color = "gray30") +
  annotate("text", x = 290, y = c(1, 3, 5) + 0.2, 
           label = c("1 day", "3 days", "5 days"), 
           hjust = 1, size = 3) +
  labs(title = "Minimal Detectable Wait Time Difference by Sample Size",
       subtitle = paste("At", power_fixed*100, "% power for different significance levels"),
       x = "Sample Size per Group",
       y = "Minimal Detectable Difference (Days)") +
  theme_minimal() +
  theme(legend.position = "right",
        plot.title = element_text(face = "bold"))

print(mde_plot)

# Create a comparison of contrast vs. ANOVA power
sample_sizes_contrast <- seq(10, 150, by = 5)
effect_contrast <- 0.40  # Effect size for main contrast (medium)

# Power for planned contrast
contrast_power_data <- data.frame(
  Sample_Size = sample_sizes_contrast,
  Power_Contrast = NA,
  Power_ANOVA = NA
)

# Calculate power for contrast and ANOVA
for (i in 1:nrow(contrast_power_data)) {
  # Power for contrast
  contrast_power_data$Power_Contrast[i] <- pwr::pwr.t.test(
    n = contrast_power_data$Sample_Size[i],
    d = effect_contrast,
    sig.level = 0.05,
    type = "two.sample"
  )$power
  
  # Power for ANOVA (approximation)
  contrast_power_data$Power_ANOVA[i] <- pwr::pwr.anova.test(
    k = 3,  # Number of groups
    n = contrast_power_data$Sample_Size[i],
    f = effect_contrast / 2,  # Convert to f (approximation)
    sig.level = 0.05
  )$power
}

# Plot comparison
analysis_approach_plot <- ggplot(contrast_power_data, 
                                aes(x = Sample_Size)) +
  geom_line(aes(y = Power_Contrast, color = "Planned\nContrast"), size = 1.2) +
  geom_line(aes(y = Power_ANOVA, color = "ANOVA +\nPost Hoc"), size = 1.2, linetype = "dashed") +
  geom_hline(yintercept = 0.8, linetype = "dotted", color = "red") +
  scale_color_manual(values = c("Planned\nContrast" = "blue", "ANOVA +\nPost Hoc" = "darkgreen"),
                    name = "Analysis\nApproach") +
  labs(title = "Power Comparison: Planned Contrast vs. ANOVA Approach",
       subtitle = "For detecting differences between Urinary incontinence+Prolapse and IC diagnoses",
       x = "Sample Size per Group",
       y = "Statistical Power") +
  scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.1)) +
  theme_minimal() +
  theme(legend.position = "right",
        plot.title = element_text(face = "bold"))

print(analysis_approach_plot)

# Create a visualization of expected differences between all combinations
wait_combinations <- data.frame(
  Combination = c("Urinary incontinence (Private)", "Urinary incontinence (Medicaid)", 
                 "Prolapse (Private)", "Prolapse (Medicaid)",
                 "IC (Private)", "IC (Medicaid)"),
  Mean_Wait = c(mean_wait_A_BCBS, mean_wait_A_BCBS * insurance_effect,
               mean_wait_B_BCBS, mean_wait_B_BCBS * insurance_effect,
               mean_wait_IC_BCBS, mean_wait_IC_BCBS * insurance_effect)
)

# Create all pairwise differences
n_combinations <- nrow(wait_combinations)
pairwise_diff <- data.frame(
  Combination1 = character(),
  Combination2 = character(),
  Difference = numeric(),
  Effect_Size = numeric()
)

for (i in 1:(n_combinations-1)) {
  for (j in (i+1):n_combinations) {
    diff_days <- wait_combinations$Mean_Wait[j] - wait_combinations$Mean_Wait[i]
    effect_size <- abs(diff_days) / total_sd
    
    pairwise_diff <- rbind(pairwise_diff, data.frame(
      Combination1 = wait_combinations$Combination[i],
      Combination2 = wait_combinations$Combination[j],
      Difference = diff_days,
      Effect_Size = effect_size
    ))
  }
}

# Create tile plot of differences
diff_matrix <- matrix(NA, nrow = n_combinations, ncol = n_combinations)
rownames(diff_matrix) <- wait_combinations$Combination
colnames(diff_matrix) <- wait_combinations$Combination

for (i in 1:n_combinations) {
  diff_matrix[i, i] <- 0  # Diagonal is zero
  
  # Fill in the other cells
  for (j in 1:nrow(pairwise_diff)) {
    if (pairwise_diff$Combination1[j] == wait_combinations$Combination[i]) {
      col_idx <- which(wait_combinations$Combination == pairwise_diff$Combination2[j])
      diff_matrix[i, col_idx] <- pairwise_diff$Difference[j]
      diff_matrix[col_idx, i] <- -pairwise_diff$Difference[j]
    }
  }
}

# Convert to long format for ggplot
diff_long <- as.data.frame(as.table(diff_matrix))
names(diff_long) <- c("Group1", "Group2", "Difference")

# Plot heatmap of differences
diff_heatmap <- ggplot(diff_long, aes(x = Group2, y = Group1, fill = Difference)) +
  geom_tile() +
  geom_text(aes(label = round(Difference, 1)), 
            color = ifelse(abs(diff_long$Difference) > 10, "white", "black")) +
  scale_fill_gradient2(low = "blue", high = "red", mid = "white", 
                      midpoint = 0, name = "Difference\n(Days)") +
  labs(title = "Expected Wait Time Differences Between All Groups",
       subtitle = "Row minus Column (negative values indicate shorter wait times)",
       x = "Group",
       y = "Group") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(face = "bold"))

print(diff_heatmap)

library(ggplot2)
library(ggforce)
library(ggpubr)

# Create a single image showing a doctor being called 6 times
plot <- ggplot() +
  theme_void() +
  xlim(0, 15) +
  ylim(0, 12)

# Add the doctor in the center
plot <- plot +
  # Doctor body (stylized as a medical professional)
  annotate("rect", xmin = 7, xmax = 8, ymin = 5, ymax = 7, 
           fill = "white", color = "black", size = 0.8) +
  # Doctor head
  geom_circle(aes(x0 = 7.5, y0 = 7.5, r = 0.6), fill = "white", color = "black", size = 0.8) +
  # Doctor stethoscope
  annotate("path", 
           x = c(6.8, 6.5, 6.2), 
           y = c(6.5, 6.3, 6.5), 
           color = "darkblue", size = 0.8) +
  # Doctor label
  annotate("text", x = 7.5, y = 4, 
           label = "ONE PHYSICIAN", fontface = "bold", size = 6)

# Add a background circle to visually connect all calls
geom_circle(aes(x0 = 7.5, y0 = 6, r = 4.5), 
           fill = NA, color = "gray80", linetype = "dashed", size = 0.5)
## mapping: x0 = 7.5, y0 = 6, r = 4.5 
## geom_circle: 
## stat_circle: n = 360, na.rm = FALSE
## position_identity

# Define positions for the 6 calls (in a circle)
# Position calls at 1, 3, 5, 7, 9, 11 o'clock positions
angles <- seq(pi/6, 2*pi + pi/6, length.out = 7)[1:6]
radius <- 4.5
positions <- data.frame(
  x = 7.5 + radius * cos(angles),
  y = 6 + radius * sin(angles)
)

# Define diagnosis and insurance combinations
diagnoses <- c("Diagnosis A", "Urinary Incontinence", "Pelvic Organ Prolapse",
               "Diagnosis A", "Urinary Incontinence", "Pelvic Organ Prolapse")
insurances <- c("Blue Cross/Blue Shield", "Blue Cross/Blue Shield", "Blue Cross/Blue Shield",
                "Medicaid", "Medicaid", "Medicaid")

# Define colors for diagnoses and insurances
diagnosis_colors <- c("#3366CC", "#FF9900", "#CC3333")
insurance_colors <- c("#0066CC", "#DD3366")

# Get color for each combination
diag_fill <- c(diagnosis_colors[1], diagnosis_colors[2], diagnosis_colors[3],
               diagnosis_colors[1], diagnosis_colors[2], diagnosis_colors[3])
ins_fill <- c(insurance_colors[1], insurance_colors[1], insurance_colors[1],
              insurance_colors[2], insurance_colors[2], insurance_colors[2])

# Add the callers
for (i in 1:6) {
  # Determine which diagnosis index to use (1, 2, or 3)
  diag_idx <- (i - 1) %% 3 + 1
  
  # Add caller icon (phone)
  plot <- plot +
    # Phone handset icon
    annotate("path", 
             x = c(positions$x[i]-0.3, positions$x[i]+0.3, positions$x[i]+0.3, positions$x[i]-0.3),
             y = c(positions$y[i]-0.2, positions$y[i]-0.2, positions$y[i]+0.2, positions$y[i]+0.2),
             fill = "#F5F5F5", color = "black") +
    
    # Connecting line
    annotate("segment", 
             x = positions$x[i], y = positions$y[i], 
             xend = 7.5 + (positions$x[i] - 7.5) * 0.3, 
             yend = 6 + (positions$y[i] - 6) * 0.3, 
             arrow = arrow(length = unit(0.2, "cm"), type = "closed"), 
             size = 0.6, color = "#666666") +
    
    # Diagnosis label
    annotate("label", x = positions$x[i], y = positions$y[i] + 0.7, 
             label = diagnoses[i], fill = diag_fill[i], color = "white", 
             size = 4, fontface = "bold") +
    
    # Insurance label
    annotate("label", x = positions$x[i], y = positions$y[i] - 0.7, 
             label = insurances[i], fill = ins_fill[i], color = "white", 
             size = 4, fontface = "bold")
}

# Add title and explanation
plot <- plot +
  annotate("text", x = 7.5, y = 11.2, 
           label = "Study Design: One Physician Called Six Times", 
           size = 8, fontface = "bold") +
  annotate("text", x = 7.5, y = 10.2, 
           label = "Each physician receives calls for all 3 diagnoses with both insurance types", 
           size = 6) +
  annotate("text", x = 7.5, y = 9.5, 
           label = "allowing within-physician comparison of wait times", 
           size = 6)

# Add a legend for diagnoses
plot <- plot +
  annotate("text", x = 2, y = 2.5, label = "Diagnoses:", fontface = "bold", size = 5, hjust = 0) +
  
  annotate("rect", xmin = 2, xmax = 2.5, ymin = 1.7, ymax = 2.2, fill = diagnosis_colors[1]) +
  annotate("text", x = 2.7, y = 1.95, label = "Diagnosis A", size = 4, hjust = 0) +
  
  annotate("rect", xmin = 2, xmax = 2.5, ymin = 1.1, ymax = 1.6, fill = diagnosis_colors[2]) +
  annotate("text", x = 2.7, y = 1.35, label = "Urinary Incontinence", size = 4, hjust = 0) +
  
  annotate("rect", xmin = 2, xmax = 2.5, ymin = 0.5, ymax = 1.0, fill = diagnosis_colors[3]) +
  annotate("text", x = 2.7, y = 0.75, label = "Pelvic Organ Prolapse", size = 4, hjust = 0)

# Add a legend for insurance types
plot <- plot +
  annotate("text", x = 10, y = 2.5, label = "Insurance Types:", fontface = "bold", size = 5, hjust = 0) +
  
  annotate("rect", xmin = 10, xmax = 10.5, ymin = 1.7, ymax = 2.2, fill = insurance_colors[1]) +
  annotate("text", x = 10.7, y = 1.95, label = "Blue Cross/Blue Shield", size = 4, hjust = 0) +
  
  annotate("rect", xmin = 10, xmax = 10.5, ymin = 1.1, ymax = 1.6, fill = insurance_colors[2]) +
  annotate("text", x = 10.7, y = 1.35, label = "Medicaid", size = 4, hjust = 0)

# Print the plot
print(plot)


# Save the plot
ggsave("physician_call_diagram.png", plot, width = 12, height = 9, dpi = 300)