UpSet gallery
Show setup script
<script lang="ts">
import { UpSet, computeUpset, summarize } from '../../../lib/index.js';
import { genres, makeData } from '../../demo-data.js';
const data = makeData();
const result = computeUpset(data, genres, { minSize: 25 });
const ratings = summarize(result, 'rating');
const lo = 0;
const hi = 10;
</script>Compact, no labels, no stripes
Show code
<UpSet
{data}
sets={genres}
minSize={8}
cellSize={18}
barHeight={120}
setBarWidth={90}
labelWidth={80}
fontSize={10}
showLabels={false}
stripes={false}
/>Big dots and thick connectors
Show code
<UpSet
{data}
sets={genres}
minSize={25}
cellSize={34}
dotRadius={0.38}
connectorWidth={6}
barPadding={0.15}
barRadius={6}
/>Axis titles
Show code
<UpSet
{data}
sets={genres}
minSize={15}
intersectionLabel="Intersection size"
setLabel="Set size"
/>Annotation row
An annotation snippet renders once per column, between the bars and the matrix, aligned with the
column. Here it shows each column's share of all items.
Show code
<UpSet {data} sets={genres} minSize={25} annotationHeight={22}>
{#snippet annotation({ intersection })}
<small>{Math.round((100 * intersection.size) / data.length)}%</small>
{/snippet}
</UpSet>Box plot per column (annotation)
summarize(result, 'rating') gives quartiles, whiskers and outliers per column; the annotation snippet draws one box plot each on a shared y-axis.
Show code
<UpSet {data} sets={genres} minSize={25} annotationHeight={90}>
{#snippet annotation({ intersection, width, height })}
{@const s = ratings.get(intersection.id)}
{@const y = (v: number) => height - 4 - ((v - lo) / (hi - lo)) * (height - 8)}
{#if s && s.n > 0}
<svg {width} {height} style="display:block" data-boxplot>
<line x1={width / 2} x2={width / 2} y1={y(s.whiskerLow)} y2={y(s.whiskerHigh)} stroke="currentColor" />
<rect
x={width * 0.2}
y={y(s.q3)}
width={width * 0.6}
height={Math.max(1, y(s.q1) - y(s.q3))}
fill="var(--upset-bar, steelblue)"
fill-opacity="0.45"
stroke="currentColor"
/>
<line x1={width * 0.2} x2={width * 0.8} y1={y(s.median)} y2={y(s.median)} stroke="currentColor" stroke-width="2" />
{#each s.outliers as o}
<circle cx={width / 2} cy={y(o)} r="1.5" fill="none" stroke="currentColor" />
{/each}
</svg>
{/if}
{/snippet}
</UpSet>maxWidth: scroll inside the root
40 intersections capped at 600px; the plot scrolls horizontally.
Show code
<UpSet {data} sets={genres} maxWidth={600} limit={40} data-upset="maxwidth" />fit: shrink cells to the container
20 columns in a 500px container; cells shrink toward minCellSize.
Show code
<div style="width: 500px">
<UpSet {data} sets={genres} fit limit={20} data-upset="fit" />
</div>fit="fill": span the container
3 columns in a 700px container. fit="fill" pads with empty slots so the plot spans the container
without growing the rows; showLabels="auto" drops labels that would collide.
Show code
<div style="width: 700px" data-testid="fill-box">
<UpSet {data} sets={genres.slice(0, 4)} fit="fill" showLabels="auto" limit={3} />
</div>Percent axis
normalize="total" labels the axes and bar labels as a share of all items. Without a known total
(precomputed, no total) counts stay.
Show code
<UpSet {data} sets={genres} minSize={20} normalize="total" />Percent axis (unknown total)
Precomputed counts without total keep showing counts.
Show code
<UpSet
sets={['A', 'B']}
intersections={[
{ sets: ['A'], size: 30 },
{ sets: ['B'], size: 20 }
]}
normalize="total"
/>