Syndicate content

Tools of the trade: Doing Stratified Randomization with Uneven Numbers in some Strata

David McKenzie's picture

This is a joint post with Miriam Bruhn.

Consider an experiment in which you are randomizing individuals into a control group and 5 different treatment groups, and you have several variables you would like to stratify the treatment on. The textbook description makes it seem very easy: simply create strata, and then within strata, randomly allocate 1/6th of the sample to each group. The difficulty that arises in practice is that often you end up with strata with cell sizes not divisible by the number of treatment and control groups. This causes both some minor conceptual problems and some slightly tricky programming issues. Since this is something that seems to come up in discussions on several projects, we thought it might be worth explaining what needs to be done, and giving an example in Stata.

To make it concrete, here are some data from a project we are working on, in which we have 4 variables we want to randomize over: labeled variables A-D. Variable A has 4 values (e.g. 4 geographic regions), variable B is binary (e.g. gender), variables C and D have 3 values each (e.g. education groupings, or household asset groupings). All told this gives 72 distinct strata. The total sample is 1751 individuals. Some combinations of these variables are more common than others, so the strata cellsize ranges from 1 to 99.

Then if we start and just allocate by 6s within each cell, we randomly allocate 1590/1751 individuals to a treatment or control group – the first part of this stata code does that. The key question is then what to do with the remaining 161 observations. One approach would be to group them all into a new strata (“the misfits”) and randomly divide this into 6 groups. However, the downside of this is that it doesn’t preserve balance within the original strata – we might for example end up allocating all of the women in this “misfits” strata to treatment.

It is clear then that we want to randomly allocate the leftovers within each strata, taking care that if we have X units (where X ranges from 1 to 5) left in a strata, we allocate them in such a way that:

a)      No treatment or control group gets allocated more than one of these units within the strata, and

b)      We randomly choose which treatment groups get the extra units.

This is clearest when there is only 1 left over unit – then we just draw a random number, and if it is less than 0.166, allocate to treatment 1, if it’s between 0.166 and 0.333 allocate to treatment 2, etc.

With 2 left over units, it gets more complex. There are 6 choose 2 = 15 possible ways of allocating the leftover 2 units among the 6 treatment groups. So we draw a random number at the strata level, and if this is less than 0.0667, the 2 units go one each in treatment 1 and treatment 2, if it is between 0.0667 and 0.1333, the 2 units go one each in treatment 1 and treatment 3, etc.

With 3 left over units, there are 6 choose 3 = 20 different ways to allocate the extra units. You can see our code does this the brute force way – going through all 20 options – in most cases the number of distinct treatments is likely to be no more than what we have here, so this should be fine, but more elegant programming will be needed if you have heaps of different treatment conditions.

The stata code goes through all these cases, and you see at the end all 1751 cases have been allocated among the strata in a way which gives good balance within strata. Then, following our earlier paper, we recommend you control for all these strata by including strata dummies in your estimation.

Of course if you have a different number of treatment groups, or a different maximum number of individuals in any given strata, you will need to modify this code before using it – but hopefully it is at least helpful to some people who are trying to implement this.

Comments

