--- title: "Exploring Contextual Effects with merTools" author: "Jared Knowles and Carl Frederick" date: "Written 2026-05-29 (last updated 2026-05-29)" output: rmarkdown::html_vignette vignette: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{Exploring Contextual Effects with merTools} %\VignetteEncoding{UTF-8} --- ## Contextual effects In a multilevel model, a predictor measured at the individual level can also be aggregated to the group level, and the two can carry *different* meanings. The classic example is socio-economic status (SES): a student's own SES captures a *within-school* relationship, while the average SES of a student's school captures a *contextual* (or *compositional*) effect -- the additional influence of attending a more or less advantaged school, over and above a student's own background. This vignette reproduces the kind of analysis described in the `modelbased` package's article ["Studying some effects in context"][modelbased] using the tools in `merTools`. The data and model follow the contextual-effects example that has long been used to teach multilevel modeling (Raudenbush and Bryk, 2002; Gelman and Hill, 2007). [modelbased]: https://easystats.github.io/modelbased/articles/practical_context_effect.html ## The data `merTools` ships with `hsb`, a subset of the 1982 *High School and Beyond* survey: 7,185 students nested in 160 schools. It contains both a student-level SES measure (`ses`) and the school-mean SES (`meanses`) -- exactly the pair we need to separate within-school and contextual effects. ``` r data(hsb) str(hsb[, c("schid", "mathach", "ses", "meanses")]) ``` ``` #> 'data.frame': 7185 obs. of 4 variables: #> $ schid : chr "1224" "1224" "1224" "1224" ... #> $ mathach: num 5.88 19.71 20.35 8.78 17.9 ... #> $ ses : num -1.528 -0.588 -0.528 -0.668 -0.158 ... #> $ meanses: num -0.428 -0.428 -0.428 -0.428 -0.428 -0.428 -0.428 -0.428 -0.428 -0.428 ... ``` ## A contextual-effects model We model math achievement as a function of a student's own SES and the average SES of their school, allowing each school its own intercept: ``` r cm <- lmer(mathach ~ ses + meanses + (1 | schid), data = hsb) arm::display(cm) ``` ``` #> lmer(formula = mathach ~ ses + meanses + (1 | schid), data = hsb) #> coef.est coef.se #> (Intercept) 12.66 0.15 #> ses 2.19 0.11 #> meanses 3.68 0.38 #> #> Error terms: #> Groups Name Std.Dev. #> schid (Intercept) 1.64 #> Residual 6.08 #> --- #> number of obs: 7185, groups: schid, 160 #> AIC = 46578.6, DIC = 46559 #> deviance = 46563.8 ``` The coefficient on `ses` is the *within-school* slope; the coefficient on `meanses` is the *contextual* effect. `merTools::FEsim()` summarizes the uncertainty in both, and `plotFEsim()` highlights the terms whose interval excludes zero: ``` r fe <- FEsim(cm, n.sims = 500) plotFEsim(fe) + labs(title = "Within-school (ses) vs. contextual (meanses) effects") ```