Split legend into two or multiple columns in a plot using ggnewscale::new_scale() with ggplot2 in R

Viewed 558

after following Stefan's very helpful answer to this post, where he uses ggnewscale::new_scale(), I now am stuck with the following question:

"How to arrange the custom legends from ggnewscale into multiple vertical columns?" like it is usually done with a command such as guides(scale_shape_manual=guide_legend(ncol=2)) in ggplot2.

Minimal reproducible example:

# fictional data in the scheme of https://stackoverflow.com/q/66804487/16642045

mutated <- list()
for(i in 1:10) {
  mutated[[i]] <- data.frame(Biological.Replicate = rep(1,4),
                           Reagent.Conc = c(10000, 2500, 625, 156.3),
                           Reagent = rep(1,8),
                           Cell.type = rep(LETTERS[i],4),
                           Mean.Viable.Cells.1 = rep(runif(n = 10, min = 0, max = 1),4))
}
mutated <- do.call(rbind.data.frame, mutated)

The modified code after the answer of user "stefan" looks like this:

# from https://stackoverflow.com/a/66808609/16642045

library(ggplot2)
library(ggnewscale)
library(dplyr)
library(magrittr)

mutated <- mutated %>% 
  mutate(Cell.type = as.factor(Cell.type),
         Reagent = factor(Reagent, 
                          levels = c("0", "1", "2")))

mean_mutated <- mutated %>%
  group_by(Reagent, Reagent.Conc, Cell.type) %>%
  split(.$Cell.type)

layer_geom_scale <- function(Cell.type) {
  list(geom_point(mean_mutated[[Cell.type]], mapping = aes(shape = Reagent)),
       geom_line(mean_mutated[[Cell.type]], mapping = aes(group = Reagent, linetype = Reagent)),
       scale_linetype_manual(name = Cell.type, values = c("solid", "dashed", "dotted"), drop=FALSE),
       scale_shape_manual(name = Cell.type, values = c(15, 16, 4), labels = c("0", "1", "2"), drop=FALSE) 
  )
}

my_plot <- 
  ggplot(mapping = aes(
    x = as.factor(Reagent.Conc),
    y = Mean.Viable.Cells.1)) +
  layer_geom_scale(unique(mutated$Cell.type)[1])

for(current_Cell.type_index in 2:length(unique(mutated$Cell.type))) {
  my_plot <- 
    my_plot +
    ggnewscale::new_scale("shape")  +
    ggnewscale::new_scale("linetype") +
    layer_geom_scale(unique(mutated$Cell.type)[current_Cell.type_index])
}

my_plot


This results in:

Now, I want the legends to be displayed side-by-side, in two columns, and I tried this (without success):

my_plot +
  guides(scale_shape_manual=guide_legend(ncol=2))

EDIT: A picture of the way I want the legends to be arranged

Is there anyone who could help me?

Thanks!

1 Answers

Note: This answer addresses the question before clarification was made at question edit # 4 and beyond.

Horizontal legend

Adding theme(legend.box = "horizontal") will make the legend elements appear side by side.

Multiple columns and ggnewscale

Using guides globally will result in the modification of scales after ggnewscale updates them. In this context, only the variable RKO will be updated:

layer_geom_scale <- function(cell_type, color) {
  list(geom_point(mean_mutated[[cell_type]], mapping = aes(shape = Reagent), color = color),
       geom_line(mean_mutated[[cell_type]], mapping = aes(group = Reagent, linetype = Reagent), color = color),
       scale_linetype_manual(name = cell_type, values = c("solid", "dashed", "dotted"), drop=FALSE),
       scale_shape_manual(name = cell_type, values = c(15, 16, 4), labels = c("0", "1", "2"), drop=FALSE)
  )
}

