Setting up a trial¶
Setting up a trial to use the minimization method requires you to choose a few settings, based on the design of the trial and what you think will be most important to balance. These are:
The arms that participants will be assigned to.
The prognostic factors that need to be balanced.
The function that calculates the amount of imbalance within each factor
The function that combines the factor imbalance scores to find the total imbalance
How probabilities will be assigned to each arm, based on the imbalance scores.
Some of those pieces might require additional settings, e.g. providing the probability that the most favoured arm will be assigned.
Putting these all together, a full specification for a trial might look like:
from smallerize import Arm, Factor, Minimizer
arms = [Arm('Control', allocation_ratio=1),
Arm('Active', allocation_ratio=2)]
factors = [Factor('Sex', levels=['Female', 'Male']),
Factor('Site', levels=['Site A', 'Site B', 'Site C'])]
minimizer = Minimizer(
factors=factors,
arms=arms,
d_imbalance_method='marginal_balance',
total_imbalance_method='sum',
probability_method='biased_coin',
preferred_p=0.8
)
Trial arms¶
First we outline all the arms that participants will be assigned to.
Each arm requires a unique name
Each arm might have a different allocation ratio, if the trial design requires different proportions in each group. This is optional, if you don’t provide them all arms will be assigned in equal proportions.
Example:
# Equal allocation ratios
equal_arms = [Arm('Placebo'), Arm('Surgery')]
# Different allocation ratios
unbalanced_arms = [
Arm('Control', allocation_ratio=1),
Arm('Active', allocation_ratio=2),
Arm('Waitlist', allocation_ratio=1)
]
Note
When using unequal allocation ratios, you might want to use the ‘biased coin’ method to assign probabilities
Factors¶
Factors are participant characteristics that we want to balance because they might affect the trial outcomes. The minimization method allows you to balance participants across multiple factors simultaneously - unlike in stratification methods, this doesn’t split participants up into increasingly small strata.
Factors must be categorical - they must have a finite set of levels that each participant will match up to.
Each factor requires a unique name.
Each factor will have its own factor levels.
(Optional): factors can be given different weights, so that they contribute different amounts to the total imbalance score. When using different weights, you must use the “weighted sum” method to calculate the total imbalance score.
Examples:
sex = Factor('Sex', levels=['Female', 'Male'])
# Factors must be categorical - bin numeric variables
age = Factor('Age', levels=['20-29', '30-39', '40+'])
# Optional weights
weighted = [
Factor('Severity', levels=['Low', 'High'], weight=2.0),
Factor('Sex', levels=['Female', 'Male'], weight=1.0)
]
Imbalance within each factor¶
When assigning a new participant, we could consider what would happen if the new participant was assigned to each arm.
Within each factor, we look at the current participants who match the new participant’s factor level, e.g. if for sex we currently have
Arm |
Male |
Female |
---|---|---|
Placebo |
9 |
11 |
Active |
12 |
8 |
and we are assigning a female participant, then
assigning the participant to Placebo would result
in Placebo: 12, Active: 8
and assigning them
to Active would result in Placebo: 11, Active: 9
.
While assigning a female participant, the number of male
participants assigned to each arm doesn’t contribute
to any imbalance calculations.
We take these potential counts, and use a function that scores the amount of imbalance between arms. E.g. if we use the range, then assigning to Placebo would give \(\text{range}(12, 8) = 4\) and assigning to Active would give \(\text{range}(11, 9) = 2\).
From Pocock + Simon (1975) and Han (2009), the different
functions we can use for d_imbalance_method
are:
'range'
: The highest count in an arm minus the smallest.'standard_deviation'
: The standard deviation of counts.'variance'
: Variance. Assigns an increasingly high score to large amounts of imbalance, so may be better at preventing extreme imbalance than the range or standard deviation.'over_max_range'
: A binary indicator that is 1 if the range exceeds a specified threshold, and 0 otherwise.If using this function, you must also supply the threshold as an additional argument
d_max_range
, e.g.d_max_range=2
'is_largest'
: A binary indicator that is 1 if the potential arm will have the highest count, and 0 otherwise (only works when the trial has exactly 2 arms).'marginal_balance'
: A relative measure between 0.0 (all treatments equal) and 1.0 (all participants in 1 treatment), as outlined in Han (2009). Weights factor levels with many participants lower, so that rare factor levels also contribute to the total imbalance.
We carry out this calculation for each factor, generating a set of scores like:
Potential arm |
Resulting imbalance within Sex |
Resulting imbalance within Severity |
---|---|---|
Placebo |
2 |
1 |
Active |
1 |
3 |
Note
When arms have different allocation ratios, we divide the
count in each arm by its allocation ratio first, meaning
the degree of imbalance is calculated relative to
the desired counts. This doesn’t entirely account for
the different allocation ratios though, so using
the 'biased_coin'
method to assign probabilities
is still recommended.
Examples:
minimizer = Minimizer(
factors=[sex, age],
arms=[Arm('Placebo'), Arm('Active')],
d_imbalance_method='over_max_range',
# over_max_range requires an additional argument
d_max_range=3
)
preferred_p argument was not provided. Using default value of 0.75
Total imbalance¶
Once we have the degree of imbalance within each factor,
we combine them into a total score, the overall imbalance
that would result from assigning each potential arm.
We can just use the sum here, although we can also apply
different weights to each factor and use a weighted sum. Valid
values for total_imbalance_method
are:
'sum'
'weighted_sum'
: Weight the sum by theweight
attribute of each factor.
E.g. using the table above with Sex and Severity, and using the sum to combine scores, assigning to Placebo would result in a score of 3, and assigning to Active would result in a score of 4.
Assigning probabilities¶
After we have the total imbalance that would result from
assigning the new participant to each arm, we rank
the arms in increasing order of imbalance, and
apply different probabilities. The different options
for probability_method
are:
'best_only'
: The arm that would result in the least imbalance is assigned with a high probability, and the remaining probability is divided among the remaining arms.An additional argument
preferred_p
sets the probability for the arm with lowest imbalance, e.g.preferred_p=0.7
. Setting this to 1.0 means the arm that produces the lowest imbalance is always assigned, but may make the allocation sequence too predictable.
'rank_all'
: The full ranking of arms is taken into account, with each successive arm being assigned a lower probability than the last.An additional argument
q
sets the degree to which the arm with lowest imbalance is favoured.q
must be between \(1 / N\) and \(2 / (N - 1)\), where \(N\) is the number of arms in the trial, and higher values mean the arm with lower imbalance is favoured more. E.g. with \(N = 4, q = 1 / 2\), the probabilities assigned are 0.4, 0.3, 0.2, 0.1.
'biased_coin'
: The biased-coin minimization method outlined in Han (2009), which correctly accounts for trials where arms have different allocation ratios.An additional argument
preferred_p
sets the probability that the arm with the lowest allocation ratio will be assigned, when it produces the lowest imbalance. Arms with other allocation ratios have their probabilities adjusted accordingly.