Sunburst Chart of Spend by Department
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.