A Practical Guide to sreg

0.1 Overview

The sreg package estimates average treatment effects (ATEs) in stratified randomized experiments under covariate-adaptive randomization. It supports:

The package’s main functions are sreg(), which performs estimation and inference, and sreg.rgen(), which generates simulated experiments. The print() and plot() methods summarize fitted sreg objects.

library(sreg)
#> Package 'sreg' version 2.1.0
#> Type 'citation("sreg")' for citing this R package in publications.

0.2 The basic workflow

A typical analysis has four steps:

  1. Identify the randomization unit and the stratification design.
  2. Prepare the outcome, treatment, stratum, cluster, and covariate variables.
  3. Call sreg() with arguments matching the experimental design.
  4. Inspect, print, or plot the returned object.

The core call is:

sreg(
  Y, S = NULL, D,
  G.id = NULL, Ng = NULL,
  X = NULL, HC1 = TRUE,
  small.strata = FALSE, k = NULL
)

0.2.1 Required data and indexing conventions

Argument Meaning Important convention
Y Observed outcome One value per observation
D Treatment assignment Control must be coded 0; active treatments are 1, 2, ...
S Stratum indicator Strata must be coded 1, 2, ...; use NULL for no stratification
G.id Cluster identifier Use NULL for individual-level assignment
Ng Cluster size Required for the intended cluster-size weighting; if omitted, observed cluster counts are used
X Adjustment covariates Use NULL for the unadjusted estimator

All supplied variables must refer to the same observations and contain valid numeric or data-frame inputs as described in help("sreg").

0.2.2 Choosing the design options

Design Recommended call
No stratification S = NULL, small.strata = FALSE
Large strata S = S, small.strata = FALSE
Uniform matched pairs or k-tuples small.strata = TRUE; k is optional
Mixed pairs or triplets plus large strata small.strata = TRUE; automatic detection is available
Mixed general k-tuples plus large strata small.strata = TRUE, k = <known size>
Cluster-randomized version of any design Also supply G.id and, preferably, Ng

small.strata selects an inference procedure; it should reflect the actual randomization design rather than being chosen after comparing numerical results.

0.3 Large-strata designs

When small.strata = FALSE, sreg() applies the large-strata estimator. This is the default. The following example generates an individual-level experiment with four strata, a control arm, and two active treatments.

set.seed(101)

large_data <- sreg.rgen(
  n = 600,
  tau.vec = c(0.3, 0.7),
  n.strata = 4,
  cluster = FALSE
)

head(large_data)
#>          Y S D      x_1      x_2
#> 1 2.154491 1 0 3.527956 2.336471
#> 2 4.238873 3 2 7.534395 2.274160
#> 3 2.552000 2 2 5.528556 1.166914
#> 4 3.726746 2 2 4.390154 2.802584
#> 5 3.882640 2 2 5.534880 2.172677
#> 6 3.392253 3 0 2.128014 1.625707
table(large_data$S, large_data$D)
#>    
#>      0  1  2
#>   1 30 28 28
#>   2 67 67 67
#>   3 69 69 69
#>   4 36 35 35

0.3.1 Unadjusted and adjusted estimation

Omit X for the unadjusted estimator:

fit_large_unadjusted <- sreg(
  Y = large_data$Y,
  S = large_data$S,
  D = large_data$D
)

fit_large_unadjusted$tau.hat
#> [1] 0.4844155 0.7490444
fit_large_unadjusted$se.rob
#> [1] 0.1441396 0.1480421

Supply a matrix or data frame to request linear covariate adjustment:

X_large <- large_data[c("x_1", "x_2")]

fit_large <- sreg(
  Y = large_data$Y,
  S = large_data$S,
  D = large_data$D,
  X = X_large
)

print(fit_large)
#> Saturated Model Estimation Results under CAR with linear adjustments
#> Observations: 600 
#> Number of treatments: 2 
#> Number of strata: 4 
#> Setup: large strata 
#> Standard errors: adjusted (HC1) 
#> Treatment assignment: individual level 
#> Covariates used in linear adjustments: x_1, x_2
#> ---
#> Coefficients:
#>       Tau   As.se  T-stat P-value CI.left(95%) CI.right(95%) Significance
#> 1 0.47972 0.10108 4.74581       0      0.28160       0.67783          ***
#> 2 0.84412 0.10318 8.18100       0      0.64189       1.04635          ***
#> ---
#> Signif. codes:  0 `***` 0.001 `**` 0.01 `*` 0.05 `.` 0.1 ` ` 1

