Search code examples
rggplot2overlap

Overlapping text on top of geom_bar in ggplot2


I have made a barplot similar to the one below using ggplot2. Example Plot

I cannot get the percentages on top of the bars to be centered and not overlapping of other bars and numbers. Sample code is below.

library(tidyverse)

cat1=c("cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1",
       "cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2",
       "cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3",
       "cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4")
cat2=c("c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12",
       "c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12",
       "c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12",
       "c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12")
count1=round(rnorm(48,10))
fakeperc=rnorm(48,9)
df1=cbind(count1,fakeperc)
df2=cbind(cat1,cat2)
finaldf=as.data.frame(cbind(df1,df2))
finaldf$cat1=as.factor(finaldf$cat1)
finaldf$fakeperc=as.numeric(finaldf$fakeperc)

#finaldf$cat1=factor(finaldf$cat1,levels = c("cat1","cat2","cat3","cat4"))
finaldf$cat2 = factor(finaldf$cat2,
                levels = c("c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12"))

a=ggplot(data=finaldf,aes(x=cat1, y=count1,
                              fill=cat2,group=cat2)) +
  geom_bar(stat='identity',color='black',width=.65,position=position_dodge(width=.9))+
  scale_y_discrete(limits=0:50,breaks=c(0,10,20,30,40,50))+
  scale_fill_brewer(palette="Set3") +
  theme_classic() +
  geom_text(data = finaldf,
            aes(x=cat1,y=count1,group=cat2,
            label=format(paste(round(fakeperc),"%",sep = ""))),inherit.aes = F,
            color='black',position=position_dodge(.9),vjust=-.5,size=3)
a

When trying to add either nudge_y or nudge_x to the geom_text call, nothing happens. I suspect this is because there is already a position_dodge call. I am open any and all solutions to make these percentages non-overlapping and legible.


Solution

  • What do you think of this?

    # I think you meant count1 to be numeric
    finaldf$count1 <- as.numeric(finaldf$count1)
    
    ggplot(data = finaldf,
           aes(x     = cat1, 
               y     = count1,
               fill  = cat2,
               group = cat2)) +
     geom_col(color = 'black',
              width = 0.65,
              position = position_dodge(width = 0.9)) +
     geom_text(data = finaldf,
               aes(x     = cat1,
                   y     = count1,
                   group = cat2,
                   label = scales::percent(fakeperc/100, accuracy = 0.01)),
               inherit.aes = FALSE,
               color = 'black',
               position = position_dodge(0.9),
               hjust = -0.1,
               size = 3) +
     scale_y_continuous(limits = c(0,50), breaks = c(0,10,20,30,40,50)) +
     scale_fill_brewer(palette = "Set3") +
     theme_classic() +
     coord_flip()
    

    enter image description here

    • I cleaned up a bit the code (according to my taste)
    • I changed scale_y_numeric to scale_y_continuous (since count1 should be numeric)
    • I used coord_flip() to make it more readable
    • I used scales::percent to write percentage numbers

    (don't know why you set up limits from 0 to 50 but I left them as I suppposed they were intended)


    If you don't want to use coor_flip:

    finaldf$count1 <- as.numeric(finaldf$count1)
    
    ggplot(data = finaldf,
           aes(x     = cat1, 
               y     = count1,
               fill  = cat2,
               group = cat2)) +
     geom_col(color = 'black',
              width = 0.65,
              position = position_dodge(width = 0.9)) +
     geom_text(data = finaldf,
               aes(x     = cat1,
                   y     = count1,
                   group = cat2,
                   label = scales::percent(fakeperc/100, accuracy = 0.01)),
               inherit.aes = FALSE,
               color = 'black',
               position = position_dodge(0.9),
               hjust = -0.1,
               angle = 90,
               size = 3) +
     scale_y_continuous(limits = c(0,50), breaks = c(0,10,20,30,40,50)) +
     scale_fill_brewer(palette = "Set3") +
     theme_classic()
    

    enter image description here