Reputation: 190
I need to adjust the midpoint location for a heatmap via ggplot2. I've googled around and have seen scale_fill_gradient2
be a great fit but the colors don't seem to match up to what I'm looking for. I know z needs a range from 0 to 1. Example dataset generation below:
library(ggplot2)
library(tibble)
library(RColorBrewer)
set.seed(5)
df <- as_tibble(expand.grid(x = -5:5, y = 0:5, z = NA))
df$z <- runif(length(df$z), min = 0, max = 1)
I tried plotting with the scale_fill_gradient2
but the blue color isn't coming as "dark" as I'd like.
ggplot(df, aes(x = x, y = y)) +
geom_tile(aes(fill = z)) +
scale_fill_gradient2(
low = 'red', mid = 'white', high = 'blue',
midpoint = 0.7, guide = 'colourbar', aesthetics = 'fill'
) +
scale_x_continuous(expand = c(0, 0), breaks = unique(df$x)) +
scale_y_continuous(expand = c(0, 0), breaks = unique(df$y))
Therefore, I'm using scale_fill_distiller
with the color palette 'RdBu' which comes out with the color scale I need but the ranges and the midpoints aren't right.
ggplot(df, aes(x = x, y = y)) +
geom_tile(aes(fill = z)) +
scale_fill_distiller(palette = 'RdBu') +
scale_x_continuous(expand = c(0, 0), breaks = unique(df$x)) +
scale_y_continuous(expand = c(0, 0), breaks = unique(df$y))
Is there a way to get the 2nd color scale but with the option to set midpoint range as the first?
Upvotes: 6
Views: 11568
Reputation: 37933
Claus' answer is great (and I'm a fan of his work), but I'd like to add that you can retain control within vanilla ggplot as well if you use the scale_fill_gradientn()
function:
library(ggplot2)
library(tibble)
set.seed(5)
df <- as_tibble(expand.grid(x = -5:5, y = 0:5, z = NA))
df$z <- runif(length(df$z), min = 0, max = 1)
ggplot(df, aes(x = x, y = y)) +
geom_tile(aes(fill = z)) +
scale_fill_gradientn(
colours = c("red", "white", "blue"),
values = c(0, 0.7, 1)
) +
scale_x_continuous(expand = c(0, 0), breaks = unique(df$x)) +
scale_y_continuous(expand = c(0, 0), breaks = unique(df$y))
A notable downside is that you'd have to provide the values
argument in rescaled space, so between 0-1. Consider if your fill values range from 0-10 instead and want the midpoint on 0.7, you'd have to provide values = c(0, 0.07, 1)
.
Upvotes: 9
Reputation: 17790
The color scales provided by the colorspace package will generally allow you much more fine-grained control. First, you can use the same colorscale but set the mid-point.
library(ggplot2)
library(tibble)
library(colorspace)
set.seed(5)
df <- as_tibble(expand.grid(x = -5:5, y = 0:5, z = NA))
df$z <- runif(length(df$z), min = 0, max = 1)
ggplot(df, aes(x = x, y = y)) +
geom_tile(aes(fill = z)) +
scale_fill_continuous_divergingx(palette = 'RdBu', mid = 0.7) +
scale_x_continuous(expand = c(0, 0), breaks = unique(df$x)) +
scale_y_continuous(expand = c(0, 0), breaks = unique(df$y))
However, as you see, this creates the same problem as before, because you'd have to be further away from the midpoint to get darker blues. Fortunately, the divergingx color scales allow you to manipulate either branch independently, and so we can create a scale that turns to dark blue much faster. You can play around with l3
, p3
, and p4
until you get the result you want.
ggplot(df, aes(x = x, y = y)) +
geom_tile(aes(fill = z)) +
scale_fill_continuous_divergingx(palette = 'RdBu', mid = 0.7, l3 = 0, p3 = .8, p4 = .6) +
scale_x_continuous(expand = c(0, 0), breaks = unique(df$x)) +
scale_y_continuous(expand = c(0, 0), breaks = unique(df$y))
Created on 2019-11-05 by the reprex package (v0.3.0)
Upvotes: 13