Charts/Category charts
Forest
<Forest /> · 10 examples
One estimate and its confidence interval per row, against a line of no effect.
Overview
A single study is an anecdote with a p-value. Ten studies of the same question are a conversation, and the interesting part is whether the ten are saying roughly the same thing. A forest plot lines them up so you can hear that: each study gets a row, a square for its best guess, and a horizontal whisker for how far wrong that guess could be.
The chart grew up in medicine, where it is the standard picture of a meta-analysis. If you have seen the logo of the Cochrane) collaboration, you have looked at one: it is a forest plot of seven trials on steroids given to women at risk of premature birth, and the diamond that used to summarise them is a small monument to a treatment that was underused for years because nobody had put the trials in a single row. The name is usually explained as a joke about the trees: so many lines, and somewhere in there, the wood.
It earns the page anywhere you have estimates with uncertainty and a meaningful reference value. Drug trials, A/B tests run in twelve countries, benchmark scores across seeds. The vertical line in the middle is the reference, and the question the chart asks every row is: do you cross it, or not?
Props
API
<Forest data={trials} name={|s: &Study| s.name} estimate={|s: &Study| s.effect}
low={|s: &Study| s.low} high={|s: &Study| s.high} weight={|s: &Study| s.patients} null={1.} />| Prop | Type | Default |
|---|---|---|
estimate | Fn(&T) -> f64: the point estimate | required |
low, high | Fn(&T) -> f64: the confidence interval | required |
weight | Fn(&T) -> f64: sizes each square, such as the study size | equal squares |
null | f64: the dashed line of no effect | 0. |
color | colour | the first series colour |
value_axis | AxisConfig; pass .scale(ScaleKind::Log { base: 10. }) for ratios | AxisConfig::new() |
tooltip, class | as on <XYChart> |
One row per study, in your order from the top. Each row is a line from low to high with a square at estimate. Built on <XYChart>, so the squares can be hovered.
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
- 01Find the dashed line first. It is the reference: no effect, no difference, a ratio of 1.
- 02For each row, ask whether the whisker crosses that line. If it does, the study alone cannot rule out no effect.
- 03Then compare rows. Whiskers that overlap each other tell you the studies agree. One that sits far from the rest is the one to ask questions about.
- 04Look at the squares' sizes. A big square with a tight whisker carries the result. A small square with a very long whisker adds a voice and little else.
- 05Hover a row for its name, the estimate and, if you passed one, its weight.
When to use it, and when not to
Reach for it when
- You have several estimates of the same quantity with their uncertainty: trials, sites, segments, model runs.
- The reference value matters: zero difference, a ratio of one, a target. The dashed line is the whole reason to draw this chart.
- You want readers to see consistency or its absence at a glance, not to read a table of 30 confidence intervals.
- You are comparing subgroups in one study. Forest plots are the usual picture for a subgroup analysis, where the sad thing to look for is a subgroup nobody planned to look at.
Skip it when
- You have raw data, not estimates. Show the distribution with a beeswarm or a histogram and let the reader judge the spread.
- Your intervals do not mean the same thing across rows. A 95% interval beside a standard error beside a range is not a forest, it is a trap.
- You want to show a pooled result. This chart draws the rows only. The pooled estimate has to come from your analysis, and you can add it as a final row.
- You have hundreds of rows. Past 40 or so the labels have to go, and without labels it is just a lot of horizontal lines.
The math behind it
What each row is
For study of you provide a point estimate , a lower bound and an upper bound (in the same units; the chart does not compute the interval, so it can be a 95% interval, a standard-error band or anything else you choose), and optionally a weight . A reference value draws the line of no effect, null, which defaults to .
Rows are drawn in the order you pass them, first row at the top. A row's vertical position is , counting from zero, so the list reads the same way a table does.
The weight and the square
When you pass a weight, the marker for each estimate is a square whose size is scaled between 6 and 20 pixels according to . Without a weight every square is 9 pixels. In a meta-analysis the natural weight is the inverse variance of each study, so precise studies draw big squares and noisy ones draw small ones.
The weight is not used for any computation: the chart does not pool the studies and draws no summary diamond. It draws what you give it.
Ratios and the log axis
Odds ratios, risk ratios and hazard ratios multiply: a ratio of and a ratio of are the same size of effect in opposite directions. On a linear axis they are not the same distance from the reference of , which makes harmful effects look more dramatic than protective ones. Set null={1.} and a log value_axis, and equal ratios are equal distances.
On the log scale the interval for a ratio is symmetric around its estimate, which is why papers report ratios with confidence intervals that look lopsided in the table and balanced in the plot.
Real life usage
Seven hospitals, one dashed line
Priya was a clinical informatics lead at a regional health network that ran a sepsis alert across seven hospitals. Each hospital had measured the same outcome after the alert went live: the odds of dying within thirty days. Each hospital had a report, a slide deck and a very strong opinion.
The reports were honest, and no two looked alike. One gave an odds ratio of 0.62 with a confidence interval she had to find in a footnote. One said "significant improvement". One said "no significant change" with the same interval width and a point estimate that was higher. The chief medical officer asked her for a one-page answer, which in that building meant three paragraphs and an apology.
What she needed was not a verdict but a comparison on a shared axis: seven estimates, seven intervals, one reference. And she needed to keep it honest. Odds ratios live on a log scale, so a plain axis would make the two hospitals that did worse look much worse than the ones that did better looked good.
She put the rows into a <Forest> from zopra-charts with the odds ratio as the estimate, the 95% bounds as low and high, the number of patients as the weight, null set to 1 and a log value axis. Six lines of Rust, and one comment from the colleague who reviewed it: "that's it?"
It was it. Five hospitals sat to the left of the dashed line, two of them with tiny whiskers because they were the big ones. Two sat across it, with intervals so long that their squares were barely visible. The picture said what none of the reports could: the alert helps, the effect is consistent, and two sites simply do not have enough patients to say much.
The chief medical officer approved the rollout to the remaining three hospitals the same afternoon. In the next quarter the network's estimate across all ten sites came in at an odds ratio of 0.71, close to what the first seven suggested. Priya got the credit for the number. The plot got the credit for the speed.
Her only complaint was that the two small hospitals now called to ask why their squares were so small. She told them it was because the chart was being polite.
Pitfalls
- Linear axis for ratios. If the estimates are odds, risk or hazard ratios, pass
null={1.}and a logvalue_axis. Otherwise halving and doubling are different distances from the line. - Crossing the line is not proof of anything. A whisker that touches the reference means that study did not detect an effect. It does not mean there is none.
- Weights that do not match the intervals. Square sizes should reflect precision. If you size by something unrelated (sample size when the variance is dominated by another factor), the plot suggests more confidence than you have.
- Sorting to taste. Rows are drawn in input order. Sorting by estimate makes the picture cleaner and the data look more consistent than the study order did. Say so if you do it.
From around the web