Search code examples
rggplot2legend

Adding a manual legend without extending the plot


I have been trying to add a manual legend to my graph using the following code. However when I add this code it extends the x-axis and I don't understand why.

df %>%
  group_by(Site) %>%
  ggplot(aes(x = Site, y = Phosphate, fill = cut(Phosphate, breaks = c(0, 0.069, 0.173, 1.003, 12)))) +
  geom_rect(aes(ymin = 0, ymax = 0.069, xmin = 0, xmax = 11, fill = '0-0.069'), alpha=.1) +
  geom_rect(aes(ymin = 0.069, ymax = 0.173, xmin = 0, xmax = 11, fill = '0.069-0.173'), alpha=.1) +
  geom_rect(aes(ymin = 0.173, ymax = 1.003, xmin = 0, xmax = 11, fill = '0.173-1.003'), alpha=.1) +
  geom_rect(aes(ymin = 1.003, ymax = 12, xmin = 0, xmax = 11, fill = '1.003-12'), alpha=.1) +
  geom_boxplot(fill="darkolivegreen3") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 12)) +
  scale_x_discrete(limits = c("cell_1_t", "cell_1_b", "cell_2_t",
                              "cell_2_b", "cell_3_t", "cell_3_b",
                              "river_mun_us ", "river_mun_ds"),
                   labels = c("Cell 1 Top", "Cell 1 Bottom", "Cell 2 Top",
                              "Cell 2 Bottom", "Cell 3 Top", "Cell 3 Bottom",
                              "River Upstream", "River Downstream"),
                   expand = c(0, 0)) + # Adjusting the expand argument)+
  annotate(geom = "text", x = 1:6, y = 1, fontface = "bold", label = "(15)") +
  annotate(geom = "text", x = 7, y = 3, fontface = "bold", label = "(15)") +
  annotate(geom = "text", x = 8, y = 5, fontface = "bold", label = "(15)") +
  ggtitle("River Mun ICW") +
  ylab(expression("Phosphate (mg P L"^-1*")")) +
  xlab(NULL) +
  scale_fill_manual(name = 'Water Quality',
                    breaks = c('0-0.069', '0.069-0.173', '0.173-1.003', '1.003-12'),
                    labels = c('Good', 'Moderate', 'Poor', 'Bad'),
                    values = c('bisque4', 'bisque3', 'bisque2', 'bisque')) +
  theme_bw() +
  theme(plot.title = element_text(hjust = 1, vjust = -10),
        axis.text.x = element_text(angle = 90, vjust = 1, hjust = 1, size = 12))+
  theme(legend.position = c(.9, .75),
      legend.background = element_rect(fill = "transparent"))

enter image description here

enter image description here

I have tried the code above, adding a legend using scale_fill_manual. However this has extended the x-axis, but I can't understand why


Solution

  • When you use a discrete scale in ggplot, it is actually a continuous scale "under the hood", with the factor levels placed at the integer values. In your example, you have 8 boxes plotted along the x axis, so these will be placed at the integer positions 1 through 8.

    The problem is that you are including geom_rect layers with an xmax value of 11. That means that the x axis will be drawn large enough to included the value 11. There will therefore be 3 units' worth of blank space to the right of your discrete axis labels.

    You have not included any data that would allow us to run your code, so let's make a simple reproducible example that is similar to yours. First let us draw the boxplot without the extra geom_rect layers:

    library(ggplot2)
    
    set.seed(1)
    
    df <- data.frame(x = rep(LETTERS[1:8], each = 20), 
                     y = rnorm(160, 
                               rep(seq(8, 2, len = 8), each = 20), 
                               rep(seq(2, 1, len = 8), each = 20)))
    
    p <- ggplot(df, aes(x, y)) +
      geom_boxplot(fill = "#a2cd5a") +
      scale_x_discrete(limits = LETTERS[1:8], labels = 1:8) +
      scale_fill_manual(values = c('bisque3', 'bisque4')) +
      theme_classic() +
      theme(panel.background = element_rect(fill = "#ffe8c8", color = 'black'))
    
    p
    

    enter image description here

    But look what happens when we add the rectangles:

    p +   
      geom_rect(aes(ymin = 0, ymax = 0.1, xmin = 0, xmax = 11, fill = 'Good'), 
                    alpha = 0.5, data = df[1,]) +
      geom_rect(aes(ymin = 0, ymax = 1, xmin = 0, xmax = 11, fill = 'Bad'), 
                alpha = 0.5, data = df[1,])
    

    enter image description here

    If we want the rectangles without them affecting the x axis scale, we can set their minimum value to -Inf and maximum value to Inf

    p +   
      geom_rect(aes(ymin = 0, ymax = 0.1, xmin = -Inf, xmax = Inf, fill = 'Good'), 
                    alpha = 0.5, data = df[1,]) +
      geom_rect(aes(ymin = 0, ymax = 1, xmin = -Inf, xmax = Inf, fill = 'Bad'), 
                alpha = 0.5, data = df[1,]) 
    

    enter image description here

    Now our x axis scale is unaffected by the geom_rect layers and simply fits the box plots.