When I facet a plot I often want to point out interesting comparisons between groups. For instance, in the plot produced by this code I'd like to point out that the second and third columns are nearly identical.
library(tidyverse)
ggplot(mtcars, aes(x = as.factor(am), y = mpg)) +
stat_summary(fun.y = "mean", geom = "col") +
stat_summary(fun.data = mean_se, geom = "errorbar", width = .1) +
facet_grid(~ vs)
Currently I can only make this annotation by exporting my plot to another app like Preview or Powerpoint and manually adding the lines and text across facets.
My efforts to add an annotation across facets results in annotations that do not leave their own facet. See below.
ggplot(mtcars, aes(x = as.factor(am), y = mpg)) +
stat_summary(fun.y = "mean", geom = "col") +
stat_summary(fun.data = mean_se, geom = "errorbar", width = .1) +
facet_grid(~ vs) +
annotate("errorbarh", xmin = 2, xmax = 3, y = 25, height = .5,
color = "red") +
annotate("text", x = 2.5, y = 27, label = "NS", color = "red")
Any advice about how to extend lines and annotations across facets would be greatly appreciated.
Related
Do you know how to add the yellow highlight effect of this 538 graph for both text and graphs using ggplot2?
Thanks in advance!
Update after clarification
It really depends on the structure of the data and what you are using to plot. However, if you wanted to add large highlights to particular plots, then you could plot the same geom_line but change the aesthetics of it (though the highlight will not connect to adjacent plots).
library(ggplot2)
hlines <- mtcars %>%
group_by(cyl) %>%
summarise(MN = min(wt))
ggplot(mtcars) +
geom_line(aes(mpg, wt), colour = "lightyellow", size = 80) +
geom_line(aes(mpg, wt)) +
geom_hline(
data = hlines,
aes(yintercept = MN),
linetype = "dotted",
color = "grey",
size = 1.5
) +
facet_wrap( ~ cyl) +
theme_bw()
Output
For text, in ggplot2, you can add fill to the background of annotations. But it again really depends on the structure and how you are plotting the text. You could split up the annotations, so that you could fill one and not the other part of the text.
ggplot(mtcars) +
geom_line(aes(mpg, wt), colour = "lightyellow", size = 80) +
geom_line(aes(mpg, wt)) +
annotate(
geom = "text",
x = 30,
y = 5,
label = "It hasn't really dropped off"
) +
annotate(
geom = "label",
x = 30,
y = 4.75,
label = "since he first won office in 2016",
fill = "lightyellow",
label.size = NA
)
Output
First Answer
It depends on what exactly you are looking for/what your data looks like. But if you are wanting to place a line at the minimum under a line graph in a faceted plot, then you could do something like this:
library(ggplot2)
hlines <- mtcars %>%
group_by(cyl) %>%
summarise(MN = min(wt))
ggplot(mtcars) +
geom_line(aes(mpg, wt)) +
geom_hline(
data = hlines,
aes(yintercept = MN),
linetype = "dotted",
color = "grey",
size = 1.5
) +
facet_wrap( ~ cyl) +
theme_bw()
Output
If you just have a single plot, then you can use geom_hline and just provide the y intercept.
ggplot(mtcars) +
geom_line(aes(mpg, wt)) +
geom_hline(yintercept = 3.5,
linetype = "dotted",
color = "grey",
size = 1.5
) +
theme_bw()
I am trying to make a figure that will have density plots on the bottom and corresponding boxplots above the density plots. My density plots and boxplots are filled/colored by a categorical variable. I cannot figure out a way to get the boxplots to be above the density plots and also dodged. This is what I've been able to get so far:
d <- mtcars
d$cyl <- as.factor(d$cyl)
fig <- ggplot(data = d) +
geom_density(aes(x = mpg, fill = cyl),
position = "dodge",
na.rm = TRUE) +
geom_boxplot(aes(x = mpg, color = cyl),
position = ggstance::position_dodgev(height = 1),
width = .05, show.legend = FALSE,
na.rm = TRUE) +
facet_grid(~am, scales = "free_x") +
scale_fill_brewer(palette = "Set2") +
scale_color_brewer(palette = "Set2") +
theme_minimal() +
guides(color = FALSE, fill = FALSE)
fig
But, as you can see, this does not uniformly move the boxplots above the density plots. I've also used
geom_boxplot(aes(x = mpg, color = cyl),
position = position_nudge(x = 0, y = .3),
width = .05, show.legend = FALSE,
na.rm = TRUE) +
but I end up having my boxplots overlap (they are no longer vertically dodged). Basically, I'm looking for a way to set a vertical height for my group of boxplots so they're above my density plots AND keep them vertically dodged from one another. Any suggestions are much appreciated.
Map the value you want your boxes to be centered around to y, inside the aes for geom_boxplot. E.g.:
ggplot(data = d) +
geom_density(aes(x = mpg, fill = cyl)) +
geom_boxplot(aes(x = mpg, color = cyl, y = 1),
position = ggstance::position_dodgev(height = 0.2),
width = .05, show.legend = FALSE) +
facet_grid(~am, scales = "free_x") +
scale_fill_brewer(palette = "Set2") +
scale_color_brewer(palette = "Set2") +
theme_minimal() +
guides(color = FALSE, fill = FALSE)
Also, don't try to dodge geom_density.
I have my boxplot and I added the mean with stat_summary as a line over the box plot. I want to add the standard error, but I don't want errorbar.
Basically, I want to add the standard error as shaded area, as you can do using geom_ribbon.
I used the PlantGrowth dataset to show you briefly what I've tried.
library(ggplot2)
ggplot(PlantGrowth, aes(group, weight))+
stat_boxplot( geom='errorbar', linetype=1, width=0.5)+
geom_boxplot(fill="yellow4",colour="black",outlier.shape=NA) +
stat_summary(fun.y=mean, colour="black", geom="line", shape=18, size=1,aes(group=1))+
stat_summary(fun.data = mean_se, geom = "errorbar")
I did it using geom_errorbar in stat_summary, and tried to substitute geom_errorbar with geom_ribbon, as I saw in some other examples around the web, but it doesn't work.
Something like this one, but with the error as shaded area instead of error bars (which make it a bit confusing to see)
Layering so many geoms becomes hard to read, but here's a simplified version with a few options. Aside from just paring things down a bit to see what I was editing, I added a tile as a summary geom; tile is similar to rect, except it assumes it will be centered at whatever its x value is, so you don't need to worry about the x-axis placement that geom_rect requires. You might experiment with fill colors and opacity—I made the boxplots white just to illustrate better.
library(ggplot2)
gg <- ggplot(PlantGrowth, aes(x = group, y = weight)) +
stat_boxplot(geom = "errorbar", width = 0.5) +
geom_boxplot(fill = "white", outlier.shape = NA, width = 0.7) +
stat_summary(aes(group = 1), fun.y = mean, geom = "line")
gg +
stat_summary(fun.data = mean_se, geom = "tile", width = 0.7,
fill = "pink", alpha = 0.6)
Based on your comments that you want a ribbon, you could instead use a ribbon with group = 1 the same as for the line.
gg +
stat_summary(aes(group = 1), fun.data = mean_se, geom = "ribbon",
fill = "pink", alpha = 0.6)
The ribbon doesn't make a lot of sense across a discrete variable, but here's an example with some dummy data for a continuous group, where this setup becomes more reasonable (though IMO still hard to read).
pg2 <- PlantGrowth
set.seed(123)
pg2$cont_group <- floor(runif(nrow(pg2), 1, 6))
ggplot(pg2, aes(x = cont_group, y = weight, group = cont_group)) +
stat_boxplot(geom = "errorbar", width = 0.5) +
geom_boxplot(fill = "white", outlier.shape = NA, width = 0.7) +
stat_summary(aes(group = 1), fun.y = mean, geom = "line") +
stat_summary(aes(group = 1), fun.data = mean_se, geom = "ribbon",
fill = "pink", alpha = 0.6)
I am trying to combine 2 dependent variables (or 2 graphs) in one graph using ggplot function. All the suggestions I could find online were not really helpful in my case.
Graph1 <- ggplot(mydata, aes(age, conf))
Graph1 + stat_summary(fun.y = mean, geom = "point") +
stat_summary(fun.y = mean, geom = "line", aes(group = 1)) +
stat_summary(fun.data = mean_cl_boot, geom = "errorbar", width = 0.2) +
labs(x = "Age Group", y = "Accuracy (%)") + ylim(0, 1)
Graph2 <- ggplot(mydata, aes(age, acc))
Graph2 + stat_summary(fun.y = percent(1), geom = "point") +
stat_summary(fun.y = mean, geom = "line", aes(group = 1), linetype = "dashed") +
stat_summary(fun.data = mean_cl_boot, geom = "errorbar", width = 0.2) +
labs(x = "Age Group", y = "Accuracy (%)") + ylim(0, 1)
In addition to this, I will need to have the means and error bars not overlapping. Any advice would be greatly appreciated.
After further investigation I have found the following suggestion which seems to be a great solution. However, I cannot install tidyr as it incompatible with the current R version. I have tried different options to download the package, without success.
library(tidyr)
home.land.byyear <- gather(housing.byyear, value = "value", key = "type",
Home.Value, Land.Value)
ggplot(home.land.byyear, aes(x=Date, y=value, color=type)) + geom_line()
see http://tutorials.iq.harvard.edu/R/Rgraphics/Rgraphics.html
library(ggplot2)
library(Hmisc)
data(mtcars)
myplot <- ggplot(mtcars, aes(x = as.factor(cyl), y = qsec)) +
geom_boxplot() +
stat_summary(fun.y = mean, geom = "point", shape = 5, size = 2) +
stat_summary(fun.data = mean_cl_normal, geom = "errorbar",
width = 0.2)
produces
I'd like to dodge the mean and error bars a bit to the right, such that the error bars don't obscure the IQR line of the boxplot. Specifying position=position_dodge(.5) doesn't seem to work, because geom_errorbardoesn't know about geom_boxplot.
You can introduce a new variable which you use as the x offset for your errorbars:
library(ggplot2)
library(Hmisc)
data(mtcars)
mtcars$cyl.n <- as.numeric(as.factor(mtcars$cyl)) + .5
(myplot <- ggplot(mtcars, aes(x = as.factor(cyl), y = qsec)) +
geom_boxplot() +
stat_summary(aes(x = cyl.n), fun.y = mean, geom = "point", shape = 5, size = 2) +
stat_summary(aes(x = cyl.n), fun.data = mean_cl_normal, geom = "errorbar",
width = 0.2))
The as.numeric(as.factor(.)) makes sure that the new error bar is spaced at the same position as the boxplots but shifted by 0.5 units.