Associate a color palette with ggplot2 theme - r

I want my ggplot2 theme to use a specific set of colors, but don't see how to avoid a separate line outside of the theme.
I have this data:
library(ggplot2)
mycars <- mtcars
mycars$cyl <- as.factor(mycars$cyl)
And here's a dummy theme I plot with:
mytheme <- theme(panel.grid.major = element_line(size = 2))
ggplot(mycars, aes(x = wt, y = mpg)) +
geom_point(aes(color = cyl)) +
mytheme
I want the point colors to default to my custom palette:
mycolors <- c("deeppink", "chartreuse", "midnightblue")
Can I somehow add that to my ggplot2 theme so that I don't constantly repeat this extra line of code at the end:
ggplot(mycars, aes(x = wt, y = mpg)) +
geom_point(aes(color = cyl)) +
mytheme +
scale_color_manual(values = mycolors)
I tried:
mytheme2 <- mytheme + scale_color_manual(values = mycolors)
But got:
Error: Don't know how to add scale_color_manual(values = mycolors) to
a theme object

Hi you can put your custom element in a list :
# Data
library("ggplot2")
mycars <- mtcars
mycars$cyl <- as.factor(mycars$cyl)
# Custom theme
mytheme <- theme(panel.grid.major = element_line(size = 2))
mycolors <- c("deeppink", "chartreuse", "midnightblue")
# put the elements in a list
mytheme2 <- list(mytheme, scale_color_manual(values = mycolors))
# plot
ggplot(mycars, aes(x = wt, y = mpg)) +
geom_point(aes(color = cyl)) +
mytheme2

I sometimes use this little trick to change colour palettes between plots,
library(ggplot2)
mycars <- mtcars
mycars$cyl <- as.factor(mycars$cyl)
scale_colour_discrete <- function(...) scale_colour_manual(values=palette())
(p <- ggplot(mycars, aes(x = wt, y = mpg)) +
geom_point(aes(color = cyl)) )
palette(c("deeppink", "chartreuse", "midnightblue"))
p
palette(RColorBrewer::brewer.pal(5, "Set1"))
p

Related

Can I manually change boxplot color with ggMarginal?

I'm using ggMarginal to make marginal boxplots. Is there a way to manually change the color and/or fill of the boxplots without a grouping variable? I'd like to have different colors on the x boxplot and the y boxplot.
library(tidyverse)
library(ggExtra)
foo <- data.frame(x=rnorm(100,mean=1,sd=1),
y=rnorm(100,mean=2,sd=2))
p1 <- ggplot(data = foo,aes(x=x,y=y)) +
geom_point() + coord_equal()
ggMarginal(p1, type="boxplot", size=12)
Provided I have understood you correctly, you can do the following
p1 <- ggplot(data = foo, aes(x = x, y = y)) +
geom_point() +
coord_equal()
ggMarginal(
p1,
type = "boxplot",
size = 12,
xparams = list(colour = "blue"),
yparams = list(colour = "red"))

Make ggplot panels the same height in ggarrange

I have two ggplots that I'm combining with ggarrange(). One plot has long labels, that I wrap with scale_x_discrete(labels = function(x) str_wrap(x, width = 10)), where str_wrap() is from the stringr package. However, when I combine them, the first panel is reduced in height to accommodate for the space required by the wrapped labels. I already tried all kinds of variations of adjusting the margins both in theme(plot.margin = margin()) as well as axis.text.x=element_text(margin = unit(c(), "cm")), to no avail. I'm probably missing something very obvious, but I just can't get the panels to match in height, regardless of how much space is needed by the axis labels.
Thanks!
plot1 <- ggplot(data=dat, aes(x=x, y=y)) +
theme_bw() +
geom_point(size=2) +
labs(x='', y='', title='') +
scale_x_discrete(labels = function(x) str_wrap(x, width = 10))
plot2 <- ggplot(data=dat, aes(x=x2, y=y2)) +
theme_bw() +
geom_point(size=2) +
labs(x='', y='', title='')
ggarrange(plot1, plot2, ncol=2)
it's doable within ggarrange using the argument align.
Here using the iris example from above
# The plots
plot1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
geom_boxplot() +
scale_x_discrete(labels = c("setosa" = "Setosa", "versicolor" = "Versicolor", "virginica" = "A very long name for \n Vircinica just to reproduce \n the problem"))
plot2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
geom_density(alpha = 0.8) + theme(legend.position = "none")
Default:
ggarrange(plot1, plot2, ncol = 2)
With align:
ggarrange(plot1, plot2, ncol = 2, align = "h")
Apparently, I'm not allowed to post images so you'll have to click on the link or run the code.
Hope that helps!
One option to stick with ggarrange is to adjust the heights of your plots. To illustrate the case with iris data:
plot1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
geom_boxplot() +
scale_x_discrete(labels = c("setosa" = "Setosa", "versicolor" = "Versicolor", "virginica" = "A very long name for \n Vircinica just to reproduce \n the problem"))
plot2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
geom_density(alpha = 0.8) + theme(legend.position = "none")
ggarrange(plot1, plot2, ncol = 2)
You can then use ggplotGrob and grid::unit.pmax to get the same hight for both plots:
pg1 <- ggplotGrob(plot1)
pg2 <- ggplotGrob(plot2)
maxHeight = grid::unit.pmax(pg1$heights, pg2$heights)
pg1$heights <- as.list(maxHeight)
pg2$heights <- as.list(maxHeight)
ggarrange(pg1, pg2, ncol = 2)

