5

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!

David
  • 51
  • 5
  • 2
    Check this https://stackoverflow.com/a/68369737/14027775 or this https://stackoverflow.com/a/52067836/14027775. The idea is basically to create multiple plots and combine their legends in a custom way. – marcguery Aug 13 '21 at 08:13

1 Answers1

2

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)
marcguery
  • 441
  • 1
  • 4
  • 12
  • Thank you very much for your answer, marcguery! I noticed that my question might not have been specific enough. I now have enough reputation to post pictures in-thread and modified my post. I want the **individual legends** arranged in to (or more) columns like my lower pictures depicts. – David Aug 12 '21 at 11:32
  • Does this answer your question now? – marcguery Aug 12 '21 at 12:14
  • unfortunately not, I'm sorry! In my own project I have about 20 individual legend entries. I want them to be split up into two or three columns. If I use your `theme(legend.box = "horizontal")`-code, all 20 legends will be plotted horizontally. I would like them to be separated into two columns of 10 entries beneath each other each. – David Aug 12 '21 at 20:36
  • 1
    Ok I finally understand what you meant by multiple columns. I am not aware of any means to do that. Maybe you should update your reproducible example to better fit your requirements so someone can give you a better answer. – marcguery Aug 12 '21 at 21:10
  • Yes, thank you! I made some changes that hopefully clarify what I mean! – David Aug 13 '21 at 08:31