Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: midpoint argument in scale_color_gradientn() #3738

Open
EmilHvitfeldt opened this issue Jan 16, 2020 · 10 comments · May be fixed by #5824
Open

Feature request: midpoint argument in scale_color_gradientn() #3738

EmilHvitfeldt opened this issue Jan 16, 2020 · 10 comments · May be fixed by #5824
Labels
feature a feature request or enhancement scales 🐍

Comments

@EmilHvitfeldt
Copy link

Since there are now divergent continuous color palettes available in different packages, it would be nice to be able to specify a midpoint without manually having to calculate the limits.

related issue: thomasp85/scico#6

@clauswilke
Copy link
Member

It's not clear to me that adding a mid argument to scale_color_gradientn() is the right approach, versus adding a new scale function, e.g. scale_color_gradientn_div(). Sequential and diverging scales are quite different conceptually, and I think it's important to emphasize this in the API rather than muddle things together. (If you think about how you'd implement the requested feature, you'd probably end up with a parameter mid with default NULL and would write two entirely different function bodies depending on whether mid is NULL or not.)

In the same vein, maybe scale_color_gradient2() should be deprecated in favor of something like scale_color_gradient_div().

@thomasp85
Copy link
Member

Just a thought on your last idea. I don’t think gradient2 is used for diverging scales as these usually have a midpoint colour, not just two extremes. I see no reason to deprecate it despite adding a sequential scale type (which I think is the right approach to this)

@clauswilke
Copy link
Member

scale_*_gradient() sets up a sequential scale between two extremes:

scale_colour_gradient <- function(..., low = "#132B43", high = "#56B1F7", space = "Lab",
na.value = "grey50", guide = "colourbar", aesthetics = "colour") {
continuous_scale(aesthetics, "gradient", seq_gradient_pal(low, high, space),
na.value = na.value, guide = guide, ...)
}

scale_*_gradient2() sets up a diverging scale between two extremes, with a midpoint:

scale_colour_gradient2 <- function(..., low = muted("red"), mid = "white", high = muted("blue"),
midpoint = 0, space = "Lab", na.value = "grey50", guide = "colourbar",
aesthetics = "colour") {
continuous_scale(aesthetics, "gradient2",
div_gradient_pal(low, mid, high, space), na.value = na.value, guide = guide, ...,
rescaler = mid_rescaler(mid = midpoint))
}

You just made my case for renaming scale_*_gradient2() :-)

@thomasp85
Copy link
Member

Haha - I guess I have been starring at code for too long

@thomasp85 thomasp85 added feature a feature request or enhancement scales 🐍 labels Jan 21, 2020
@jimjam-slam

This comment has been minimized.

@clauswilke

This comment has been minimized.

@jimjam-slam

This comment has been minimized.

@ijlyttle
Copy link

ijlyttle commented Feb 8, 2020

Here's how I approached this problem, following the example from this related Stack Overflow question:

There's an internal function in ggplot2, mid_rescaler() that I find to be very useful - it's visible in @clauswilke's post above. I have adapted it a bit here:

library("ggplot2")

mid_rescaler <- function(mid = 0) {
  function(x, to = c(0, 1), from = range(x, na.rm = TRUE)) {
    scales::rescale_mid(x, to, from, mid)
  }
}

grid <- expand.grid(lon = seq(0, 360, by = 2), lat = seq(-90, 0, by = 2))
grid$z <- with(grid, cos(lat*pi/180) - .7)

ggplot(grid, aes(lon, lat)) +
  geom_raster(aes(fill = z)) +
  scale_fill_distiller(palette = "RdBu", rescaler = mid_rescaler())

Created on 2020-02-08 by the reprex package (v0.3.0)

As you know, the ... in color- and fill-scale functions get passed on to discrete_scale() or continuous_scale(). The continuous_scale() function has a rescaler argument which, itself, expects a function.

The mid_rescaler() function lets me specify the midpoint to build a rescaler function. To me, it seems like a minimally-disruptive solution, which ggplot2 is itself already using.

Could it be useful to make such a function available, publicly, in ggplot2?

Also thanks to @dpseidel, who listened very patiently as I waved my hands trying to describe this at rstudio::conf, while the right approach was to make a reprex.

dlcomeaux added a commit to CMAP-REPOS/cmapplot that referenced this issue Oct 16, 2020
This appears to do the trick - I used a tip from tidyverse/ggplot2#3738 (comment)

@gritzenthaler would you mind testing this out to make sure I didn't miss anything?
@ThomasDelSantoONeill
Copy link

ThomasDelSantoONeill commented Feb 11, 2022

Hi, say I built a plot where the Y axis maps a numerical variable and the X axis a factor variable of 4 levels (i.e. 4 groups). Then I decide to map the data with a geom_jitter and colour the plot with a divergent gradient. My question is: is there a way for the midpoint of the divergent scale colour to start exactly at the median of each level (for this of course the legend would not make sense but as a way of visualising medians with colours).

image

@teunbrand
Copy link
Collaborator

Because the rescaler argument accepts lambda syntax, one doesn't even need to write a new function for this.

library("ggplot2")

grid <- expand.grid(lon = seq(0, 360, by = 2), lat = seq(-90, 0, by = 2))
grid$z <- with(grid, cos(lat*pi/180) - .7)

ggplot(grid, aes(lon, lat)) +
  geom_raster(aes(fill = z)) +
  scale_fill_distiller(
    palette = "RdBu", 
    rescaler = ~ scales::rescale_mid(.x, mid = 0)
  )

Created on 2022-12-03 by the reprex package (v2.0.1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement scales 🐍
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants