Add ylab to ggplot with fivethirtyeight ggtheme - r

Is it possible to add a label to the y axis if you are using theme_fivethirtyeight? I tried ylab but it does not work:
library(ggplot2)
library(ggthemes)
p2 <- ggplot(mtcars, aes(x = wt, y = mpg, colour = factor(gear))) +
geom_point() +
ggtitle("Cars")
p2 + geom_smooth(method = "lm", se = FALSE) +
scale_color_fivethirtyeight("cyl") +
theme_fivethirtyeight() + ylab('SOMETHING')

You can, but it'll take a bit more work than ylab because you need to change some of the theme settings that are the defaults in theme_fivethirtyeight. If you take a look at the code for theme_fivethirtyeight (just run theme_fivethirtyeight in your console to see the code), you'll see that axis.title is set to element_blank(). So this theme has no axis titles at all. You'll need to change this if you want to set a y axis label.
For example, you could add
theme(axis.title = element_text()) + ylab('Something')
to your graph, but then you'll get an x axis label, as well.
An alternative would be to use
theme(axis.title = element_text(), axis.title.x = element_blank()) + ylab('Something')
Asaxis.title.y inherits from axis.title, it didn't work to just set axis.title.y to element_text().

Related

R / ggplot2: How to make a facet panel label background color conditional on a value in another column in the dataframe

Here's the code I'm using to plot a regression line for each US state, using facet wrapping in ggplot2:
ggplot(data = allYearsSlope, aes(x = year, y = residual)) +
geom_smooth(method = "lm", se = FALSE) +
facet_wrap(~ state, ncol = 8) +
theme(
strip.text = element_text(size = rel(0.5), face = "bold"),
panel.background = element_rect(fill = "white"),
panel.border = element_rect(color="purple",fill=NA),
plot.background = element_blank(),
axis.ticks.x = element_blank()
) +
scale_x_continuous(labels = NULL)
It works fine. But now I'm trying to color the facet panel label (the box that, in this case, contains the name of the state that appears above each panel) according to a value ("blue" or "green") that I'm storing in the dataframe that contains the rest of the data. The color is stored in allYearsSlope$panelColor. Having a really hard time figuring out how to do this.
I've read the solution here: Conditional formatting of panel background in GGplot2. Along with the few other SO answers it refers to. But I don't really see a way to do this coming out of those answers. Any ideas, anyone?
Big thanks for any help.
The least hacky way I know of to do this is via the ggh4x package:
library(ggh4x)
#> Loading required package: ggplot2
ggplot(iris, aes(Sepal.Width, Petal.Length)) +
geom_point() +
geom_smooth(formula = y ~ x, method = "lm") +
facet_grid2(.~Species, scales = 'free',
strip = strip_themed(
background_x = list(element_rect(fill = "red"),
element_rect(fill = "green"),
element_rect(fill = "yellow"))))
Created on 2022-12-24 with reprex v2.0.2
Edit
It's not possible to map strip color as an aesthetic, so this needs to be done by writing your own automation. Instead of passing a list of element_rect manually, we can create a list of element_rect whose fill color is dependent on the properties of a regression each faceted subset. This isn't quite as tricky as it sounds. Here for example, we color the strips according to the slope of the regression line in the panel - red for < 0.5 and green for > 0.5:
library(ggh4x)
ggplot(iris, aes(Sepal.Width, Petal.Length)) +
geom_point() +
geom_smooth(formula = y ~ x, method = "lm") +
facet_grid2(.~Species, scales = 'free',
strip = strip_themed(
background_x = lapply(split(iris, iris$Species), function(x) {
element_rect(fill = ifelse(
coef(lm(Petal.Length ~ Sepal.Width, data = x))[2] < 0.5,
"red",
"green"))
})
))

Stacking multiple figures together in ggplot

