Lattice Grid Buy a licence

the statistics layer · v1.103.2

JavaScript Statistics for a Data Grid

The grid already knows what the data says. When the grid holds the data in memory, it holds every number on the page and recomputes over them as the filters change. The statistics layer is that knowledge made reachable: a median or a 95th percentile in the totals row, a full column profile in a panel, and columns that track how a value has moved since you opened the page. Every figure is computed over the rows the grid holds, through the same column handles the totals row uses, so a median in the panel and a median in the footer are the same number by the same definition. Point the grid at a windowed remote or pushdown source instead and these figures describe the rows currently loaded, the view in front of you; for statistics over an entire dataset, either pull the whole matching set in or, over a DuckDB connection, push the aggregation down to the engine so the figures are computed over everything.

Building…
Loading a live grid…

reductions in the totals row

More than a sum at the bottom.

A totals row usually means one thing: a sum. Here every numeric column names its own reduction, so one footer reads a median price, a 95th-percentile bid, the deviation of the day's change and the spread of volume at once. The reductions are kernels, more than twenty of them, and they run over the filtered rows: narrow to one desk and every figure narrows with it. Quantiles use the R type 7 definition, the one a spreadsheet's PERCENTILE agrees with, so the number is the one a reviewer expects rather than one of the nine other reasonable answers.

Building…
Loading a live grid…

The same column, drawn: a histogram of the distribution the footer is reducing, with a density curve over the bars so the shape the median and the deviation describe is one you can see.

Building…
Loading a live grid…

shadow columns

A column that remembers.

A shadow column is declared against another column and maintained by the grid. It has no field in the data, and it is not a computed column either, because its value depends on what happened before: the value the row had when the page loaded, how many times it has changed since, which way it is trending. It is a real column throughout, sortable, filterable, groupable, exportable, saveable into a view. The feed below is live, because a delta against a still data set is a column of zeroes.

Building…
Loading a live grid…

Delta and percent change are measured against the value at page load, not the previous tick. Moves counts every change since; Streak counts consecutive moves in one direction and resets on a turn, because "seven rises" means something that "seven changes" does not.

the gesture nothing else does

Sort by places moved. Watch the top movers.

The second family of shadow columns answers where a row sits among the others rather than what it did before: rank, percentile, quartile, and the one that matters, places moved since the baseline. Because it is a real column, sorting by it is one gesture, and the result is a live top-movers view that reorders itself as the feed runs. Rank is computed over every tracked row rather than the filtered set, so the order does not shift under you as you filter, and the column never disagrees with itself between two views. "Mark all" in the rail resets every baseline to now, and the race starts again.

Building…
Loading a live grid…

capability against a tolerance

The process, and the limits it has never heard of.

Declare the customer's tolerance on a column and the capability figures, a control chart and any out-of-tolerance rule all read the same limits. The chart draws two sets of lines that mean different things: the control limits, three sigma from the process itself, and the spec limits, dashed, which the process has never heard of. A point breaking a rule is marked.

Building…
Loading a live grid…

Control chart → Cp, Cpk, Pp, Ppk → A production line →

is this column normal?

A straight line, or a story.

A Q-Q plot puts a column's quantiles against a normal distribution's. Points on the line are normal; a bend at the ends is a skew or a heavy tail. The reference line runs through the quartiles rather than being least-squares fitted, because a fitted line is dragged by the very tails you are inspecting.

Building…
Loading a live grid…

Q-Q plot → Cumulative distribution → Concentration →

every pair at once

Which columns move together.

Before the two-column questions below, the whole matrix: a correlogram reads every numeric column against every other and shades each cell by the correlation, so the pairs that move together and the pairs that do not are one glance rather than a search. It reads the filtered rows like everything else here, so narrowing the grid recomputes the matrix.

Building…
Loading a live grid…

The correlation matrix → The statistics API →

numbers you can reconcile

A figure that agrees with the spreadsheet beside it.

