Violin Plot
A violin plot is the same chart as a box plot — one raw y[] distribution per
series on a categorical axis — but draws a smoothed kernel-density estimate (KDE) of the
distribution instead of (or in addition to) the quartile box. Violin is a thin wrapper around BoxPlot with kind="violin"; everything (orientation, pan/zoom, legend, dual axes, tooltips,
controls) is shared.
Basic Usage
Pass one series per distribution. Bandwidth defaults to Silverman’s rule; override per series or
globally via bandwidth ('silverman', 'scott', or a number).
svelte<script lang="ts">
import { Violin } from 'matterviz'
const make_dist = (seed, n = 200, center = 0, spread = 1) => {
let state = seed
const next = () => (state = (state * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff
return Array.from({ length: n }, () => {
const u1 = Math.max(next(), 1e-9)
return center + spread * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * next())
})
}
const series = [
{ y: make_dist(1, 200, 0, 1), label: `A`, color: `#4c6ef5` },
{ y: make_dist(2, 200, 0.5, 1.6), label: `B`, color: `#fa5252` },
{ y: make_dist(3, 200, -0.4, 0.8), label: `C`, color: `#40c057` },
]
</script>
<Violin {series} x_axis={{ label: `Model` }} y_axis={{ label: `Error` }} style="height: 400px" />Violin + Box Overlay
Set kind="violin+box" (per series or on the component) to draw the quartile box and whiskers
inside the violin, the way plotly.express.violin(box=True) does.
svelte<script lang="ts">
import { Violin } from 'matterviz'
const make_dist = (seed, n = 250, center = 0, spread = 1) => {
let state = seed
const next = () => (state = (state * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff
return Array.from({ length: n }, () => {
const u1 = Math.max(next(), 1e-9)
return center + spread * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * next())
})
}
const series = [
{ y: make_dist(4, 250, 0, 1), label: `A`, color: `#4c6ef5` },
{ y: make_dist(5, 250, 1, 1.4), label: `B`, color: `#fa5252` },
]
</script>
<Violin
{series}
kind="violin+box"
x_axis={{ label: `Group` }}
y_axis={{ label: `Value` }}
style="height: 400px"
/>One-Sided Violins (RMSD style)
A horizontal, one-sided violin+box with the KDE clipped to non-negative values reproduces the
matbench-discovery RMSD figure. side="positive" draws only the upper half; kde_clip={[0, null]} keeps the density physical; show_value_labels with value_label_stat="mean" prints the mean.
svelte<script lang="ts">
import { Violin } from 'matterviz'
// Half-normal-ish positive samples (RMSD is >= 0)
const make_rmsd = (seed, n = 250, scale = 0.05) => {
let state = seed
const next = () => (state = (state * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff
return Array.from({ length: n }, () => {
const u1 = Math.max(next(), 1e-9)
return Math.abs(scale * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * next()))
})
}
const colors = [`#636EFA`, `#EF553B`, `#00CC96`, `#AB63FA`]
const names = [`eqV2`, `MACE`, `CHGNet`, `M3GNet`]
const series = names.map((label, idx) => ({
y: make_rmsd(idx + 1, 250, 0.03 + idx * 0.015),
label,
color: colors[idx],
}))
</script>
<Violin
{series}
kind="violin+box"
side="positive"
orientation="horizontal"
kde_clip={[0, null]}
show_value_labels
value_label_stat="mean"
value_label_format=".3~g"
x_axis={{ label: `RMSD (Å)`, range: [0, null] }}
style="height: 420px"
/>Split Violins
Give two series the same category and opposite side values to compare two distributions in one
slot. Series in a shared slot are identified by the legend rather than colored axis ticks.
svelte<script lang="ts">
import { Violin } from 'matterviz'
const make_dist = (seed, n = 200, center = 0, spread = 1) => {
let state = seed
const next = () => (state = (state * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff
return Array.from({ length: n }, () => {
const u1 = Math.max(next(), 1e-9)
return center + spread * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * next())
})
}
// Each series is labeled by its element; the legend_group header conveys the dataset
// (Predicted = left/blue, DFT = right/red), avoiding repeated identical legend entries.
const series = [
...[`Si`, `Ge`, `C`].flatMap((cat, idx) => [
{
y: make_dist(idx * 2 + 1, 200, idx * 0.5, 1),
category: cat,
side: `negative`,
label: cat,
color: `#4c6ef5`,
legend_group: `Predicted`,
},
{
y: make_dist(idx * 2 + 2, 200, idx * 0.5 + 0.3, 1.1),
category: cat,
side: `positive`,
label: cat,
color: `#fa5252`,
legend_group: `DFT`,
},
]),
]
</script>
<Violin
{series}
show_legend
legend={{ style: `left: auto; right: 4px; top: 50%; transform: translateY(-50%)` }}
padding={{ t: 20, b: 50, l: 50, r: 120 }}
x_axis={{ label: `Element` }}
y_axis={{ label: `Property` }}
style="height: 400px"
/>