Erik Bodg
Erik Bodg

Reputation: 282

Create a plot with a table under the plot

I have an insert dataset

df <- structure(list(Cluster = c("Cluster1", "Cluster1", "Cluster1", 
"Cluster1", "Cluster1", "Cluster1", "Cluster2", "Cluster2", "Cluster2", 
"Cluster2", "Cluster2", "Cluster2", "Cluster3", "Cluster3", "Cluster3", 
"Cluster3", "Cluster3", "Cluster3", "Cluster4", "Cluster4", "Cluster4", 
"Cluster4", "Cluster4", "Cluster4", "Cluster5", "Cluster5", "Cluster5", 
"Cluster5", "Cluster5", "Cluster5", "Cluster6", "Cluster6", "Cluster6", 
"Cluster6", "Cluster6", "Cluster6", "Cluster7", "Cluster7", "Cluster7", 
"Cluster7", "Cluster7", "Cluster7", "Cluster8", "Cluster8", "Cluster8", 
"Cluster8", "Cluster8", "Cluster8"), Level = c("Associate", "Director", 
"Entry level", "Executive", "Intership", "Mid-Senior Level", 
"Associate", "Director", "Entry level", "Executive", "Intership", 
"Mid-Senior Level", "Associate", "Director", "Entry level", "Executive", 
"Intership", "Mid-Senior Level", "Associate", "Director", "Entry level", 
"Executive", "Intership", "Mid-Senior Level", "Associate", "Director", 
"Entry level", "Executive", "Intership", "Mid-Senior Level", 
"Associate", "Director", "Entry level", "Executive", "Intership", 
"Mid-Senior Level", "Associate", "Director", "Entry level", "Executive", 
"Intership", "Mid-Senior Level", "Associate", "Director", "Entry level", 
"Executive", "Intership", "Mid-Senior Level"), value = c("30%", 
"7%", "18%", "5%", "3%", "37%", "30%", "9%", "21%", "2%", "5%", 
"32%", "31%", "5%", "32%", "1%", "4%", "26%", "36%", "3%", "29%", 
"1%", "2%", "29%", "33%", "4%", "36%", "0%", "6%", "22%", "32%", 
"5%", "29%", "2%", "4%", "29%", "25%", "7%", "23%", "2%", "2%", 
"41%", "24%", "2%", "18%", "2%", "3%", "50%")), class = "data.frame", row.names = c(NA, 
-48L))

Is it possible to use it to create plot like this?

enter image description here

A plot with a table down from every bar

Upvotes: 4

Views: 1284

Answers (1)

Sinh Nguyen
Sinh Nguyen

Reputation: 4487

Reference to @teunbrand recommendation I work on it a bit with some little hack to achieve something similar but not perfectly match what OP share.

library(dplyr) # for maninpulate data
library(ggplot2) # for ploting
library(magrittr) # for `%<>%` syntax
library(scales) # for labeling
library(tidyr) # for manipulate data
library(patchwork) # for align plot/tables

# generate a table to be displayed at bottom of graph  with Cluster as column
table_display <- df %>% pivot_wider(names_from = Cluster, values_from = value)
# Conver value into actual numeric values in percentages point
df %<>% mutate(value = as.numeric(gsub("%", "", value)) / 100)
# Create an empty ticks to align the table later.
empty_tick <- df %>% filter(Cluster == "Cluster1") %>%
  mutate(value = 0, Cluster = "")
# Generate the plot with the empty tick
p1 <- ggplot() +
  geom_bar(data = bind_rows(empty_tick, df), aes(x = Cluster, y = value,
           group = Level, fill = Level),
           stat = "identity", position = "dodge") + 
  # here the empty tick is the first tick which would align with Level
  # column of the table at bottom
  scale_x_discrete(breaks = c("", unique(df$Cluster))) +
  # label Y-Axis
  scale_y_continuous(labels = percent, expand = c(0, 0)) +
  # remove X/Y labels
  xlab(NULL) + ylab(NULL) +
  # Using a default whi
  theme_bw()
# Extract legend for the main plot
legend <- get_legend(p1)
p1 <- p1 + theme(legend.position = "none")

# Generate table grob with no header rows/cols
p2 <- gridExtra::tableGrob(table_display, rows = NULL, cols = NULL)
# Set widths/heights to 'fill whatever space I have'
p2$widths <- unit(rep(1, ncol(p2)), "null")
p2$heights <- unit(rep(1, nrow(p2)), "null")

# Format table as plot
p3 <- ggplot() +
  annotation_custom(p2)

# Patchwork magic
p1 + legend + p3 + plot_layout(ncol = 2, widths = c(4, 1))

Here is the output plot

Plot result

Upvotes: 4

Related Questions