geoscience123
geoscience123

Reputation: 230

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.

Upvotes: 2

Views: 2060

Answers (2)

Duck
Duck

Reputation: 39595

Is this what you are looking for:

library(ggplot2)
#Code
ggplot(data=finaldf,aes(x=cat2, y=count1,
                          fill=cat2,group=cat2)) +
  geom_bar(stat='identity',color='black',
           position=position_dodge(width=1))+
  scale_fill_brewer(palette="Set3") +
  theme_bw() +
  geom_text(aes(x=cat2,y=count1,group=cat2,
                label=format(paste(round(fakeperc),"%",sep = ""))),inherit.aes = F,
            color='black',position=position_dodge(1),
            size=3,vjust=-0.5)+
  facet_wrap(.~cat1,scales = 'free_x',nrow = 1,strip.position = 'bottom')+
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        legend.position = 'top',
        strip.background = element_blank(),
        panel.spacing = unit(2, "lines"),
        panel.grid = element_blank())+
  guides(fill = guide_legend(nrow = 1))

Output:

enter image description here

Upvotes: 1

Edo
Edo

Reputation: 7818

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

Upvotes: 3

Related Questions