How to add geom_tile outside of axis?

Viewed 346

I recently asked this question about how to add a custom bar along the axis of a ggplot. The code I used to produce this plot was:

library(ggnewscale)
library(tidyverse)

  df <- data.frame(
  a = paste0("a", c(1,1,1,2,2,2,3,3,3)),
  b = paste0("b", c(1,2,3,1,2,3,1,2,3)),
  c = c(-10, 3, 5, -2, 9, 1, -5, -2, 0)
)

z <- c(2, 4, 1, 7, 9, 3)

# set color palettes
library(colorspace)
pal <- rev(diverging_hcl(palette = "Blue-Red", n = 11))
palEdge <- rev(sequential_hcl(palette = "Plasma", n = 11))

# plot
library(ggplot2)

ggplot(df, aes(a, b)) +
  geom_tile(aes(fill = c)) +
  scale_fill_gradientn(
    colors = pal,
    guide = guide_colorbar(
      frame.colour = "black",
      ticks.colour = "black"
    ),
    name = "c"
  ) +
  theme_classic() +
  labs(x = "A", y = "B") +
  new_scale_fill() +
  geom_tile(
    data = tibble(a = paste0("a", 1:3), z = z[1:3]),
    aes(x = a, y = 0.5, fill = z, height = 0.1)
  ) +
  geom_tile(
    data = tibble(b = paste0("b", 1:3), z = z[4:6]),
    aes(x = 0.45, y = b, fill = z, width = 0.1)
  ) +
  scale_fill_gradientn(
    colors = palEdge,
    guide = guide_colorbar(
      frame.colour = "black",
      ticks.colour = "black"
    ),
    name = "Main\neffects"# for beta hat symbol
  )

This produces something that looks like this:

heatmap

However, I was wondering if it's possible to move the coloured bar outside of the plot. For example, in this mock up I made in photoshop:

heatmap mock up

[EDIT] @teunbrand answer below is good... however, I have had to edit my question to more accurately reflect my problem... and as a result I cant seem to apply @teunbrand answer

1 Answers

You can place the bars "outside" the plot by drawing beyond the limits and then not clipping the data. This has two drawbacks:

  • You need continuous scales instead of discrete ones. You can later re-label the continuous scales as if they were discrete if necessary.
  • You have to balance the precise placement of the bars against the plot margins manually. How to balance this depends on the size of your device window and I haven't found a solution that works out of the box for every case.

Example below:

df <- data.frame(
  a = paste0("a", c(1,1,1,2,2,2,3,3,3)),
  b = paste0("b", c(1,2,3,1,2,3,1,2,3)),
  c = c(-10, 3, 5, -2, 9, 1, -5, -2, 0)
)

# From discrete to continuous
df$a <- match(df$a, sort(unique(df$a)))
df$b <- match(df$b, sort(unique(df$b)))
z <- c(2, 4, 1, 7, 9, 3)

library(tidyverse)
library(ggnewscale)

ggplot(df) +
  geom_tile(aes(a, b, fill = c)) +
  scale_fill_continuous(guide = guide_legend(order = 1)) +
  new_scale_fill() +
  geom_tile(data = tibble(a = 1:3, # <- continuous
                          d = z[1:3]),
            aes(x = a, y = 0, fill = d, height = 0.1)) +
  geom_tile(data = tibble(b = 1:3, # <- continuous
                          d = z[4:6]),
            aes(x = 0, y = b, fill = d, width = 0.1)) +
  scale_fill_viridis_c(option = "D", 
                       guide = guide_legend(order = 2)) +
  # Here be the extra bit
  coord_cartesian(clip = "off", xlim = c(0.5, NA), ylim = c(0.5, NA)) +
  theme(
    aspect.ratio = 1,
    plot.margin = margin(5.5, 5.5, 25, 55, "pt")
  )

EDIT: Code for updated example:

library(ggnewscale)
library(tidyverse)
library(colorspace)
library(ggplot2)

df <- data.frame(
  a = paste0("a", c(1,1,1,2,2,2,3,3,3)),
  b = paste0("b", c(1,2,3,1,2,3,1,2,3)),
  c = c(-10, 3, 5, -2, 9, 1, -5, -2, 0)
)

df$a <- match(df$a, sort(unique(df$a)))
df$b <- match(df$b, sort(unique(df$b)))

z <- c(2, 4, 1, 7, 9, 3)

pal <- rev(diverging_hcl(palette = "Blue-Red", n = 11))
palEdge <- rev(sequential_hcl(palette = "Plasma", n = 11))

ggplot(df, aes(a, b)) +
  geom_tile(aes(fill = c)) +
  scale_fill_gradientn(
    colors = pal,
    guide = guide_colorbar(
      frame.colour = "black",
      ticks.colour = "black"
    ),
    name = "c"
  ) +
  theme_classic() +
  labs(x = "A", y = "B") +
  new_scale_fill() +
  geom_tile(
    data = tibble(a = 1:3, z = z[1:3]),
    aes(x = a, y = 0, fill = z, height = 0.1)
  ) +
  geom_tile(
    data = tibble(b = 1:3, z = z[4:6]),
    aes(x = 0, y = b, fill = z, width = 0.1)
  ) +
  scale_fill_gradientn(
    colors = palEdge,
    guide = guide_colorbar(
      frame.colour = "black",
      ticks.colour = "black"
    ),
    name = "Main\neffects"# for beta hat symbol
  ) +
  coord_cartesian(clip = "off", xlim = c(0.5, NA), ylim = c(0.5, NA)) +
  theme(
    aspect.ratio = 1,
    plot.margin = margin(5.5, 5.5, 25, 55, "pt")
  )
Related