Lattice Grid

blog

Sunburst Chart of Spend by Department

Building…
Loading a live grid…

If you are the procurement lead, the question is not how much the company spent. It is where the money sits: which department, which cost centre inside it, and which vendor inside that, so you know which supplier to call before the next renewal.

A sunburst chart answers that in one picture. The centre is all spend, the first ring is the departments, the second ring is each department’s cost centres and the outer ring is the vendors, each segment sized by its share of the segment inside it. The chart above is bound to the grid beneath it, one row per vendor and cost centre, so filtering the rows redraws the rings. Filter the Department column to Operations and the chart is only Operations.

Why a sunburst and not a treemap

A treemap packs the same hierarchy into rectangles and is better when you need to read exact sizes. A sunburst is better when the point is the path, because a vendor sits visibly on the ring outside its cost centre, which sits outside its department, so the eye follows department to cost centre to vendor without a legend. Click a segment to drill into it.

The chart over a grid

The chart reads the grid’s rows, so there is nothing to keep in step. For a hierarchy, x is an array of columns, top level first, and y is the measure that sizes each segment. Here is the example in plain JavaScript, with sample rows inline in place of your own.

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

const rows = [
  { id: 1, department: 'Engineering', costCentre: 'Platform', vendor: 'Supplier A', spend: 420 },
  { id: 2, department: 'Engineering', costCentre: 'Platform', vendor: 'Supplier B', spend: 260 },
  { id: 3, department: 'Engineering', costCentre: 'Data', vendor: 'Supplier D', spend: 310 },
  { id: 4, department: 'Marketing', costCentre: 'Brand', vendor: 'Supplier G', spend: 230 },
  { id: 5, department: 'Marketing', costCentre: 'Events', vendor: 'Supplier I', spend: 280 },
  { id: 6, department: 'Operations', costCentre: 'Logistics', vendor: 'Supplier O', spend: 390 },
  { id: 7, department: 'Operations', costCentre: 'Facilities', vendor: 'Supplier M', spend: 340 },
];

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

const grid = createGrid(gridEl, {
  rowKey: 'id',
  rows,
  columns: [
    { field: 'department', title: 'Department', filter: { type: 'set' } },
    { field: 'costCentre', title: 'Cost centre', filter: { type: 'set' } },
    { field: 'vendor', title: 'Vendor', filter: { type: 'set' } },
    { field: 'spend', title: 'Spend (thousands)', type: 'number' },
  ],
});

createChart({ grid, container: chartEl, type: 'sunburst',
  x: ['department', 'costCentre', 'vendor'], y: 'spend', drill: true });

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';

const LatticeGrid = createLatticeGrid({ React, createGrid });
const rows = [
  { id: 1, department: 'Engineering', costCentre: 'Platform', vendor: 'Supplier A', spend: 420 },
  { id: 2, department: 'Engineering', costCentre: 'Data', vendor: 'Supplier D', spend: 310 },
  { id: 3, department: 'Marketing', costCentre: 'Events', vendor: 'Supplier I', spend: 280 },
  { id: 4, department: 'Operations', costCentre: 'Logistics', vendor: 'Supplier O', spend: 390 },
  { id: 5, department: 'Operations', costCentre: 'Facilities', vendor: 'Supplier M', spend: 340 },
];
const columns = [
  { field: 'department', title: 'Department', filter: { type: 'set' } },
  { field: 'costCentre', title: 'Cost centre' },
  { field: 'vendor', title: 'Vendor' },
  { field: 'spend', title: 'Spend (thousands)', type: 'number' },
];

export function SpendSunburst() {
  const gridRef = useRef(null);
  const chartRef = useRef(null);
  useEffect(() => {
    const chart = createChart({ grid: gridRef.current.grid, container: chartRef.current,
      type: 'sunburst', x: ['department', 'costCentre', 'vendor'], y: 'spend', drill: true });
    return () => chart.destroy();
  }, []);
  return (<>
    <LatticeGrid ref={gridRef} rowKey="id" columns={columns} rows={rows} style={{ height: 220 }} />
    <div ref={chartRef} style={{ height: 380 }} />
  </>);
}

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 'sunburst' here, one of the chart types the charts module draws.
x array of strings On the hierarchical types, the columns that name the levels top down. Here department, cost centre, then vendor.
y string The measure column that sizes each segment. Here the spend.
drill boolean Clicking a group drills into it.
legend boolean or object Shows the legend. values: true appends each entry’s own total after its name.
title string Drawn above the plot.

Reading the rings

The wider the segment, the bigger its share of the ring inside it. In the example, Engineering is the largest department with Operations second, and within Operations, Logistics carries more than Facilities, led by a single vendor. Where a name does not fit on a thin segment it is left off, and the legend and the grid still carry it. Click a cost centre to make it the whole chart, and filter the grid to a few departments to see the rings sized again from the rows that are left.

Next

See the sunburst beside the other hierarchy charts in the hierarchy charts family 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

  • Treemap of Disk Usage by Folder

    Bind a treemap to a grid of folders: tile size is bytes, branches are age bands, so stale space stands out. Filter the rows to redraw the chart.

  • Donut Chart of Budget Used Against Remaining

    Bind a donut chart to a grid of budget lines: show spend by category with the remaining budget in the centre, and filter the rows to redraw both.

  • 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.

All posts RSS