1 Overview

MRStdLCRT implements model-robust standardization for longitudinal cluster randomized trials (LCRTs), with emphasis on stepped-wedge cluster-randomized trials (SW-CRTs) in this tutorial. The package computes four treatment-effect estimands using either unadjusted (nonparametric) or augmented (model-robust standardized) estimators.

A key feature of the implementation is that the working model is fit on the full dataset, while the **aggregation step uses only period j=2,…,J-1. Periods with all clusters untreated or all clusters treated are excluded from aggregation, but remain in the working-model fit.

1.1 Estimands

The API returns all four estimands:

  • h-iATE: horizontal individual average treatment effect
  • h-cATE: horizontal cluster average treatment effect
  • v-iATE: vertical individual average treatment effect
  • v-cATE: vertical cluster average treatment effect

1.2 Estimators and variance

  • Unadjusted: nonparametric estimators based on cluster-period means.
  • Adjusted: augmented estimators based on model-robust standardization with covariate adjustment. The working model can be:
    • Linear mixed effects model (lmer) for continuous outcomes
    • Generalized linear mixed effects model (glmer) for binary outcomes
    • Generalized estimating equations (gee) for continuous or binary outcomes
  • Variance: delete-1 cluster jackknife standard errors and covariance matrix.

1.3 Design diagnostics in summary()

Each summary() call prints key SW-CRT diagnostics, including:

  • which periods are kept by the mixture rule,
  • a period mixture table (min/max of treatment by period),
  • and whether the design matches the stepped-wedge edge pattern (first period all control; last period all treated).

By default, summary() also prints a global informative-size (ICS) F-test comparing all four estimands. You may customize or disable this test via the ics and ics_method arguments.

2 Installation

Install the development version directly from GitHub:

# install.packages("remotes")
remotes::install_github("fancy575/MRStdLCRT")

Load the package:

suppressPackageStartupMessages({
  library(MRStdLCRT)
  library(dplyr)
  library(ggplot2)
})

3 Example data: stepped-wedge CRTs

Two example datasets are included for this tutorial:

  • sw_c: continuous outcome dataset (outcome column: y)
  • sw_b: binary outcome dataset (outcome column: y)

Both contain:

  • cluster ID: cluster
  • period: period
  • treatment indicator: trt (0/1)
  • covariates: x1, x2 (and possibly additional covariates depending on the simulated dataset)
data("sw_c", package = "MRStdLCRT")
data("sw_b", package = "MRStdLCRT")

3.1 Design overview

The heatmap below shows the treatment assignment for each cluster-period cell.
Cells in blue indicate treatment (trt=1); cells in gray indicate control (trt=0).

4 Outcome overview

4.1 Continuous outcome (sw_c)

4.2 Binary outcome (sw_b)

5 API at a glance

  • Fit
    mrstdlcrt_fit(data, formula, cluster_id, period, trt, method, family, corstr, scale)

  • Summarize
    summary(object, level = 0.95, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))

  • Plot
    plot(object, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))

5.1 Notes on formulas

  • For GEE, it is recommended to omit random effects terms like (1|cluster); the package strips them internally if present.
  • The package supports complex fixed effects, including interactions such as factor(period) * trt, etc.

6 Continuous outcome (stepped-wedge) — working models

We keep the same covariates and vary only the working model specification.

fml_c_lmer_full <- y ~ 0 + factor(period) + trt +
  x1 + x2 +
  (1 | cluster) + (1 | cluster:period)

fml_c_lmer_cluster <- y ~ 0 + factor(period) + trt +
  x1 + x2 +
  (1 | cluster)

fml_c_gee <- y ~ 0 + factor(period) + trt + x1 + x2

6.1 lmer: random intercepts (1|cluster) + (1|cluster:period)

fit_c_lmer_full <- mrstdlcrt_fit(
  data       = sw_c,
  formula    = fml_c_lmer_full,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "lmer",
  family     = "gaussian"
)

