demo D182
Correlation and weighted averages in htmx
Two-column figures from the API, over the filtered rows
grid.statistics.correlation(a, b)
This grid computes two-column figures, correlation and weighted averages, from the API over the filtered rows. It answers how two measures relate across the current selection, updating as the filter changes.
This is the htmx version. A server-rendered table is upgraded in place, so the page is readable before any script runs and gains the full grid when the module loads, which suits a progressively enhanced app.
The configuration
<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.min.css">
<!-- A real, server-rendered table: readable with JavaScript off. data-field on
each header names the column that cell becomes, and data-id on each row is
the key the grid carries it under. -->
<table id="correlation-table" data-lattice-grid>
<thead>
<tr>
<th data-field="symbol">Symbol</th>
<th data-field="desk">Desk</th>
<th data-field="price">Price</th>
<th data-field="bid">Bid</th>
<th data-field="volume">Volume</th>
</tr>
</thead>
<tbody>
<tr data-id="INS-0000000"><td>ELMW.G0</td><td>Rates</td><td>462.0258</td><td>461.2688</td><td>239420</td></tr>
<tr data-id="INS-0000001"><td>RVNS.A1</td><td>Credit</td><td>241.8711</td><td>241.7978</td><td>119353</td></tr>
<tr data-id="INS-0000002"><td>NRTH.E2</td><td>Commodities</td><td>98.9614</td><td>98.8353</td><td>160242</td></tr>
<!-- the rest of the rows, rendered by your server -->
</tbody>
</table>
<div id="stats" style="margin-top: 12px; font: 13px system-ui; display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem"></div>
<script type="module">
import { hydrateTable } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/htmx.esm.min.js';
// The markup carries the rows; a server-rendered table's column types are
// declared in the config hydrateTable takes. The grid replaces the table
// where it stood.
const grid = hydrateTable(document.getElementById('correlation-table'), {
toolPanel: { side: 'left', panels: ['filters', 'columns'], exportName: 'book' },
columnDefaults: { filter: true },
columns: [
{ field: 'symbol', title: 'Symbol', layout: { pin: 'start', width: 120 } },
{ field: 'desk', title: 'Desk', filter: { type: 'set' } },
{ field: 'price', title: 'Price', type: 'number', format: { decimals: 4 } },
{ field: 'bid', title: 'Bid', type: 'number', format: { decimals: 4 } },
{ field: 'volume', title: 'Volume', type: 'number', format: { notation: 'compact' } },
],
});
// Two-column figures read straight from the grid over the rows that survive the
// filters: how tightly two columns move together, a line fitted through them,
// and an average that weights each price by its volume.
const panel = document.getElementById('stats');
const num = (v, d = 3) => (typeof v === 'number' ? v.toFixed(d) : String(v));
const report = () => {
const s = grid.statistics;
const r = s.correlation('price', 'bid');
const reg = s.regression('price', 'bid');
const wa = s.weightedAverage('price', 'volume');
panel.innerHTML =
'<span><strong>corr(price, bid)</strong> ' + num(r) + '</span>' +
'<span><strong>fit</strong> slope ' + num(reg && reg.slope, 4) + ', r² ' + num(reg && reg.r2) + '</span>' +
'<span><strong>volume-weighted price</strong> ' + num(wa, 4) + '</span>';
};
grid.on('filter:changed', report); // the figures move as a desk is filtered
report();
</script>