my_plot <- 
  ggplot(mapping = aes(
    x = as.factor(Reagent.Conc),
    y = Mean.Viable.Cells.1)) +
  layer_geom_scale("HCT", "#999999") +
  ggnewscale::new_scale("linetype") +
  ggnewscale::new_scale("shape") +
  layer_geom_scale("RKO", "#E69F00") +
  theme(legend.box = "horizontal") +
  guides(shape = guide_legend(ncol = 2),
         linetype = guide_legend(ncol = 2))

my_plot

Plot after global guide was used

To modify the same scales for all variables, the guide should be added inside the scale definitions:

layer_geom_scale <- function(cell_type, color) {
  list(geom_point(mean_mutated[[cell_type]], mapping = aes(shape = Reagent), color = color),
       geom_line(mean_mutated[[cell_type]], mapping = aes(group = Reagent, linetype = Reagent), color = color),
       scale_linetype_manual(name = cell_type, values = c("solid", "dashed", "dotted"), drop=FALSE,
                             guide = guide_legend(ncol = 2)),
       scale_shape_manual(name = cell_type, values = c(15, 16, 4), labels = c("0", "1", "2"), drop=FALSE,
                          guide = guide_legend(ncol = 2))
  )
}

my_plot <- 
  ggplot(mapping = aes(
    x = as.factor(Reagent.Conc),
    y = Mean.Viable.Cells.1)) +
  layer_geom_scale("HCT", "#999999") +
  ggnewscale::new_scale("linetype") +
  ggnewscale::new_scale("shape") +
  layer_geom_scale("RKO", "#E69F00") +
  theme(legend.box = "horizontal")

my_plot

Plot after guide on each scale


Raw data

# from https://stackoverflow.com/q/66804487/16642045

mutated <- structure(list(
  Biological.Replicate = c(1L, 1L, 1L, 1L, 
                           1L, 1L, 1L, 1L, 
                           1L, 1L, 1L, 1L, 
                           1L, 1L, 1L, 1L, 
                           1L, 1L, 1L, 1L, 
                           1L, 1L, 1L, 1L), 
  Reagent.Conc = c(10000, 2500, 625, 156.3,
                   39.1, 9.8, 2.4, 0.6, 
                   10000, 2500, 625, 156.3, 
                   39.1, 9.8, 2.4, 0.6, 
                   10000, 2500, 625, 156.3, 
                   39.1, 9.8, 2.4, 0.6),
  Reagent = c(1L, 1L, 1L, 1L, 
              1L, 1L, 1L, 1L, 
              2L, 2L, 2L, 2L, 
              2L, 2L, 2L, 2L, 
              0L, 0L, 0L, 0L, 
              0L, 0L, 0L, 0L),
  Cell.type = c("HCT", "HCT", "HCT", "HCT",
                "HCT", "HCT", "HCT", "HCT", 
                "HCT", "HCT", "HCT", "HCT",
                "HCT", "HCT", "HCT", "HCT",
                "RKO", "RKO", "RKO", "RKO", 
                "RKO", "RKO", "RKO", "RKO"), 
  Mean.Viable.Cells.1 = c(1.014923966, 1.022279854, 1.00926559, 0.936979842, 
                          0.935565248, 0.966403395, 1.00007073, 0.978144524, 
                          1.019673384, 0.991595836, 0.977270557, 1.007353643, 
                          1.111928183, 0.963518289, 0.993028364, 1.027409034, 
                          1.055452733, 0.953801253, 0.956577449, 0.792568337, 
                          0.797052961, 0.755623576, 0.838482346, 0.836773918)), 
  row.names = 9:32, 
  class = "data.frame")
# from https://stackoverflow.com/a/66808609/16642045

library(ggplot2)
library(ggnewscale)
library(dplyr)
library(magrittr)

mutated <- mutated %>% 
  mutate(Cell.type = factor(Cell.type, 
                            levels = c("HCT", "RKO")),
         Reagent = factor(Reagent, 
                          levels = c("0", "1", "2")))

mean_mutated <- mutated %>%
  group_by(Reagent, Reagent.Conc, Cell.type) %>%
  split(.$Cell.type)
Related