Visualization VocabularyDataviz vocabulary
Diagram: tornado chart
Drawing: Adrien Taudière (CC0)
ggplot2 example: tornado chart
Adrien Taudière (CC0); R code ↓; data: palmerpenguins (Horst, Hill & Gorman 2020) (CC0)

Classification

  • chart
  • bars › diverging bars
  • deviation
  • the name evokes: nature

Channels

Scientific literature

OpenAlex: 113 papers since 2001, 36% in the last five years; mostly in Energy, Economics, Econometrics and Finance, Engineering; Wikidata

Seminal references

R packages

R code of the ggplot2 example

library(ggplot2)
library(dplyr)
library(palmerpenguins)

## Linear model of body mass. Starting from a reference penguin (a female
## Adelie with median measures), each predictor varies alone: from the 10th
## to the 90th percentile for a measure, across its values for species and sex.
birds <- filter(penguins, !is.na(sex))
model <- lm(body_mass_g ~ bill_length_mm + bill_depth_mm + flipper_length_mm + species + sex, data = birds)
reference <- data.frame(bill_length_mm = median(birds$bill_length_mm),
                        bill_depth_mm = median(birds$bill_depth_mm),
                        flipper_length_mm = median(birds$flipper_length_mm),
                        species = factor("Adelie", levels = levels(birds$species)),
                        sex = factor("female", levels = levels(birds$sex)))
change <- function(variable, values) {
  others <- reference[rep(1, length(values)), ]
  others[[variable]] <- values
  range(predict(model, others) - predict(model, reference))
}
swings <- bind_rows(
  tibble(predictor = "Bill length", range = list(change("bill_length_mm", quantile(birds$bill_length_mm, c(0.1, 0.9))))),
  tibble(predictor = "Bill depth", range = list(change("bill_depth_mm", quantile(birds$bill_depth_mm, c(0.1, 0.9))))),
  tibble(predictor = "Flipper length", range = list(change("flipper_length_mm", quantile(birds$flipper_length_mm, c(0.1, 0.9))))),
  tibble(predictor = "Species", range = list(change("species", levels(birds$species)))),
  tibble(predictor = "Sex", range = list(change("sex", levels(birds$sex))))
) |>
  mutate(low = sapply(range, min), high = sapply(range, max),
         predictor = reorder(predictor, high - low))

p <- ggplot(swings, aes(y = predictor)) +
  geom_rect(aes(xmin = low, xmax = 0, ymin = as.integer(predictor) - 0.35, ymax = as.integer(predictor) + 0.35),
            fill = "#2e7891") +
  geom_rect(aes(xmin = 0, xmax = high, ymin = as.integer(predictor) - 0.35, ymax = as.integer(predictor) + 0.35),
            fill = "#9b2367") +
  geom_vline(xintercept = 0, colour = "#535353", linewidth = 0.4) +
  annotate("text", x = c(-150, 600), y = 5.65, label = c("Lighter", "Heavier"),
           colour = c("#2e7891", "#9b2367"), size = 3) +
  scale_x_continuous(labels = lex_number) +
  scale_y_discrete(expand = expansion(add = c(0.6, 0.9))) +
  labs(title = "Species moves the predicted mass most, flipper length next",
       subtitle = "Change from a reference penguin as each predictor varies alone",
       x = "Change in predicted body mass (g)", y = NULL,
       caption = "Data: palmerpenguins (Horst, Hill & Gorman 2020)") +
  theme_lex_viz(grid = "x", arrows = "")

Sources (1)

Cite this entry

Stable addresshttps://dataviz.adrientaudiere.com/en-only/term/tornado-chart.html

ReferenceTaudière Adrien. 2026. tornado chart. In Visualization Vocabulary, version 1.0.0. IdEst. https://dataviz.adrientaudiere.com/en-only/term/tornado-chart.html

BibTeX
@incollection{taudiere2026-tornado-chart,
  author    = {Taudière, Adrien},
  title     = {tornado chart},
  booktitle = {Visualization Vocabulary},
  year      = {2026},
  version   = {1.0.0},
  publisher = {IdEst},
  url       = {https://dataviz.adrientaudiere.com/en-only/term/tornado-chart.html},
  note      = {CC0 1.0}
}

Page created on 2026-10-13. Text last changed on 2026-11-11. Images last changed on 2026-11-02. Numbers computed on 2026-10-22.