The only prior question I could find was this one from 2012 but it doesn't seem to help in my case.
I want to plot the means of groups by subgroup using colors. I want to display error bars (confidence intervals) around the means. And I want the error bars to be less wide than the default.
Example code which data from iris (random data used for the fill
variable):
library(ggplot2)
#data
df_sum = structure(list(Species = structure(c(1L, 1L, 2L, 2L, 3L, 3L), .Label = c("setosa",
"versicolor", "virginica"), class = "factor"), fillvar = c("A",
"B", "A", "B", "A", "B"), mean = c(1.43636363636364, 1.48214285714286,
4.16666666666667, 4.34615384615385, 5.49130434782609, 5.6037037037037
), n = c(22, 28, 24, 26, 23, 27), se = c(0.0429161341346969,
0.0281969506722072, 0.103676382414373, 0.0830626768411882, 0.112273142983994,
0.109320809356896), groupvar = structure(c(1L, 1L, 2L, 2L, 3L,
3L), .Label = c("setosa", "versicolor", "virginica"), class = "factor"),
ci_bar = c(2.07961384472768, 2.05183051648029, 2.06865761041905,
2.0595385527533, 2.07387306790403, 2.05552943864287)), .Names = c("Species",
"fillvar", "mean", "n", "se", "groupvar", "ci_bar"), row.names = c(NA,
-6L), class = "data.frame")
ggplot(df_sum, aes(x = groupvar, y = mean, fill = fillvar)) +
geom_bar(stat="identity", position = "dodge") +
geom_errorbar(aes(ymin = mean - ci_bar*se, ymax = mean + ci_bar*se), position = position_dodge(), width = .2) +
xlab("Species") + ylab("Petal.Length")
We see the problem. The error bars are dodged, but due to the width
setting, they are not dodged far enough.
If we plot without the width
setting:
ggplot(df_sum, aes(x = groupvar, y = mean, fill = fillvar)) +
geom_bar(stat="identity", position = "dodge") +
geom_errorbar(aes(ymin = mean - ci_bar*se, ymax = mean + ci_bar*se), position = position_dodge()) +
xlab("Species") + ylab("Petal.Length")
Which is what we expect, but the errors bars are too wide for my taste.
The problem seems to be that position_dodge
has a width
parameter and so does geom_errorbar
. If one puts width=0.2
in the geom_errorbar
, it gets forwarded to position_dodge
as well which causes the dodge-offset problem. Notice that if one instead sets width
inside position_dodge
, it does not get forwarded to geom_errorbar
(which is outside). This thus only results in the offset being off without the error bars' width changing:
ggplot(df_sum, aes(x = groupvar, y = mean, fill = fillvar)) +
geom_bar(stat="identity", position = "dodge") +
geom_errorbar(aes(ymin = mean - ci_bar*se, ymax = mean + ci_bar*se), position = position_dodge(width = .2)) +
xlab("Species") + ylab("Petal.Length")
This is the reverse of what I want. The question then seems to be how to set width=0.2
inside geom_errorbar
without ggplot2 forwarding it automatically to position_dodge
.
I only found a slight hacking solution, which is to set the width
to the default value (which seems to be 0.9) inside position_dodge
to prevent it from getting filled in with the value from geom_errorbar
:
ggplot(df_sum, aes(x = groupvar, y = mean, fill = fillvar)) +
geom_bar(stat="identity", position = "dodge") +
geom_errorbar(aes(ymin = mean - ci_bar*se, ymax = mean + ci_bar*se), position = position_dodge(width = .9), width = .2) +
xlab("Species") + ylab("Petal.Length")
Is there a non-hack solution to this problem?