Lattice Grid Buy a licence

demo D286

Project a time series forward in Angular

Fit a column over the filtered rows and project the months ahead, drawn as its own line carrying on from the last reading, with the fit quality shown beside it

grid.statistics.forecast(col, { by, method, horizon })

This grid fits a column over the rows currently in view and projects the months ahead, drawn as its own line carrying on from the last reading, with the fit quality shown beside it. It answers where a series is heading, computed in the browser from the data on screen.

Building…
Loading a live grid…

This is the Angular version. A standalone component takes the whole configuration through one config input, surfaces grid events as outputs, and exposes the live grid on a getter for anything the inputs do not cover.

The configuration

import { Component, signal } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { createGrid, createHeadlessGrid, type Grid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import { LatticeGridComponent, LatticeChartComponent, provideLattice } from '@toclocoinc/lattice-grid/angular';

const rows = [/* one row a month, in order: t, units */];

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [LatticeGridComponent, LatticeChartComponent],
  template:
    '@if (chartGrid(); as g) {' +
    '  <lattice-chart [grid]="g" [config]="chartConfig" style="display:block;height:360px"></lattice-chart>' +
    '}' +
    '<lattice-grid [config]="gridConfig" (gridReady)="onGridReady($event)" style="display:block;height:420px;margin-top:14px"></lattice-grid>',
})
export class AppComponent {
  chartGrid = signal<Grid | null>(null);
  chartConfig = { type: 'line', x: 'x', y: 'y', series: 'kind', legend: true, title: 'Monthly demand, with the months ahead projected' };

  gridConfig = {
    rowKey: 'id',
    columns: [
      { field: 't', title: 'Month', type: 'number', layout: { width: 90 } },
      { field: 'units', title: 'Units', type: 'number', total: 'avg' },
    ],
    rows,
  };

  onGridReady(grid: Grid) {
    // Read 'units' ordered by 't', fit it and project six months ahead, over
    // the grid's filtered rows. result.r2 is the fit quality; result.points
    // is the months ahead as [{ at, mean }, ...].
    const result = (grid as any).statistics.forecast('units', { by: 't', method: 'linear', horizon: 6 });

    const last: any = rows[rows.length - 1];
    const points = rows.map((r: any) => ({ x: r.t, y: r.units, kind: 'Actual' }));
    points.push({ x: last.t, y: last.units, kind: 'Forecast' });
    for (const p of result.points) points.push({ x: p.at, y: Math.round(p.mean), kind: 'Forecast' });

    this.chartGrid.set(createHeadlessGrid({
      rowKey: (r: any) => r.kind + ':' + r.x,
      columns: [
        { field: 'x', title: 'Month', type: 'number' },
        { field: 'y', title: 'Units', type: 'number' },
        { field: 'kind', title: 'Series' },
      ],
      rows: points,
    }));
  }
}

bootstrapApplication(AppComponent, {
  providers: [provideLattice({ createGrid, createChart })],
});

Where the numbers are heading, from the grid you already have

A column of monthly figures tells you where you have been. The question a room usually asks next is where it is going. This reads the column over the rows currently in the grid, fits it, and projects the months ahead, all in the browser with nothing sent to a server. The projection is drawn as its own line carrying on from the last real reading, so where the history ends and the estimate begins is never in doubt.

You ask for the forecast the same way you ask for any other figure about a column, off the grid you already have. Name the column to project and the column that carries its time axis, choose how far ahead to look, and pick how the line is fitted: a straight least-squares trend for a steady climb, or a seasonal method when the figures rise and fall on a cycle. The fit quality comes back with the result, so you can see how well the line follows the history before you trust where it points.

Because the forecast reads the filtered rows, it follows the grid. Narrow to one region or one product and the projection reshapes to that slice, so a question about part of the data is answered from the part, not the whole. The same grid that holds the numbers is the one that tells you where they are heading.

How do I forecast a column?

Call grid.statistics.forecast with the column to project and an options object: by names the column that orders the time axis, method chooses how the line is fitted, and horizon is how many steps ahead to return. You get back the projected points, each stamped with its position on the time axis and its value, plus the fit quality for a straight-line fit. Draw those points as a second line on a chart, starting from the last real reading so the two meet, and the projection reads as a continuation of the history rather than a separate picture. Because it reads the filtered rows, the forecast updates as you filter the grid.