Diverging Bar Chart — ggplot2

A diverging bar chart displays bars extending in opposite directions from a central baseline, typically at zero. This visualization is ideal for comparing positive and negative values, showing responses above and below a neutral point, or contrasting opposing categories. Different colors distinguish positive from negative values, making it easy to identify magnitude and direction at a glance.

Diverging Bar Chart rendered with ggplot2

Renders

R source (ggplot2)

#' anyplot.ai
#' bar-diverging: Diverging Bar Chart
#' Library: ggplot2 3.5.1 | R 4.4.1
#' Quality: 86/100 | Created: 2026-08-18

library(ggplot2)
library(dplyr)
library(ragg)

set.seed(42)

# --- Theme tokens -------------------------------------------------------
THEME <- Sys.getenv("ANYPLOT_THEME", "light")
PAGE_BG <- if (THEME == "light") "#FAF8F1" else "#1A1A17"
INK <- if (THEME == "light") "#1A1A17" else "#F0EFE8"
INK_SOFT <- if (THEME == "light") "#4A4A44" else "#B8B7B0"
IMPRINT_PALETTE <- c(
  "#009E73", "#C475FD", "#4467A3", "#BD8233",
  "#AE3030", "#2ABCCD", "#954477", "#99B314"
)

# --- Data -----------------------------------------------------------------
# Employee engagement survey: net promoter score per department
# (% promoters - % detractors), sentiment polarity -> green/red semantic exception
df <- tibble::tibble(
  department = c(
    "Product Design", "Data Science", "Customer Success", "Engineering",
    "Marketing", "R&D", "Human Resources", "Legal & Compliance",
    "Finance", "Procurement", "Facilities", "IT Support",
    "Sales", "Call Center", "Warehouse Ops", "Field Service"
  ),
  nps = c(
    62, 54, 47, 41, 33, 28, 19, 12,
    -6, -14, -19, -22, -27, -34, -41, -49
  )
) %>%
  mutate(
    sentiment = ifelse(nps >= 0, "Promoters lead", "Detractors lead"),
    department = factor(department, levels = department[order(nps)])
  )

# Direct-label only the two extremes (best/worst department) so the funnel's
# endpoints carry their exact score without cluttering the 14 bars in between.
top_row <- df[which.max(df$nps), ]
bottom_row <- df[which.min(df$nps), ]

# --- Plot -------------------------------------------------------------------
p <- ggplot(df, aes(x = nps, y = department, fill = sentiment)) +
  geom_col(width = 0.7) +
  geom_vline(xintercept = 0, color = INK_SOFT, linewidth = 0.5) +
  annotate(
    "text",
    x = top_row$nps, y = top_row$department,
    label = sprintf("%+d", top_row$nps), hjust = -0.25,
    size = 3, color = INK, fontface = "bold"
  ) +
  annotate(
    "text",
    x = bottom_row$nps, y = bottom_row$department,
    label = sprintf("%+d", bottom_row$nps), hjust = 1.25,
    size = 3, color = INK, fontface = "bold"
  ) +
  scale_fill_manual(
    values = c(
      "Promoters lead" = IMPRINT_PALETTE[1],
      "Detractors lead" = IMPRINT_PALETTE[5]
    ),
    name = NULL
  ) +
  scale_x_continuous(breaks = seq(-60, 60, 20), expand = expansion(mult = 0.08)) +
  labs(
    title = "bar-diverging · r · ggplot2 · anyplot.ai",
    x = "Net Promoter Score",
    y = NULL
  ) +
  theme_minimal(base_size = 8) +
  theme(
    plot.background = element_rect(fill = PAGE_BG, color = PAGE_BG),
    panel.background = element_rect(fill = PAGE_BG, color = NA),
    panel.border = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_line(color = INK, linewidth = 0.25),
    axis.title = element_text(color = INK, size = 10),
    axis.text = element_text(color = INK_SOFT, size = 8),
    axis.ticks = element_blank(),
    plot.title = element_text(color = INK, size = 12, face = "bold"),
    plot.title.position = "plot",
    plot.margin = margin(t = 10, r = 18, b = 8, l = 10),
    legend.position = "top",
    legend.justification = "left",
    legend.margin = margin(t = 2, b = 4),
    legend.spacing.x = unit(6, "pt"),
    legend.text = element_text(color = INK_SOFT, size = 8),
    legend.key.size = unit(10, "pt")
  )

# --- Save -------------------------------------------------------------------
ggsave(
  filename = sprintf("plot-%s.png", THEME),
  plot = p,
  device = ragg::agg_png,
  width = 8,
  height = 4.5,
  units = "in",
  dpi = 400
)

Part of Diverging Bar Chart on anyplot.ai.

Other implementations