Is it possible to desaturate a ggplot easily?

2019-05-03 07:27发布

问题:

Is it possible to desaturate a ggplot easily?


In principle, there could be two possible strategies.
First, apply some function to a ggplot object (or, possibly, Grob object) to desaturate all colors. Second, some trick to print ggplot desaturated while rendering a .rmd file. Both strategies would be ok for me, but first one is, of course, more promissing.
Creating ggplot in greys from the beginning is not a good option as the idea is to have the same plot as if it was printed in shades of grey.


There were some similar questions and remarkably good answers on how to perform desaturation in R. Here is a convenient way to desaturate color palette. And here is the way of desaturating a raster image. What I'm looking for, is a simple way of desaturating the whole ggplot.

回答1:

Just came across this question. The experimental package colorblindr (written by Claire McWhite and myself) contains a function that can do this in a generic way. I'm using the example figure from @hrbrmstr:

library(ggplot2)
library(viridis)

gg <- ggplot(mtcars) + 
  geom_point(aes(x=mpg, y=wt, fill=factor(cyl), size=factor(carb)), 
             color="black", shape=21) + 
  scale_fill_viridis(discrete = TRUE) +
  scale_size_manual(values = c(3, 6, 9, 12, 15, 18)) +
  facet_wrap(~am)
gg

Now let's desaturate this plot, using the edit_colors() function from colorblindr:

library(colorblindr) # devtools::install_github("clauswilke/colorblindr")
library(colorspace) # install.packages("colorspace", repos = "http://R-Forge.R-project.org") --- colorblindr requires the development version
# need also install cowplot; current version on CRAN is fine.

gg_des <- edit_colors(gg, desaturate)
cowplot::ggdraw(gg_des)

The function edit_colors() takes a ggplot2 object or grob and applies a color transformation function (here desaturate) to all colors in the grob.

We can provide additional arguments to the transformation function, e.g. to do partial desaturation:

gg_des <- edit_colors(gg, desaturate, amount = 0.7)
cowplot::ggdraw(gg_des)

We can also do other transformations, e.g. color-blind simulations:

gg_des <- edit_colors(gg, deutan)
cowplot::ggdraw(gg_des)

Finally, we can manipulate line colors and fill colors separately. E.g., we could make all filled areas blue. (Not sure this is useful, but whatever.)

gg_des <- edit_colors(gg, fillfun = function(x) "lightblue")
cowplot::ggdraw(gg_des)



回答2:

As per my comment above, this might be the quickest/dirtiest way to achieve the desaturation for a ggplot2 object:

library(ggplot2)

set.seed(1)
p <- qplot(rnorm(50), rnorm(50), col="Class")
print(p)

pdf(file="p.pdf", colormodel="grey")
  print(p)
dev.off()



回答3:

I tried this with the new viridis color palette since it desaturates well (i.e. it should be noticeable between the colored & non-colored plots):

library(ggplot2)
library(grid)
library(colorspace)
library(viridis) # devtools::install_github("sjmgarnier/viridis") for scale_fill_viridis

gg <- ggplot(mtcars) + 
  geom_point(aes(x=mpg, y=wt, fill=factor(cyl), size=factor(carb)), 
             color="black", shape=21) + 
  scale_fill_viridis(discrete = TRUE) +
  scale_size_manual(values = c(3, 6, 9, 12, 15, 18)) +
  facet_wrap(~am)

gb <- ggplot_build(gg)
gb$data[[1]]$colour <- desaturate(gb$data[[1]]$colour)
gb$data[[1]]$fill <- desaturate(gb$data[[1]]$fill)

gt <- ggplot_gtable(gb)

grid.newpage()
grid.draw(gt)

You end up having to manipulate on the grob level.

Here's the plot pre-desaturate:

and here's the plot post-desature:

I'm trying to figure out why the legend got skipped and this may miss other highly customized ggplot aesthetics & components, so even while it's not a complete answer, perhaps it might be useful (and perhaps someone else can tack on to it or expand on it in another answer). It should just be a matter of replacing the right bits in either the gb object or gt object.

UPDATE I managed to find the right grob element for the legend:

gt$grobs[[12]][[1]][["99_9c27fc5147adbe9a3bdf887b25d29587"]]$grobs[[4]]$gp$fill <- 
    desaturate(gt$grobs[[12]][[1]][["99_9c27fc5147adbe9a3bdf887b25d29587"]]$grobs[[4]]$gp$fill)
gt$grobs[[12]][[1]][["99_9c27fc5147adbe9a3bdf887b25d29587"]]$grobs[[6]]$gp$fill <- 
    desaturate(gt$grobs[[12]][[1]][["99_9c27fc5147adbe9a3bdf887b25d29587"]]$grobs[[6]]$gp$fill)
gt$grobs[[12]][[1]][["99_9c27fc5147adbe9a3bdf887b25d29587"]]$grobs[[8]]$gp$fill <- 
    desaturate(gt$grobs[[12]][[1]][["99_9c27fc5147adbe9a3bdf887b25d29587"]]$grobs[[8]]$gp$fill)

grid.newpage()
grid.draw(gt)

The machinations to find the other gp elements that need desaturation aren't too bad either.