Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to desaturate a ggplot easily?

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.

like image 387
ikashnitsky Avatar asked Aug 21 '15 18:08

ikashnitsky


3 Answers

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

enter image description here

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)

enter image description here

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)

enter image description here

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

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

enter image description here

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)

enter image description here

like image 142
Claus Wilke Avatar answered Oct 31 '22 23:10

Claus Wilke


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)

enter image description here

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

enter image description here

like image 34
Forrest R. Stevens Avatar answered Oct 31 '22 23:10

Forrest R. Stevens


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:

enter image description here

and here's the plot post-desature:

enter image description here

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)

enter image description here

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

like image 2
hrbrmstr Avatar answered Nov 01 '22 01:11

hrbrmstr