Customize position of common legend in R

I need to arrange multiple plots with common legend in a single page and used following code:
library(ggpubr)
ggarrange(fig1, fig2, fig3, nrow=2, ncol=2, common.legend = TRUE, legend="right")
Now, I'm wondering if there are other packages/ways where I can customize position of the common legend since the 'ggarrange' has only 'top', 'bottom', 'right', 'left' and 'none' options for this. Since second row and second column is empty, I want the legend be appeared there.
Perhaps the cowplot package will work?
Minimal Reproducible Example:
library(tidyverse)
library(ggpubr)
fig1 <- ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point()
fig2 <- ggplot(mtcars, aes(x = cyl, y = mpg, group = cyl, fill = factor(cyl))) +
geom_boxplot() +
scale_fill_viridis_d()
fig3 <- ggplot(mtcars, aes(x = disp, y = mpg)) +
geom_point()
ggarrange(fig1, fig2, fig3, nrow=2, ncol=2, common.legend = TRUE, legend="right")
Using the cowplot package:
library(cowplot)
legend <- get_legend(fig2)
fig2_no_legend <- ggplot(mtcars, aes(x = cyl, y = mpg, group = cyl, fill = factor(cyl))) +
geom_boxplot() +
scale_fill_viridis_d() +
theme(legend.position = "none")
cowplot::plot_grid(fig1, fig2_no_legend, fig3, legend, nrow = 2, ncol = 2)

chose colors for scale_colour_colorblind() in ggthemes

I would like to chose specific colors of the colorblind_pal() from ggthemes
This works:
library(ggplot2)
library(ggthemes)
p <- ggplot(mtcars) + geom_point(aes(x = wt, y = mpg,
colour = factor(gear))) + facet_wrap(~am)
p + theme_igray() + scale_colour_colorblind()
Now I would like to chose specific colors of the colorblind_pal() for my plot. How can I chose them?
I tried following with no success:
my_palette <- palette(c("#000000","#F0E442","#D55E00"))
p + theme_igray() + scale_colour_colorblind(my_palette)
You could use scale_color_manual in order to manually specify the colors to use:
library(ggplot2)
library(ggthemes)
p <- ggplot(mtcars) +
geom_point(aes(x = wt, y = mpg, colour = factor(gear))) +
facet_wrap(~am) +
theme_igray() +
scale_color_manual(values = c("#000000","#F0E442","#D55E00"))
p
Since you already have the colors, you can just use scale_color_manual:
library(ggthemes)
library(ggplot2)
COLS=colorblind_pal()(8)
COLS = COLS[c(1,5,7)]
p <- ggplot(mtcars) + geom_point(aes(x = wt, y = mpg,
colour = factor(gear))) + facet_wrap(~am)
p + theme_igray() + scale_colour_manual(values=COLS))

Draw a "grid" between arranged plots

I'm working with 4 different plots and I'm using ggarrange() from the ggpubr-package to put them in a single plot. I've prepared an example:
library(ggpubr)
library(ggplot2)
p1 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() + ggtitle("Plot 1")
p2 <- ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point() + ggtitle("Plot 2")
p3 <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Width)) + geom_point() + ggtitle("Plot 3")
p4 <- ggplot(iris, aes(x = Petal.Length, y = Sepal.Width)) + geom_point() + ggtitle("Plot 4") +
facet_wrap(~Species)
plot.list <- list(p1, p2, p3, p4)
ggarrange(plotlist = plot.list)
Output:
I would like to draw a border around the single plots, like so:
Is there any way to draw this border? Thanks!
grid.polygon() is quite manual but I think it can do the trick:
Using RStudio
library("ggpubr")
library(ggplot2)
library(gridExtra)
library(grid)
p1 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() + ggtitle("Plot 1")
p2 <- ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point() + ggtitle("Plot 2")
p3 <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Width)) + geom_point() + ggtitle("Plot 3")
p4 <- ggplot(iris, aes(x = Petal.Length, y = Sepal.Width)) + geom_point() + ggtitle("Plot 4") +
facet_wrap(~Species)
plot.list <- list(p1, p2, p3, p4)
ggarrange(plotlist = plot.list)
x = c(0, 0.5, 1, 0.5, 0.5, 0.5)
y = c(0.5, 0.5, 0.5,0, 0.5, 1)
id = c(1,1,1,2,2,2)
grid.polygon(x,y,id)
Using Shiny (Edit)
When doing it within a shiny-app, ones needs to add the grid using annotation_custom(), as follows:
ggarrange(plotlist = plot.list) +
annotation_custom(
grid.polygon(c(0, 0.5, 1, 0.5, 0.5, 0.5),
c(0.5, 0.5, 0.5,0, 0.5, 1),
id = c(1,1,1,2,2,2),
gp = gpar(lwd = 1.5)))
Not sure if this will work for you, but you can just put borders around your individual plots. However, this includes borders on the outside of the layout. Your description seems like you're not opposed to that, but there are only inner gridlines in your example plot.
You could add the theme call when you create the plots; rather than editing the plot creation, I just did it to each plot in the list before sticking them all together.
library(ggpubr)
library(ggplot2)
#### same plot creation here ######
plot.list <- lapply(list(p1, p2, p3, p4),
function(p) p + theme(plot.background = element_rect(color = "black")))
ggarrange(plotlist = plot.list)

Resources