A number is only worth having if it survives being checked against the tools your team already trusts. Every figure here is computed by a named method, so it reconciles rather than starts an argument.

  • A median that matches the spreadsheet

    Quantiles use the R type 7 definition, the same one NumPy computes by default and Excel's PERCENTILE.INC returns. So a median or a 95th percentile here lands on the value a stakeholder gets in their own sheet, not one of the nine other reasonable answers.

  • A correlation that refuses to invent one

    Run a correlation against a column that never varies and you get nothing back rather than a confident-looking number that means nothing. Every correlation also says how many rows it was drawn from, because a figure over forty pairs is a different claim from one over a thousand, and the count is part of the answer.

  • Sample or population, said out loud

    A deviation states which one it is rather than leaving you to guess, with the sample form as the default and the population form there by name. The label travels with the figure, so a reviewer knows exactly what they are checking.

  • The same definitions when the engine computes them

    These figures are computed client-side, and they reconcile with R, pandas and Excel because they use named, documented definitions. When you push the aggregation down to a DuckDB connection, the engine is asked for those same definitions, so the common statistics match: a quantile is the R type 7 the spreadsheet returns, a deviation is the sample form. Two differences are stated rather than hidden. The statistical mode is computed by the engine's own convention and can disagree at the edges, so it stays client-side under the recommended policy, and a weightedQuantile is always computed in the browser because there is no faithful way to express it in SQL.

why the numbers hold up

Numbers you can put in front of someone.

A figure on a screen is a claim, and a claim you cannot defend is worse than no figure at all. These are the guarantees behind the numbers here, in the terms that matter when a reviewer, an auditor or your own spreadsheet checks them.

  • A spread that can never come back nonsense

    A batch variance or standard deviation here is computed by Welford's method, which accumulates the deviation directly rather than subtracting two large, nearly equal sums of squares. The textbook shortcut can return a negative variance on a near-constant column; this cannot. The number is numerically stable and never negative, so a deviation on tightly clustered data is a real figure rather than an artefact of how it was computed.

  • A percentile that matches R, pandas, Excel and DuckDB

    Quantiles use the R type 7 definition, the same one pandas and NumPy compute by default, Excel's PERCENTILE.INC returns and DuckDB's quantile_cont uses. So a median or a 95th percentile here reconciles with the tool the reader opens next, rather than being one of the nine other reasonable answers a quantile can take.

  • Sample by default, the way the engines mean it

    A variance or standard deviation is the sample form with Bessel's n − 1 correction by default, matching var_samp and stddev_samp in SQL and the default in R and pandas. The population form is there by name when you want it, so the figure means the same thing on both sides of the check.

  • The engine agrees, and the two exceptions are named

    Push the aggregation down to a DuckDB connection and the common statistics compute identically, because the grid asks the engine for the same named definitions it uses itself. Two figures are documented as exceptions rather than left to surprise you: the statistical mode follows the engine's own convention and can differ at the edges, so it stays client-side under the recommended policy, and weightedQuantile is always computed in the browser because there is no faithful way to express it in SQL.

  • No figure without its scope

    Every statistic here is computed over a stated set of rows, never an unstated one. When the grid holds the data in memory, that set is the rows the filter matches. On a windowed remote or pushdown source, a statistic describes the rows currently loaded, and in development the grid warns you by name the moment a figure would be computed over only that window, so a window number is never quietly passed off as a whole-dataset one. You always know which rows a number is about.

two columns from the API

And the figures a panel cannot hold.

The panel and the totals row cover a column at a time. grid.statistics answers the two-column and whole-series questions directly: correlation and regression, weighted averages and quantiles, volatility and drawdown over a date-ordered series. Declare a tolerance on a column and the process-capability figures, a control chart and any out-of-tolerance rule all read the same limits, so Cpk well above Ppk tells you the process drifted rather than that it is merely variable.

Read the statistics guide →

// two columns at a time
grid.statistics.correlation('spend', 'revenue');
grid.statistics.regression('spend', 'revenue');
grid.statistics.weightedAverage('price', 'qty');

// a whole date-ordered series
grid.statistics.series('price', { by: 'date' });
// { volatility, growth, maxDrawdown, and more }

// declare a spec, get capability + a control chart
{ field: 'mm', type: 'number',
  spec: { lower: 9.5, upper: 10.8, target: 10 } }
grid.statistics.capability('mm');
// { cp, cpk, pp, ppk, defectRate, violations, and more }

four desks, four grids

The same layer, in the room it was built for.

Each of these is a realistic grid where the types and the statistics belong together rather than being demonstrated at. Every one is live, and repaints in the low hundreds of milliseconds with a feed running.

  • A trading book

    Money as a custom unit, shadow columns for change and rank, a running P&L and a live price feed, sorted by the top movers.

  • Analytical chemistry

    Molarity, ppb and dose types, with one contaminated batch the outlier and z-score rules find and nothing else.

  • Manufacturing and process

    A machined bore with a tolerance driving capability, drifting in one shift so Cpk and Ppk separate.

  • Fleet and network

    Address, data-size and rate types, latency long-tailed so the 99th percentile is far from the median.

Process capability → Column profiling → Variation over time → Selection statistics → Running totals → The API → All of it in one application →