I am attempting to make publication ready figures where the bottom axis (with tick marks) of one figure is cleanly combined with the top axis of the figure below it. Here is an example of what it might look like, although this one doesn't have tick marks on each panel:
Here is my attempt to do so, by simply using grid.arrange:
#Libraries:
library(ggplot2)
library(dplyr)
library(gridExtra)
#Filter to create two separate data sets:
dna1 <- DNase %>% filter(Run == 1)
dna2 <- DNase %>% filter(Run == 2)
#Figure 1:
dna1_plot <- ggplot(dna1, aes(x = conc, y = density)) + geom_point() + theme_classic() +
theme(axis.title.x = element_blank())
#Figure 2:
dna2_plot <- ggplot(dna2, aes(x = conc, y = density)) + geom_point() + theme_classic()
#Using grid.arrange to combine:
dna <- grid.arrange(dna1_plot, dna2_plot, nrow = 2)
And an attempt with some adjustments to the plot margins, although this didn't seem to work:
dna1_plot_round2 <- ggplot(dna1, aes(x = conc, y = density)) + geom_point() + theme_classic() +
theme(axis.title.x = element_blank(),
plot.margin = (0,0,0,0), "cm")
dna2_plot_round2 <- ggplot(dna2, aes(x = conc, y = density)) + geom_point() + theme_classic() +
theme(plot.margin = unit(c(-0.5,-1,0,0), "cm"))
dna_round2 <- grid.arrange(dna1_plot_round2, dna2_plot_round2, nrow = 2)
Does anyone know the best way to stack figures like this in ggplot? Is there a better way than using grid.arrange? If possible it would be great to see how to do it with/without tick marks on each x axis as well.
Thank you!
You don't need any non-native ggplot stuff. Keep your data in one data frame and use facet_grid.
dna <- DNase %>% filter(Run %in% 1:2)
ggplot(dna, aes(x = conc, y = density)) +
geom_point() +
theme_bw() +
facet_grid(rows = vars(Run)) +
theme(panel.spacing = unit(0, "mm"))
The R package deeptime has a function called ggarrange2 that can achieve this. Instead of just pasting the plots together like grid.arrange (and ggarrange), it lines up all of the axes and axis labels from all of the plots.
# remove bottom axis elements, reduce bottom margin, add panel border
dna1_plot_round2 <- ggplot(dna1, aes(x = conc, y = density)) + geom_point() + theme_classic() +
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank(), axis.title.x = element_blank(),
plot.margin = margin(0,0,-.05,0, "cm"), panel.border = element_rect(fill = NA))
# reduce top margin (split the difference so the plots are the same height), add panel border
dna2_plot_round2 <- ggplot(dna2, aes(x = conc, y = density)) + geom_point() + theme_classic() +
theme(plot.margin = margin(-.05,0,0,0, "cm"), panel.border = element_rect(fill = NA))
dna_round2 <- ggarrange2(dna1_plot_round2, dna2_plot_round2, nrow = 2)
You might also try the fairly recent patchwork package, although I don't have much experience with it.
Note that while Gregor's answer may be fine for this specific example, this answer might be more appropriate for other folks that come across this question (and see the example at the top of the question).
For your purposes, I believe Gregor Thomas' answer is best. But if you are in a situation where facets aren't the best option for combining two plots, the newish package {{patchwork}} handles this more elegantly than any alternatives I've seen.
Patchwork also provides lots of options for adding annotations surrounding the combined plot. The readME and vignettes will get you started.
library(patchwork)
(dna1_plot / dna2_plot) +
plot_annotation(title = "Main title for combined plots")
Edit to better address #Cameron's question.
According to the package creator, {{patchwork}} does not add any space between the plots. The white space in the example above is due to the margins around each individual ggplot. These margins can be adjusted using the plot.margin argument in theme(), which takes a numeric vector of the top, right, bottom, and left margins.
In the example below, I set the bottom margin of dna1_plot to 0 and strip out all the bottom x-axis ticks and text. I also set the top margin of dna2_plot to 0. Doing this nearly makes the y-axis lines touch in the two plots.
dna1_plot <- ggplot(dna1, aes(x = conc, y = density)) + geom_point() + theme_classic() +
theme(axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.x = element_blank(),
plot.margin = unit(c(1,1,0,1), "mm"))
#Figure 2:
dna2_plot <- ggplot(dna2, aes(x = conc, y = density)) + geom_point() + theme_classic() +
theme(plot.margin = unit(c(0,1,1,1), "mm"))
(dna1_plot / dna2_plot)

Displaying multiple factors with Sina plots

