import { ViolinPlot } from "semiotic"
ViolinPlot shows the full distribution shape of numeric data per category using kernel density estimation. The symmetric area shape reveals the probability density at different values — wider sections represent more common values. Unlike a box plot which only shows summary statistics, a violin plot shows the entire distribution shape including bimodality and skew.
Quick Start
A violin plot requires just data — provide multiple data points per category and the component estimates the density automatically.
import { ViolinPlot } from "semiotic" const frameProps = { /* --- Data --- */ data: [ { category: "Morning", value: 62 }, { category: "Morning", value: 58 }, { category: "Morning", value: 71 }, // ...multiple data points per category { category: "Afternoon", value: 78 }, { category: "Afternoon", value: 85 }, // ...more data points ], /* --- Process --- */ valueAccessor: "value", categoryAccessor: "category", /* --- Customize --- */ categoryLabel: "Time of Day", valueLabel: "Response Time (ms)" } export default () => { return <ViolinPlot {...frameProps} /> }
Examples
Without IQR Lines
Set showIQR to false to hide the interquartile range lines inside the violin.
import { ViolinPlot } from "semiotic" const frameProps = { /* --- Data --- */ data: responseTimeData, /* --- Process --- */ valueAccessor: "value", categoryAccessor: "category", showIQR: false, /* --- Customize --- */ categoryLabel: "Time of Day", valueLabel: "Response Time (ms)" } export default () => { return <ViolinPlot {...frameProps} /> }
Colored by Category
Use colorBy to give each violin a distinct color.
import { ViolinPlot } from "semiotic" const frameProps = { /* --- Data --- */ data: [ { category: "Region A", value: 120, zone: "Urban" }, { category: "Region A", value: 135, zone: "Urban" }, // ...data with zone field for coloring ], /* --- Process --- */ valueAccessor: "value", categoryAccessor: "category", /* --- Customize --- */ colorBy: "zone", categoryLabel: "Region", valueLabel: "Latency (ms)" } export default () => { return <ViolinPlot {...frameProps} /> }
Horizontal Violin Plot
Set orientation to "horizontal" for horizontal violins.
import { ViolinPlot } from "semiotic" const frameProps = { /* --- Data --- */ data: responseTimeData, /* --- Layout --- */ orientation: "horizontal", /* --- Process --- */ valueAccessor: "value", categoryAccessor: "category", /* --- Customize --- */ categoryLabel: "Time of Day", valueLabel: "Response Time (ms)" } export default () => { return <ViolinPlot {...frameProps} /> }
With Grid Lines
Enable grid lines for easier value reading.
import { ViolinPlot } from "semiotic" const frameProps = { /* --- Data --- */ data: responseTimeData, /* --- Process --- */ valueAccessor: "value", categoryAccessor: "category", showIQR: true, /* --- Customize --- */ showGrid: true, categoryLabel: "Time of Day", valueLabel: "Response Time (ms)" } export default () => { return <ViolinPlot {...frameProps} /> }
Brush + Zoom
Enable brush on an overview to select a value range, then pass it as frameProps.rExtent to a detail chart to zoom in.
Drag to select a value range
Detail view (brush above to zoom)
JSX
const [extent, setExtent] = useState(null) <ViolinPlot data={data} brush onBrush={setExtent} height={160} /> <ViolinPlot data={data} frameProps={extent ? { rExtent: extent.r } : undefined} height={250} />
Props
| Prop | Type | Required | Default | Description |
|---|
data | array | Yes | — | Array of data points. Multiple points per category are used to estimate the density distribution. |
categoryAccessor | string | function | — | "category" | Field name or function to access category values. |
valueAccessor | string | function | — | "value" | Field name or function to access numeric values. |
orientation | "vertical" | "horizontal" | — | "vertical" | Chart orientation. |
bins | number | — | 25 | Number of bins for density estimation. More bins = smoother shape. |
curve | string | — | "catmullRom" | Interpolation curve for the violin shape. |
showIQR | boolean | — | true | Show interquartile range lines inside the violin. |
categoryLabel | string | — | — | Label for the category axis. |
valueLabel | string | — | — | Label for the value axis. |
valueFormat | function | — | — | Format function for value axis tick labels. |
colorBy | string | function | — | — | Field name or function to determine violin color. |
colorScheme | string | array | — | "category10" | Color scheme name or custom colors array. |
categoryPadding | number | — | 20 | Padding between categories in pixels. |
enableHover | boolean | — | true | Enable hover annotations showing distribution details. |
showGrid | boolean | — | false | Show background grid lines. |
showLegend | boolean | — | true (when colorBy set) | Show a legend. Defaults to true when colorBy is specified. |
tooltip | object | function | — | — | Tooltip configuration or render function. |
width | number | — | 600 | Chart width in pixels. |
height | number | — | 400 | Chart height in pixels. |
margin | object | — | { top: 50, bottom: 60, left: 70, right: 40 } | Margin around the chart area. |
title | string | — | — | Chart title displayed at the top. |
brush | boolean | — | — | Enable value-axis brush selection. |
onBrush | function | — | — | Callback with { r: [min, max] } or null when brush clears. |
linkedBrush | string | object | — | — | LinkedCharts brush integration name. |
frameProps | object | — | — | Additional StreamOrdinalFrame props for advanced customization. Escape hatch to the full Frame API. |
Graduating to the Frame
When you need more control — custom density rendering, overlaid data points, annotations — graduate toStreamOrdinalFrame directly.
Chart (simple)
JSX
import { ViolinPlot } from "semiotic" <ViolinPlot data={responseTimeData} categoryAccessor="category" valueAccessor="value" showIQR categoryLabel="Time of Day" valueLabel="Response Time (ms)" />
Frame (full control)
JSX
import { StreamOrdinalFrame } from "semiotic" <StreamOrdinalFrame data={responseTimeData} oAccessor="category" rAccessor="value" summaryType={{ type: "violin", bins: 25, curve: "catmullRom" }} summaryStyle={d => ({ fill: "#6366f1", stroke: "#6366f1", fillOpacity: 0.6 })} oPadding={20} axes={[ { orient: "left", label: "Response Time (ms)" }, { orient: "bottom", label: "Time of Day" } ]} summaryHoverAnnotation={true} size={[600, 400]} margin={{ top: 50, bottom: 60, left: 70, right: 40 }} />
- Histogram — discrete binned frequency distribution
- BoxPlot — summary statistics (median, quartiles, outliers) per category
- SwarmPlot — show every individual data point as non-overlapping circles
- StreamOrdinalFrame — the underlying Frame with full control over every rendering detail