Positioning SE bars with different aesthetics in geom_errorbar using ggplot2 in R

55 Views Asked by At

I have to give SE values manually in a grouped bar plot in R. However, SE lines don't take the same positions with bars. I understand the x variables that I defined (x = Species) in each geom_errorbar are causing this but I have to define it since I'm using multiple datasets.

So here's my data:

Datatemp = data.frame( Species = c("Oak", "Oak", "Pine", "Pine","Oak", "Oak", "Pine", "Pine"),
                       Status = rep(c("Above", "Below"),4),
                       Sex = c(rep("Male",4), rep("Female",4)),
                       Value = c(6.86, 7.65, 30.13, 35.71, 7.13, 10.33, 29.24, 31.09),
                       SE = c(0.7354684, 1.9648560,3.6734597, 4.5276121, 
                              0.7881132, 1.9564864, 3.4784320, 4.243139))

Here's the code for the barplot:

DatatempA = Datatemp %>% filter(Species == "Oak")
DatatempB = Datatemp %>% filter(Species == "Pine")

ggplot() +
  geom_bar(data = DatatempB, 
           mapping = aes(x = Species, y = Value, fill = Status), 
           stat='identity', position = position_dodge(width = 0.73), width=.67) +
  geom_errorbar(data = DatatempB,aes(x = Species, ymin = Value-SE, ymax = Value+SE), 
                position = position_dodge(.9), width = 0.2) +
  scale_fill_manual(name = "Status trees", 
                    labels = c("Above","Below"),
                    values = c("#7393B3","#0F52BA"),
                    guide = guide_legend(override.aes = list(fill=c("gray75", "gray25")))) +
  new_scale_fill() + 
  geom_bar(data = DatatempA, 
           mapping = aes(x = Species, y = Value, fill = Status), 
           stat='identity', position = position_dodge(width = 0.73), width=.67, show.legend=FALSE) +
  geom_errorbar(data = DatatempA, aes(x = Species, ymin = Value-SE, ymax = Value+SE), 
                position = position_dodge(.9), width = 0.2) +
  scale_fill_manual(name = "Status trees", 
                    labels = c("Above","Below"),
                    values = c("#EADDCA","#F4BB44")) +
  facet_grid(Sex ~ .) + 
  scale_y_continuous(sec.axis = dup_axis(name= "Sex of trees")) +
  xlab("Species") + 
  ylab("Value") +
  theme_light() + theme(axis.text.y.right = element_blank(),
    axis.ticks.y.right = element_blank(),
    axis.ticks.length.y.right = unit(0, "pt"))

And here's the output: enter image description here

As you can see, the error bars were grouped in the middle and It would be great if you could suggest a way to position them in the respective bars. Many thanks in advance!

2

There are 2 best solutions below

3
stefan On BEST ANSWER

As in the approach by @TarJae I think that the ggnewscale package is not really needed to achieve your desired result and you can simplify by using just one dataset and mapping the interaction of Species and Status on fill. To still achieve your desired result you can drop the fill legend and use a fake legend for Status for which I map Status on the color aes. Afterwards I set the colors to "transparent" and override the fill color for the legend as you did using override.aes:

library(ggplot2)

ggplot(Datatemp, aes(x = Species)) +
  geom_col(
    aes(
      y = Value,
      fill = interaction(Status, Species),
      color = Status
    ),
    position = position_dodge(width = 0.73), width = .67
  ) +
  geom_errorbar(
    aes(ymin = Value - SE, ymax = Value + SE, group = Status),
    position = position_dodge(.73), width = 0.2
  ) +
  facet_grid(Sex ~ .) +
  scale_fill_manual(
    values = c("#EADDCA", "#F4BB44", "#7393B3", "#0F52BA"),
    guide = "none"
  ) +
  scale_color_manual(
    values = rep("transparent", 2),
    guide = guide_legend(override.aes = list(fill = c("gray75", "gray25")))
  ) +
  scale_y_continuous(sec.axis = dup_axis(name = "Sex of trees")) +
  xlab("Species") +
  ylab("Value") +
  theme_light() +
  theme(
    axis.text.y.right = element_blank(),
    axis.ticks.y.right = element_blank(),
    axis.ticks.length.y.right = unit(0, "pt")
  )

enter image description here

1
TarJae On

I would use this strategy:

  1. Define the fill aesthetics,
  2. Create a interaction term SpeciesStatus,
  3. group and fill with SpeciesStatus,
  4. do your grouped bars with errorbar as usual.
colors <- c("Oak_Above" = "#F4BB44", "Oak_Below" = "#EADDCA",
            "Pine_Above" = "#0F52BA", "Pine_Below" = "#7393B3")

library(ggplot2)
library(dplyr)

Datatemp %>% 
  mutate(SpeciesStatus = interaction(Species, Status, sep = "_")) %>% 
  ggplot(aes(x = Species, y = Value, fill = SpeciesStatus, group = SpeciesStatus)) +
  geom_bar(stat = "identity", position = position_dodge()) +
  geom_errorbar(aes(ymin = Value - SE, ymax = Value + SE), 
                position = position_dodge(0.9), width = 0.25) +
  scale_fill_manual(values = colors) +
  facet_grid(Sex ~ .) +
  scale_y_continuous(sec.axis = dup_axis(name= "Sex of trees")) +
  xlab("Species") + 
  ylab("Value") +
  theme_light() + theme(axis.text.y.right = element_blank(),
                        axis.ticks.y.right = element_blank(),
                        axis.ticks.length.y.right = unit(0, "pt"))

enter image description here