Constantinos
Constantinos

Reputation: 1327

R - How can I add a bivariate legend to my ggplot2 chart?

I'm trying to add a bivariate legend to my ggplot2 chart but I don't know whether (a) this is possible through some guides options and (b) how to achieve it.

The only way I've managed to produce something close to the desired outcome was by specifically creating a new chart which resembles a legend (named p.legend below) and inserting it, via the cowplot package, somewhere in the original chart (named p.chart below). But surely there must be a better way than this, given that this approach requires creating the legend in the first place and fiddling with its size/location to fit it in the original chart.

Here's code for a dummy example of my approach:

library(tidyverse)

# Create Dummy Data #
set.seed(876)
n <- 2
df <- expand.grid(Area = LETTERS[1:n],
                  Period = c("Summer", "Winter"),
                  stringsAsFactors = FALSE) %>% 
      mutate(Objective = runif(2 * n, min = 0, max = 2),
             Performance = runif(2 * n) * Objective) %>% 
      gather(Type, Value, Objective:Performance)

# Original chart without legend #
p.chart <- df %>% 
            ggplot(., aes(x = Area)) + 
              geom_col(data = . %>% filter(Type == "Objective"),
                       aes(y = Value, fill = Period),
                       position = "dodge", width = 0.7, alpha = 0.6) + 
              geom_col(data = . %>% filter(Type == "Performance"),
                       aes(y = Value, fill = Period),
                       position = "dodge", width = 0.7) + 
              scale_fill_manual(values = c("Summer" = "#ff7f00", "Winter" = "#1f78b4"), guide = FALSE) + 
              theme_minimal() + 
              theme(panel.grid.major.x = element_blank(),
                    panel.grid.minor.y = element_blank())

# Create a chart resembling a legend #
p.legend <- expand.grid(Period = c("Summer", "Winter"),
                        Type   = c("Objective", "Performance"),
                        stringsAsFactors = FALSE) %>% 
              ggplot(., aes(x = Period, y = factor(Type, levels = c("Performance", "Objective")),
                                                   fill = Period, alpha = Type)) + 
                geom_tile() + 
                scale_fill_manual(values = c("Summer" = "#ff7f00", "Winter" = "#1f78b4"), guide = FALSE) + 
                scale_alpha_manual(values = c("Objective" = 0.7, "Performance" = 1), guide = FALSE) + 
                ggtitle("Legend") + 
                theme_minimal() + 
                theme(plot.title = element_text(hjust = 0.5),
                      rect = element_rect(fill = "transparent"),
                      axis.title = element_blank(),
                      panel.grid.major = element_blank())

# Add legend to original chart #
p.final <- cowplot::ggdraw() + 
                  cowplot::draw_plot(plot = p.chart) + 
                  cowplot::draw_plot(plot = p.legend, x = 0.5, y = 0.65, width = 0.4, height = 0.28, scale = 0.7)

# Save chart #
cowplot::ggsave("Bivariate Legend.png", p.final, width = 8, height = 6, dpi = 500)

... and the resulting chart: Chart with Bivariate Legend

Is there an easier way of doing this?

Upvotes: 1

Views: 325

Answers (1)

Axeman
Axeman

Reputation: 35307

This might work at some point, but right now the colorbox seems to ignore all breaks, names and labels (@ClausWilke?). Probably because the multiscales package is in really early stages.

Posting since it might work when future readers are here.

library(multiscales)

df %>% 
  mutate(
    period = as.numeric(factor(Period)),
    type = as.numeric(factor(Type))
  ) %>% 
  ggplot(., aes(x = Area, y = Value, fill = zip(period, type), group = interaction(Area, Period))) + 
  geom_col(width = 0.7, position = 'dodge') + 
  bivariate_scale(
    "fill", 
    pal_hue_sat(c(0.07, 0.6), c(0.4, 0.8)),
    guide = guide_colorbox(
      nbin = 2, 
      name = c("Period", "Type"),                       #ignored
      breaks = list(1:2, 1:2),                          #ignored
      labels = list(levels(.$Period), levels(.$Type))   #ignored
  )

enter image description here

Upvotes: 1

Related Questions