Charts/Distribution charts
Beeswarm
<Beeswarm /> · 10 examples
Every value as a dot, nudged sideways just enough that no two dots overlap.
Overview
Put a hundred dots on a number line and some of them will land on top of each other. You can make them see-through, which turns clumps into smudges. You can add random jitter, which makes the picture different every time you open it and, in a bad mood, slightly dishonest. Or you can do what the beeswarm does: place each dot at its true value along one axis, and use the other axis only to keep the dots from touching.
The result looks like the insects it is named after, hovering where the data is dense. It is a one-dimensional scatter plot with the free dimension put to honest use. The horizontal position is exact. The vertical wobble is just packing, so it carries no information and the spread of a swarm is itself a rough picture of the density.
That is why people like it more than a box plot, which is a drawing of five numbers, and more than a violin, which is a drawing of a smoother. A beeswarm shows each observation, and you can still see the shape.
Props
API
<Beeswarm data={people} value={|p: &Person| p.salary} group={|p: &Person| p.team} />| Prop | Type | Default |
|---|---|---|
data | as above | required |
value | Fn(&T) -> f64 | required |
group | Fn(&T) -> &str: one lane and colour per group | one lane |
point_size | f32, pixels | 5. |
x_axis | AxisConfig | AxisConfig::new() |
legend, tooltip, class | as on <XYChart> |
Every value is a dot at its exact x. Dots that would overlap are pushed up or down within their lane, so the lane's outline is the shape of the distribution.
Run them
10 examples
Press Run to load an example into the live render at the top of the page. Open one to read its code.
How to read it
- 01Horizontal position is the value. Vertical position inside a swarm means nothing. Ignore it as a coordinate.
- 02The width of a swarm at a given value tells you how crowded that value is. Where the swarm is wide, many observations share it.
- 03Count the dots if you need a sample size. Unlike a box plot, nothing is hidden.
- 04Compare groups by where their swarms sit and how they are shaped, not by their height on the page.
- 05Hover a dot for its value. The legend (if you turn it on with
legend) names the groups by colour.
When to use it, and when not to
Reach for it when
- You have a few dozen to a few hundred observations per group, and each one is worth seeing: lab replicates, race times, survey scores.
- You want to compare groups and show the sample size at the same time. A group of six dots looks different from a group of six hundred, which a box plot hides.
- Your audience does not like abstractions. Dots are the most literal chart there is.
- You suspect two clusters (bimodality) that a mean or a box would flatten.
Skip it when
- You have thousands of points per group. The swarm will fill its width and fall back to the center line. Use a ridgeline or a histogram.
- You need the vertical position to mean something. Here it never does, if the second axis is data, you want a scatter plot.
- Values are tied a lot (counts, ratings from 1 to 5). Ties become neat piles, which are fine, but a bar chart of the counts may say it better.
- You need to compare an exact quantile. Dots do not give you a median. Draw a barcode with a marker, or compute the number and print it.
The math behind it
What is fixed and what is free
Each finite value is placed at exactly that position on the value axis. The perpendicular axis is a category axis: group is centered at the integer , and a dot gets a perpendicular offset from the center. Offsets are limited to , so swarms in neighbouring groups never touch.
The chart needs a notion of "close" in value. It takes it from the data: the spread is one ninetieth of the range of all the values, so roughly ninety dots fit side by side before they have to stack.
The packing rule
Within a group the values are processed in ascending order. For each value, the algorithm tries offsets in turn: , then , , , , and so on, where is the step. It takes the first offset at which the new dot is clear of every dot already placed. Two dots are clear when their distance, measured with the horizontal gap scaled by and the vertical gap scaled by , is at least 1.
Only dots whose values are within of the new one can collide, and because the values are sorted the search can stop as soon as it reaches one that is further than that. A dot in an isolated region therefore always lands on the center line.
When the swarm runs out of room
If a dot needs an offset larger than , the algorithm gives up on it and leaves it on the center line. In a very dense swarm you will therefore see a spine of overlapping dots in the middle. That is the chart telling you there are too many points for the available room: shrink point_size, give the chart more height, or switch to a histogram.
Dots are drawn at 5 pixels unless you pass point_size. Groups are numbered in the order of their first appearance, and each group gets its own colour from the theme.
Real life usage
Two hundred runners and a suspicious bulge
Noor organised a small city half-marathon, and the year after a tough course change she wanted to know whether the new hill had broken anyone. The race had 214 finishers, a results spreadsheet and a volunteer who insisted that the hill was fine and that people were just out of shape.
The obvious summary was the mean finishing time by age group. Noor wrote it down and then did not like it. Age groups had different sizes, some had six runners, and a mean of six runners is a rumour.
She wanted to see every runner, grouped by age band, on a single time axis. No bins, because the bins would have to be wide enough to hide the exact thing she was looking for, and no random jitter, because she was going to show it to a committee and the committee asks "why did you move my dots".
She loaded the finishers into a <Beeswarm> from zopra-charts, with finishing time as the value and the age band as the group. One component, a closure for each of the two accessors, and a point size of 4 to fit the 214 dots comfortably.
The 30 to 39 and 40 to 49 swarms looked like what she expected: a hump around two hours, with a few fast outliers on the left. The 50 to 59 swarm did not. It had a second small cluster about eleven minutes after the main one, six dots, all clearly separate. A mean would have averaged right over it.
Six runners from one running club had stopped at the new hill to wait for a friend who was walking it. It was a lovely thing to do and a terrible thing for a time-based awards list. Noor added a note to the standings, moved the water station to the base of the hill, and emailed the club with an apology and a photo of the dots.
The committee asked for the chart again next year. They said it was because it was useful. Noor suspects it was because they could find their own dot.
Pitfalls
- Reading the swarm's vertical spread as a measurement. It is a packing artefact. Two swarms of equal density but different sizes will look different in width.
- Too many dots. When the swarm exceeds its room, dots fall back to the center line and overlap. If you see a dark spine, lower
point_sizeor switch chart. - Different dot sizes across charts. The spread is derived from the value range, so the same data on a different axis range packs differently. Compare dot patterns within one chart, not between two.
- Group order. Groups follow the order of their first row. Sort your data before you pass it in, or the strips come out in an accident.
From around the web