library(ggplot2)
library(dplyr)
library(readr)Nested Column Bar Chart
Overview
This code creates an example Nested Column Chart of the type developed by Brittany Rosenau.
Setup
Create Sample Data
csv_data <- "Region,Segment,Revenue
West,Consumer,364
West,Corporate,232
West,Home Office,143
East,Consumer,357
East,Corporate,204
East,Home Office,131
Central,Consumer,254
Central,Corporate,158
Central,Home Office,91
South,Consumer,196
South,Corporate,122
South,Home Office,74"
df <- read_csv(csv_data, show_col_types = FALSE)
df# A tibble: 12 × 3
Region Segment Revenue
<chr> <chr> <dbl>
1 West Consumer 364
2 West Corporate 232
3 West Home Office 143
4 East Consumer 357
5 East Corporate 204
6 East Home Office 131
7 Central Consumer 254
8 Central Corporate 158
9 Central Home Office 91
10 South Consumer 196
11 South Corporate 122
12 South Home Office 74
Data Preparation
# Ensure Region is a factor with the desired order
df$Region <- factor(df$Region, levels = c("West", "East", "Central", "South"))
# Ensure Segment is a factor with the desired order for legend/coloring
df$Segment <- factor(df$Segment, levels = c("Consumer", "Corporate", "Home Office"))
# Calculate total revenue per region
region_totals <- df %>%
group_by(Region) %>%
summarise(TotalRevenue = sum(Revenue), .groups = "drop")
region_totals# A tibble: 4 × 2
Region TotalRevenue
<fct> <dbl>
1 West 739
2 East 692
3 Central 503
4 South 392
Define Custom Colors
segment_colors <- c(
"Consumer" = "#2ECC71", # Green
"Corporate" = "#F39C12", # Orange
"Home Office" = "#3498DB" # Blue
)Create the Nested Column Chart
p <- ggplot() +
# Layer 1: Grey background bars representing total revenue per region
geom_col(
data = region_totals,
aes(x = Region, y = TotalRevenue),
fill = "grey85",
alpha = 0.8,
width = 0.8
) +
# Layer 2: Dodged bars for segment revenue
geom_col(
data = df,
aes(x = Region, y = Revenue, fill = Segment),
position = position_dodge(width = 0.8),
width = 0.7
) +
# Layer 3: Labels for individual segment bars
geom_text(
data = df,
aes(x = Region, y = Revenue, label = paste0("$", Revenue, "K"), group = Segment),
position = position_dodge(width = 0.8),
vjust = -0.5,
size = 2.75,
family = "sans"
) +
# Layer 4: Labels for total region revenue
geom_text(
data = region_totals,
aes(x = Region, y = TotalRevenue, label = paste0("$", TotalRevenue, "K")),
vjust = -2.0,
size = 3.5,
family = "sans"
) +
# Layer 5: Labels for Region Names
geom_text(
data = region_totals,
aes(x = Region, y = TotalRevenue, label = as.character(Region)),
vjust = -3.5,
size = 4,
fontface = "bold",
family = "sans"
) +
# Apply custom color scale
scale_fill_manual(values = segment_colors) +
# Set plot title
ggtitle("Total Sales by Region and Segment ($K)") +
# Customize theme
theme_void(base_size = 12, base_family = "sans") +
theme(
plot.title = element_text(
hjust = 0,
face = "bold",
size = 16,
margin = margin(b = 15)
),
legend.position = "top",
legend.justification = "left",
legend.title = element_blank(),
legend.text = element_text(size = 11),
legend.key.size = unit(0.5, "cm"),
legend.margin = margin(b = -10),
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.title = element_blank(),
plot.margin = margin(20, 20, 20, 20)
) +
# Adjust y-axis limits for label space
scale_y_continuous(
limits = c(0, max(region_totals$TotalRevenue) * 1.35),
expand = expansion(mult = c(0, 0))
)
print(p)