The printed table reports the ATE relative to control for each active treatment, its asymptotic standard error, test statistic, p-value, and 95% confidence interval. It also records the inferred design, treatment-assignment level, HC1 setting, and covariates used for adjustment.

0.4 Plotting treatment effects

Calling plot() on an sreg object displays the estimated ATEs and their confidence intervals. Treatment labels, colors, axes, grid lines, and other graphical elements can be customized.

plot(
  fit_large,
  treatment_labels = c("Program A", "Program B"),
  title = "Estimated treatment effects",
  x_axis_title = "ATE relative to control",
  bar_fill = c("#3B82F6", "#14B8A6"),
  point_fill = "white",
  zero_line = TRUE
)

Estimated treatment effects and 95 percent confidence intervals

The method invisibly returns a ggplot object, so it can be stored or further modified:

p <- plot(fit_large, treatment_labels = c("Program A", "Program B"))
p + ggplot2::theme_minimal()

See help("plot.sreg") for all graphical arguments.

0.5 Uniform small-strata designs

Set small.strata = TRUE for designs such as matched pairs and k-tuples. In a uniform small-strata design, all strata have the same size. The package observes that common size directly, so the k argument is optional.

set.seed(102)

small_data <- sreg.rgen(
  n = 300,
  tau.vec = c(0.3, 0.7),
  cluster = FALSE,
  small.strata = TRUE,
  k = 3,
  treat.sizes = c(1, 1, 1)
)

table(table(small_data$S))
#> 
#>   3 
#> 100

Here, every stratum is a triplet containing one observation in each of the three treatment arms. Estimation does not require k = 3 because the common size is unambiguous:

fit_small <- sreg(
  Y = small_data$Y,
  S = small_data$S,
  D = small_data$D,
  X = small_data[c("x_1", "x_2")],
  small.strata = TRUE
)

print(fit_small)
#> Saturated Model Estimation Results under CAR with linear adjustments
#> Observations: 300 
#> Number of treatments: 2 
#> Number of strata: 100 
#> Setup: small strata 
#> Strata size (k): 3 
#> Standard errors: adjusted (HC1) 
#> Treatment assignment: individual level 
#> Covariates used in linear adjustments: x_1, x_2
#> ---
#> Coefficients:
#>       Tau   As.se  T-stat P-value CI.left(95%) CI.right(95%) Significance
#> 1 0.19561 0.14924 1.31072 0.18995     -0.09689       0.48810             
#> 2 0.85428 0.13635 6.26530 0.00000      0.58704       1.12153          ***
#> ---
#> Signif. codes:  0 `***` 0.001 `**` 0.01 `*` 0.05 `.` 0.1 ` ` 1

Supplying k = 3 gives the same design choice and adds a validation check. If the supplied value does not match the observed common size, sreg() returns an error.

sreg(
  Y = small_data$Y,
  S = small_data$S,
  D = small_data$D,
  small.strata = TRUE,
  k = 3
)

The small-strata estimators are intended for experiments with a fixed treatment allocation within each stratum. The researcher remains responsible for verifying that the supplied assignments follow the randomization protocol; sreg() does not independently verify every feature of that protocol.

0.6 Mixed small- and large-strata designs

A mixed design contains a recurring small-stratum component and a large-strata component. sreg.rgen() can generate both components in one call. For individual-level designs, n.small is the number of observations assigned to small strata. For cluster-randomized designs, it is the number of clusters assigned to small strata.

The next example uses 80 observations in 20 four-tuples and distributes the remaining 40 observations across four large strata.

set.seed(103)

mixed_data <- sreg.rgen(
  n = 120,
  tau.vec = 0.5,
  cluster = FALSE,
  mixed.strata = TRUE,
  n.small = 80,
  k = 4,
  treat.sizes = c(2, 2),
  n.strata = 4
)

