Search code examples
rggplot2geom-baryaxis

Breaking y-axis in ggplot2 with geom_bar


I'm having a hard time dealing with this plot. The height of values in ANI>96 making it hard to read the red and blue percentage text. I failed to break the y-axis by looking at answers from other posts in StackOverflow.

Any suggestions?

Thanks.

library(data.table)
library(ggplot2)

dt <- data.table("ANI"= sort(c(seq(79,99),seq(79,99))), "n_pairs" = c(5, 55, 13, 4366, 6692, 59568, 382873, 397996, 1104955, 282915,
                 759579, 261170, 312989, 48423, 120574, 187685, 353819, 79468, 218039, 66314, 41826, 57668, 112960, 81652, 28613,
                 64656, 21939, 113656, 170578, 238967, 610234, 231853, 1412303, 5567, 4607268, 5, 14631942, 0, 17054678, 0, 3503846, 0),
                 "same/diff" = rep(c("yes","no"), 21))

for (i in 1:nrow(dt)) {
  if (i%%2==0) {
    next
  }
  total <- dt$n_pairs[i] + dt$n_pairs[i+1]
  dt$total[i] <- total
  dt$percent[i] <- paste0(round(dt$n_pairs[i]/total *100,2), "%")
  dt$total[i+1] <- total
  dt$percent[i+1] <- paste0(round(dt$n_pairs[i+1]/total *100,2), "%")
}

ggplot(data=dt, aes(x=ANI, y=n_pairs, fill=`same/diff`)) +
  geom_text(aes(label=percent), position=position_dodge(width=0.9), hjust=0.75, vjust=-0.25) +
  geom_bar(stat="identity") + scale_x_continuous(breaks = dt$ANI) +
  labs(x ="ANI", y = "Number of pairs", fill = "Share one common species taxonomy?") + 
  theme_classic() + theme(legend.position="bottom")

Solution

  • Here is the list of major changes I made:

    • I reduced the y axis by zooming into the chart with coord_cartesian (which is called by coord_flip).

    • coord_flip shouuld also improve the readability of the chart by switching x and y. I don't know if the switch is a desirable output for you.

    • Also now position_dodge, works as expected: two bars next to each other with the labels on top (on the left in this case).

    • I set geom_bar before geom_text so that the text is always in front of the bars in the chart.

    • I set scale_y_continuous to change the labels of the y axis (in the chart the x axis because of the switch) to improve the readability of the zeros.

    ggplot(data=dt, aes(x = ANI, y = n_pairs, fill = `same/diff`)) +
        geom_bar(stat = "identity", position = position_dodge2(width = 1), width = 0.8) + 
        geom_text(aes(label = percent), position = position_dodge2(width = 1), hjust = 0, size = 3) +
        scale_x_continuous(breaks = dt$ANI) +
        scale_y_continuous(labels = scales::comma) +
        labs(x ="ANI", y = "Number of pairs", fill = "Share one common species taxonomy?") + 
        theme_classic() + 
        theme(legend.position = "bottom") +
        coord_flip(ylim = c(0, 2e6))
    

    enter image description here


    EDIT

    Like this columns and labels are stacked but labels never overlap.

    ggplot(data=dt, aes(x = ANI, y = n_pairs, fill = `same/diff`)) +
        geom_bar(stat = "identity", width = 0.8) + 
        geom_text(aes(label = percent,
                                    hjust = ifelse(`same/diff` == "yes", 1, 0)), 
                            position = "stack", size = 3) +
        scale_x_continuous(breaks = dt$ANI) +
        scale_y_continuous(labels = scales::comma) +
        labs(x ="ANI", y = "Number of pairs", fill = "Share one common species taxonomy?") + 
        theme_classic() + 
        theme(legend.position = "bottom") +
        coord_flip(ylim = c(0, 2e6))
    

    enter image description here

    Alternatively, you can avoid labels overlapping with check_overlap = TRUE, but sometimes one of the labels will not be shown.

    ggplot(data=dt, aes(x = ANI, y = n_pairs, fill = `same/diff`)) +
        geom_bar(stat = "identity", width = 0.8) + 
        geom_text(aes(label = percent), hjust = 1, position = "stack", size = 3, check_overlap = TRUE) +
        scale_x_continuous(breaks = dt$ANI) +
        scale_y_continuous(labels = scales::comma) +
        labs(x ="ANI", y = "Number of pairs", fill = "Share one common species taxonomy?") + 
        theme_classic() + 
        theme(legend.position = "bottom") +
        coord_flip(ylim = c(0, 2e6))
    

    enter image description here