rggplot2

How do I set a gradient color scale with a fixed midpoint with ggplot?


I want to make a bar plot with the custom fill color scale. Suppose the following, the data has both positive and negative values for z_score:

data_1 <- data.frame(
  name = paste('Pathway', 1:5),
  p_val = c(0.01, 0.05, 0.03, 0.001, 0.02),
  z_score = c(-2, -3, 3, 4, 2.5)
)

data_1 |>
  ggplot(aes(x = p_val, y = fct_reorder(name, p_val))) +
  geom_bar(aes(fill = z_score), stat = 'identity') +
  scale_fill_gradientn(
    colours = colorRampPalette(c('#709AE1', '#FFFFFF', '#FD7446'))(100)
  )

enter image description here

The code above works as expected. Now, suppose a data with only negative z_score:

data_2 <- data.frame(
  name = paste('Pathway', 1:5),
  p_val = c(0.01, 0.05, 0.03, 0.001, 0.02),
  z_score = c(-2, -3, -3, -4, -2.5)
)
data_2 |>
  ggplot(aes(x = p_val, y = fct_reorder(name, p_val))) +
  geom_bar(aes(fill = z_score), stat = 'identity') +
  scale_fill_gradientn(
    colours = colorRampPalette(c('#709AE1', '#FFFFFF', '#FD7446'))(100)
  )

This produces incorrect plot, since I want colors to map from white to blue (all z_score values are negative):

enter image description here

I tried changing the fill color so that there's only white and blue colors in case all z_score values are negative, but this gives an incorrect plot: enter image description here

For negative-only z_score, I want a white color to be used only for values close to zero. Somehow, the scale should be fixed. For example, a z_score of -2 shouldn't map to white, but should still be blue even though it's the smallest value. Does this make sense?

Appreciate any tips!


Solution

  • See if the scale_fill_gradient2 function achieves your goal. You can set colours for low, high and mid values. The mid-point is set to zero with a white colour by default.

    With this function, you can use the same fill scale no matter the values are negative or positive.

    library(tidyverse)
    
    data_1 |>
      ggplot(aes(x = p_val, y = fct_reorder(name, p_val))) +
      geom_bar(aes(fill = z_score), stat = 'identity') +
      scale_fill_gradient2(low = '#709AE1', high = '#FD7446')
    

    data_2 |>
      ggplot(aes(x = p_val, y = fct_reorder(name, p_val))) +
      geom_bar(aes(fill = z_score), stat = 'identity') +
      scale_fill_gradient2(low = '#709AE1', high = '#FD7446')