Search code examples
rggplot2heatmapgeom-tile

How to order axes of geom_tile heatmap with different colours in the upper and lower triangle?


I want to make a heatmap with different colours in the upper and lower triangles. This works as I expect:

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

data <- data.frame(
  Var1 = c("b", "c", "a", "c", "a", "b"),
  Var2 = c("a", "a", "b", "b", "c", "c"),
  val = c(-2.1113581, 0.6189813, -0.4770620, 0.8119133, -0.4029937, 0.7977290)
)

plot_data = data %>%
  mutate(triangle =  ifelse(Var1 < Var2, "Lower", "Upper")) %>%
  group_split(triangle)


ggplot() +
  geom_tile(data=plot_data[[1]], aes(x=Var1, y=Var2, fill=val)) +
  scale_fill_gradient(low = "lightblue", high = "blue", name = "Upper Values") +

  new_scale_fill() +  # Part of ggnewscale, allows multiple fill scales
  # Lower triangle heatmap with a green color scale
  geom_tile(data = plot_data[[2]], aes(x=Var1, y=Var2, fill=val)) +
  scale_fill_gradient(low = "lightgreen", high = "darkgreen", name = "Lower Values")

enter image description here

However, when I try and order the axes, then it ends up switching over some of the colours from the triangles, i.e. Var1 = b and Var2 = a is now blue rather than green:

ggplot() +
  geom_tile(data=plot_data[[1]], aes(x=Var1, y=Var2, fill=val)) +
  scale_fill_gradient(low = "lightblue", high = "blue", name = "Upper Values") +

  new_scale_fill() +  # Part of ggnewscale, allows multiple fill scales
  # Lower triangle heatmap with a green color scale
  geom_tile(data = plot_data[[2]], aes(x=Var1, y=Var2, fill=val)) +
  scale_fill_gradient(low = "lightgreen", high = "darkgreen", name = "Lower Values") +
  scale_x_discrete(limits = c("b", "a", "c")) +
  scale_y_discrete(limits = c("b", "a", "c"))

How can I keep the right colours in the upper and lower triangles and keep a particular order of the factors?

enter image description here


Solution

  • Instead of setting the order via the limits= convert your variables to factors with the desired order (and set drop=FALSE in scale_x/y_discrete).

    library(ggnewscale)
    library(ggplot2)
    library(dplyr, warn = FALSE)
    
    plot_data <- data %>%
      mutate(
        Var1 = factor(Var1, c("b", "a", "c")),
        Var2 = factor(Var2, c("b", "a", "c"))
      ) |>
      mutate(triangle = ifelse(as.integer(Var1) < as.integer(Var2), "Lower", "Upper")) %>%
      group_split(triangle)
    
    
    ggplot() +
      geom_tile(data = plot_data[[1]], aes(x = Var1, y = Var2, fill = val)) +
      scale_x_discrete(drop = FALSE) +
      scale_y_discrete(drop = FALSE) +
      scale_fill_gradient(low = "lightblue", high = "blue", name = "Upper Values") +
      new_scale_fill() +
      geom_tile(data = plot_data[[2]], aes(x = Var1, y = Var2, fill = val)) +
      scale_fill_gradient(low = "lightgreen", high = "darkgreen", name = "Lower Values")