---
title: "randotools"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{randotools}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```


Randomisation is one of the key aspects of clinical trials, ensuring that treatment groups are balanced and that the results are not biased. The `randotools` package provides functions to create randomisation lists in R, with a focus on flexibility and ease of use.


```{r setup}
library(randotools)
```

## Creating randomization lists

Randomisation lists are easily created with the `randolist` function. Specify the number of participants to randomise (per strata, if there are any), any strata, the arms to randomise between, and the block sizes. 

```{r}
randolist(n = 20, arms = c("Trt1", "Trt2"))
```

In the above call, 

- `n` specifies the number of participants to randomise per stratum. In this case, we are randomising 20 participants in a single stratum.
- `arms` specifies the names of the arms to randomise between.

Any number of arms can be specified, so `randolist` can be used for trials with two, three, even 10 or more arms, so platform trials can be accommodated by `randolist`, although implementing them within the database is not trivial.

### Block randomisation

By default, `randolist` uses block randomisation - rather than using random selection along the whole list in the hope that the arms are balanced, it creates blocks of randomisation, whereby each block is balanced, and block sizes are chosen at random. This not only helps with balancing, but also makes it harder to guess the next allocation. Block sizes are controlled via the `blocksizes` argument, where the values should be the potential number of each arm to include in any given block. E.g. `c(1,2)` would produce blocks with either one of each arm, or two of each arm, for a total block size or 2 or 4.

```{r}
r <- randolist(n = 10, 
               arms = c("Trt1", "Trt2"), 
               blocksizes = c(1,2))
```

Additionally, `blocksizes` are selected approximately proportional to Pascal's Triangle, so that medium sizes blocks are more likely to be selected than small or large blocks. This is done to ensure that the randomisation list is not too predictable, and helps with balance by reducing the chance of finishing mid-way through a large block. 

To disable block randomisation, set the block size to `n` divided by the number of arms (in this case `n`/2, so 10):

```{r}
randolist(n = 20, 
          arms = c("Trt1", "Trt2"),
          blocksizes = 10)
```

The `blockrand` function can also be used for non-stratified randomisation lists.

### Stratified randomisation lists

It is very common to need a stratified randomisation where the randomisation is balanced within strata. This is done by specifying the `strata` argument, which should be a list of the stratifying variables. The function will then create a randomisation list for each stratum, and combine them into a single list. The list for each strata contains `n` participants. 

```{r}
rs <- randolist(n = 10, 
                strata = list(sex = c("Male", "Female"),
                              age = c("Teen", "Adult")))

table(rs$sex)
table(rs$sex, rs$arm)
```

By using factors to specify the strata, the labels and levels are available for use when exporting the randomisation list, which is useful for importing into electronic data capture systems such as REDCap, which requires a specific format.


### Unbalanced randomisation lists

It is not uncommon to have unbalanced randomisation lists. E.g. 2 control participants per experimental participant. This is easily done by changing the `arms` argument:

```{r}
r2 <- randolist(n = 10, 
               arms = c("A", "A", "B"))

table(r2$arm)
```

Adaptive trials sometimes modify the randomisation balance part way through a trial, which can be accomplished via this method.

## Summarizing randomisation lists

It can be helpful to summarize the randomisation list to check that the requirements, such as the balance, coding, etc, are met. The `randolist` package includes a `summary` precisely for this purpose:

```{r}
randolist(n = 20, arms = c("Trt1", "Trt2")) |> summary()
```

The summary for stratified randomisation lists also includes information at the strata level.

## Exporting randomisation lists

Once a randomisation list is created, it needs to be transferred into a system that will ultimately perform the randomisation. We primarily use two systems for this: REDCap and SecuTrial, but you might use others, which may require other modifications. The `randolist` package includes a function to convert the randomisation list into a format that should be, with minimal effort, be importable into these systems.

```{r}
# create a very small randomisation list for demonstration purposes
rs2 <- randolist(n = 2, blocksizes = 1,
                 arms = c("Aspirin", "Placebo"),
                 strata = list(sex = c("Male", "Female"),
                               age = c("Teen", "Adult")))
```

The `randolist_to_db` function is used to convert the randomisation list into a format that can be imported into the system. The function takes the randomisation list as input, and converts it to a data frame with the columns appropriate for the target database (`target_db`). In the case of REDCap, it is necessary to provide a data frame which maps the arms provided in `randolist` to the database variables. 

```{r}
randolist_to_db(rs2, target_db = "REDCap", 
                rando_enc = data.frame(arm = c("Aspirin", "Placebo"),
                                       rand_result = c(1, 2)),
                strata_enc = list(sex = data.frame(sex = c("Male", "Female"), code = 1:2),
                                  age = data.frame(age = c("Teen", "Adult"), code = 1:2)))
```

SecuTrial uses a more standardised format, so `rando_encoding` is not required. 

```{r, warning=FALSE}
randolist_to_db(rs2, target_db = "secuTrial",
                strata_enc = list(sex = data.frame(sex = c("Male", "Female"), code = 1:2),
                                  age = data.frame(age = c("Teen", "Adult"), code = 1:2)))
```

The dataframe returned can then be exported to CSV or xlsx and imported into the relevant database.