sort(table(mixed_data$S))
#> 
#>  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 24 22 23 
#>  4  4  4  4  4  4  4  4  4  4  4  4  4  4  4  4  4  4  4  4  6  6 10 18

Because this mixed design uses 4-tuples, pass the known value k = 4 to sreg():

fit_mixed <- suppressWarnings(sreg(
  Y = mixed_data$Y,
  S = mixed_data$S,
  D = mixed_data$D,
  small.strata = TRUE,
  k = 4
))

print(fit_mixed)
#> Saturated Model Estimation Results under CAR
#> Observations: 120 
#> Number of treatments: 1 
#> Number of strata: 24 
#> Setup: mixed design (includes both small and large strata) 
#> Strata size (k, small strata only): 4 
#> Standard errors: adjusted (HC1) 
#> Treatment assignment: individual level 
#> Covariates used in linear adjustments: 
#> ---
#> Coefficients:
#>       Tau   As.se  T-stat P-value CI.left(95%) CI.right(95%) Significance
#> 1 0.43248 0.27423 1.57707 0.11478       -0.105       0.96996             
#> ---
#> Signif. codes:  0 `***` 0.001 `**` 0.01 `*` 0.05 `.` 0.1 ` ` 1

Normally, this call emits an informative warning stating that a mixed design was detected, identifying k = 4 as the small-stratum size, and noting that weighted estimators are used. The warning is suppressed above only to keep the vignette output compact.

0.6.1 How k and the 25% rule work

For a mixed design:

  1. If k is supplied, strata of exactly that size are candidates for the small component.
  2. At least 25% of all strata must have that exact size.
  3. Those strata are analyzed with the small-strata estimator; all remaining strata use the large-strata estimator.
  4. The component estimates are combined using weights based on their numbers of observations.

The 25% threshold concerns the proportion of strata, not the proportion of observations. If fewer than 25% have the supplied size, sreg() returns an error rather than selecting another value.

When k = NULL, the package retains automatic detection for conventional matched pairs and triplets. Supply k for mixed 4-tuples or larger k-tuples. Providing the design value explicitly is also useful whenever several stratum sizes recur and automatic classification would be ambiguous.

If small.strata = FALSE is used on data with a substantial small-strata component, sreg() proceeds with the large-strata estimator for the complete sample and warns that the resulting standard errors may be inappropriate for the design.

0.7 Cluster-randomized experiments

Supply G.id when treatment is assigned at the cluster level. Each cluster must belong to one stratum and receive one treatment. Ng contains cluster sizes. When Ng = NULL, the package uses the number of observed records in each cluster and reports a warning.

set.seed(104)

cluster_data <- sreg.rgen(
  n = 60,
  tau.vec = 0.5,
  n.strata = 4,
  cluster = TRUE
)

c(
  observations = nrow(cluster_data),
  clusters = length(unique(cluster_data$G.id)),
  strata = length(unique(cluster_data$S))
)
#> observations     clusters       strata 
#>         1780           60            4
fit_cluster <- sreg(
  Y = cluster_data$Y,
  S = cluster_data$S,
  D = cluster_data$D,
  G.id = cluster_data$G.id,
  Ng = cluster_data$Ng,
  X = cluster_data[c("x_1", "x_2")]
)

print(fit_cluster)
#> Saturated Model Estimation Results under CAR with linear adjustments
#> Observations: 1780 
#> Clusters: 60 
#> Number of treatments: 1 
#> Number of strata: 4 
#> Setup: large strata 
#> Standard errors: adjusted (HC1) 
#> Treatment assignment: cluster level 
#> Covariates used in linear adjustments: x_1, x_2
#> ---
#> Coefficients:
#>      Tau   As.se  T-stat P-value CI.left(95%) CI.right(95%) Significance
#> 1 0.2872 0.18441 1.55741 0.11937     -0.07423       0.64863             
#> ---
#> Signif. codes:  0 `***` 0.001 `**` 0.01 `*` 0.05 `.` 0.1 ` ` 1
#> [1] TRUE

For cluster-randomized small or mixed designs, stratum size k means the number of distinct clusters per small stratum—not the number of individual observations. Individual-level covariates cannot be used directly for cluster-level adjustment; if supplied covariates vary within cluster, sreg() aggregates them to cluster averages and reports this behavior.