summary(fit_c_lmer_full, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: lmer   Family: gaussian   Scale: RD
#> Clusters: 30   Periods kept: 4 (of 6 total)
#> Kept periods: 2, 3, 4, 5
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 6 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 6 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     0     1 TRUE    
#> 5      5     0     1 TRUE    
#> 6      6     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2  1767         30         30
#> 2      3  1555         30         30
#> 3      4  1808         30         30
#> 4      5  1826         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1   164         4
#>  2       2   265         4
#>  3       3   120         4
#>  4       4   277         4
#>  5       5   225         4
#>  6       6   223         4
#>  7       7   319         4
#>  8       8   290         4
#>  9       9   250         4
#> 10      10   285         4
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F p_value note
#> 1  unadjusted   3  29 13.14064 1.3e-05     
#> 2    adjusted   3  29 13.42155 1.1e-05     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.202067 1.248330 5.648944 10.75519
#> adjusted   8.270074 1.249275 5.715020 10.82513
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 7.361880 1.345551 4.609919 10.11384
#> adjusted   7.419341 1.332643 4.693780 10.14490
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.131903 1.238139 5.599625 10.66418
#> adjusted   8.200893 1.239615 5.665596 10.73619
#> 
#> v-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 5.499351 1.126920 3.194540 7.804162
#> adjusted   5.584481 1.108943 3.316439 7.852523
plot(fit_c_lmer_full, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))

6.2 lmer: random intercept (1|cluster)

fit_c_lmer_cluster <- mrstdlcrt_fit(
  data       = sw_c,
  formula    = fml_c_lmer_cluster,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "lmer",
  family     = "gaussian"
)

