Lattice Grid Buy a licence

demo D244

Regression diagnostics

A fitted line with its confidence band, residual and QQ charts read straight from the fit shadow columns, and the coefficients in a panel that refits as you filter

grid.statistics.regressionModel · regressionPlots

Building…
Loading a live grid…

The configuration

'regression-diagnostics': () => ({
  rows: [], config: {},
  foot: ['a fitted line with its 95% confidence band', 'the residual charts read the fit shadow columns', 'the panel reports the coefficients over the filtered rows'],
  mount: (el: HTMLElement, LG: any) => {
    const rnd = seeded(20260904);
    const gauss = () => Math.sqrt(-2 * Math.log(Math.max(rnd(), 1e-9))) * Math.cos(2 * Math.PI * rnd());
    const rows: any[] = Array.from({ length: 80 }, (_, i) => {
      const x = 1 + (i * 20) / 79;
      const y = 2.4 + 1.8 * x + gauss() * 2.4;
      return { id: 'r' + i, x: Math.round(x * 100) / 100, y: Math.round(y * 100) / 100 };
    });
    rows.push({ id: 'hi1', x: 27, y: 61 }, { id: 'hi2', x: 28, y: 39 });
    const model = { predictors: ['x'], response: 'y' };
    const fitEl = document.createElement('div');
    fitEl.id = 'reg-fit';
    fitEl.style.cssText = 'height:240px;border:1px solid var(--rule);border-radius:9px;background:var(--paper);min-width:0';
    const pairEl = document.createElement('div');
    pairEl.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:12px';
    pairEl.innerHTML =
      '<div id="reg-resid" style="height:210px;border:1px solid var(--rule);border-radius:9px;background:var(--paper);min-width:0"></div>' +
      '<div id="reg-qq" style="height:210px;border:1px solid var(--rule);border-radius:9px;background:var(--paper);min-width:0"></div>';
    const gridEl = document.createElement('div');
    gridEl.style.cssText = 'flex:1;min-height:0';
    el.append(fitEl, pairEl, gridEl);
    const grid = LG.createGrid(gridEl, {
      rowKey: 'id', theme: 'light',
      toolPanel: {
        side: 'left',
        panels: ['columns', { name: 'regression', props: { predictors: ['x'], response: 'y' } }],
        openPanel: 'regression',
      },
      columns: [
        { field: 'x', title: 'x', type: 'number', format: { decimals: 2 }, filter: { type: 'number' } },
        { field: 'y', title: 'y', type: 'number', format: { decimals: 2 }, filter: { type: 'number' } },
        { id: 'yhat', title: 'Fitted', type: 'number', format: { decimals: 2 }, shadow: { kind: 'fitPredicted', model } },
        { id: 'resid', title: 'Residual', type: 'number', format: { decimals: 2 }, shadow: { kind: 'fitResidual', model } },
        { id: 'sresid', title: 'Std residual', type: 'number', format: { decimals: 2 }, shadow: { kind: 'fitStdResidual', model } },
        { id: 'lev', title: 'Leverage', type: 'number', format: { decimals: 3 }, shadow: { kind: 'fitLeverage', model } },
        { id: 'cook', title: "Cook's D", type: 'number', format: { decimals: 3 }, shadow: { kind: 'fitCooksD', model } },
      ],
      rows,
    });
    let charts: any[] = [];
    loadCharts()
      .then(({ createChart, regressionPlots }: any) => {
        const { plots } = regressionPlots(grid, {
          spec: model, fitted: 'yhat', residual: 'resid', stdResidual: 'sresid', leverage: 'lev', cooksD: 'cook',
        });
        const draw = (container: string, plot: any, title: string) => {
          if (plot && plot.spec) charts.push(createChart({ grid, container, ...plot.spec, title }));
        };
        draw('#reg-fit', plots.fit, 'Fit with 95% confidence band');
        draw('#reg-resid', plots.residualsFitted, 'Residuals against fitted');
        draw('#reg-qq', plots.qq, 'Normal QQ of the residuals');
      })
      .catch((err) => console.error('[regression-diagnostics]', err));
    return () => { for (const c of charts) c?.destroy?.(); grid?.destroy?.(); };
  },
})

Fitting a line and reading its residuals

A regression is not finished when it has coefficients; it is finished when the residuals have been looked at. Lattice Grid fits the model over the filtered rows and reports the coefficient table, R squared and the collinearity flag in a panel beside the grid. Filter the grid and the model refits over what is left, so the numbers always describe the data you are actually looking at. This gives an analyst working in a JavaScript data grid the diagnostics they would otherwise leave for a separate notebook, in the same place as the data.

The fitted model also lives in the data as shadow columns: the fitted value, the residual, the standardised residual, the leverage and Cook’s distance. They are ordinary numeric cells that sort, filter, group and export like any other, and they read the fit by row key. That turns each diagnostic picture into a plain chart over columns: the fit with its confidence band, the residuals against the fitted value, and a normal QQ plot of the residuals. Two high-leverage points in the sample keep leverage and Cook’s distance meaningful rather than flat.

How do you check a regression fit in a data grid?

Read the coefficient table, R squared and the collinearity flag from the regression panel, which recomputes over the filtered rows. Add the fit as shadow columns to bring the fitted value, residual, standardised residual, leverage and Cook’s distance into the data itself, then chart the fit with its confidence band, the residuals against the fitted value, and a QQ plot of the residuals to see where the model holds and where it does not.