Submitted by Gabe on
* for residuals gen random_r = runiform() if (group==.) & vtag ==1 //those that haven't been allocated yet and village by strata: egen rank_r = rank(random_r) gen random_strata = runiform() if rank_r==1 by strata: egen m_random_strata = max(random_strata) if rank_r!=. /* by strata: egen rank_mod = rank(runiform()) if vtag == 1 & (low==. & high==. & control==.) //randomly assign the leftovers in the strata to one of three groups sort strata mauzaid rank_mod replace rank_mod = rank_mod[_n-1] if mauzaid == mauzaid[_n-1] */ #delimit ; mat M7C1 = (1\2\3\4\5\6\7); mat M7C2 = (1,2\ 1,3\ 1,4\ 1,5\ 1,6\ 1,7\ 2,3\ 2,4\ 2,5\ 2,6\ 2,7\ 3,4\ 3,5\ 3,6\ 3,7\ 4,5\ 4,6\ 4,7\ 5,6\ 5,7\ 6,7); mat M7C3 = (1,2,3\ 1,2,4\ 1,2,5\ 1,2,6\ 1,2,7\ 1,3,4\ 1,3,5\ 1,3,6\ 1,3,7\ 1,4,5\ 1,4,6\ 1,4,7\ 1,5,6\ 1,5,7\ 1,6,7\ 2,3,4\ 2,3,5\ 2,3,6\ 2,3,7\ 2,4,5\ 2,4,6\ 2,4,7\ 2,5,6\ 2,5,7\ 2,6,7\ 3,4,5\ 3,4,6\ 3,4,7\ 3,5,6\ 3,5,7\ 3,6,7\ 4,5,6\ 4,5,7\ 4,6,7\ 5,6,7); mat M7C4 = (1,2,3,4\ 1,2,3,5\ 1,2,3,6\ 1,2,3,7\ 1,2,4,5\ 1,2,4,6\ 1,2,4,7\ 1,2,5,6\ 1,2,5,7\ 1,2,6,7\ 1,3,4,5\ 1,3,4,6\ 1,3,4,7\ 1,3,5,6\ 1,3,5,7\ 1,3,6,7\ 1,4,5,6\ 1,4,5,7\ 1,4,6,7\ 1,5,6,7\ 2,3,4,5\ 2,3,4,6\ 2,3,4,7\ 2,3,5,6\ 2,3,5,7\ 2,3,6,7\ 2,4,5,6\ 2,4,5,7\ 2,4,6,7\ 2,5,6,7\ 3,4,5,6\ 3,4,5,7\ 3,4,6,7\ 3,5,6,7\ 4,5,6,7); mat M7C5 = (1,2,3,4,5\ 1,2,3,4,6\ 1,2,3,4,7\ 1,2,3,5,6\ 1,2,3,5,7\ 1,2,3,6,7\ 1,2,4,5,6\ 1,2,4,5,7\ 1,2,4,6,7\ 1,2,5,6,7\ 1,3,4,5,6\ 1,3,4,5,7\ 1,3,4,6,7\ 1,3,5,6,7\ 1,4,5,6,7\ 2,3,4,5,6\ 2,3,4,5,7\ 2,3,4,6,7\ 2,3,5,6,7\ 2,4,5,6,7\ 3,4,5,6,7); mat M7C6 = (1,2,3,4,5,6\ 1,2,3,4,5,7\ 1,2,3,4,6,7\ 1,2,3,5,6,7\ 1,2,4,5,6,7\ 1,3,4,5,6,7\ 2,3,4,5,6,7); #delimit cr ** strata with 1 residual replace group = 1 if div ==1 & random_r <= `f7' replace group = 2 if div ==1 & random_r > `f7' & random_r<=2*`f7' replace group = 3 if div ==1 & random_r > 2*`f7' & random_r<=3*`f7' replace group = 4 if div ==1 & random_r > 3*`f7' & random_r<=4*`f7' replace group = 5 if div ==1 & random_r > 4*`f7' & random_r<=5*`f7' replace group = 6 if div ==1 & random_r > 5*`f7' & random_r<=6*`f7' replace group = 7 if div ==1 & random_r > 6*`f7' & random_r!=. ** strata with 2 residuals local f21 = 1/21 forval i = 1/21{ forval j = 1/2{ replace group = M7C2[`i',`j'] if div==2 & m_random_strata>(`i'-1)*`f21' & m_random_strata <= `i'*`f21' & rank_r==`j' } } ** strata with 3 residuals local f35 = 1/35 forval i = 1/35{ forval j = 1/3{ replace group = M7C3[`i',`j'] if div ==3 & m_random_strata>(`i'-1)*`f35' & m_random_strata <= `i'*`f35' & rank_r==`j' } } ** strata with 4 residuals forval i = 1/35{ forval j = 1/4{ replace group = M7C4[`i',`j'] if div ==4 & m_random_strata>(`i'-1)*`f35' & m_random_strata <= `i'*`f35' & rank_r==`j' } } ** strata with 5 residuals forval i = 1/21{ forval j = 1/5{ replace group = M7C5[`i',`j'] if div ==5 & m_random_strata>(`i'-1)*`f21' & m_random_strata <= `i'*`f21' & rank_r==`j' } } ** strata with 6 residuals forval i = 1/7{ forval j = 1/6{ replace group = M7C6[`i',`j'] if div ==6 & m_random_strata>(`i'-1)*`f7' & m_random_strata <= `i'*`f7' & rank_r==`j' } }