5  Clinical Trial Design and Analysis

5.1 Introduction to Clinical Trial Design in R

Clinical trial design requires careful consideration of statistical principles to ensure valid conclusions. R provides powerful tools for designing, simulating, and analyzing clinical trials across all phases of development.

5.1.1 The Role of R in Clinical Trial Design

R has become a central tool in clinical trial design and analysis due to its:

  1. Flexibility: Customizable designs for complex trial scenarios
  2. Statistical rigor: Implementation of advanced methodologies
  3. Reproducibility: Documented workflows that support regulatory submissions
  4. Visualization: Clear communication of design elements and results
  5. Simulation capabilities: Evaluation of operating characteristics under different scenarios

5.2 Sample Size Calculation and Power Analysis

5.2.1 Traditional Sample Size Approaches

Determining appropriate sample sizes is fundamental to trial design:

Code
library(pwr)
library(ggplot2)
library(dplyr)

# Power calculation for t-test
power_result <- pwr.t.test(
  d = 0.5,          # Effect size (Cohen's d)
  sig.level = 0.05, # Significance level
  power = 0.8,      # Desired power
  type = "two.sample", # Two independent samples
  alternative = "two.sided" # Two-sided test
)

# Display results
power_result

# Visualize power curve across different sample sizes
sample_sizes <- seq(10, 100, by = 5)
power_values <- sapply(sample_sizes, function(n) {
  pwr.t.test(
    n = n,
    d = 0.5,
    sig.level = 0.05,
    type = "two.sample",
    alternative = "two.sided"
  )$power
})

power_df <- data.frame(
  sample_size = sample_sizes,
  power = power_values
)

ggplot(power_df, aes(x = sample_size, y = power)) +
  geom_line(size = 1.2, color = "#0072B2") +
  geom_hline(yintercept = 0.8, linetype = "dashed", color = "red") +
  geom_vline(xintercept = ceiling(power_result$n), linetype = "dashed", color = "red") +
  labs(title = "Power Curve for Two-Sample t-test",
       subtitle = paste("Effect size =", round(power_result$d, 2), 
                        ", Required n per group =", ceiling(power_result$n)),
       x = "Sample Size per Group",
       y = "Statistical Power") +
  scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.1)) +
  theme_minimal()

# Sample size for survival analysis (log-rank test)
library(powerSurvEpi)

surv_power <- powerSurvEpi::powerCT.default(
  k = 10,           # Accrual time (months) 
  f = 24,           # Additional follow-up time (months)
  alpha = 0.05,     # Significance level
  power = 0.8,      # Desired power
  p0 = 0.3,         # Event probability in control group
  p1 = 0.15,        # Event probability in treatment group
  accrual = "uniform" # Accrual pattern
)

# Display results
surv_power

5.2.2 Sample Size for Non-Inferiority and Equivalence Trials

Specialized calculations for non-standard trial designs:

Code
# Sample size for non-inferiority trial
library(TrialSize)

# For continuous outcome
ni_continuous <- TrialSize::nNormal(
  delta = 5,        # Non-inferiority margin
  sd = 15,          # Standard deviation
  alpha = 0.025,    # One-sided alpha
  beta = 0.2,       # Type II error rate (1-power)
  r = 1             # Allocation ratio
)

# For binary outcome
ni_binary <- TrialSize::nBinomial(
  p1 = 0.75,        # Expected proportion in treatment group
  p2 = 0.75,        # Expected proportion in control group
  delta = -0.1,     # Non-inferiority margin
  alpha = 0.025,    # One-sided alpha
  beta = 0.2,       # Type II error rate
  r = 1             # Allocation ratio
)

# For equivalence trial
eq_continuous <- TrialSize::nEquivalence(
  delta = 5,        # Equivalence margin
  sd = 15,          # Standard deviation
  alpha = 0.05,     # Significance level
  beta = 0.2,       # Type II error rate
  r = 1             # Allocation ratio
)

# Display results
ni_continuous
ni_binary
eq_continuous

For more complex designs, including adaptive and multi-arm multi-stage trials, refer to the specialized sections later in this chapter.

5.3 Randomization and Allocation

5.3.1 Simple Randomization

Basic randomization methods:

Code
set.seed(123) # For reproducibility

# Simple randomization for 100 subjects
n <- 100
simple_rand <- data.frame(
  subject_id = 1:n,
  treatment = sample(c("A", "B"), n, replace = TRUE)
)

# Check balance
table(simple_rand$treatment)
prop.table(table(simple_rand$treatment))

# Visualize
ggplot(simple_rand, aes(x = treatment, fill = treatment)) +
  geom_bar() +
  geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
  labs(title = "Treatment Allocation with Simple Randomization",
       x = "Treatment Group", 
       y = "Number of Subjects") +
  theme_minimal() +
  theme(legend.position = "none")

5.3.2 Block Randomization

Ensuring balance throughout the trial:

Code
# Block randomization
library(blockrand)

# Generate block randomization for 100 subjects
block_rand <- blockrand(
  n = 100,          # Number of subjects
  num.levels = 2,   # Number of treatment groups
  levels = c("A", "B"), # Treatment labels
  block.sizes = c(4, 6, 8), # Varying block sizes
  id.prefix = "SUBJ" # Subject ID prefix
)

# View first 10 allocations
head(block_rand, 10)

# Check balance
table(block_rand$treatment)

# Visualize cumulative allocation
block_rand <- block_rand %>%
  arrange(id) %>%
  mutate(
    sequence = 1:n(),
    cumulative_A = cumsum(treatment == "A"),
    cumulative_B = cumsum(treatment == "B"),
    imbalance = abs(cumulative_A - cumulative_B)
  )

# Create line plot of cumulative assignments
ggplot(block_rand, aes(x = sequence)) +
  geom_line(aes(y = cumulative_A, color = "Treatment A"), size = 1) +
  geom_line(aes(y = cumulative_B, color = "Treatment B"), size = 1) +
  labs(title = "Cumulative Treatment Assignments",
       subtitle = "Block Randomization",
       x = "Subject Sequence", 
       y = "Cumulative Count",
       color = "Treatment") +
  theme_minimal()

For stratified randomization, minimization, and advanced adaptive randomization methods, see the next parts of this chapter.

5.4 References