I have a violin+boxplots plot I do with the iris
data as an example, like this:
data(iris)
iris_long <- as.data.frame(tidyr::pivot_longer(iris, -Species, names_to = "variable", values_to = "value"))
P <- ggplot2::ggplot(iris_long, ggplot2::aes(x=variable, y=value)) +
ggplot2::geom_violin() +
ggplot2::geom_boxplot(width=0.2, alpha=0, outlier.shape=NA, coef=0, show.legend=FALSE) +
ggplot2::stat_summary(fun=mean, geom="point", shape=16, size=2, show.legend=FALSE) +
ggplot2::theme_light()
grDevices::pdf(file="test.pdf", height=4, width=8)
print(P)
grDevices::dev.off()
Which produces this plot:
Now I also have a data frame with cutoff values per variable level, like this one:
cutoffs <- data.frame(variable=sort(names(iris)[-length(names(iris))]), cutoff=c(7,2.5,8,4.5))
I just want to plot them as red cutoff bars on top of each violin+boxplot, so the result looks similar to this:
What would be the best way to go about this? geom_segment
? or is there an easier way with maybe geom_errorbar
?
EDIT
Now I am trying to accomplish the same, but including the Species
information in the violin+boxplots plot. Once I add fill=Species
(with the data formatted appropriately), I get an error with geom_segment
that I do not know how to debug.
Besides, even without geom_segment
the violins and the boxplots do not align well.
How can I debug the error below and align the violins and boxplots correctly?
Note that for the segments I still just want one cutoff per variable level (one for Sepal height, one for Sepal width, one for Petal height, and one for Petal Width).
This is the new code:
data(iris)
iris_long <- as.data.frame(tidyr::pivot_longer(iris, cols=names(iris)[-length(names(iris))],
names_to = "variable", values_to = "value"))
cutoffs <- data.frame(variable=sort(names(iris)[-length(names(iris))]), cutoff=c(7,2.5,8,4.5))
P <- ggplot2::ggplot(iris_long, ggplot2::aes(x=variable, y=value, fill=Species)) +
ggplot2::geom_violin() +
ggplot2::geom_boxplot(width=0.2, alpha=0, outlier.shape=NA, coef=0, show.legend=FALSE) +
ggplot2::stat_summary(fun=mean, geom="point", shape=16, size=2, show.legend=FALSE) +
ggplot2::geom_segment(data=cutoffs, ggplot2::aes(x=as.numeric(as.factor(variable)) - .25,
xend=as.numeric(as.factor(variable)) + .25,
y=cutoff, yend=cutoff), color = "red", alpha=0.75) +
ggplot2::theme_light()
grDevices::pdf(file="test.pdf", height=4, width=8)
print(P)
grDevices::dev.off()
And the error:
Error in
ggplot2::geom_segment()
: ! Problem while computing aesthetics. ℹ Error occurred in the 4th layer. Caused by error: ! object 'Species' not found Runrlang::last_trace()
to see where the error occurred.
Yes, I would simply add a geom_segment
:
ggplot(iris_long, aes(x = variable, y = value)) +
geom_violin() +
geom_boxplot(width = 0.2, alpha = 0, outlier.shape = NA, coef = 0, show.legend = FALSE) +
stat_summary(fun = mean, geom = "point", shape = 16, size = 2, show.legend = FALSE) +
geom_segment(data = cutoffs,
aes(x = as.numeric(as.factor(variable)) - .25,
xend = as.numeric(as.factor(variable)) + .25,
y = cutoffs, yend = cutoffs),
color = "red") +
theme_light()
which produces this plot:
N.B. I guess that your cutoffs are a bit messed up (hence the lines do not line up)
To incoprorate the fill aesthetic you need to provide an explicit position_dodge
to geom_violin
, geom_boxplot
and stat_summary
. Furthermore, you should set the fill aesthatic for the segment to NULL
otherwise it tries to look for it:
ggplot(iris_long, aes(x = variable, y = value, fill = Species)) +
geom_violin(position = position_dodge(width = 1)) +
geom_boxplot(width = 0.2, alpha = 0, outlier.shape = NA, coef = 0, show.legend = FALSE,
position = position_dodge(width = 1)) +
stat_summary(fun = mean, geom = "point", shape = 16, size = 2, show.legend = FALSE,
position = position_dodge(width = 1)) +
geom_segment(data = cutoffs,
aes(x = as.numeric(as.factor(variable)) - .25,
xend = as.numeric(as.factor(variable)) + .25,
y = cutoff, yend = cutoff, fill = NULL),
color = "red") +
theme_light()
produces: