Google Summer of Code 2026 - Avanish Salunke

plotEffects and plotInteraction on CompactLinearModel, and the anova method

July 30, 2026 | 5 Minute Read

Last week five methods landed on CompactLinearModel. This week added two more - plotEffects and plotInteraction - and brought the anova method to LinearModel for the first time.

Project Status

Week 1 was project planning and setup.

Week 2 was the class shell: properties, access rules, the display method, and the subsref gatekeeper.

Week 3 was the constructor and fitlm: the math solver, all the diagnostics, the full 26-property object, and the front-end parser.

Week 4 was applying mentor feedback, refactoring the constructor, and adding predict and random.

Week 5 was feval, coefCI, coefTest, dwtest, and addTerms.

Week 6 was removeTerms, plotResiduals, plotDiagnostics, plotEffects, and a set of bug fixes.

Week 7 was the midterm evaluation, robust regression, plotAdjustedResponse, and plotAdded.

Week 8 was plot, plotInteraction, compact, and the initial CompactLinearModel structure.

Week 9 was coefCI, coefTest, predict, random, and feval on CompactLinearModel, plus two bug fixes.

This week was plotEffects and plotInteraction on CompactLinearModel, and the anova method on LinearModel.


plotEffects - here

plotEffects shows how much each predictor moves the response, on its own, holding everything else fixed. It was already working on LinearModel, and it worked by going straight to the training data every time it was called: moving one predictor from its lowest to its highest observed value, keeping every other predictor at its average, and reading off how much the fitted response changes.

That approach can’t carry over to CompactLinearModel as-is, because a compact model deliberately throws away the training data to stay small. So instead of computing the answer live, LinearModel now works out the exact same contrast once, right when the model is fit, and stores it in a new hidden property called EffectContrasts - one row per predictor. This gets carried over automatically whenever compact is called, the same way properties like TermsMatrix already are.

LinearModel.plotEffects itself was changed to just read from EffectContrasts instead of recomputing it every time it’s called - a pure refactor, so the numbers it draws are unchanged from before. CompactLinearModel.plotEffects is a new, separate implementation that reads the same cached contrast, matching how the rest of the package keeps each class’s methods independent instead of sharing code across files.

load carsmall
mdl  = fitlm ([Weight, Horsepower], MPG, 'linear');
cmdl = compact (mdl);

plotEffects (mdl)     ## already worked
plotEffects (cmdl)    ## now works too, using the cached contrast

plotEffects on CompactLinearModel

plotInteraction - here

plotInteraction shows whether two predictors interact, by comparing each one’s effect on its own against its effect when the other predictor is held at a few different values. The same problem applied here: it needed the training data to compute those comparisons.

The same fix was used. A second new hidden property, InteractionContrasts, stores the contrasts needed for every pair of predictors that appear together in the model, computed once at fit time and carried over by compact just like EffectContrasts. CompactLinearModel.plotInteraction reads from it directly.

plotInteraction (cmdl, 'x1', 'x2')                ## effects mode, default

plotInteraction on CompactLinearModel

anova on LinearModel - here

anova answers a simple question: out of everything in the model, how much is each individual predictor actually pulling its weight? It takes a fitted model and returns a table that breaks the total variation in the data into one row per predictor, plus a leftover “Error” row for whatever the model couldn’t explain.

octave:28> load carsmall
octave:29> mdl = fitlm ([Weight, Horsepower], MPG, 'linear');
octave:30> anova (mdl)
ans =
  3x5 table

              SumSq     DF    MeanSq        F         pValue       
             _______    __    _______    _______    ___________    

    x1       645.868     1    645.868     39.042    1.35188e-08    
    x2       83.7784     1    83.7784    5.06431        0.02686    
    Error    1488.86    90    16.5429        NaN            NaN  

Each row shows how much that one predictor contributes on its own, along with a p-value telling you whether that contribution is likely to be real or just noise. A small p-value means that predictor is probably doing genuine work in the model.

There’s also a summary view, which zooms out and shows the bigger picture instead of one row per predictor - how much the whole model explains versus what’s left unexplained:

octave:44> anova (mdl, 'summary')
ans =
  3x5 table

                 SumSq     DF    MeanSq       F         pValue       
                _______    __    _______    ______    ___________    

    Total       6004.76    92    65.2691       NaN            NaN    
    Model        4515.9     2    2257.95    136.49    5.57376e-28    
    Residual    1488.86    90    16.5429       NaN            NaN    

Under the hood there’s more than one accepted way to measure “how much a predictor contributes,” and anova supports the main ones MATLAB does, with one less common variant still on the to-do list rather than shipped half-working.

Worth a quick note on why this was written from scratch rather than reused: there’s another anova already in the package, built for a different GSoC project, that also happens to accept a fitted model as input. But it’s really a different tool underneath - it builds its own separate kind of object, not the plain table that LinearModel.anova is supposed to return. Since the two don’t actually do the same job, the right move was a fresh implementation built specifically around how LinearModel stores its terms and coefficients, rather than bending an unrelated tool to fit.


Development Branch

All the code is on this branch: gsoc-2026-linearmodel


Next Steps

  • anova on CompactLinearModel.
  • step and stepwiselm - automated model selection by forward and backward stepping.

Stay tuned for the next update!