Skip to content

Incorrect mapping of low/high values to colors in scale_color_gradient2 #6722

@shashkat

Description

@shashkat

I found a problem with scale_color_gradient2(). When I give it a low/high argument, it doesn't seem to map the low/high value of my data to the given color, but does something similar to shifting the color palette, to match the midpoint.

I expected the color specified to low/high argument to map the lowest/highest values in my data. I expected this, because in the documentation (https://ggplot2.tidyverse.org/reference/scale_gradient.html), about the arguments low/high, it says: "Colours for low and high ends of the gradient." which I believe indicates what I expect. If its just trimming the gradient range, then how is it different from limits argument?

Here is the code to reproduce the bug:

# load libraries
library(tidyverse)
library(patchwork)
library(scales)
#> 
#> Attaching package: 'scales'
#> The following object is masked from 'package:purrr':
#> 
#>     discard
#> The following object is masked from 'package:readr':
#> 
#>     col_factor

iris <- iris %>% as_tibble()
iris$Sepal.Length2 <- iris$Sepal.Length - 5.5 # just so that we have negative and positive values, which demonstrates the bug better
# now the range of iris$Sepal.Length2 is c(-1.2, 2.4)

# gather the min and max values as they will be handy below
min_val = iris$Sepal.Length2 %>% min()
max_val = iris$Sepal.Length2 %>% max()

# lets just take the datapoints with the minimum and maximum for Sepal.Length2 so that there is no confusion about averaging of 
# values to make the plot
iris_subset <- iris %>% filter(Sepal.Length2 %in% c(min_val, max_val))

# first plot, using scale_color_gradient2, which should have mapped the lowest value, -1.2 to steelblue, but looking at the plot 
# it is clear that that is not the case
p1 <- ggplot(iris_subset, aes(x = Species, y = 0, color = Sepal.Length2)) + 
  geom_point(size = 10) + 
  theme_minimal() +
  scale_color_gradient2(low = 'steelblue', mid = 'white', high = 'darkred', midpoint = 0)

# make a second plot which does exactly what I had expected scale_color_gradient2 would do. Here, I use scale_color_gradientn, and 
# manually scale the values between 0 and 1, and specify the colors.
p2 <- ggplot(iris_subset, aes(x = Species, y = 0, color = Sepal.Length2)) + 
  geom_point(size = 10) + 
  theme_minimal() +
  scale_color_gradientn(colors = c('steelblue', 'white', 'darkred'), values = scales::rescale(c(min_val, 0, max_val), to = c(0,1)))

patchwork::wrap_plots(p1, p2, ncol = 1)
# we can see from the left point (setosa) that the color in the first plot is fainter, which means that the mapping of lowest value 
# to the color supplied to low argument hasn't been done appropriately

Created on 2025-11-03 with reprex v2.1.1

Thanks a lot!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions