Introduction to the mergedblocks package

library(mergedblocks)

Merged block randomization is a restricted randomization method designed for small clinical trials (at most 100 subjects) or trials with small strata, for example in multicentre trials. It can be used for more than two groups (treatment arms) or unequal randomization ratios.

There are two functions in the mergedblocks package:

The package can be installed by typing install.packages("mergedblocks") in the R terminal. In addition to the package, a Shiny app is available from https://svdpas.shinyapps.io/mergedblocks/, which can be used to create randomization lists for two groups (treatment arms).

A detailed description of the method is available in Van der Pas (2019). Merged block randomisation: A novel randomisation procedure for small clinical trials. Clinical Trials 16(3):246-252.

In this vignette, we provide a guided tour of the two functions in the package, illustrating the options within each function. The sections are:

  1. Merged block randomization for a single stratum: mergedblocks()
  1. Merged block randomization for multiple strata: mergedblocksmulti()

1. Merged block randomization for a single stratum: mergedblocks()

1.1 Two treatment arms, 1:1 allocation

For two treatment arms with 1:1 allocation, mergedblocks() only requires the number of individuals to be randomized as input, via the argument n. For example, we obtain a randomization list for 50 individuals via:

mergedblocks(n = 50)
#>  [1] "1" "2" "2" "1" "2" "1" "1" "2" "2" "1" "1" "2" "2" "1" "2" "1" "2" "1" "1"
#> [20] "2" "1" "2" "2" "1" "2" "1" "1" "2" "1" "2" "1" "2" "2" "1" "1" "2" "2" "1"
#> [39] "2" "2" "1" "1" "2" "1" "2" "2" "1" "1" "2" "1"

By default, the two groups will get numeric labels, in this case “1” and “2”. You can select custom labels with the labels argument. For example:

mergedblocks(n = 50, labels = c("treatment", "placebo"))
#>  [1] "treatment" "treatment" "placebo"   "placebo"   "treatment" "placebo"  
#>  [7] "treatment" "placebo"   "placebo"   "placebo"   "treatment" "treatment"
#> [13] "placebo"   "placebo"   "treatment" "treatment" "treatment" "placebo"  
#> [19] "treatment" "placebo"   "treatment" "placebo"   "placebo"   "treatment"
#> [25] "treatment" "treatment" "placebo"   "treatment" "placebo"   "placebo"  
#> [31] "treatment" "placebo"   "treatment" "placebo"   "placebo"   "placebo"  
#> [37] "treatment" "treatment" "placebo"   "placebo"   "treatment" "treatment"
#> [43] "treatment" "placebo"   "placebo"   "placebo"   "treatment" "treatment"
#> [49] "treatment" "placebo"

1.2 Two treatment arms, unequal allocation

The argument ratio can be used to select the desired randomization ratio. For example, if we wish to randomize 50 individuals to two groups in a 1:2 ratio, with allocations labelled as “A” and “B”, we would use:

mergedblocks(n = 50, ratio = c(1, 2), labels = c("A", "B"))
#>  [1] "B" "B" "B" "A" "A" "B" "B" "B" "A" "A" "B" "A" "B" "B" "B" "B" "A" "B" "A"
#> [20] "B" "B" "A" "B" "A" "B" "B" "B" "A" "B" "B" "B" "A" "B" "A" "B" "B" "B" "A"
#> [39] "B" "A" "B" "B" "B" "A" "B" "B" "A" "B" "B" "B"

Or for a 3:2 ratio:

mergedblocks(n = 50, ratio = c(3, 2), labels = c("A", "B"))
#>  [1] "A" "B" "B" "A" "A" "B" "B" "A" "A" "A" "A" "A" "A" "B" "A" "B" "B" "A" "A"
#> [20] "A" "A" "B" "A" "A" "B" "B" "A" "B" "A" "A" "B" "A" "B" "A" "B" "B" "A" "B"
#> [39] "A" "A" "A" "B" "B" "A" "A" "A" "B" "A" "B" "A"

1.3 More than two treatment arms

The argument ratio can be used to obtain randomization lists for more than two treatment arms. Suppose the number of treatment arms is \(N\). Then the ratio argument is set to a vector of length \(N\), with the entries of the vector indicating the desired randomization ratio.

For example, to randomize 50 subjects to three treatment arms in a 1:1:1 ratio:

mergedblocks(n = 50, ratio = c(1, 1, 1))
#>  [1] "1" "3" "1" "2" "2" "2" "3" "1" "3" "3" "3" "2" "1" "3" "2" "1" "1" "2" "3"
#> [20] "1" "2" "1" "2" "1" "2" "3" "3" "3" "2" "1" "3" "1" "2" "2" "3" "3" "1" "3"
#> [39] "2" "1" "1" "2" "1" "3" "2" "1" "2" "2" "3" "3"

If other labels than the default numeric ones are desired, the argument labels can be used. It requires a vector of the same length as ratio. For example, 1:1:1 allocation to treatments “A”, “B” and “C”:

mergedblocks(n = 50, ratio = c(1, 1, 1), labels = c("A", "B", "C"))
#>  [1] "A" "B" "A" "C" "C" "C" "B" "B" "A" "A" "C" "B" "A" "B" "C" "C" "A" "A" "C"
#> [20] "B" "A" "B" "C" "B" "A" "A" "B" "C" "B" "B" "C" "C" "A" "C" "A" "B" "B" "C"
#> [39] "A" "B" "C" "A" "C" "A" "A" "B" "B" "C" "B" "A"

For e.g. five treatment arms:

mergedblocks(n = 50, ratio = c(1, 1, 1, 1, 1), labels = c("A", "B", "C", "D", "E"))
#>  [1] "B" "E" "C" "D" "C" "D" "A" "E" "B" "A" "C" "E" "B" "A" "A" "D" "B" "D" "E"
#> [20] "B" "C" "C" "D" "A" "E" "B" "D" "C" "B" "E" "C" "A" "E" "E" "C" "A" "D" "D"
#> [39] "B" "B" "E" "C" "D" "A" "A" "D" "B" "A" "C" "D"

Unequal allocation is also possible. For example, to randomize 50 individuals to three treatment arms, “A”, “B” and “C”, in a 1:1:2 ratio:

mergedblocks(n = 50, ratio = c(1, 1, 2), labels = c("A", "B", "C"))
#>  [1] "A" "B" "C" "A" "B" "C" "C" "C" "C" "B" "A" "C" "A" "C" "C" "B" "A" "C" "C"
#> [20] "A" "B" "A" "C" "C" "B" "C" "B" "C" "C" "C" "A" "B" "C" "C" "A" "C" "A" "B"
#> [39] "B" "C" "B" "B" "A" "A" "C" "C" "C" "C" "A" "B"

The arguments of ratio and labels are linked In the example above, treatment “C” was assigned twice as often as “A” or “B”. With the following code, treatment “A” is assigned twice as often as “B” or “C” (2:1:1 ratio).

mergedblocks(n = 50, ratio = c(2, 1, 1), labels = c("A", "B", "C"))
#>  [1] "A" "B" "B" "A" "C" "A" "C" "A" "B" "A" "A" "B" "C" "C" "A" "A" "C" "A" "A"
#> [20] "B" "A" "A" "C" "B" "A" "A" "B" "B" "C" "A" "C" "B" "A" "B" "C" "A" "A" "C"
#> [39] "A" "C" "B" "A" "A" "A" "A" "B" "A" "A" "C" "C"

As a final example, to randomize 100 individuals to four treatment arms in a 2:2:3:3 ratio:

four.arms <- mergedblocks(n = 100, ratio = c(2, 2, 3, 3), labels = c("A", "B", "C", "D"))

four.arms
#>   [1] "D" "B" "C" "B" "D" "C" "C" "D" "C" "A" "A" "A" "D" "D" "D" "C" "A" "C"
#>  [19] "B" "D" "B" "A" "D" "C" "B" "C" "D" "D" "C" "C" "A" "B" "B" "C" "B" "A"
#>  [37] "C" "D" "A" "D" "C" "B" "D" "B" "C" "D" "A" "B" "C" "C" "D" "C" "D" "A"
#>  [55] "C" "B" "D" "C" "D" "D" "C" "B" "C" "A" "D" "D" "A" "A" "A" "B" "A" "D"
#>  [73] "B" "D" "C" "B" "A" "D" "B" "C" "C" "D" "D" "C" "B" "C" "A" "C" "D" "A"
#>  [91] "C" "D" "B" "A" "B" "C" "B" "B" "D" "C"

table(four.arms)
#> four.arms
#>  A  B  C  D 
#> 19 22 30 29

1.4 Saving the randomization lists

The randomization lists can be saved using e.g. write.table() or write.csv(). For example:

example.list <- mergedblocks(n = 50, ratio = c(1, 2), labels = c("A", "B"))

write.csv2(example.list, file = "./YourFilePath/ExampleList.csv")

This particular example will yield a CSV file with two columns. The first column contains the numbers 1-50, the second column contains the allocations.

2. Merged block randomization for multiple strata: mergedblocksmulti()

The function mergedblocksmulti() can be used to create randomization lists for multiple strata, as in a multicentre trial. The function allows for different sample sizes per stratum, and for unequal randomization ratios.

The function mergedblocksmulti() has four arguments. The arguments ratio and labels work the same as for the single stratum function mergedblocks(). The focus of this part of the vignette will be on K and n, the two arguments that work differently compared to the arguments of mergedblocks().

2.1 Multiple strata, same sample size per stratum

The argument K is set to the number of strata. If the number of individuals per stratum is the same for each stratum, the argument n can be set to this number.

For example, to create randomization lists for three strata with 25 individuals per stratum:

mergedblocksmulti(K = 3, n = 25)
#>    V1 V2 V3
#> 1   1  2  1
#> 2   1  1  2
#> 3   2  2  1
#> 4   2  1  2
#> 5   2  1  1
#> 6   2  2  1
#> 7   1  1  2
#> 8   1  2  2
#> 9   2  2  2
#> 10  1  1  1
#> 11  1  2  2
#> 12  2  1  2
#> 13  1  1  1
#> 14  1  2  1
#> 15  2  2  2
#> 16  2  1  1
#> 17  1  2  2
#> 18  2  2  1
#> 19  2  1  1
#> 20  1  1  2
#> 21  1  1  1
#> 22  2  1  2
#> 23  1  2  1
#> 24  2  2  2
#> 25  2  1  2

In the code above, default 1:1 allocation was used, with default numeric treatment labels. The output is a dataframe with one column for each stratum. As for mergedblocks(), the allocation ratio can be changed to e.g. 1:2 allocation, and custom labels can be used, e.g. “treatment” and “placebo”:

mergedblocksmulti(K = 3, n = 25, ratio = c(1, 2), labels = c("treatment", "placebo"))
#>           V1        V2        V3
#> 1  treatment treatment treatment
#> 2    placebo   placebo   placebo
#> 3    placebo   placebo   placebo
#> 4    placebo   placebo treatment
#> 5  treatment treatment treatment
#> 6    placebo treatment   placebo
#> 7  treatment   placebo   placebo
#> 8    placebo   placebo   placebo
#> 9    placebo   placebo   placebo
#> 10   placebo treatment   placebo
#> 11   placebo   placebo treatment
#> 12   placebo   placebo   placebo
#> 13 treatment   placebo   placebo
#> 14   placebo treatment   placebo
#> 15   placebo   placebo treatment
#> 16 treatment   placebo   placebo
#> 17   placebo treatment   placebo
#> 18 treatment treatment treatment
#> 19 treatment   placebo   placebo
#> 20   placebo treatment   placebo
#> 21   placebo   placebo   placebo
#> 22   placebo   placebo treatment
#> 23 treatment   placebo   placebo
#> 24   placebo   placebo treatment
#> 25   placebo treatment   placebo

As a further example, the following code yields randomization lists for four strata, 30 individuals per stratum, with 1:1:1 allocation of three treatments labelled “A”, “B” and “C”.

mergedblocksmulti(K = 4, n = 30, ratio = c(1, 1, 1), labels = c("A", "B", "C"))
#>    V1 V2 V3 V4
#> 1   C  C  B  A
#> 2   B  C  A  B
#> 3   C  B  C  A
#> 4   A  A  C  B
#> 5   A  B  A  C
#> 6   B  A  B  C
#> 7   A  B  B  A
#> 8   C  B  B  C
#> 9   C  A  C  B
#> 10  A  A  C  B
#> 11  B  C  A  C
#> 12  B  C  A  A
#> 13  C  C  B  C
#> 14  B  A  C  B
#> 15  A  B  A  C
#> 16  C  A  B  A
#> 17  B  A  C  B
#> 18  A  B  C  A
#> 19  A  C  A  C
#> 20  B  A  B  A
#> 21  C  C  B  B
#> 22  C  B  C  C
#> 23  B  B  A  B
#> 24  C  B  A  B
#> 25  B  C  B  C
#> 26  A  C  B  A
#> 27  A  A  C  A
#> 28  B  A  C  B
#> 29  A  C  A  A
#> 30  B  C  A  C

2.2 Multiple strata, sample sizes vary per stratum

The argument n can be used to incorporate varying sample sizes. For example, suppose we have three strata, with sample sizes 20, 30 and 25 In each stratum, 1:1 allocation to treatments “A” and “B” is desired. We could use the following code:

mergedblocksmulti(K = 3, n = c(20, 30, 25), ratio = c(1, 1), labels = c("A", "B"))
#>      V1 V2   V3
#> 1     A  B    B
#> 2     B  A    A
#> 3     A  A    A
#> 4     B  B    A
#> 5     B  A    B
#> 6     A  A    B
#> 7     B  B    A
#> 8     A  B    B
#> 9     B  B    B
#> 10    A  B    B
#> 11    B  A    A
#> 12    A  A    A
#> 13    A  B    A
#> 14    B  A    B
#> 15    B  A    B
#> 16    A  B    B
#> 17    B  B    A
#> 18    B  A    A
#> 19    A  A    A
#> 20    A  B    B
#> 21 <NA>  A    A
#> 22 <NA>  B    B
#> 23 <NA>  A    A
#> 24 <NA>  B    B
#> 25 <NA>  B    B
#> 26 <NA>  B <NA>
#> 27 <NA>  A <NA>
#> 28 <NA>  A <NA>
#> 29 <NA>  B <NA>
#> 30 <NA>  A <NA>

The argument n is now set to a vector with the desired sample sizes. The output is a dataframe with three columns, one per stratum. The columns with sample sizes 20 and 25 are padded with NAs.

As a further example, with the following code we obtain randomization lists for five strata, sample sizes 35, 30, 40, 20 and 15, for 1:1:1 allocation to treatments “A”, “B” and “C”.

mergedblocksmulti(K = 5, n = c(35, 30, 40, 20, 15), ratio = c(1, 1, 1), labels = c("A", "B", "C"))
#>      V1   V2 V3   V4   V5
#> 1     A    C  B    A    A
#> 2     C    A  A    A    B
#> 3     B    B  C    B    C
#> 4     B    C  A    C    A
#> 5     C    A  C    B    B
#> 6     A    A  B    B    C
#> 7     C    B  B    C    B
#> 8     A    A  C    C    A
#> 9     B    B  A    A    C
#> 10    B    C  B    A    A
#> 11    C    A  C    A    C
#> 12    A    C  A    B    B
#> 13    A    B  C    C    A
#> 14    B    A  B    C    C
#> 15    C    C  A    B    B
#> 16    B    C  A    C <NA>
#> 17    A    B  C    B <NA>
#> 18    A    B  B    C <NA>
#> 19    C    A  A    A <NA>
#> 20    B    B  C    B <NA>
#> 21    C    C  B <NA> <NA>
#> 22    C    A  B <NA> <NA>
#> 23    B    C  A <NA> <NA>
#> 24    A    B  B <NA> <NA>
#> 25    B    A  C <NA> <NA>
#> 26    C    C  C <NA> <NA>
#> 27    A    B  B <NA> <NA>
#> 28    C    A  A <NA> <NA>
#> 29    A    A  A <NA> <NA>
#> 30    B    B  B <NA> <NA>
#> 31    A <NA>  C <NA> <NA>
#> 32    A <NA>  A <NA> <NA>
#> 33    B <NA>  C <NA> <NA>
#> 34    C <NA>  C <NA> <NA>
#> 35    C <NA>  A <NA> <NA>
#> 36 <NA> <NA>  B <NA> <NA>
#> 37 <NA> <NA>  A <NA> <NA>
#> 38 <NA> <NA>  B <NA> <NA>
#> 39 <NA> <NA>  C <NA> <NA>
#> 40 <NA> <NA>  B <NA> <NA>

2.3 Saving the randomization lists

The randomization lists can be saved using e.g. write.table() or write.csv(). For example:

example.list <- mergedblocksmulti(K = 5, n = c(35, 30, 40, 20, 15), ratio = c(1, 1, 1), labels = c("A", "B", "C"))

write.csv2(example.list, file = "./YourFilePath/ExampleList.csv")

This particular example will yield a CSV file with six columns. The first column contains the numbers 1-40, the remaining five columns contain the allocations per stratum (one column per stratum).