Block randomisation with different predefined block sizes


#1

Hello,

I would like to randomize 189 participants to one of three treatment conditions. I would like to do so using a block randomisation method with 3 blocks, two of size 45 and one of size 54. How can I do this using R?


#2

You may want to take a look at the blockrand package. The function blockrand() (inside of the package) takes several arguments, including block.sizes, which is a vector of integers specifying the sizes of blocks to use (in your case 45, 45, and 54). The documentation (see link above) will give you further detail.

Hope that helps.


#3

True, I have looked at it but this function can also give me 3 blocks of 54 and one of 45, which is actually not what I want.


#4

Even if you specify block.sizes = c(45, 45, 54)?

This thread might be relevant as well:
http://r.789695.n4.nabble.com/random-permuted-block-randomization-td1401407.html


#5

Shouldn't the sum of the block sizes equal the number of participants?


#6

Okay, admitted... I like puzzles... Is this what you are looking for? :slightly_smiling_face:

# Load libraries
library(tidyverse)

# Set seed for reproducible example
set.seed(498729)

# Define function for creating the Randomized Block Design
rand_block_design = function(block_sizes, n_treatments){

  # Check arguments
  if(!all(block_sizes %% n_treatments == 0)){
    stop("All blocks_sizes MUST be divisible with n_treatments")
  }
  
  # Sub function for generation repeats of block_1, block_..., block_n
  # Each repeated according to corresponding block size
  get_blocks = function(block_sizes){
    blocks = paste0("block_", rep(seq(1, length(block_sizes)), block_sizes))
    return(blocks)
  }
  
  # Sub function for assuring balanced randomized treatment assignments
  # Creates a vector of shuffled, but balanced treatment_1, treatment_..., treatment_n
  # according to each of the block sizes
  get_treatments = function(block_sizes, n_treatments){
    list_of_treatments = sapply(block_sizes, function(x){
      list_of_groups = sapply(seq(1, n_treatments), function(i){
        return(rep(LETTERS[i], x/n_treatments))
      })
      return(sample(unlist(list_of_groups)))
    })
    return(unlist(list_of_treatments))
  }
  
  # Create data frame of Randomized Block Design
  rbd = data.frame(
    id = seq(1, sum(block_sizes)),
    block = get_blocks(block_sizes),
    treatment = get_treatments(block_sizes, n_treatments)
  )
  
  # Done, return!
  return(rbd)
}

d = rand_block_design(block_sizes = c(45, 45, 54), n_treatments = 3) %>% as_tibble
d %>% count(block)
# # A tibble: 3 x 2
#   block       n
#   <fct>   <int>
# 1 block_1    45
# 2 block_2    45
# 3 block_3    54
d %>% count(block, treatment)
# # A tibble: 9 x 3
#   block   treatment     n
#   <fct>   <fct>     <int>
# 1 block_1 A            15
# 2 block_1 B            15
# 3 block_1 C            15
# 4 block_2 A            15
# 5 block_2 B            15
# 6 block_2 C            15
# 7 block_3 A            18
# 8 block_3 B            18
# 9 block_3 C            18
d %>% pull(treatment)
#   [1] A C C C A C B A B C B C C B C A B B A B A C A B C C B C C A B A A C B B A B B A B A C A A A B
#  [48] B B A B B A A A C A A C A B A C C C A C B C B B A C C C C B C C A B B C A C B A B B A B B C B
#  [95] C B C C A C C A A A B C B A C B A B A C A C B A B C A B C A C A B A A A A A C B C B B B A B C
# [142] B C C
# Levels: A B C

#7

Sorry you are right, it should be three blocks of 45 participants and one of 54. So block sizes should be (45, 45, 45, 54).


#8

Then try to run my code above like so:

d = rand_block_design(block_sizes = c(45, 45, 45, 54), n_treatments = 3) %>% as_tibble

and see if you get the expected output? :slightly_smiling_face:


#9

It works perfectly, thank you so much!


#10

You're welcome! Please mark the answer, which holds the solution :slightly_smiling_face:


#11

So, for clarity, @sasazxx, the block_sizes = argument works as expected when you explicitly specify them in a vector.


#12

...and as always, please run some tests, to see if the function I wrote does indeed yield the expected output - No guarantees given. Also, remember to use the set.seed(), so you can reproduce your randomisation :slightly_smiling_face: