Search code examples
rggplot2bar-chartlegend

Order ggplot legend differently than elements in plot


Is there a way to order a legend in ggplot differently than the elements on the plot. for example I want the legend to be ordered Calanoida, Cladocera, Rotifera, and Cyclopoida but I want the ggplot to look the exact same with Calanoida on the bottom of the stacked bars and Cyclopoida on the top.

Thank you in advance. Also if you know how to add spaces in between bars on a stacked group barplot that would be helpful as well. I usually use

geom_bar(stat="identity", position = position_dodge(width = 6)) but the position argument is being used as geom_bar(position = "stack") when making a stacked bar plot...

enter image description here

Zoop_df_Ab = structure(list(Type = c("Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", "Zooplankton", 
"Zooplankton", "Zooplankton"), Analysis = c("Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance", "Abundance", "Abundance", 
"Abundance", "Abundance", "Abundance"), Lake = structure(c(1L, 
1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 5L, 5L, 5L, 1L, 1L, 
1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 5L, 5L, 5L, 1L, 1L, 1L, 
2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 5L, 5L, 5L, 1L, 1L, 1L, 2L, 
2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 5L, 5L, 5L), levels = c("Lake N11", 
"Area 8", "East Lake", "Lake 3", "Lake D2/D3"), class = "factor"), 
    Season = structure(c(1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 
    1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 
    1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 
    1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 
    1L, 2L, 3L, 1L, 2L, 3L), levels = c("July", "August", "September"
    ), class = "factor"), Class = structure(c(1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
    2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 
    3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 
    4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L), levels = c("Cyclopoida", 
    "Rotifera", "Cladocera", "Calonoida"), class = "factor"), 
    Value = c(31.98162171, 36.39715076, 10.4136626, 12.06768565, 
    38.64860417, 3.742104317, 19.28343448, 51.39177662, 42.69303059, 
    10.89828073, 37.6923044, 30.19541624, 72.52209628, 76.49344251, 
    49.13209458, 34.6058036, 34.72740008, 86.59848507, 54.32721503, 
    56.46492886, 95.39481296, 50.91696935, 34.22043376, 44.75895084, 
    71.24798268, 47.33509161, 64.89139552, 11.5376884, 8.058506181, 
    42.18223086, 0.214567442, 0.858128087, 0.859215877, 1.016869217, 
    0.255412534, 0.254841312, 2.904754731, 1.33692291, 3.971509741, 
    5.106726241, 3.157013231, 0.874656528, 5.499848364, 0.833803688, 
    3.943039326, 33.19801615, 28.01733131, 2.128631466, 32.58822874, 
    4.631055299, 0.608235357, 26.89484164, 13.05085317, 8.576504648, 
    12.74700931, 11.81559686, 4.038527706, 10.44035937, 14.61425814, 
    4.742638092)), row.names = c(NA, -60L), class = c("tbl_df", 
"tbl", "data.frame"))
Ab_plot_Zoop <- ggplot(Zoop_df_Ab,                         # Draw barplot with grouping & stacking
                        aes(x = Lake,
                            y = Value,
                            fill = Class)) + 
  geom_bar(stat = "identity",
           position = "stack", width = 0.6) +
  geom_vline(data = dat_vline, aes(xintercept = xintercept), lineend = "square") +
  geom_hline(data = dat_hline, aes(yintercept = yintercept), lineend = "square") +
  theme_classic() + theme(panel.border = element_rect(colour = NA, fill=NA, size=0),
                          text = element_text(size = 8, family = "sans"),
  ) + scale_y_continuous(expand = c(0,0)) +  labs(x = "", y = expression(paste("Relative Abundance (%)"))) + 
  facet_wrap(~factor(Season, levels=c('July','August','September')), strip.position = "bottom", scales = "free_x") +
  theme(panel.background = element_blank(),
        panel.spacing = unit(0, "line"), 
        strip.background = element_blank(),
        strip.placement = "outside") + theme(plot.title.position = 'plot', 
                                             plot.title = element_text(hjust = 0.5)) + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  scale_x_discrete(NULL, expand = c(0.3, 0.3)) + scale_fill_manual(values = c('black', 'lightgrey', 'white','darkgrey')) +
  scale_pattern_manual(
    "Class",
    values = c('none', 'none', 'crosshatch', 'stripe')
  ) +
  scale_pattern_angle_manual(
    "Class",
    values = c(0,0, 30, 45)
  ) +
  geom_col_pattern(
    aes(pattern = Class),
    pattern_size = .05,
    pattern_density = .1,
    pattern_spacing = .02,
    pattern_fill = "black",
    color = "black",
  )
Ab_plot_Zoop

Solution

  • If you specify breaks=, you can change the order within a legend; this needs to be done for all scales shown in the legend, so it'll be done multiple times.

    When doing work like this, I tend to use a data.frame that matches the Class values with its scale parameters, such as:

    classes <- tibble::tribble(
        ~Class      , ~fill      , ~pattern    , ~pattern_angle, ~order
      , "Calonoida" , "darkgrey" , "stripe"    ,             45,      1
      , "Cladocera" , "white"    , "crosshatch",             30,      2
      , "Rotifera"  , "lightgrey", "none"      ,              0,      3
      , "Cyclopoida", "black"    , "none"      ,              0,      4
    )
    

    The use of tribble is certainly not required to make this frame, I use it here because it shows everything lined up nicely. Also, I added the order column here because it was convenient to create this table elsewhere and sort into this final form.

    The other thing I much prefer to do is to provide named values when using a scale_*_manual. That is, c(0,0,30,45) works fine until at some point you change the order of values in your factor for whatever reason and forget to change the order of these numbers. By using a named-vector, we guard against that silent corruption (in a sense).

    I'm commenting out the geom_hline and geom_vline since I don't have those data.

    This is your original code:

    ggplot(Zoop_df_Ab,                         # Draw barplot with grouping & stacking
           aes(x = Lake,
               y = Value,
               fill = Class)) + 
      geom_bar(stat = "identity",
               position = "stack", width = 0.6) +
      # geom_vline(data = dat_vline, aes(xintercept = xintercept), lineend = "square") +
      # geom_hline(data = dat_hline, aes(yintercept = yintercept), lineend = "square") +
      theme_classic() +
      theme(panel.border = element_rect(colour = NA, fill=NA, size=0),
            text = element_text(size = 8, family = "sans")) +
      scale_y_continuous(expand = c(0,0)) +
      labs(x = "", y = expression(paste("Relative Abundance (%)"))) + 
      facet_wrap(~factor(Season, levels=c('July','August','September')), strip.position = "bottom", scales = "free_x") +
      theme(panel.background = element_blank(),
            panel.spacing = unit(0, "line"), 
            strip.background = element_blank(),
            strip.placement = "outside") +
      theme(plot.title.position = 'plot', 
            plot.title = element_text(hjust = 0.5)) +
      theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
      scale_x_discrete(NULL, expand = c(0.3, 0.3)) +
      scale_fill_manual(values = c('black', 'lightgrey', 'white','darkgrey')) +
      scale_pattern_manual(
        "Class",
        values = c('none', 'none', 'crosshatch', 'stripe')
      ) +
      scale_pattern_angle_manual(
        "Class",
        values = c(0,0, 30, 45)
      ) +
      geom_col_pattern(
        aes(pattern = Class),
        pattern_size = .05,
        pattern_density = .1,
        pattern_spacing = .02,
        pattern_fill = "black",
        color = "black",
        )
    

    original ggplot

    Here is the updated code with my changes:

    ggplot(Zoop_df_Ab,                         # Draw barplot with grouping & stacking
           aes(x = Lake,
               y = Value,
               fill = Class)) + 
      geom_bar(stat = "identity",
               position = "stack", width = 0.6) +
      # geom_vline(data = dat_vline, aes(xintercept = xintercept), lineend = "square") +
      # geom_hline(data = dat_hline, aes(yintercept = yintercept), lineend = "square") +
      theme_classic() +
      theme(panel.border = element_rect(colour = NA, fill=NA, size=0),
            text = element_text(size = 8, family = "sans")) +
      scale_y_continuous(expand = c(0,0)) +
      labs(x = "", y = expression(paste("Relative Abundance (%)"))) + 
      facet_wrap(~factor(Season, levels=c('July','August','September')), strip.position = "bottom", scales = "free_x") +
      theme(panel.background = element_blank(),
            panel.spacing = unit(0, "line"), 
            strip.background = element_blank(),
            strip.placement = "outside") +
      theme(plot.title.position = 'plot', 
            plot.title = element_text(hjust = 0.5)) +
      theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
      scale_x_discrete(NULL, expand = c(0.3, 0.3)) +
      scale_fill_manual(
        values = setNames(classes$fill, classes$Class),
        breaks = classes$Class
      ) +
      scale_pattern_manual(
        "Class",
        values = setNames(classes$pattern, classes$Class),
        breaks = classes$Class
      ) +
      scale_pattern_angle_manual(
        "Class",
        values = setNames(classes$pattern_angle, classes$Class),
        breaks = classes$Class
      ) +
      geom_col_pattern(
        aes(pattern = Class),
        pattern_size = .05,
        pattern_density = .1,
        pattern_spacing = .02,
        pattern_fill = "black",
        color = "black",
        )
    

    updated ggplot