NOTE: I have updated this post following discussion with Z. Lin. Originally, I had simplified my problem to a two factor design (see section "Original question"). However, my actual data consists of four factors, requiring facet_grid. I am therefore providing an example for a four factor design further below (see section "Edit").
Original question
Let's assume I have a two factor design with dv as my dependent variable and iv.x and iv.y as my factors/independent variables. Some quick sample data:
DF <- data.frame(dv = rnorm(900),
iv.x = sort(rep(letters[1:3], 300)),
iv.y = rep(sort(rep(rev(letters)[1:3], 100)), 3))
My goal is to display each condition separately as can nicely be done with violin plots:
ggplot(DF, aes(iv.x, dv, colour=iv.y)) + geom_violin()
I have recently come across Sina plots and would like to do the same here. Unfortunately Sina plots don't do this, collapsing the data instead.
ggplot(DF, aes(iv.x, dv, colour=iv.y)) + geom_sina()
An explicit call to position dodge doesn't help either, as this produces an error message:
ggplot(DF, aes(iv.x, dv, colour=iv.y)) + geom_sina(position = position_dodge(width = 0.5))
The authors of Sina plots have already been made aware of this issue in 2016:
https://github.com/thomasp85/ggforce/issues/47
My problem is more in terms of time. We soon want to submit a manuscript and Sina plots would be a great way to display our data. Can anyone think of a workaround for Sina plots such that I can still display two factors as in the example with violin plots above?
Edit
Sample data for a four factor design:
DF <- data.frame(dv=rnorm(400),
iv.w=sort(rep(letters[1:2],200)),
iv.x=rep(sort(rep(letters[3:4],100)), 2),
iv.y=rep(sort(rep(rev(letters)[1:2],50)),4),
iv.z=rep(sort(rep(letters[5:6],25)),8))
An example with violin plots of what I would like to create using Sina plots:
ggplot(DF, aes(iv.x, dv, colour=iv.y)) +
facet_grid(iv.w ~ iv.z) +
geom_violin(aes(y = dv, fill = iv.y),
position = position_dodge(width = 1))+
stat_summary(aes(y = dv, fill = iv.y), fun.y=mean, geom="point",
colour="black", show.legend = FALSE, size=.2,
position=position_dodge(width=1))+
stat_summary(aes(y = dv, fill = iv.y), fun.data=mean_cl_normal, geom="errorbar",
position=position_dodge(width=1), width=.2, show.legend = FALSE,
colour="black", size=.2)
Edited solution, since OP clarified that facets are required:
ggplot(DF, aes(x = interaction(iv.y, iv.x),
y = dv, fill = iv.y, colour = iv.y)) +
facet_grid(iv.w ~ iv.z) +
geom_sina() +
stat_summary(fun.y=mean, geom="point",
colour="black", show.legend = FALSE, size=.2,
position=position_dodge(width=1))+
stat_summary(fun.data=mean_cl_normal, geom="errorbar",
position=position_dodge(width=1), width=.2,
show.legend = FALSE,
colour="black", size=.2) +
scale_x_discrete(name = "iv.x",
labels = c("c", "", "d", "")) +
theme(panel.grid.major.x = element_blank(),
axis.text.x = element_text(hjust = -4),
axis.ticks.x = element_blank())
Instead of using facets to simulate dodging between colours, this approach creates a new variable interaction(colour.variable, x.variable) to be mapped to the x-axis.
The rest of the code in scale_x_discrete() & theme() are there to hide the default x-axis labels / ticks / grid lines.
axis.text.x = element_text(hjust = -4) is a hack that shifts x-axis labels to approximately the right position. It's ugly, but considering the use case is for a manuscript submission, I assume the size of plots will be fixed, and you just need to tweak it once.
Original solution:
Assuming your plots don't otherwise require facetting, you can simulate the appearance with facets:
ggplot(DF, aes(x = iv.y, y = dv, colour = iv.y)) +
geom_sina() +
facet_grid(~iv.x, switch = "x") +
labs(x = "iv.x") +
theme(axis.text.x = element_blank(), # hide iv.y labels
axis.ticks.x = element_blank(), # hide iv.y ticks
strip.background = element_blank(), # make facet strip background transparent
panel.spacing.x = unit(0, "mm")) # remove horizontal space between facets

title font size in ggplot2 does not change

I know this is a very basic question, but I have trouble changing font size of axis labels in ggplot2. I used the code like below:
a <- ggplot(data1, aes(x=data2, y=data3)) +
geom_hline(yintercept=c(1, -1)) +
labs(x = data2, y = data3) +
theme_bw() +
theme(axis.text=element_text(size=10))
I tried to change the axis label size but they do not change.... Does anybody have suggestion?
Check out here how to alter labels in ggplot:
http://www.sthda.com/english/wiki/ggplot2-title-main-axis-and-legend-titles
In the example below we use axis.title to change the size, colour and face of the text.
library(ggplot2)
ggplot(mtcars, aes(x=mpg, y=disp)) +
geom_point() +
theme_bw() +
theme(axis.title=element_text(size=10, colour = "red", face = "bold"))

How does one move the tick labels closer to the axis?

library(ggplot2)
p <- ggplot(mtcars, aes(x=mpg, y=wt*1000, color = factor(cyl))) + geom_point()
p + ylab("weight (lb)") +theme_bw()
I would like to move 5000, 4000, 3000, and 2000 closer to the vertical axis. I know one can instead use theme(axis.title.y=element_text(vjust=0.36,hjust=.36)) or similar to move the axis title further away, but sometimes I really want to move the tick labels, not the axis title.
Version 2.0.0 introduced the new margin() which we can use here:
ggplot(mtcars, aes(x = mpg, y = wt*1000, color = factor(cyl))) +
geom_point() +
ylab("weight (lb)") +
theme_bw() +
theme(axis.text.y = element_text(margin = margin(r = 0)))
My reading of this issue on github is, that you should use vjust only for the y-axis and hjust only for the x-axis. To alter the distance between tick-label and axis, use margin(r = x) on the y-axis, and margin(t = x) on the x-axis. Doc for element_text reads: "When creating a theme, the margins should be placed on the side of the text facing towards the center of the plot."
One solution would be to use axis.ticks.margin= element of theme() and set it to 0. But this will influence both axis.
library(ggplot2)
library(grid)
ggplot(mtcars, aes(x=mpg, y=wt*1000, color = factor(cyl))) + geom_point() +
ylab("weight (lb)") +theme_bw()+
theme(axis.ticks.margin=unit(0,'cm'))

Resources