R code examples

Simple randomisation with different treatments by domain

Simple randomisation function with 50% probability (1:1 ratio) to randomise to A or B in domain 1 and 80% vs. 20% probability (4:1 ratio) to randomise to A or C in domain 2. The domain comes from the randomisation form and can be accessed in the R code as randomisation$domain. The allocation sequence uses a seed to make it reproducible. Metadata is used to stored the seed state between function calls.

if (is.null(metadata$seed)) {
  # Set seed on first run
  set.seed(57897231)
} else {
  # Restore SEED state
  .Random.seed <- metadata$seed
}

if (randomisation$domain == "Domain 1") {
  # Domain 1
  group <- sample(c("A", "B"), size = 1, prob = c(0.5, 0.5))
} else {
  # Domain 2
  group <- sample(c("A", "C"), size = 1, prob = c(0.8, 0.2))
}

list(
  group = group,
  metadata = list(
    seed = .Random.seed
  )
)

Stratified random permuted block lists

This method uses the rpbrPar method from the randomizeR package to create a blocked list for each stratum and keep track of the location in the list to return the next available treatment.

library("randomizeR")
library("digest")

# List length per stratum - recommended to make this same as trial sample size
# so that we don't run out of allocations during the trial. Also note that if
# you change this mid-trial you will start a different blocked list!
N_PER_STRATUM = 200

# Block sizes
BLOCK_SIZES = c(4, 6)

# Treatment groups
GROUPS = c("Intervention", "Control")

# Treatment group ratios
RATIO = c(1, 1)

# Stratification factors
STRATA = c(randomisation$Sex, randomisation$AgeGroup) 

# Initialise metadata
if (!is.list(metadata$counts)) {
  message("initialise metadata")
  metadata = list("counts" = list(), "seeds" = list())
}

# Keep track of position in randomisation list for each stratum
get_list_position <- function(stratum, metadata) {
  if (!(stratum %in% names(metadata$counts))) {
    # First randomisation in this stratum
    message("stratum not in metadata")
    metadata$counts[stratum] = 1
  }
  return(metadata$counts[[stratum]])
}

# Initialise if new stratum
initialise_seed <- function(stratum, metadata) {
  if (!(stratum %in% names(metadata$seeds))) {
    # First randomisation in this stratum
    message("seed not in metadata")
    metadata$seeds[stratum] = sample(1:99999999, 1)
  }
  return(metadata)
}

increment_list_position <- function(stratum, metadata) {
  metadata$counts[stratum] <- get_list_position(stratum, metadata) + 1
  return(metadata)
}

randomise <- function(stratum, metadata) {
  n <- get_list_position(stratum, metadata)
  message("List position ", n)
  r <- rpbrPar(N_PER_STRATUM, BLOCK_SIZES, K = 2, ratio = RATIO, filledBlock = FALSE)
  metadata <- increment_list_position(stratum, metadata)
  metadata <- initialise_seed(stratum, metadata)
  seed <- metadata$seeds[[stratum]]
  message("Using seed ", seed)
  s <- genSeq(r, 1, seed)$M[,n] + 1
  return(list(group = GROUPS[s], metadata = metadata))
}

stratum <- digest(STRATA, algo="sha256")
message(paste("Randomising in strata:", toString(STRATA), "=", stratum))
randomise(stratum, metadata)

Response adaptive randomisation

Response adaptive randomisation function to group A or B, with c = n/2N. Based on Practical Bayesian Adaptive Randomization in Clinical Trials (Peter F Thall, J Kyle Wathen).

Outcome data comes from a follow-up form (a Yes/No field called Success) and is passed into the R-code as auxiliary_data$outcomes. All previous randomisations (containing the group  field) are passed in as auxiliary_data$randomisations .

The message function is used to log comments, so they can be viewed on the View R randomisation log page.

rar <- function(outcome_data, randomisation, randomisations, metadata, sample_size) {
  # --- Calculate Success Rates (Response-Adaptive Component) ---
  
  # Handle the initial state where there is no outcome data yet.
  # We assume a neutral success rate of 0.5 for each group to start.
  if (nrow(outcome_data) == 0) {
    sr_a <- 0.5
    sr_b <- 0.5
  } else {
    # Subset outcomes for each group
    outcomes_a <- outcome_data[outcome_data$group == "A", ]
    outcomes_b <- outcome_data[outcome_data$group == "B", ]
    
    # Calculate success rate for Group A, using 0.5 if no data exists
    if (nrow(outcomes_a) == 0) {
      sr_a <- 0.5
    } else {
      sr_a <- sum(outcomes_a$Success) / nrow(outcomes_a)
    }
    
    # Calculate success rate for Group B, using 0.5 if no data exists
    if (nrow(outcomes_b) == 0) {
      sr_b <- 0.5
    } else {
      sr_b <- sum(outcomes_b$Success) / nrow(outcomes_b)
    }
  }
  
  # Normalize success rates into probabilities. Handle case where both are 0.
  total_sr <- sr_a + sr_b
  if (total_sr == 0) {
    prob_a_adaptive <- 0.5
  } else {
    prob_a_adaptive <- sr_a / total_sr
  }
  prob_b_adaptive <- 1 - prob_a_adaptive
  
  # --- Determine Final Probabilities ---
      
  # Use the adaptive probabilities with c=n/2N
  c = nrow(randomisations) / (2 * sample_size)
  final_prob_a <- prob_a_adaptive^c / (prob_a_adaptive^c + prob_b_adaptive^c)
  final_prob_b <- 1 - final_prob_a
  message("ID: ", randomisation$id)
  message("Using adaptive probabilities with c = ", c)
  message(
    "Success Rates -> A: ", round(sr_a, 2), ", B: ", round(sr_b, 2),
    " | Final Probs -> A: ", final_prob_a, ", B: ", final_prob_b 
  )
 
  # --- Randomize and Return Assignment ---
  
  # Use the final probabilities to randomly sample the assigned group
  group <- sample(c("A", "B"), size = 1, prob = c(final_prob_a, final_prob_b))
  
  return(list(group = group, metadata = metadata))
}

# Convert Yes/No to booleans
auxiliary_data$outcomes$Success = auxiliary_data$outcomes$Success == "Yes"

# Convert auxiliary data into format required by the function
if (is.data.frame(auxiliary_data$randomisations)) {
  randomisations <- auxiliary_data$randomisations
} else {
  randomisations <- data.frame(
    subjectId = integer(),
    group = character()
  )
}

if (is.data.frame(auxiliary_data$outcomes) && is.data.frame(auxiliary_data$randomisations)) {
  historical_data <- merge(
    auxiliary_data$outcomes, 
    auxiliary_data$randomisations, 
    by = "subjectId"
  )
  
  outcome_data <- data.frame(
    subjectId = historical_data$subjectId,
    group = historical_data$group,
    Success = historical_data$Success
  )
} else {
  outcome_data <- data.frame(
    subjectId = integer(),
    group = character(),
    Success = logical()
  )
}

rar(
  outcome_data,
  randomisation,
  randomisations,
  metadata,
  100 
)

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us