Convenience wrapper function for anticlustering using different defaults and additional functionality, as used in Wintermantel et al (2026; <doi:10.48550/arXiv.2607.07543>), provided by Dimitry Wintermantel. This function assigns subjects to treatment groups while balancing specified covariates and ensuring replicates per group. If a table of groups within treatments is supplied via `group_data`, the subjects (already assigned to treatments) are instead allocated to those groups within each treatment, again balancing the covariates.

experimental_allocation(
  data,
  covariates,
  treatments = NULL,
  treatment_var = "Treatment",
  group_data = NULL,
  group_var = NULL,
  n_replicates = NULL,
  ensure_equal_n_replicates = TRUE,
  keep_excluded_data = FALSE,
  objective = "variance",
  method = "local-maximum",
  repetitions = 10,
  match_within = NULL,
  standardize = TRUE
)

Arguments

data

A data frame containing the subjects to be allocated, including columns for the covariates. If `group_data` is supplied, it must also contain the treatment column named in `treatment_var`.

covariates

A vector of column names in `data` representing covariates to balance.

treatments

A vector specifying the treatment group labels. Required unless `group_data` is supplied, in which case the target groups are taken from `group_data`.

treatment_var

A string specifying the name of the treatment column. If `group_data` is not supplied, this column is created in the output (defaults to `"Treatment"`). If `group_data` is supplied, it names the existing treatment column, which must be present in both `data` and `group_data`.

group_data

An optional data frame describing the available groups within each treatment. Must include columns named in `group_var` and `treatment_var` indicating which groups belong to which treatment. Any further columns (e.g. group-level covariates) are merged into the output but are not used for balancing. Defaults to `NULL`.

group_var

A string specifying the name of the group column in `group_data` to which subjects will be allocated within each treatment (e.g., site, cage, plot). Required when `group_data` is supplied.

n_replicates

Integer. Specifies the number of replicates per group. If `NULL`, either all subjects are allocated or the highest possible equal number per group is allocated depending on `ensure_equal_n_replicates`. Defaults to `NULL`.

ensure_equal_n_replicates

Logical. If `TRUE`, ensures equal numbers of replicates across groups. Defaults to `TRUE`. If `FALSE`, all subjects are allocated.

keep_excluded_data

Logical. If `TRUE`, retains subjects that were not allocated within the specified constraints. Defaults to `FALSE`.

objective

A string specifying the objective function for anticlustering. Options include `"variance"` (default), `"diversity"`, `"average-diversity"`, `"kplus"`, and `"dispersion"`.

method

A string specifying the optimization method for anticlustering. Options include `"local-maximum"` (default), `"exchange"`, `"brusco"`, `"ilp"`.

repetitions

Integer. Specifies the number of times the optimization is repeated when using heuristic methods (`"exchange"`, `"local-maximum"`, `"brusco"`). The best solution is selected. Defaults to `10`.

match_within

A column name in `data` (optional). Specifies a variable within which matching should occur, ensuring that subjects are grouped within subsets defined by this variable. Defaults to `NULL`.

standardize

Logical. If `TRUE`, covariates are standardized via `scale()` before optimization starts. Defaults to `TRUE`.

Value

A data frame with subjects assigned to treatments, in the column named by `treatment_var`. If `group_data` is supplied, the output instead contains the subjects assigned to groups within their treatment (in the column named by `group_var`), merged with the corresponding group information from `group_data`. If `keep_excluded_data = TRUE`, excluded subjects are included in the output with `NA` in the allocation column.

Details

The function balances covariates among groups by creating sets of similar individuals based on the specified covariates, and then assigning the target labels to these sets so that similar individuals are spread across different groups.

If `group_data` is supplied, the allocation is carried out separately for each treatment: for every treatment level present in `data[[treatment_var]]`, the subjects of that treatment are allocated to the groups that belong to it in `group_data`, and the result is merged with the corresponding rows of `group_data`.

- If `ensure_equal_n_replicates = TRUE`, the number of replicates per group is enforced. - If `keep_excluded_data = TRUE`, subjects that cannot be allocated under the constraints are retained in the output with `NA` in the allocation column. - Covariates can be scaled to standardize their ranges if `standardize = TRUE`. - If `match_within` is specified, subjects are matched within levels of the specified variable, ensuring allocations respect the structure defined by this variable.

Validations

The function validates the following conditions: - The required arguments are provided (`data`, `covariates`, and either `treatments` or, when `group_data` is supplied, `group_var`). - Covariates exist in `data`, are numeric, and do not take the same value across all rows. - When `group_data` is supplied, `treatment_var` exists in both `data` and `group_data`, and `group_var` exists in `group_data`. - `match_within`, if specified, exists in `data`.

How to cite

If you use this function in academic work, please cite: Wintermantel, D., Osterman, J., Mair, M. M., & Hartig, F. (2026). *Equivalence testing in pesticide risk assessment – Evaluation and practical https://doi.org/10.48550/arXiv.2607.07543

The treatment allocation implemented here relies on anticlustering algorithms. Users are encouraged to also cite the `anticlust` package where appropriate (see `citation("anticlust")`)

Author

Dimitry Wintermantel dimitry.wintermantel@nature.uni-freiburg.de

Examples


# Example dataset: Bee subjects
example_bee_data <- data.frame(
  ID = as.factor(as.character(seq(1, 100, 1))),
  Bee_count = rnorm(100, mean = 1000, sd = 200),
  Colony_weight = rnorm(100, mean = 500, sd = 100)
)

# Example dataset: Sites (groups) with a site-level covariate and a blocking variable
example_site_data <- data.frame(
  Site = as.factor(as.character(seq(1, 16, 1))),
  Irrigated = as.factor(as.character(c(rep("yes", 4), rep("no", 12)))),
  Field_quality = rnorm(16, mean = 3, sd = 0.5)
)

treatments <- c("Control", "Pesticide")
bee_covariates <- c("Bee_count", "Colony_weight")

# 1) Allocate subjects to treatments (balancing colony-level covariates)
allocated_bee_data <- experimental_allocation(
  data = example_bee_data,
  treatments = treatments,
  covariates = bee_covariates
)

# 2) Allocate sites to treatments (balancing site quality, matching within irrigation status)
allocated_site_data <- experimental_allocation(
  data = example_site_data,
  treatments = treatments,
  covariates = "Field_quality",
  match_within = "Irrigated"
)

# 3) Allocate subjects to specific sites within each treatment
allocated_data <- experimental_allocation(
  data = allocated_bee_data,
  covariates = bee_covariates,
  treatment_var = "Treatment",
  group_data = allocated_site_data,
  group_var = "Site"
)
head(allocated_data)
#>   Treatment Site Irrigated Field_quality ID Bee_count Colony_weight
#> 1 Pesticide   12        no      2.796512 80  839.9583      613.1227
#> 2 Pesticide   12        no      2.796512 85  756.2232      346.4663
#> 3 Pesticide   12        no      2.796512 25  885.9459      680.1028
#> 4 Pesticide   12        no      2.796512 49 1255.2765      398.8396
#> 5 Pesticide   12        no      2.796512 41  841.4614      428.3508
#> 6 Pesticide   12        no      2.796512 29 1325.5175      524.7961