summary(fit_c_lmer_cluster, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: lmer   Family: gaussian   Scale: RD
#> Clusters: 30   Periods kept: 4 (of 6 total)
#> Kept periods: 2, 3, 4, 5
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 6 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 6 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     0     1 TRUE    
#> 5      5     0     1 TRUE    
#> 6      6     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2  1767         30         30
#> 2      3  1555         30         30
#> 3      4  1808         30         30
#> 4      5  1826         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1   164         4
#>  2       2   265         4
#>  3       3   120         4
#>  4       4   277         4
#>  5       5   225         4
#>  6       6   223         4
#>  7       7   319         4
#>  8       8   290         4
#>  9       9   250         4
#> 10      10   285         4
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F p_value note
#> 1  unadjusted   3  29 13.14064 1.3e-05     
#> 2    adjusted   3  29 13.42642 1.1e-05     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.202067 1.248330 5.648944 10.75519
#> adjusted   8.269740 1.248793 5.715671 10.82381
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 7.361880 1.345551 4.609919 10.11384
#> adjusted   7.419017 1.332088 4.694592 10.14344
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.131903 1.238139 5.599625 10.66418
#> adjusted   8.200562 1.239139 5.666238 10.73489
#> 
#> v-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 5.499351 1.126920 3.194540 7.804162
#> adjusted   5.584460 1.108341 3.317648 7.851271

6.3 GEE: independence

fit_c_gee_ind <- mrstdlcrt_fit(
  data       = sw_c,
  formula    = fml_c_gee,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "gee",
  family     = "gaussian",
  corstr     = "independence"
)

summary(fit_c_gee_ind, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: gee   Family: gaussian   Scale: RD
#> GEE corstr: independence
#> Clusters: 30   Periods kept: 4 (of 6 total)
#> Kept periods: 2, 3, 4, 5
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 6 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 6 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     0     1 TRUE    
#> 5      5     0     1 TRUE    
#> 6      6     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2  1767         30         30
#> 2      3  1555         30         30
#> 3      4  1808         30         30
#> 4      5  1826         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1   164         4
#>  2       2   265         4
#>  3       3   120         4
#>  4       4   277         4
#>  5       5   225         4
#>  6       6   223         4
#>  7       7   319         4
#>  8       8   290         4
#>  9       9   250         4
#> 10      10   285         4
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F p_value note
#> 1  unadjusted   3  29 13.14064 1.3e-05     
#> 2    adjusted   3  29 13.42656 1.1e-05     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.202067 1.248330 5.648944 10.75519
#> adjusted   8.269544 1.249144 5.714757 10.82433
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 7.361880 1.345551 4.609919 10.11384
#> adjusted   7.418796 1.332380 4.693772 10.14382
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.131903 1.238139 5.599625 10.66418
#> adjusted   8.200374 1.239530 5.665251 10.73550
#> 
#> v-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 5.499351 1.126920 3.194540 7.804162
#> adjusted   5.584761 1.108609 3.317401 7.852122

6.4 GEE: exchangeable

fit_c_gee_exch <- mrstdlcrt_fit(
  data       = sw_c,
  formula    = fml_c_gee,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "gee",
  family     = "gaussian",
  corstr     = "exchangeable"
)

summary(fit_c_gee_exch, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: gee   Family: gaussian   Scale: RD
#> GEE corstr: exchangeable
#> Clusters: 30   Periods kept: 4 (of 6 total)
#> Kept periods: 2, 3, 4, 5
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 6 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 6 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     0     1 TRUE    
#> 5      5     0     1 TRUE    
#> 6      6     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2  1767         30         30
#> 2      3  1555         30         30
#> 3      4  1808         30         30
#> 4      5  1826         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1   164         4
#>  2       2   265         4
#>  3       3   120         4
#>  4       4   277         4
#>  5       5   225         4
#>  6       6   223         4
#>  7       7   319         4
#>  8       8   290         4
#>  9       9   250         4
#> 10      10   285         4
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F p_value note
#> 1  unadjusted   3  29 13.14064 1.3e-05     
#> 2    adjusted   3  29 13.42644 1.1e-05     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.202067 1.248330 5.648944 10.75519
#> adjusted   8.269738 1.248794 5.715669 10.82381
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 7.361880 1.345551 4.609919 10.11384
#> adjusted   7.419015 1.332088 4.694589 10.14344
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.131903 1.238139 5.599625 10.66418
#> adjusted   8.200560 1.239140 5.666235 10.73489
#> 
#> v-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 5.499351 1.126920 3.194540 7.804162
#> adjusted   5.584459 1.108341 3.317646 7.851272

6.5 Interaction example (continuous outcome)

This example illustrates period-specific treatment effects and covariate interactions that are common in stepped-wedge analyses.

fml_c_lmer_int <- y ~ 0 + factor(period) * trt +
  factor(period) * x1 + x2 +
  (1 | cluster) + (1 | cluster:period)
fit_c_lmer_int <- mrstdlcrt_fit(
  data       = sw_c,
  formula    = fml_c_lmer_int,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "lmer",
  family     = "gaussian"
)

summary(fit_c_lmer_int, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: lmer   Family: gaussian   Scale: RD
#> Clusters: 30   Periods kept: 4 (of 6 total)
#> Kept periods: 2, 3, 4, 5
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 6 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 6 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     0     1 TRUE    
#> 5      5     0     1 TRUE    
#> 6      6     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2  1767         30         30
#> 2      3  1555         30         30
#> 3      4  1808         30         30
#> 4      5  1826         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1   164         4
#>  2       2   265         4
#>  3       3   120         4
#>  4       4   277         4
#>  5       5   225         4
#>  6       6   223         4
#>  7       7   319         4
#>  8       8   290         4
#>  9       9   250         4
#> 10      10   285         4
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F p_value note
#> 1  unadjusted   3  29 13.14064 1.3e-05     
#> 2    adjusted   3  29 13.55606 1.0e-05     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.202067 1.248330 5.648944 10.75519
#> adjusted   8.276692 1.236036 5.748714 10.80467
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 7.361880 1.345551 4.609919 10.11384
#> adjusted   7.419988 1.334398 4.690837 10.14914
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 8.131903 1.238139 5.599625 10.66418
#> adjusted   8.206131 1.226589 5.697474 10.71479
#> 
#> v-cATE
#>            Estimate       SE     LCL      UCL
#> unadjusted 5.499351 1.126920 3.19454 7.804162
#> adjusted   5.584906 1.115516 3.30342 7.866393

7 Binary outcome (stepped-wedge) — working models (scale = OR)

For binary outcomes, we recommend specifying the reporting scale via scale = "OR", which reports the log odds ratio and also prints exponentiated OR in the summary output.

fml_b_glmer_full <- y ~ 0 + factor(period) + trt +
  x1 + x2 +
  (1 | cluster) + (1 | cluster:period)

fml_b_glmer_cluster <- y ~ 0 + factor(period) + trt +
  x1 + x2 +
  (1 | cluster)

fml_b_gee <- y ~ 0 + factor(period) + trt + x1 + x2

7.1 glmer: (1|cluster) + (1|cluster:period), scale = OR (log OR)

fit_b_glmer_full_or <- mrstdlcrt_fit(
  data       = sw_b,
  formula    = fml_b_glmer_full,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "glmer",
  family     = "binomial",
  scale      = "OR"
)

summary(fit_b_glmer_full_or, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: glmer   Family: binomial   Scale: OR
#> Clusters: 30   Periods kept: 2 (of 4 total)
#> Kept periods: 2, 3
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 4 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 2 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2   894         30         30
#> 2      3   823         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1    35         2
#>  2       2    44         2
#>  3       3    85         2
#>  4       4    28         2
#>  5       5    86         2
#>  6       6    68         2
#>  7       7    57         2
#>  8       8    94         2
#>  9       9    62         2
#> 10      10    33         2
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F  p_value note
#> 1  unadjusted   3  29 7.466581 0.000753     
#> 2    adjusted   3  29 7.213823 0.000926     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.296039 0.266029 1.751949 2.840129
#> adjusted   2.283548 0.260173 1.751434 2.815662
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 9.934751  5.765828  17.11797
#> adjusted   9.811431  5.762861  16.70423
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.003200 0.327325 1.333745 2.672655
#> adjusted   1.997093 0.323183 1.336110 2.658076
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 7.412739  3.795228  14.47836
#> adjusted   7.367609  3.804217  14.26882
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.286101 0.264796 1.744532 2.827671
#> adjusted   2.273927 0.258807 1.744607 2.803248
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 9.836514  5.723224  16.90603
#> adjusted   9.717490  5.723650  16.49815
#> 
#> v-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 1.762751 0.287326 1.175104 2.350398
#> adjusted   1.760738 0.282326 1.183315 2.338160
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 5.828450  3.238481  10.48974
#> adjusted   5.816726  3.265181  10.36215
plot(fit_b_glmer_full_or, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))

7.2 glmer: (1|cluster), scale = OR (log OR)

fit_b_glmer_cluster_or <- mrstdlcrt_fit(
  data       = sw_b,
  formula    = fml_b_glmer_cluster,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "glmer",
  family     = "binomial",
  scale      = "OR"
)

summary(fit_b_glmer_cluster_or, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: glmer   Family: binomial   Scale: OR
#> Clusters: 30   Periods kept: 2 (of 4 total)
#> Kept periods: 2, 3
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 4 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 2 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2   894         30         30
#> 2      3   823         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1    35         2
#>  2       2    44         2
#>  3       3    85         2
#>  4       4    28         2
#>  5       5    86         2
#>  6       6    68         2
#>  7       7    57         2
#>  8       8    94         2
#>  9       9    62         2
#> 10      10    33         2
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F  p_value note
#> 1  unadjusted   3  29 7.466581 0.000753     
#> 2    adjusted   3  29 7.221413 0.000920     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.296039 0.266029 1.751949 2.840129
#> adjusted   2.284734 0.260309 1.752343 2.817126
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 9.934751  5.765828  17.11797
#> adjusted   9.823075  5.768099  16.72870
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.003200 0.327325 1.333745 2.672655
#> adjusted   1.998302 0.322935 1.337825 2.658779
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 7.412739  3.795228  14.47836
#> adjusted   7.376520  3.810746  14.27884
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.286101 0.264796 1.744532 2.827671
#> adjusted   2.275069 0.258939 1.745480 2.804659
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 9.836514  5.723224  16.90603
#> adjusted   9.728593  5.728648  16.52144
#> 
#> v-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 1.762751 0.287326 1.175104 2.350398
#> adjusted   1.761877 0.282002 1.185119 2.338635
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 5.828450  3.238481  10.48974
#> adjusted   5.823358  3.271075  10.36708

7.3 GEE: independence, scale = OR (log OR)

fit_b_gee_ind_or <- mrstdlcrt_fit(
  data       = sw_b,
  formula    = fml_b_gee,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "gee",
  family     = "binomial",
  corstr     = "independence",
  scale      = "OR"
)

summary(fit_b_gee_ind_or, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: gee   Family: binomial   Scale: OR
#> GEE corstr: independence
#> Clusters: 30   Periods kept: 2 (of 4 total)
#> Kept periods: 2, 3
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 4 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 2 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2   894         30         30
#> 2      3   823         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1    35         2
#>  2       2    44         2
#>  3       3    85         2
#>  4       4    28         2
#>  5       5    86         2
#>  6       6    68         2
#>  7       7    57         2
#>  8       8    94         2
#>  9       9    62         2
#> 10      10    33         2
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F  p_value note
#> 1  unadjusted   3  29 7.466581 0.000753     
#> 2    adjusted   3  29 7.216555 0.000924     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.296039 0.266029 1.751949 2.840129
#> adjusted   2.284458 0.259908 1.752886 2.816031
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 9.934751  5.765828  17.11797
#> adjusted   9.820366  5.771234  16.71039
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.003200 0.327325 1.333745 2.672655
#> adjusted   1.997916 0.322800 1.337715 2.658116
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 7.412739  3.795228  14.47836
#> adjusted   7.373670  3.810328  14.26938
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.286101 0.264796 1.744532 2.827671
#> adjusted   2.274790 0.258577 1.745941 2.803640
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 9.836514  5.723224  16.90603
#> adjusted   9.725880  5.731293  16.50461
#> 
#> v-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 1.762751 0.287326 1.175104 2.350398
#> adjusted   1.761609 0.281899 1.185061 2.338157
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 5.828450  3.238481  10.48974
#> adjusted   5.821796  3.270887  10.36212

7.4 GEE: exchangeable, scale = OR (log OR)

fit_b_gee_exch_or <- mrstdlcrt_fit(
  data       = sw_b,
  formula    = fml_b_gee,
  cluster_id = "cluster",
  period     = "period",
  trt        = "trt",
  method     = "gee",
  family     = "binomial",
  corstr     = "exchangeable",
  scale      = "OR"
)

summary(fit_b_gee_exch_or, estimand = c("h-iATE","h-cATE","v-iATE","v-cATE"))
#> 
#> MRS-LCRT Summary
#> ========================================================================
#> Method: gee   Family: binomial   Scale: OR
#> GEE corstr: exchangeable
#> Clusters: 30   Periods kept: 2 (of 4 total)
#> Kept periods: 2, 3
#> CIs: 95.0% (t, df = 29)
#> Stepped-wedge edge pattern: YES (first period 1 all control; last period 4 all treatment)
#> 
#> Period mixture table (min/max of Z_ij by period)
#> ------------------------------------------------------------------------
#> # A tibble: 4 × 4
#>   period  minZ  maxZ is_mixed
#>    <int> <int> <int> <lgl>   
#> 1      1     0     0 FALSE   
#> 2      2     0     1 TRUE    
#> 3      3     0     1 TRUE    
#> 4      4     1     1 FALSE   
#> 
#> Counts used in aggregation (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 2 × 4
#>   period n_obs n_clusters n_cp_cells
#>    <int> <int>      <int>      <int>
#> 1      2   894         30         30
#> 2      3   823         30         30
#> 
#> Per-cluster counts (kept periods only)
#> ------------------------------------------------------------------------
#> # A tibble: 30 × 3
#>    cluster n_obs n_periods
#>      <int> <int>     <int>
#>  1       1    35         2
#>  2       2    44         2
#>  3       3    85         2
#>  4       4    28         2
#>  5       5    86         2
#>  6       6    68         2
#>  7       7    57         2
#>  8       8    94         2
#>  9       9    62         2
#> 10      10    33         2
#> # ℹ 20 more rows
#> 
#> ICS hypothesis test (linear-contrast F test)
#> ------------------------------------------------------------------------
#> Contrasts (rows of C):
#>      h-iATE h-cATE v-iATE v-cATE
#> [1,]      1     -1      0      0
#> [2,]      0      0      1     -1
#> [3,]      1      0     -1      0
#> 
#>   method_type df1 df2        F  p_value note
#> 1  unadjusted   3  29 7.466581 0.000753     
#> 2    adjusted   3  29 7.193214 0.000942     
#> 
#> h-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.296039 0.266029 1.751949 2.840129
#> adjusted   2.283684 0.259827 1.752278 2.815089
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 9.934751  5.765828  17.11797
#> adjusted   9.812761  5.767727  16.69467
#> 
#> h-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.003200 0.327325 1.333745 2.672655
#> adjusted   1.997582 0.322699 1.337588 2.657576
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 7.412739  3.795228  14.47836
#> adjusted   7.371210  3.809843  14.26168
#> 
#> v-iATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 2.286101 0.264796 1.744532 2.827671
#> adjusted   2.274056 0.258463 1.745439 2.802673
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 9.836514  5.723224  16.90603
#> adjusted   9.718744  5.728418  16.48867
#> 
#> v-cATE
#>            Estimate       SE      LCL      UCL
#> unadjusted 1.762751 0.287326 1.175104 2.350398
#> adjusted   1.761323 0.281769 1.185040 2.337607
#> 
#> Exponentiated (OR)
#>               Ratio Ratio_LCL Ratio_UCL
#> unadjusted 5.828450  3.238481  10.48974
#> adjusted   5.820135  3.270818  10.35642

8 Interpreting the output

Each summary() call prints:

  • Method / family / scale
  • Which periods were kept by the mixture rule
  • A period mixture table (min/max treatment by period)
  • Whether the dataset matches the stepped-wedge edge pattern
  • (Optional) the global ICS test comparing all four estimands

For each requested estimand, it reports:

  • Unadjusted and Adjusted estimates
  • Jackknife SE and t-based CI with df = clusters − 1
  • If scale is "RR" or "OR", an exponentiated ratio table is also printed

8.1 Customizing the ICS test (optional)

By default, summary() runs the global ICS test. You can:

  • disable it via ics = "none", or
  • request a reduced comparison by passing an estimand set (e.g., compare only horizontal estimands), or
  • specify pairwise contrasts.

Examples:

# Disable the ICS test
summary(fit_c_gee_ind, ics = "none")

# Test equality within a subset (e.g., horizontal estimands only)
summary(fit_c_gee_ind, ics = c("h-iATE","h-cATE"))

# Pairwise comparisons via list(pairs = ...)
summary(fit_c_gee_ind, ics = list(pairs = list(c("h-iATE","h-cATE"),
                                              c("v-iATE","v-cATE"))))

9 Session info

sessionInfo()
#> R version 4.5.2 (2025-10-31 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26200)
#> 
#> Matrix products: default
#>   LAPACK version 3.12.1
#> 
#> locale:
#> [1] LC_COLLATE=English_United States.utf8 
#> [2] LC_CTYPE=English_United States.utf8   
#> [3] LC_MONETARY=English_United States.utf8
#> [4] LC_NUMERIC=C                          
#> [5] LC_TIME=English_United States.utf8    
#> 
#> time zone: America/New_York
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] ggplot2_4.0.1   dplyr_1.1.4     MRStdLCRT_0.1.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] Matrix_1.7-4       gtable_0.3.6       jsonlite_2.0.0     compiler_4.5.2    
#>  [5] Rcpp_1.1.0         tidyselect_1.2.1   gee_4.13-29        tidyr_1.3.2       
#>  [9] jquerylib_0.1.4    splines_4.5.2      scales_1.4.0       boot_1.3-32       
#> [13] yaml_2.3.12        fastmap_1.2.0      lattice_0.22-7     R6_2.6.1          
#> [17] labeling_0.4.3     generics_0.1.4     knitr_1.50         rbibutils_2.4     
#> [21] MASS_7.3-65        tibble_3.3.0       nloptr_2.2.1       minqa_1.2.8       
#> [25] bslib_0.9.0        pillar_1.11.1      RColorBrewer_1.1-3 rlang_1.1.6       
#> [29] cachem_1.1.0       xfun_0.55          sass_0.4.10        S7_0.2.1          
#> [33] cli_3.6.5          withr_3.0.2        magrittr_2.0.4     Rdpack_2.6.4      
#> [37] digest_0.6.39      grid_4.5.2         rstudioapi_0.17.1  lme4_1.1-38       
#> [41] nlme_3.1-168       lifecycle_1.0.4    reformulas_0.4.3   vctrs_0.6.5       
#> [45] evaluate_1.0.5     glue_1.8.0         farver_2.1.2       purrr_1.2.0       
#> [49] rmarkdown_2.30     tools_4.5.2        pkgconfig_2.0.3    htmltools_0.5.9