0.8 Experiments without stratification

Set S = NULL when treatment was randomized without strata. This option uses the large-strata pathway with the entire experiment treated as one population:

fit_unstratified <- sreg(
  Y = Y,
  S = NULL,
  D = D,
  X = X,
  small.strata = FALSE
)

Do not combine S = NULL with small.strata = TRUE, because small-strata inference requires stratum identifiers.

0.9 The sreg result object

In addition to its printed summary, an sreg object exposes components useful for reporting and further analysis:

Component Contents
tau.hat ATE estimates relative to control
se.rob Estimated asymptotic standard errors
t.stat Test statistics
p.value Two-sided p-values
CI.left, CI.right Bounds of the 95% confidence intervals
beta.hat Covariate-adjustment coefficients when applicable
lin.adj Covariates used for adjustment
data Analysis data retained by the fit
small.strata Whether small-strata inference was requested
HC1 Whether the HC1 correction was requested

Mixed-design results also contain res.small, res.big, and mixed.design, which provide the component fits and identify the combined estimator.

names(fit_mixed)
#>  [1] "tau.hat"      "se.rob"       "t.stat"       "p.value"      "as.CI"       
#>  [6] "beta.hat"     "CI.left"      "CI.right"     "ols.iter"     "lin.adj"     
#> [11] "data"         "res.small"    "res.big"      "mixed.design" "small.strata"
#> [16] "HC1"
fit_mixed$tau.hat
#> [1] 0.4324797
fit_mixed$se.rob
#> [1] 0.2742298
fit_mixed$mixed.design
#> [1] TRUE

0.10 HC1 correction

HC1 = TRUE, the default, applies the package’s small-sample correction to the variance estimator. Set HC1 = FALSE to obtain the corresponding uncorrected variance estimate:

fit_hc1 <- sreg(Y = Y, S = S, D = D, HC1 = TRUE)
fit_no_hc1 <- sreg(Y = Y, S = S, D = D, HC1 = FALSE)

The choice should follow the inferential specification for the analysis rather than whichever option yields a preferred standard error.

0.11 Working with the built-in empirical data

The package includes AEJapp, replication data associated with Chong et al. (2016). The following prepares the outcome, treatment, and stratum variables used in the package example. The original treatment value 3 is recoded as control (0) to match the required treatment indexing.

data("AEJapp", package = "sreg")

Y_emp <- as.numeric(unclass(AEJapp$gradesq34))
D_raw <- as.numeric(unclass(AEJapp$treatment))
D_emp <- ifelse(D_raw == 3, 0, D_raw)
S_emp <- as.numeric(unclass(AEJapp$class_level))

fit_empirical <- sreg(
  Y = Y_emp,
  S = S_emp,
  D = D_emp
)

fit_empirical$tau.hat
#> [1] -0.05112971  0.40903373
fit_empirical$se.rob
#> [1] 0.2064541 0.2065146

See help("AEJapp") for the data description and source.

0.12 Simulation with sreg.rgen()

sreg.rgen() is intended for examples, Monte Carlo exercises, and learning how different designs are represented. Important arguments include:

Argument Role
n Number of units when cluster = FALSE; number of clusters when cluster = TRUE
tau.vec Treatment effects for active arms relative to control
n.strata Number of large strata
cluster Selects individual- or cluster-level assignment
is.cov Includes or omits generated covariates
small.strata Generates a uniform small-strata design
mixed.strata Generates both small and large components
k Number of units or clusters per small stratum
treat.sizes Fixed counts assigned to control and each active treatment within a small stratum
n.small Units or clusters assigned to the small component of a mixed design

For small strata, length(treat.sizes) must equal length(tau.vec) + 1, and its entries must sum to k. In a mixed design, n.small must be divisible by k. Use set.seed() when reproducibility matters.

0.13 Common warnings and errors

Warnings about the design should not be suppressed in substantive analyses until the researcher has confirmed that the arguments match the actual randomization procedure.

0.15 References and further help

Use the following commands for function-level documentation:

help("sreg")
help("sreg.rgen")
help("plot.sreg")
help("AEJapp")
citation("sreg")

The package help files and supplementary materials contain the formal references and estimator expressions underlying the supported designs.