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
|
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")
)

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).
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)