Treemap

Zoomable hierarchical part-of-whole charts as squarified nested rectangles (the plotly-treemap counterpart to Sunburst). Cell areas are proportional to values, branch cells carry plotly-style header strips, and clicking any cell re-tiles its subtree to fill the viewport with an animated transition (clicking a zoomed-in leaf zooms back out). Treemaps consume the same node trees, data builders (sunburst_from_paths, chem_sys_sunburst_data, spacegroup_sunburst_data, …) and value semantics as Sunburst, so switching between the two charts is a one-line change.

Basic Treemap

Pass data as a nested tree (or array of trees). Leaves carry value; cell areas are the sum of their leaves by default. Depth-1 categories auto-cycle the default palette unless you set color, and descendants inherit their ancestor’s color. Click any cell to zoom into it (plotly-style); the breadcrumb pathbar at the top (or Escape / double-clicking empty background) zooms back out.

svelte<script lang="ts">
  import { Treemap, type TreemapNode } from 'matterviz'

  const data: TreemapNode[] = [
    {
      label: `Renewable`,
      children: [
        {
          label: `Solar`,
          children: [
            { label: `PV`, value: 24 },
            { label: `CSP`, value: 4 },
          ],
        },
        { label: `Wind`, value: 30 },
        { label: `Hydro`, value: 17 },
      ],
    },
    {
      label: `Fossil`,
      children: [
        { label: `Coal`, value: 22 },
        { label: `Gas`, value: 28 },
      ],
    },
    { label: `Nuclear`, value: 18 },
  ]
</script>

<Treemap {data} style="height: 420px" />

Chemical system treemap

chem_sys_sunburst_data builds the arity → chemical-system hierarchy from a list of formulas and/or chemical systems (one entry per occurrence) — the counterpart to pymatviz’s chem_sys_treemap, sharing its data builder with the sunburst version. Entries are normalized to alphabetical element order, so Li2O, LiO and O-Li all count toward Li-O. Zoom into an arity group to compare its systems at full size.

svelte<script lang="ts">
  import { chem_sys_sunburst_data, Treemap } from 'matterviz'

  // synthetic dataset: [formula or chemical system, occurrence count]
  const distribution: [string, number][] = [
    [`Si`, 18],
    [`Fe`, 12],
    [`C`, 8], // unary
    [`Fe2O3`, 45],
    [`Li2O`, 30],
    [`GaN`, 25],
    [`Mo-S`, 20],
    [`Bi-Te`, 14], // binary
    [`LiCoO2`, 32],
    [`BaTiO3`, 28],
    [`MgAl2O4`, 22],
    [`Li-Mn-O`, 16],
    [`CsPbBr3`, 12], // ternary
    [`LiFePO4`, 38],
    [`Cu2ZnSnS4`, 9], // quaternary
  ]
  const entries = distribution.flatMap(([sys, count]) => Array(count).fill(sys))
</script>

<Treemap
  data={chem_sys_sunburst_data(entries)}
  label_text="label+value"
  style="height: 500px"
/>

Spacegroup treemap

spacegroup_sunburst_data builds the crystal-system → spacegroup hierarchy from spacegroup numbers or Hermann-Mauguin symbols (one entry per structure). Real spacegroup distributions have long tails — min_fraction groups every spacegroup below a threshold share into one “Other” cell per crystal system. Header strips label each crystal system, so no legend is needed.

