rggplot2bar-chart

How to include percentage in the circular barplots?


I am trying to create circular bar plots where each bar is colored according to its group. I would like to display the contribution of each group as a percentage on the bars, while keeping the group labels separate from the percentage values for better clarity. Could anyone suggest how to achieve this in a clear and visually appealing way? I tried below codes and arrived at circular barplots.

# Set a number of 'empty bar' to add at the end of each group
empty_bar=3
to_add = data.frame( matrix(NA, empty_bar*nlevels(data$group), ncol(data)) )
colnames(to_add) = colnames(data)
to_add$group=rep(levels(data$group), each=empty_bar)
data=rbind(data, to_add)
data=data %>% arrange(group)
data$id=seq(1, nrow(data))
 
# Get the name and the y position of each label
label_data=data
number_of_bar=nrow(label_data)
angle= 90 - 360 * (label_data$id-0.5) /number_of_bar     # I substract 0.5 because the letter must have the angle of the center of the bars. Not extreme right(1) or extreme left (0)
label_data$hjust<-ifelse( angle < -90, 1, 0)
label_data$angle<-ifelse(angle < -90, angle+180, angle)
 
# prepare a data frame for base lines
base_data=data %>% 
  group_by(group) %>% 
  summarize(start=min(id), end=max(id) - empty_bar) %>% 
  rowwise() %>% 
  mutate(title=mean(c(start, end)))
 
# prepare a data frame for grid (scales)
grid_data = base_data
grid_data$end = grid_data$end[ c( nrow(grid_data), 1:nrow(grid_data)-1)] + 1
grid_data$start = grid_data$start - 1
grid_data=grid_data[-1,]

# Exclude all "Mister" from label_data
label_data <- label_data %>% filter(!grepl("^Mister", individual))

# Make the plot
p = ggplot(data, aes(x=as.factor(id), y=value, fill=group)) +       # Note that id is a factor. If x is numeric, there is some space between the first bar
  
  geom_bar(aes(x=as.factor(id), y=value, fill=group), stat="identity", alpha=0.5) +
  
  # Add a val=100/75/50/25 lines. I do it at the beginning to make sure barplots are OVER it.
  geom_segment(data=grid_data, aes(x = end, y = 80, xend = start, yend = 80), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_data, aes(x = end, y = 60, xend = start, yend = 60), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_data, aes(x = end, y = 40, xend = start, yend = 40), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_data, aes(x = end, y = 20, xend = start, yend = 20), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  
  # Add text showing the value of each 100/75/50/25 lines
  annotate("text", x = rep(max(data$id),4), y = c(20, 40, 60, 80), label = c("-4", "-2", "0", "3") , color="black", size=3 , angle=0, fontface="bold", hjust=1) +
  
  geom_bar(aes(x=as.factor(id), y=value, fill=group), stat="identity", alpha=0.5) +
  ylim(-100,120) +
  theme_minimal() +
  theme(
    legend.position = "none",
    axis.text = element_blank(),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    plot.margin = unit(rep(-1,4), "cm") 
  ) +
  coord_polar() + 
  geom_text(data=label_data, aes(x=id, y=value+10, label=individual, hjust=hjust), color="black", fontface="bold",alpha=0.6, size=2.5, angle= label_data$angle, inherit.aes = FALSE ) +
  
  # Add base line information
  geom_segment(data=base_data, aes(x = start, y = -5, xend = end, yend = -5), colour = "black", alpha=0.8, size=0.6 , inherit.aes = FALSE )  +
  geom_text(data=base_data, aes(x = title, y = -18, label=group), hjust=c(0,1,1,1,0,0,1), colour = "black", alpha=0.8, size=4, fontface="bold", inherit.aes = FALSE)
p

It gave me: out1

but my expected result is something as follows:

out2

data for reproducibility:

data = structure(list(individual = c("Mister9", "Mister3", "Mister5", 
"Mister1", "Mister6", "Mister7", "Mister8", "Mister4", "Mister10", 
"Mister11", "Mister2", "Mister20", "Mister26", "Mister30", "Mister14", 
"Mister19", "Mister18", "Mister16", "Mister27", "Mister12", "Mister23", 
"Mister24", "Mister17", "Mister25", "Mister15", "Mister29", "Mister13", 
"Mister21", "Mister22", "Mister28", "Mister31", "Mister32", "Mister47", 
"Mister48", "Mister45", "Mister49", "Mister46", "Mister42", "Mister41", 
"Mister35", "Mister37", "Mister43", "Mister33", "Mister34", "Mister40", 
"Mister39", "Mister44", "Mister38", "Mister36", "Mister179", 
"Mister165", "Mister166", "Mister174", "Mister155", "Mister139", 
"Mister120", "Mister152", "Mister140", "Mister119", "Mister178", 
"Mister173", "Mister177", "Mister160", "Mister172", "Mister168", 
"Mister150", "Mister175", "Mister176", "Mister153", "Mister156", 
"Mister134", "Mister161", "Mister154", "Mister164", "Mister169", 
"Mister181", "Mister157", "Mister167", "Mister65", "Mister183", 
"Mister147", "Mister170", "Mister135", "Mister158", "Mister145", 
"Mister87", "Mister171", "Mister163", "Mister182", "Mister127", 
"Mister61", "Mister128", "Mister129", "Mister118", "Mister144", 
"Mister136", "Mister149", "Mister121", "Mister112", "Mister85", 
"Mister137", "Mister180", "Mister151", "Mister138", "Mister148", 
"Mister146", "Mister91", "Mister126", "Mister117", "Mister96", 
"Mister97", "Mister95", "Mister113", "Mister92", "Mister99", 
"Mister81", "Mister98", "Mister115", "Mister107", "Mister86", 
"Mister125", "Mister110", "Mister84", "Mister89", "Mister133", 
"Mister111", "Mister109", "Mister100", "Mister123", "Mister124", 
"Mister88", "Mister94", "Mister108", "Mister116", "Mister90", 
"Mister53", "Mister114", "Mister162", "Mister102", "Mister101", 
"Mister64", "Mister143", "Mister68", "Mister93", "Mister67", 
"Mister106", "Mister79", "Mister122", "Mister63", "Mister159", 
"Mister78", "Mister103", "Mister62", "Mister51", "Mister60", 
"Mister132", "Mister83", "Mister142", "Mister54", "Mister141", 
"Mister56", "Mister130", "Mister52", "Mister131", "Mister66", 
"Mister59", "Mister50", "Mister69", "Mister82", "Mister104", 
"Mister55", "Mister105", "Mister74", "Mister73", "Mister76", 
"Mister58", "Mister71", "Mister57", "Mister80", "Mister75", "Mister70", 
"Mister72", "Mister77", "Mister184", "Mister185", "Mister191", 
"Mister196", "Mister192", "Mister195", "Mister188", "Mister193", 
"Mister187", "Mister189", "Mister186", "Mister194", "Mister190", 
"Mister201", "Mister204", "Mister206", "Mister200", "Mister207", 
"Mister208", "Mister198", "Mister199", "Mister203", "Mister197", 
"Mister202", "Mister205", "Mister226", "Mister227", "Mister225", 
"Mister229", "Mister219", "Mister218", "Mister220", "Mister228", 
"Mister222", "Mister221", "Mister223", "Mister224", "Mister212", 
"Mister213", "Mister215", "Mister217", "Mister211", "Mister230", 
"Mister216", "Mister210", "Mister214", "Mister209"), group = c("A", 
"A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "B", "B", "B", 
"B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", 
"B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C", "C", "C", 
"C", "C", "C", "C", "C", "C", "C", "C", "C", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", 
"F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "G", 
"G", "G", "G", "G", "G", "G", "G", "G", "G", "G", "G", "G", "G", 
"G", "G", "G", "G", "G", "G", "G", "G"), value = c(11.02911835, 
4.704593761, 6.084237329, 7.210865307, 8.923452205, 10.97381326, 
11.1340509, 14.20915859, 15.43248876, 18.31250393, 32.66343939, 
15.43951498, 10.07896574, 5.842966, 6.742587654, 8.212683412, 
8.884210644, 9.148645253, 9.220297077, 9.27063146, 9.382737778, 
9.82520696, 11.34338043, 11.38390891, 12.98406544, 13.41780906, 
20.71406412, 25.73528387, 25.76179696, 37.58409529, 39.1098185, 
48.89609316, 18.17295966, 13.75527894, 13.66649766, 13.39137151, 
13.05892791, 12.40772362, 12.21544586, 11.75496099, 10.39396666, 
9.969703691, 9.708871989, 9.396143117, 8.674631169, 7.160201387, 
6.869214145, 5.681204595, 5.508248872, 50.30170058, 26.53058006, 
25.19603349, 22.95011296, 22.04357295, 20.80706089, 20.5080487, 
20.40577626, 19.83179136, 19.34357996, 19.01510487, 18.50699957, 
18.34253534, 18.19692252, 16.86829523, 16.5163713, 15.73216674, 
15.66448277, 15.55217427, 13.91257473, 13.77744015, 13.76266752, 
13.62696906, 13.52299989, 13.52052077, 13.49820108, 13.236779, 
13.20177911, 12.76645405, 12.73606233, 12.70818073, 12.6929635, 
12.36679029, 11.97300121, 11.9212063, 11.65588905, 11.61932016, 
11.22252378, 10.78121331, 10.72231904, 10.49386514, 10.22051336, 
10.20964479, 9.898505446, 9.862853549, 9.846386933, 9.404480788, 
9.23736005, 8.92331421, 8.872586528, 8.668959306, 8.438496055, 
8.321278052, 7.964428845, 7.854361277, 7.775950751, 7.101006599, 
7.006092397, 6.671911803, 3.985297226, 4.682410586, 5.234034919, 
5.274506855, 5.346801683, 5.46789583, 5.567505728, 5.57424037, 
5.580555239, 5.652623005, 5.655154315, 5.81032488, 5.823886434, 
5.834908952, 5.954677792, 5.988301493, 6.02962921, 6.080394992, 
6.096620978, 6.148764325, 6.198841563, 6.21040773, 6.265290454, 
6.329712605, 6.546152866, 6.638816564, 6.7108602, 6.773907848, 
6.776519092, 7.109253987, 7.18136414, 7.314088953, 7.414507805, 
7.453068558, 7.63445725, 7.784076636, 7.814523066, 7.970740755, 
8.10098952, 8.402040125, 8.522893414, 8.572263214, 8.977074377, 
9.184918646, 9.200741549, 9.50786689, 9.689064249, 9.723051779, 
9.731553669, 9.752344506, 9.774093203, 10.02276925, 10.42839353, 
10.43899025, 10.90445102, 11.06879188, 11.18958947, 11.237825, 
11.44280574, 11.79221849, 11.97775325, 11.98579794, 12.89577317, 
13.09019857, 13.84253541, 14.34257592, 16.19968262, 16.23191164, 
16.45547868, 17.75826902, 18.6551079, 19.82079501, 20.44794895, 
22.76322419, 28.41580997, 8.725372959, 12.20014461, 15.38935919, 
26.63274737, 28.67366257, 32.22364195, 37.29689097, 41.89528827, 
43.30171877, 53.59388158, 57.8091483, 59.15980241, 76.08966091, 
62.92937225, 50.15206365, 14.90668236, 12.20001927, 10.46149594, 
9.59848128, 8.959906549, 8.447058043, 6.928790267, 0.357389166, 
1.8283806, 13.49008525, 71.64450694, 28.11151216, 15.70417498, 
15.64111399, 12.85747587, 12.58371838, 10.16885842, 8.889503684, 
4.714868371, 5.114968965, 5.809477452, 7.654519326, 8.567689096, 
19.21173213, 23.43383162, 28.10549842, 43.69929885, 44.29908174, 
48.48705434, 60.56687775, 74.14460712, 89.66578052)), class = "data.frame", row.names = c(NA, 
-230L))

Solution

  • I think I would convert your axis to numeric rather than discrete, then just precalculate the position of your bars, incorporating the gap. I also find adding text to polar plots is often easier with the geomtextpath package.

    library(tidyverse)
    
    data %>%
      mutate(individual = seq(nrow(.)),
             xpos = individual + as.numeric(factor(group)) * 10) %>%
      ggplot(aes(xpos, value, fill = group)) +
      geom_hline(yintercept = seq(0, 90, 10), color = "gray90", linewidth = 0.2) +
      geom_col() +
      annotate("line", x = c(11, 311), y = c(0, 0), linetype = 0) +
      geom_segment(data = . %>% 
                     group_by(group) %>%
                     summarize(xend = last(xpos), xpos = first(xpos), value = -10),
                   aes(xend = xend, yend = -10), linewidth = 1, color = "gray50") +
      geomtextpath::geom_textpath(data = . %>% 
                  group_by(group) %>%
                  summarize(xpos = mean(xpos), 
                            label = scales::percent(
                              sum(value)/sum(data$value), .1)) %>%
                          mutate(value = -15),
                aes(label = label)) +
      geomtextpath::geom_textpath(data = . %>% 
                                    group_by(group) %>%
                                    summarize(xpos = mean(xpos)) %>%
                                    mutate(value = -30), color = "gray50",
                                  aes(label = group), size = 6, fontface = 2) +
      scale_y_continuous(limits = c(-90, 90)) +
      scale_fill_manual(values = RColorBrewer::brewer.pal(7, name = "Set1"),
                        guide = "none") +
      coord_polar(start = 0.1) +
      theme_void() 
    

    enter image description here