Search code examples
rggplot2box

ggplot - add box on top of graph


I have the following data and graph:

 fixed <- as.data.frame(rep(0.125,8))
fixed[,2] <- c("1","2","3","4","5","6","7","8")
colnames(fixed) <- c("Percentage", "Test")

enter image description here

Is it possible to add the mean as a separate box on top of the graph? I do not want to share the same y-axis, as this can be confusing with the label.

Here is an example of what I am looking for. It would be great if I can paint the background of the box darker as well, is that possible:

enter image description here

Here is the code for my plot:

p <- ggplot(fixed,aes(x=Test ,y=Percentage,fill=Test))+
  geom_boxplot()+ 
  stat_summary(fun.data = function(y) 
    data.frame(y=0.6, label = paste(round(mean(y),2))), geom="text",size=3) +
  geom_hline(yintercept=0.55, linetype="dashed", color = "black")+
  theme(legend.position="none")+
  scale_y_continuous(limits = c(0, 0.6),breaks=seq(0,0.6,0.1),
                     labels= c("0%","10%","20%","30%","40%","50%","Mean"))+
  theme_bw()+
  labs(title = "Title")+
  xlab("Test")+
  ylab("Percetage")+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        panel.background = element_rect(fill = "grey90", colour = NA),
        plot.background = element_rect(fill = "grey90", colour = NA),
        legend.position="none",plot.title = element_text(size= 12, hjust=0.5),
        plot.margin = unit(c(1,1,1,1), "cm"))+
  scale_fill_manual(values=c("#00441b","#006d2c","#238b45","#41ab5d","#74c476",
                             "#a1d99b","#c7e9c0","#e5f5e0"))
p

Solution

  • fixed <- as.data.frame(rep(0.125, 8))
    fixed[, 2] <- c("1", "2", "3", "4", "5", "6", "7", "8")
    colnames(fixed) <- c("Percentage", "Test")
    

    Let's add some, er, structure to the ggplot2 build. And, we're going to move some data and aes() directly to stat_s and geom_s so we can do the box, er, geom_rect() annotation. Comments are in/near salient lines:

    ggplot() +
      geom_boxplot(
        data = fixed, aes(x = Test, y = Percentage, fill = Test)
      ) +
      geom_rect( # HERE IS UR BOX
        data = data.frame(),
        aes(xmin = -Inf, xmax = Inf, ymin = 0.565, ymax = Inf),
        size = 2.5, fill = "#00000000", color = "black"
      ) +
      stat_summary(
        data = fixed, aes(Test, Percentage),
        fun.data = function(y) {
          data.frame(
            y = 0.6,
            label = paste(round(mean(y), 2))
          )
        },
        geom = "text", size = 3
      ) +
      scale_y_continuous(
        limits = c(0, 0.6), breaks = seq(0, 0.6, 0.1),
        labels = c("0%", "10%", "20%", "30%", "40%", "50%", "Mean")
      ) +
      scale_fill_manual(
        values = c(
          "#00441b", "#006d2c", "#238b45", "#41ab5d", 
          "#74c476", "#a1d99b", "#c7e9c0", "#e5f5e0"
        )
      ) +
      coord_cartesian(clip = "off") + # WE NEED TO TURN CLIPPING OFF
      labs(
        x = "Test", y = "Percentage", title = "Title"
      ) +
      theme_bw() +
      theme(
        legend.position = "none", # YOU HAD THIS IN A SEPARATE theme(). IT PAYS TO HAVE A CODE STYLE
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_rect(fill = "grey90", colour = NA),
        plot.background = element_rect(fill = "grey90", colour = NA),
        plot.title = element_text(size = 12, hjust = 0.5),
        plot.margin = unit(c(1, 1, 1, 1), "cm"),
        axis.text.y = element_text( # MAKE THE "Mean" BIG & BOLD 
          size = c(rep(10, 6), 20), face = c(rep("plain", 6), "bold")
        )
      )
    

    enter image description here