# Baseline: Random Assignment
random_assignment <- function(data) {
shuffled_data <- data[sample(nrow(data)), ]
half <- nrow(shuffled_data) %/% 2
treatment <- shuffled_data[1:half, ]
control <- shuffled_data[(half + 1):nrow(shuffled_data), ]
return(list(Treatment = treatment, Control = control))
}
# Alternate Assignment
alternate_assignment <- function(data) {
sorted_data <- data[order(-data$Case_Workers), ]
treatment <- sorted_data[seq(1, nrow(sorted_data), 2), ]
control <- sorted_data[seq(2, nrow(sorted_data), 2), ]
return(list(Treatment = treatment, Control = control))
}
# Greedy Algorithm
greedy_assignment <- function(data) {
sorted_data <- data[order(-data$Case_Workers), ]
treatment <- data.frame()
control <- data.frame()
treatment_sum <- 0
control_sum <- 0
for (i in 1:nrow(sorted_data)) {
if (treatment_sum <= control_sum) {
treatment <- rbind(treatment, sorted_data[i, ])
treatment_sum <- treatment_sum + sorted_data$Case_Workers[i]
} else {
control <- rbind(control, sorted_data[i, ])
control_sum <- control_sum + sorted_data$Case_Workers[i]
}
}
return(list(Treatment = treatment, Control = control))
}
# Largest Differencing Method with Backtracking
largest_differencing_partition <- function(data) {
S <- sort(data$Case_Workers, decreasing = TRUE)
steps <- list()
while (length(S) > 1) {
largest <- S[1]
second_largest <- S[2]
steps <- c(steps, list(c(largest, second_largest)))
S <- c(S[-c(1, 2)], abs(largest - second_largest))
S <- sort(S, decreasing = TRUE)
}
subset1 <- c()
subset2 <- c()
for (step in rev(steps)) {
if (sum(subset1) <= sum(subset2)) {
subset1 <- c(subset1, step[1])
subset2 <- c(subset2, step[2])
} else {
subset1 <- c(subset1, step[2])
subset2 <- c(subset2, step[1])
}
}
treatment_df <- data[data$Case_Workers %in% subset1, ]
control_df <- data[data$Case_Workers %in% subset2, ]
return(list(Treatment = treatment_df, Control = control_df))
}
Experimental designs sometimes require randomization at the cluster level, where groups of individuals—rather than individuals themselves—are assigned to treatment or control. This approach is often necessary when the intervention naturally applies at the cluster level (e.g., policy changes targeting schools, hospitals, or workplaces) or when participants within a cluster lack independence (e.g., students within the same classroom or employees within the same company).
We encountered this exact challenge in designing a randomized controlled trial (RCT) with caseworkers. Earlier pilot studies revealed that caseworkers within the same agency often communicate during experiments, making it difficult to ensure compliance with their assigned treatment. To address this, we shifted from individual-level to agency-level randomization.
However, this introduced a new challenge: the wide variation in agency caseworker counts makes random assignment prone to substantial imbalances. Since statistical power is typically maximized when treatment and control arms have similar sizes, the key question becomes: how can we allocate agencies to treatment and control groups to achieve balanced caseworker counts?
This question boils down to a classic two-way partition problem: dividing a list of numbers (in this case, agency caseworker counts) into two groups such that the sums of the groups are as close as possible. This problem arises in many contexts, such as splitting workloads across teams or, in our case, creating balanced treatment and control groups in an RCT.
Solving this problem exactly is difficult because it is NP-hard, meaning that finding an exact solution becomes exponentially harder as the dataset grows. Approximate algorithms provide a practical alternative. These methods use simple heuristics to produce solutions quickly, without evaluating every possible combination. While they don’t guarantee a perfect split, they often achieve results that are “good enough” for most real-world applications. Additionally, approximate solutions can serve as upper bounds to help exact algorithms prune the search space, making the process more efficient.
In this blog post, I’ll share my exploration of approximate algorithms for solving this problem, including methods like alternate assignment, greedy algorithms, and the largest differencing method.
Alternate Assignment
The alternate assignment method is straightforward: agencies are first sorted by their caseworker counts, and then alternately assigned to treatment and control groups. This process ensures that both groups receive a mix of agencies with varying sizes and thus to prevent extreme imbalances.
Let’s consider six agencies with caseworker counts: \[ S = \{50,30,40,10,20,5\} \] The alternate assignment method would perform the following steps:
Caseworker counts will be sorted in descending order: {50, 40, 30, 20, 10, 5}
-
The sorted agencies will be alternately assigned to treatment and control groups
Treatment: {50, 30, 10}
Control: {40, 20, 5}
This method is easy to implement and generally ensures reasonably balanced groups. However, it struggles when the data contains clusters or extreme outliers. For example, with S = {45, 44, 43, 5, 4, 3}, the clustering of similar values (e.g. 45, 44, 43) results in imbalances between the treatment and control groups. Similarly, in datasets with extreme outliers, a few very large values can dominate the totals in one group.
Greedy Algorithm
The greedy algorithm attempts to minimize the difference in caseworker counts between treatment and control groups by iteratively assigning each agency to the group with the smaller current total.
Using the same example: \[ S = \{50,30,40,10,20,5\} \]
Caseworker counts will first be sorted in descending order: {50, 40, 30, 20, 10, 5}
-
Then we assign 50 to treatment
Treatment: {50}, Total: 50
Control: {}, Total: 0
-
We assign 40 to control as it has a smaller total
Treatment: {50}, Total: 50
Control: {40}, Total: 40
-
We assign 30 to control as it has a smaller total
Treatment: {50}, Total: 50
Control: {40, 30}, Total: 70
-
Following similar steps, eventually we have
Treatment: {50, 20, 10}, Total: 80
Control: {40, 30, 5}, Total: 75
Compared to alternate assignment, the greedy algorithm achieves better balance (absolute difference: 5 vs. 25). Its dynamic balancing prevents any group from dominating caseworker totals, effectively minimizing the impact of large agencies in datasets with high variance or outliers.
Largest Differencing Method
The largest differencing method (LDM) offers another option to balance groups by iteratively pairing and reducing discrepancies. It works by repeatedly taking the two largest remaining values, placing them in different groups, and replacing them with their difference.
Below is an example for illustration:
\[ S = \{2,8,11,12,17,18\} \]
-
After sorting caseworker counts in descending order, we pair the two largest numbers and replace them with their difference
18-17=1, {12, 11, 8, 2, 1}
12-11=1, {8, 2, 1, 1}
8-2=6, {6, 1, 1}
6-1=5, {5, 1}
5-1=4, {4}
-
We then reconstruct the treatment and control groups by backtracking through the differencing steps. Specifically, we remove the difference from the subset, with larger number replacing the difference in the same set and the smaller one adding to another set
-
From 5-1=4
Treatment: {5}
Control: {1}
-
From 6-1=5
Treatment: {6}
Control: {1, 1}
-
From 8-2=6
Treatment: {8}
Control: {2, 1, 1}
-
From 12-11=1
Treatment: {11, 8}
Control: {12, 2, 1}
-
From 18-17=1
Treatment: {17, 11, 8}
Control: {18, 12, 2}
-
The LDM approach ensures that the largest contributors to imbalance are addressed first, effectively distributing dominant values across the treatment and control groups.
Method Comparison
To systematically evaluate methods for achieving balance, I conducted a simulation analysis using different synthetic datasets. For each simulation, a random subset of a given number of agencies (clusters) was selected among 50 agencies, and these agencies were assigned to treatment and control groups to minimize the difference in caseworker totals between the groups. This process was repeated 500 times for each subset size.
Specifically, I considered three types of data structures: (1) clustered data, where caseworker counts are concentrated in two distinct clusters of high and low values; (2) dispersed data, where caseworker counts are evenly spread across a wide range; and (3) skewed data, where a few agencies have very large caseworker counts while most have smaller counts. The distributions of these datasets are displayed below.
The graphs below summarize the simulation results. As expected, random assignment without accounting for the variation in caseworker counts among agencies results in highly imbalanced samples, producing the largest mean absolute differences between groups. Among the three data types, the Greedy Algorithm consistently performs the best to create groups where caseworker counts are relatively similar. For dispersed and clustered data, both the Greedy Algorithm and LDM perform comparably well. However, for skewed data, LDM performs much worse than Greedy, suggesting its limitations in handling datasets with extreme outliers.
Some key takeaways from my exploration
Greedy algorithm and LDM are often good enough to achieve balanced groups efficiently, though LDM struggles with skewed data
Approximate vs. exact algorithms presents a trade-off between accuracy and computational efficiency
Simulate for our specific data to identify the best method based on our specific priorities (e.g. accuracy, computational efficiency)
Below is the R code implementation of these treatment assignment approaches. For those who are interested, this script implements the complete greedy algorithm to find the perfect split between treatment and control groups.