Experimental Design Exercises in R: 8 Randomization & Blocking Problems, Solved Step-by-Step
These 8 experimental design exercises in R cover randomization, blocking, and the classical designs (CRD, RCBD, Latin Square, and factorial) with runnable solutions, so you practise assigning subjects, picking the right structure, analysing block effects, and quantifying efficiency end-to-end.
Which design matches your experiment, and how do you randomize it?
The choice between a Completely Randomized Design (CRD), a Randomized Complete Block Design (RCBD), and a Latin Square hinges on two questions. Do you have a nuisance factor you can group units by? Can every treatment fit inside every group? CRD is the default when units look alike, RCBD adds one block factor, and a Latin Square removes two nuisance factors at once. The fastest way to see the difference is to randomize the same 12 plots under each design, side by side.
Three layouts, three different control strategies. The CRD scatters treatments freely and trusts randomization to balance everything. The RCBD guarantees each treatment appears once per block, so the block factor absorbs between-block variance. The Latin Square guarantees each treatment once per row and once per column, controlling two sources of variation at once. Which one you pick depends on how much nuisance structure you can identify before the experiment runs.
set.seed() before any sample() call in an experimental design so a reviewer or collaborator can reproduce the exact assignment.Try it: Generate a CRD randomization for 15 subjects across 5 treatments (call them T1 through T5) with 3 replicates each. Use set.seed(21) and save the result to ex_subjects.
Click to reveal solution
Explanation: rep() creates 15 treatment labels (3 of each), and sample() shuffles them in random order, giving a CRD.
How do you read aov() output when a block is in the model?
When a block factor enters the formula, the ANOVA table grows one row and the residual mean square (MSE) usually shrinks. Smaller residual MSE means a sharper F-test for the treatment, because the block absorbs variance that would otherwise inflate the error term. The classic npk dataset (nitrogen, phosphorus, potassium on pea yields across 6 blocks) makes this concrete.
The same nitrogen (N) effect that was barely significant in the CRD (p = 0.022) becomes strongly significant once the block is in the model (p = 0.004). Potassium (K) crosses into significance too. Nothing changed about the treatments, only about what the model called noise.
A relative efficiency of 1.96 means this blocked design is worth roughly twice as many replicates as an unblocked one, which is why blocking is the cheapest way to sharpen an experiment.
Try it: On the warpbreaks dataset, fit an ANOVA for breaks ~ wool (no tension in the model) and a second one for breaks ~ tension + wool. Compare the residual mean squares.
Click to reveal solution
Explanation: Treating tension as a block partials out the tension-driven variance, so the residual MSE falls from 167.7 to 142.2 and the wool test gains power.
Practice Exercises
Exercise 1: Simple random assignment to 3 treatments
Randomly assign 12 subjects (labelled S01 through S12) to three treatments A, B, and C, with exactly 4 subjects per treatment. Save the result as a tibble called e1_df with columns subject and treatment, then verify balance with table(e1_df$treatment).
Click to reveal solution
Explanation: rep(..., each = 4) creates 4 slots per treatment, and sample() shuffles the 12 labels into random order. The result is a CRD with equal replication.
Exercise 2: Stratified (blocked-by-sex) randomization
Randomly assign 16 subjects, 8 male and 8 female, to two treatments (Drug, Placebo), while keeping the sex ratio balanced within each treatment arm. Save the assignment as e2_df and check the 2x2 balance with table(e2_df$sex, e2_df$treatment).
Click to reveal solution
Explanation: Randomization happens independently within each sex stratum. This guarantees the sex x treatment table is balanced rather than hoping a single CRD happens to balance.
Exercise 3: Permuted block randomization for a clinical trial
Generate assignments for 24 subjects to two arms (Drug, Placebo) using permuted blocks of size 4. The goal is that after every multiple of 4 enrolments, the arms are exactly balanced. Save the assignments to e3_assign and verify with cumsum.
Click to reveal solution
Explanation: Within each 4-subject block, two Drug and two Placebo slots are shuffled. The cumulative (Drug - Placebo) count returns to zero at every block boundary, which is the defining property of permuted blocks.
Exercise 4: CRD analysis on InsectSprays
The InsectSprays dataset records insect counts for 6 sprays (A through F). Fit a one-way CRD with aov(), then run TukeyHSD to see which sprays differ. Save the fit as e4_fit and the post-hoc as e4_tukey.
Click to reveal solution
Explanation: The F-test rejects equal means across sprays (p < 2e-16), and TukeyHSD pinpoints which pairs differ. Sprays A and B are statistically indistinguishable, but C and D are clearly less effective than A.
Exercise 5: RCBD analysis on npk
Using the npk dataset, fit a model with block as a block factor and nitrogen-phosphorus interaction as the treatment structure. Save the fit to e5_fit and report the block and interaction F-tests.
Click to reveal solution
Explanation: The block F-test (3.88, p = 0.019) confirms meaningful between-block variance, so blocking earns its degrees of freedom. Nitrogen is significant, phosphorus is not, and the interaction (p = 0.29) does not reach significance, so an additive model would be simpler.
Exercise 6: Quantify blocking efficiency on PlantGrowth
The PlantGrowth dataset has no natural block, so simulate one. Assign each of the 30 plants to 5 artificial blocks of 6 plants with set.seed(6), then fit both weight ~ group (CRD) and weight ~ block + group (RCBD). Compute relative efficiency and save as e6_re.
Click to reveal solution
Explanation: Because the block was assigned at random, it carries no real nuisance variance, so relative efficiency hovers near 1 (even dipping below). In a genuine block design, you would expect RE > 1.2. This exercise shows the flip side: blocking costs degrees of freedom, so it is only worth it when blocks capture real structure.
Exercise 7: Latin Square on a 4x4 field trial
Design a 4x4 Latin Square for 4 fertilisers (F1 to F4) across 4 rows (soil strips) and 4 columns (irrigation zones). Simulate yields under the model yield = 10 + row_effect + col_effect + trt_effect + noise, then fit the Latin Square ANOVA. Save as e7_fit.
Click to reveal solution
Explanation: All three F-tests reach significance, showing the design captures both nuisance factors (row and column) plus the fertiliser effect. With only 16 data points, the Latin Square still isolates the treatment effect precisely because the design is balanced.
Exercise 8: 2x2 factorial with a blocking factor
Simulate a 2x2 factorial (factors A and B, two levels each) replicated across 3 blocks. Fit an ANOVA with the block plus the A*B interaction. Save the fit as e8_fit and report whether the interaction holds up after blocking.
Click to reveal solution
Explanation: The block accounts for 13.9 sum of squares that would otherwise be residual, and the A:B interaction is significant (p = 0.003) once that variance is removed. Without blocking the interaction F could have drowned in noise.
Complete Example: Full RCBD Workflow on a Synthetic Wheat Trial
This end-to-end worked example simulates a wheat variety trial with 4 varieties grown in 5 blocks (fields with different soil), randomizes the assignment within each block, fits the RCBD model, checks diagnostics, and runs Tukey HSD to rank the varieties.
Simulated data in hand, fit the RCBD and check residual assumptions before trusting any p-value.
Both tests are significant. Residuals show no obvious funnel or curvature, so the additive RCBD assumptions hold. Now rank the varieties.
plot(fit, which = 1) and plot(fit, which = 2) before writing up an RCBD.Variety V4 out-yields V1 and V3, V2 out-yields V1, and V3 is statistically indistinguishable from V1 and V2. A breeder could recommend V4, with V2 as a backup, and retire V3 from the next-cycle test.
Summary
| Design | When to use | R formula |
|---|---|---|
| CRD | Units are homogeneous | aov(y ~ trt) |
| RCBD | One nuisance factor groups units | aov(y ~ block + trt) |
| Latin Square | Two nuisance factors (row and column) | aov(y ~ row + col + trt) |
| Factorial + block | Two treatment factors plus rep-to-rep variance | aov(y ~ block + A * B) |
| Stratified randomization | Balance a categorical covariate (sex, site) | sample() inside each stratum |
| Permuted blocks | Balance over time in a sequential trial | sample(rep(arms, each = k/2)) per block |
Randomization is the engine that makes the inference valid, blocking is the lever that makes it precise, and the design you pick controls how much of each you get. Work through all 8 exercises and you will have hands-on experience with every design you are likely to meet in practice.
References
- Montgomery, D. C. Design and Analysis of Experiments, 10th edition. Wiley (2019). Chapters 3-5 cover CRD, RCBD, and Latin Squares. Link
agricolaeR package, CRAN documentation. Providesdesign.crd(),design.rcbd(),design.lsd()helpers. Link- Crawley, M. J. The R Book, 2nd edition. Wiley (2013). Chapter 11, Analysis of variance. Link
- R documentation,
?aov. The base-R function for balanced designs. Link - Faraway, J. J. Linear Models with R, 2nd edition. CRC Press (2014). Chapter on blocking and incomplete designs. Link
- Hinkelmann, K. and Kempthorne, O. Design and Analysis of Experiments Volume 1, 2nd edition. Wiley (2008). Foundational treatment of randomization theory. Link
Continue Learning
- Experimental Design Principles in R, the parent Core post explaining randomization, replication, and blocking as the three pillars of valid design.
- ANOVA Exercises in R, 15 sibling exercises that drill one-way and two-way ANOVA without the design-theory angle.
- One-Way ANOVA in R, background for reading any of the
aov()tables you built in these exercises.