I am trying to use another color palette for the scatter plots from ggpairs from the GGally library in R. See similar question here.
library(ggplot2)
library(GGally)
Works
ggplot(iris, aes(x=Sepal.Width, colour=Species)) + stat_ecdf() + scale_color_brewer(palette="Spectral")
Also works
ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer(palette="Spectral")
ggplot(iris, aes(x=Sepal.Width, colour=Species)) + stat_ecdf()
Does not work
ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer(palette="Spectral")
ggpairs(iris,
columns=, c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"),
colour='Species',
lower=list(continuous='points'),
axisLabels='none',
upper=list(continuous='blank')
)
but adding
putPlot(p, ggplot(iris, aes(x=Sepal.Length, colour=Species)) + stat_ecdf(), 1,1)
adds a plot in the right colors.
Workaround
I can change the plots afterwards with getPlot, but that's not pretty..
subplot <- getPlot(a, 2, 1) # retrieve the top left chart
subplotNew <- subplot + scale_color_brewer(palette="Spectral")
a <- putPlot(a, subplotNew, 2, 1)
How can I change the color scheme for the scatter plots in ggpairs? More specifically, I'd like to manually define the colors like so
scale_colour_manual(values=c("#FF0000","#000000", "#0000FF","#00FF00"))
Thanks!
Here is a hack that works:
ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer(palette="Spectral")
unlockBinding("ggplot",parent.env(asNamespace("GGally")))
assign("ggplot",ggplot,parent.env(asNamespace("GGally")))
When you assign a new value to the ggplot function, it is in the global environment. Now, GGally imports everything including ggplot when it loads (it didn't have to be that way). At that point, changing the ggplot function in your global environment has no effect, because imports from GGally have precedence. Instead, you need to update the ggplot function on the GGally:imports. There is only one problem: once a package is loaded, its bindings are locked. But we can unlock them (I am guessing this is frowned upon, hence labeling the solution a hack).
See Josh O'Brien's answer under Replace definition of built-in function in R? for more info.
as stacksia said (but also adding scale_fill_brewer)
ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer(palette="Spectral") + scale_fill_brewer(palette="Spectral")
unlockBinding("ggplot",parent.env(asNamespace("GGally")))
assign("ggplot",ggplot,parent.env(asNamespace("GGally")))
See Josh O'Brien's answer under Replace definition of built-in function in R? for more info.
Related
I would like to define a color palette for every plot built in a markdown document. In essence this would overwrite the default choices.
There are several very old answers -- thanks for the links here and here suggested by #dww -- that solve for old versions (specifically calling out a solution on 0.8.2 when the modern release several major releases ahead, currently at 3.2.x).
I'll illustrate the closest use case, setting themes. In the case of general purpose themes, this is trivial: rather than appending + theme_minimal() on every plot, I can instead set the theme which persists across all plots.
library(ggplot2)
d <- diamonds[sample(1:nrow(diamonds), 1000), ]
## without theming
ggplot(d, aes(x=carat, y=price, color=clarity)) +
geom_point() +
theme_minimal() # must set this theme call for every plot
## setting theme
theme_set(theme_minimal())
ggplot(d, aes(x=carat, y=price, color=clarity)) +
geom_point() # plot in theme, for all subsequent plots
Is there a similar, modification that exists to set the color palette throughout? For example, a theme-based replacement for calling,
ggplot(d, aes(x=carat, y=price, color=clarity)) +
geom_point() +
scale_color_brewer(palette='Set2') # requesting a global option to set this for all plots
The linked solution that does not depend on old versions instead overloads the entire ggplot function. That seems risky.
ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer(palette = 'Set1')
There is a ggplot_global environment which is used internally within ggplot2 but isn't exported. You can see its structure by temporarily unlocking the bindings of a ggplot function and modifying it to return the contents of the environment as a list. You can do this non-destructively like this:
library(ggplot2)
get_ggplot_global <- function()
{
unlockBinding("theme_set", as.environment("package:ggplot2"))
backup <- body(theme_set)[[5]]
body(theme_set)[[5]] <- substitute(return(as.list(ggplot_global)))
global_list <- theme_set(theme_bw())
body(theme_set)[[5]] <- backup
lockBinding("theme_set", as.environment("package:ggplot2"))
return(global_list)
}
global_list <- get_ggplot_global()
names(global_list)
#> [1] "date_origin" "element_tree" "base_to_ggplot" "all_aesthetics"
#> [5] "theme_current" "time_origin"
By examining this you can see that ggplot global environment has an object called theme_current, which is why you can set the various line, text and axis elements globally including their colours.
When you are talking about a colour scheme in your question, you are referring to the colours defined in a scale object. This is not part of the ggplot_global environment, and you can't change the default scale object because there isn't one. When you create a new ggplot(), it has an empty slot for "scales".
You therefore have a few options:
Wrap ggplot with my_ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer()
Overwrite ggplot with the above function (as suggested by #Phil)
Create your own theme object that you add on with standard ggplot syntax
The best thing might be to just write a wrapper around ggplot. However, the third option is also quite clean and idiomatic. You could achieve it like this:
set_scale <- function(...)
{
if(!exists("doc_env", where = globalenv()))
assign("doc_env", new.env(parent = globalenv()), envir = globalenv())
doc_env$scale <- (ggplot() + eval(substitute(...)))$scales$scales[[1]]
}
my_scale <- function() if(exists("doc_env", where = globalenv())) return(doc_env$scale)
You would use this by doing (for example)
set_scale(scale_color_brewer(palette = "Set2"))
At the start of your document.
So now you can just do + my_scale() for each plot:
d <- diamonds[sample(1:nrow(diamonds), 1000), ]
ggplot(d, aes(x=carat, y=price, color=clarity)) +
geom_point() +
my_scale()
I would like to define a color palette for every plot built in a markdown document. In essence this would overwrite the default choices.
There are several very old answers -- thanks for the links here and here suggested by #dww -- that solve for old versions (specifically calling out a solution on 0.8.2 when the modern release several major releases ahead, currently at 3.2.x).
I'll illustrate the closest use case, setting themes. In the case of general purpose themes, this is trivial: rather than appending + theme_minimal() on every plot, I can instead set the theme which persists across all plots.
library(ggplot2)
d <- diamonds[sample(1:nrow(diamonds), 1000), ]
## without theming
ggplot(d, aes(x=carat, y=price, color=clarity)) +
geom_point() +
theme_minimal() # must set this theme call for every plot
## setting theme
theme_set(theme_minimal())
ggplot(d, aes(x=carat, y=price, color=clarity)) +
geom_point() # plot in theme, for all subsequent plots
Is there a similar, modification that exists to set the color palette throughout? For example, a theme-based replacement for calling,
ggplot(d, aes(x=carat, y=price, color=clarity)) +
geom_point() +
scale_color_brewer(palette='Set2') # requesting a global option to set this for all plots
The linked solution that does not depend on old versions instead overloads the entire ggplot function. That seems risky.
ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer(palette = 'Set1')
There is a ggplot_global environment which is used internally within ggplot2 but isn't exported. You can see its structure by temporarily unlocking the bindings of a ggplot function and modifying it to return the contents of the environment as a list. You can do this non-destructively like this:
library(ggplot2)
get_ggplot_global <- function()
{
unlockBinding("theme_set", as.environment("package:ggplot2"))
backup <- body(theme_set)[[5]]
body(theme_set)[[5]] <- substitute(return(as.list(ggplot_global)))
global_list <- theme_set(theme_bw())
body(theme_set)[[5]] <- backup
lockBinding("theme_set", as.environment("package:ggplot2"))
return(global_list)
}
global_list <- get_ggplot_global()
names(global_list)
#> [1] "date_origin" "element_tree" "base_to_ggplot" "all_aesthetics"
#> [5] "theme_current" "time_origin"
By examining this you can see that ggplot global environment has an object called theme_current, which is why you can set the various line, text and axis elements globally including their colours.
When you are talking about a colour scheme in your question, you are referring to the colours defined in a scale object. This is not part of the ggplot_global environment, and you can't change the default scale object because there isn't one. When you create a new ggplot(), it has an empty slot for "scales".
You therefore have a few options:
Wrap ggplot with my_ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer()
Overwrite ggplot with the above function (as suggested by #Phil)
Create your own theme object that you add on with standard ggplot syntax
The best thing might be to just write a wrapper around ggplot. However, the third option is also quite clean and idiomatic. You could achieve it like this:
set_scale <- function(...)
{
if(!exists("doc_env", where = globalenv()))
assign("doc_env", new.env(parent = globalenv()), envir = globalenv())
doc_env$scale <- (ggplot() + eval(substitute(...)))$scales$scales[[1]]
}
my_scale <- function() if(exists("doc_env", where = globalenv())) return(doc_env$scale)
You would use this by doing (for example)
set_scale(scale_color_brewer(palette = "Set2"))
At the start of your document.
So now you can just do + my_scale() for each plot:
d <- diamonds[sample(1:nrow(diamonds), 1000), ]
ggplot(d, aes(x=carat, y=price, color=clarity)) +
geom_point() +
my_scale()
Trying to add geom_points to an autolayer() line ("fitted" in pic), which is a wrapper part of autoplot() for ggplot2 in Rob Hyndmans forecast package (there's a base autoplot/autolayer in ggplot2 too so same likely applies there).
Problem is (I'm no ggplot2 expert, and autoplot wrapper makes it trickier) the geom_point() applies fine to the main call, but how do I apply similar to the autolayer (fitted values)?
Tried type="b" like normal geom_line() but it's not an object param in autolayer().
require(fpp2)
model.ses <- ets(mdeaths, model="ANN", alpha=0.4)
model.ses.fc <- forecast(model.ses, h=5)
forecast::autoplot(mdeaths) +
forecast::autolayer(model.ses.fc$fitted, series="Fitted") + # cannot set to show points, and type="b" not allowed
geom_point() # this works fine against the main autoplot call
This seems to work:
library(forecast)
library(fpp2)
model.ses <- ets(mdeaths, model="ANN", alpha=0.4)
model.ses.fc <- forecast(model.ses, h=5)
# Pre-compute the fitted layer so we can extract the data out of it with
# layer_data()
fitted_layer <- forecast::autolayer(model.ses.fc$fitted, series="Fitted")
fitted_values <- fitted_layer$layer_data()
plt <- forecast::autoplot(mdeaths) +
fitted_layer +
geom_point() +
geom_point(data = fitted_values, aes(x = timeVal, y = seriesVal))
There might be a way to make forecast::autolayer do what you want directly but this solution works. If you want the legend to look right, you'll want to merge the input data and fitted values into a single data.frame.
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.
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)
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()
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.
Suppose I plot something like this:
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length)) + geom_point()
Then I realise that I forgot to store the result (i.e. the ggplot object).
How can I retrieve the ggplot object corresponding to the current device?
Is there some ggplot function I can feed cur.dev() into to retrieve the associated plot object, or is it gone forever?
(Note - in this case I could do p <- .Last.value, but let's assume I've typed a few commands since then so that this is not available.
Motivation - adding a hook to knitr to automagically set fig.cap to the title of the plot (if any)).
You are after last_plot
It retrieves the last plot to be modified or created and is used by ggsave
Note that it is the last plot modified or created
set_last_plot is the relevant code (see the source)
It is important note that creating modifying or rendering a ggplot object will set the last plot.
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length)) + geom_point()
f <- last_plot()
# will return the iris plot
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
last_plot()
# will return p
f
last_plot()
# is now f
It will also not count any modifications / manipulation using grid or gridExtra (such as grid.arrange / grid.text
The last object assigned (and it does not need to be a plot object) can be recovered with .Last.value
>require(ggplot2)
#Loading required package: ggplot2
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length)) + geom_point()
gp <- .Last.value
gp
This should return plot objects that have been modified by grid functions as long as there was an assignment. I'm not sure it this is true for actions that were mediated through print calls.