svelte<script lang="ts">
  import { spacegroup_sunburst_data, Treemap, type Vec2 } from 'matterviz'

  let min_fraction = $state(0.02)

  // synthetic MP-like spacegroup distribution: [spacegroup, structure count]
  const distribution: Vec2[] = [
    [225, 380], // Fm-3m
    [221, 160], // Pm-3m
    [227, 140], // Fd-3m
    [194, 200], // P6₃/mmc
    [191, 90], // P6/mmm
    [166, 170], // R-3m
    [139, 190], // I4/mmm
    [123, 80], // P4/mmm
    [62, 240], // Pnma
    [63, 110], // Cmcm
    [12, 150], // C2/m
    [14, 130], // P2₁/c
    [2, 95], // P-1
    [1, 25], // P1
    // long tail of rare spacegroups
    [19, 18],
    [33, 15],
    [61, 12],
    [88, 10],
    [122, 9],
    [148, 14],
    [161, 8],
    [176, 11],
    [198, 7],
    [205, 13],
    [215, 6],
    [230, 9],
  ]
  const spacegroups = distribution.flatMap(([spg, count]) => Array(count).fill(spg))
</script>

<label>
  Group spacegroups below
  <select bind:value={min_fraction}>
    <option value={0}>0% (show all)</option>
    <option value={0.01}>1%</option>
    <option value={0.02}>2%</option>
    <option value={0.04}>4%</option>
  </select>
  of all structures into 'Other'
</label>

<Treemap
  data={spacegroup_sunburst_data(spacegroups)}
  {min_fraction}
  label_text="label+percent"
  style="height: 500px"
/>

Metric coloring and deep zoom

Pass color_values to color cells by a numeric metric on a continuous d3 colormap instead of categorical inheritance — e.g. energy above hull per compound (cells returning null keep their categorical color, and a ColorBar shows the scale). Deep trees built from path rows via sunburst_from_paths zoom level by level: max_depth limits how many levels render below the current root, so drilling down progressively reveals structure prototypes and compounds.

current root: all
Ehull (eV/atom)
00.020.040.060.08
svelte<script lang="ts">
  import { Treemap, sunburst_from_paths } from 'matterviz'

  let zoom_root_id = $state<string | number | null>(null)

  // [crystal system, structure prototype, compound, count, energy above hull (eV/atom)]
  const compounds: [string, string, string, number, number][] = [
    [`cubic`, `rock salt`, `NaCl`, 65, 0],
    [`cubic`, `rock salt`, `MgO`, 48, 0],
    [`cubic`, `rock salt`, `FeO`, 35, 0.021],
    [`cubic`, `perovskite`, `SrTiO₃`, 42, 0],
    [`cubic`, `perovskite`, `BaTiO₃`, 35, 0.004],
    [`cubic`, `perovskite`, `MgSiO₃`, 12, 0.062],
    [`cubic`, `spinel`, `MgAl₂O₄`, 38, 0],
    [`cubic`, `spinel`, `LiMn₂O₄`, 21, 0.011],
    [`hexagonal`, `wurtzite`, `ZnO`, 29, 0.034],
    [`hexagonal`, `wurtzite`, `GaN`, 33, 0],
    [`orthorhombic`, `olivine`, `LiFePO₄`, 26, 0.008],
    [`orthorhombic`, `olivine`, `Mg₂SiO₄`, 14, 0.045],
  ]
  const data = sunburst_from_paths(
    compounds.map(([system, prototype, compound, count, e_above_hull]) => ({
      path: [system, prototype, compound],
      value: count,
      metadata: { e_above_hull },
    })),
  )
</script>

<div style="display: flex; gap: 1em; align-items: center">
  <button onclick={() => (zoom_root_id = null)} disabled={zoom_root_id === null}>
    Reset zoom
  </button>
  <span>current root: <code>{zoom_root_id ?? `all`}</code></span>
</div>

<Treemap
  {data}
  bind:zoom_root_id
  max_depth={2}
  color_values={(cell) => (cell.metadata?.e_above_hull as number | undefined) ?? null}
  colorbar={{ title: `E<sub>hull</sub> (eV/atom)` }}
  style="height: 500px"
>
  {#snippet tooltip(info)}
    <strong>{info.label_path.join(``)}</strong>: {info.value} entries
    {#if info.depth > 1}({(info.parent_fraction * 100).toFixed(0)}% of parent){/if}
  {/snippet}
</Treemap>