Lattice Grid

blog

Ridgeline Chart of Hourly Demand by Weekday

Building…
Loading a live grid…

If you are the store planner who sets the rota, the question is not how many orders a day brings. It is when in the day they arrive, and whether Saturday has the same shape as Tuesday. Staff the tills for a Tuesday lunch rush on a Saturday and you are short from mid-morning until closing.

A ridgeline chart answers that by stacking. Each weekday gets its own smooth curve of when orders land, drawn on one shared hour axis, so the humps line up and you can read the difference at a glance. The chart above is bound to the grid beneath it, one row per order, so filtering the rows redraws the chart. Filter the Weekday column to Tuesday and Saturday and compare the two ridges alone.

Why a ridgeline and not a line per day

Seven lines on one set of axes cross and tangle until none can be followed. Stacking the curves gives each weekday its own band, so the shapes compare without the clutter, and a seventh day costs one more row of height rather than one more tangle.

The chart over a grid

The chart reads the grid’s rows, so there is nothing to keep in step. A ridgeline takes a category column, x, and a measure column, y, and draws one density ridge per category over the values of the measure. Here the category is the weekday and the measure is the hour the order was placed. Ridgeline is an opt-in chart type, so it is imported on its own beside the charts module. Here is the example in plain JavaScript, with a few inline orders standing in for your own rows.

import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import '@toclocoinc/lattice-grid/modules/chart-ridgeline';

const rows = [
  { id: 1, weekday: 'Tuesday', hour: 9, demand: 2 }, { id: 2, weekday: 'Tuesday', hour: 12, demand: 3 },
  { id: 3, weekday: 'Tuesday', hour: 12, demand: 1 }, { id: 4, weekday: 'Tuesday', hour: 13, demand: 2 },
  { id: 5, weekday: 'Tuesday', hour: 17, demand: 4 }, { id: 6, weekday: 'Tuesday', hour: 18, demand: 2 },
  { id: 7, weekday: 'Saturday', hour: 10, demand: 2 }, { id: 8, weekday: 'Saturday', hour: 12, demand: 3 },
  { id: 9, weekday: 'Saturday', hour: 13, demand: 1 }, { id: 10, weekday: 'Saturday', hour: 14, demand: 4 },
  { id: 11, weekday: 'Saturday', hour: 15, demand: 2 }, { id: 12, weekday: 'Saturday', hour: 16, demand: 3 },
];

const gridEl = document.createElement('div');
const chartEl = document.createElement('div');
gridEl.style.height = '260px';
chartEl.style.height = '320px';
document.body.append(gridEl, chartEl);

const grid = createGrid(gridEl, {
  rowKey: 'id',
  rows,
  columns: [
    { field: 'weekday', title: 'Weekday', filter: { type: 'set' } },
    { field: 'hour', title: 'Hour', type: 'number' },
    { field: 'demand', title: 'Units', type: 'number' },
  ],
});

createChart({ grid, container: chartEl, type: 'ridgeline', x: 'weekday', y: 'hour',
  margin: { left: 80 }, title: 'Orders by hour of the day' });

The same thing in React

The React adapter is a factory, so you build the component once at module scope. The ref’s grid is the real grid, which is what the chart binds to.

import React, { useEffect, useRef } from 'react';
import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { createLatticeGrid } from '@toclocoinc/lattice-grid/modules/react';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import '@toclocoinc/lattice-grid/modules/chart-ridgeline';

const LatticeGrid = createLatticeGrid({ React, createGrid });
const rows = [
  { id: 1, weekday: 'Tuesday', hour: 9 }, { id: 2, weekday: 'Tuesday', hour: 12 },
  { id: 3, weekday: 'Tuesday', hour: 13 }, { id: 4, weekday: 'Tuesday', hour: 17 },
  { id: 5, weekday: 'Saturday', hour: 10 }, { id: 6, weekday: 'Saturday', hour: 13 },
  { id: 7, weekday: 'Saturday', hour: 14 }, { id: 8, weekday: 'Saturday', hour: 16 },
];
const columns = [
  { field: 'weekday', title: 'Weekday', filter: { type: 'set' } },
  { field: 'hour', title: 'Hour', type: 'number' },
];

export function DemandByWeekday() {
  const gridRef = useRef(null);
  const chartRef = useRef(null);
  useEffect(() => {
    const chart = createChart({ grid: gridRef.current.grid, container: chartRef.current,
      type: 'ridgeline', x: 'weekday', y: 'hour', margin: { left: 80 },
      title: 'Orders by hour of the day' });
    return () => chart.destroy();
  }, []);
  return (<>
    <LatticeGrid ref={gridRef} rowKey="id" columns={columns} rows={rows} style={{ height: 220 }} />
    <div ref={chartRef} style={{ height: 280 }} />
  </>);
}

The options used

These are the rows of the charts reference for the options in the listings above.

Option Type What it does
grid Grid Required. The grid to read.
container Element or string Required. Where to draw.
type string 'ridgeline' here. A name the base bundle does not know is looked up among the opt-in chart modules once imported.
x string The category column. Each distinct value, here the weekday, gets one ridge.
y string The measure column whose values each ridge is drawn over, here the hour of the order.
margin number or object Space in pixels around the plot. Widening the left margin leaves room for the weekday names beside the ridges.
title string Drawn above the plot.

Reading the ridges

Every ridge is drawn on the same hour axis, so a hump further right means demand later in the day. Tuesday shows two humps, one at lunch and one after work, while Saturday is a single broad swell through the afternoon. That is the difference a rota built from daily totals cannot show. The grid still drives it: filter to the orders from one store, or to one season, and every ridge is drawn again from what is left.

Next

See the ridgeline beside the other distribution charts in the distribution chart demo, read the full list of chart types on the charts page, or follow the how-to for a chart that follows the grid.

Read next

  • Pie Chart of Revenue Share by Channel

    Bind a pie chart to a grid of revenue by channel: show each channel as a share of the quarter, and filter the rows to redraw the slices.

  • Heatmap of Sign-ups by Hour and Weekday

    Bind a heatmap to a grid of sign-ups: shade every weekday and hour by volume to time a campaign send, and filter the rows to redraw it.

  • Violin Chart of Salary Bands by Level

    Bind a violin chart to a grid of salaries: see the shape of pay at each level, including two bands inside one, and filter the rows to redraw it.

All posts RSS