The goal of {effectsurf} is to extend traditional 2D estimated marginal means (EMMs) into interactive 3D Estimated Marginal Surfaces (EMS). It provides model-agnostic visualisation of predictions, conditional effect sizes, and conditional average treatment effects (CATE) from fitted statistical models.
Surfaces are rendered as interactive plotly 3D charts with:
- Categorical stratification – overlaid surfaces by group
- Confidence envelopes – upper/lower CI surfaces
- 2D profile projections – slices through the 3D surface
- Derivative surfaces – sensitivity, curvature, and interaction maps
- Post-prediction smoothing – smooth jagged surfaces from non-smooth
models (e.g., random forests) via
mgcv::gam()surrogates
Supports 100+ model classes via marginaleffects, with optional emmeans and gratia backends.
You can install the development version of {effectsurf} from GitHub with:
if (!require("pak")) {
install.packages("pak")
}
pak::pak("AAGI-AUS/effectsurf"){effectsurf} works with any fitted model that supports predict(). Here
is a minimal example using a GAM fitted with {mgcv}:
library(effectsurf)
model <- mgcv::gam(
mpg ~ s(wt) + s(hp) + factor(cyl),
data = mtcars
)es <- surf_prediction(
model,
x = "wt", y = "hp",
x_length = 30, y_length = 30,
labels = list(
x = "Weight (1000 lbs)",
y = "Horsepower",
z = "Fuel efficiency (mpg)"
)
)
es
#> ℹ <effectsurf> — Prediction Surface
#> • X: wt | Y: hp | Z: mpg
#> • Grid: 900 points (900 per stratum)
#> • CI: TRUE | Transform: FALSE
#> ℹ Use `plot()` to visualise, `surf_data()` to extract data.plot(es)This opens an interactive 3D surface in your browser or RStudio viewer. Drag to rotate, scroll to zoom, hover for values.
Overlay surfaces by a categorical variable to visualise interactions:
es_strat <- surf_prediction(
model,
x = "wt", y = "hp",
by = "cyl",
x_length = 25, y_length = 25,
labels = list(
x = "Weight (1000 lbs)",
y = "Horsepower",
z = "Fuel efficiency (mpg)",
title = "Prediction surface by cylinder count"
)
)
es_strat
#> ℹ <effectsurf> — Prediction Surface
#> • X: wt | Y: hp | Z: mpg
#> • Grid: 625 points (625 per stratum)
#> • Stratified by: cyl (1 levels)
#> • CI: TRUE | Transform: FALSE
#> ℹ Use `plot()` to visualise, `surf_data()` to extract data.plot(es_strat, opacity = 0.85)surf_export(es, "my_surface.html")| Function | Purpose |
|---|---|
surf_prediction() |
Prediction surface (fitted values across x, y) |
surf_comparison() |
Treatment effect surface (contrasts between groups) |
surf_slopes() |
Marginal effect surface (partial derivatives) |
surf_sensitivity() |
CATE surface (where does treatment work best?) |
surf_cate() |
CATE from causal models (e.g., causal forests) |
surf_derivatives() |
Gradient, curvature, and interaction maps |
surf_profile() |
2D profile slices from a 3D surface |
surf_contour() |
2D contour plot |
surf_optimum() |
Find the (x, y) combination that maximises/minimises z |
surf_export() |
Export to self-contained HTML |
All surf_*() functions support the smooth parameter for
post-prediction surface smoothing via mgcv::gam() – useful for random
forests, boosted trees, and other non-smooth models.
{effectsurf} includes a bundled barley_trials dataset simulated from
Australian barley trial data (GRDC Project UCS2301-002RTX).
data(barley_trials)
m_barley <- mgcv::gam(
yield ~ s(rainfall, k = 5) + nitrogen + seedrate +
variety_type + state + nitrogen:variety_type +
s(trial, bs = "re"),
data = barley_trials,
method = "REML"
)
es_barley <- surf_prediction(
m_barley,
x = "nitrogen", y = "rainfall",
by = "variety_type",
x_length = 30, y_length = 30,
labels = list(
x = "Nitrogen applied (kg/ha)",
y = "April-October rainfall (mm)",
z = "Grain yield (t/ha)",
title = "Yield response surface by variety type"
)
)
summary(es_barley)
#> Estimated Marginal Surface (EMS)
#> ================================
#>
#> Type: prediction
#> X var: nitrogen
#> Y var: rainfall
#> Z var: yield
#> Strata: variety_type (3 levels: tall, sdw, erectoides)
#> Grid: 2700 points
#> CI: TRUE
#> Transform: none
#>
#> Response summary (estimate):
#> Min: 1.8153
#> Median: 3.8023
#> Mean: 3.7412
#> Max: 5.3814
#>
#> Response by stratum:
#> variety_type mean min max
#> <fctr> <num> <num> <num>
#> tall 3.5795 1.9141 4.8689
#> sdw 3.8580 2.1059 5.2341
#> erectoides 3.7863 1.8153 5.3814
#>
#> Model: gam/glm/lm
#> N obs: 3240plot(es_barley, opacity = 0.85)citation("effectsurf")
To cite package 'effectsurf' in publications use:
Moldovan M (2026). _effectsurf: Interactive 3D Estimated Marginal
Surfaces for Conditional Effect Visualisation_. R package version
0.1.0.9000, <https://github.com/maxmoldovan/effectsurf>.
A BibTeX entry for LaTeX users is
@Manual{,
title = {effectsurf: Interactive 3D Estimated Marginal Surfaces for Conditional Effect Visualisation},
author = {Max Moldovan},
year = {2026},
note = {R package version 0.1.0.9000},
url = {https://github.com/maxmoldovan/effectsurf},
}Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.