'Ggplot2 rearranges wrongly the bars in a plot bar when value is null

Given the following data, I compose a data frame with a factor and a numeric column.

X2 <- c(4,4,3,5,4,4,2,3,4,3,5,5,4,3,3,4,2,3,3,4,3,5,3,3,4,4,3,3,5,4,5,4,4,3,5,5,3,5,4,5,5,4,4,2,3,3,3,4,4,4,2,4,4,4,4,4,2,4,4,3,3,3,5,3,4,3,3,4,4,4,4,1,3,3,4,3,3,2,4,1)
X3 <- rep("I",40)
X4 <- rep("C",40)
Group <- c(X3,X4)
dat2 <- data.frame(X2,Group)
dat2$Group <- factor(dat2$Group)
levels(dat2$Group) = c("I","C")
Group <- c("C","I")
grp.mean <- c(3.8,3.375)
mu2 <- data.frame(Group,grp.mean)

I want to compose the following bar plot with vertical lines at the mean and here's, my code:

p2 <-ggplot(dat2, aes(x=X2))+
  geom_bar(aes(color=Group,fill=Group),alpha=0.4, position= position_dodge(preserve = "single"))+
  geom_vline(data=mu2, aes(xintercept=grp.mean, color=Group), linetype="dashed")+
  xlab("Density in Responses") + 
  ylab("Levels")+
  theme_gray() +
  theme_grey(base_size = 30)+
  theme(axis.text=element_text(size=22),
        axis.title=element_text(size=20,face="bold"),
        legend.title=element_text(size=16),
        legend.text=element_text(size=14))+
  theme(plot.title = element_text(hjust = 0.5,size=19,face="bold"))

p2

And I get a plot which checks all my expectations except one. When I have a value that one of the conditions (C and I) is blank, it automatically changes the place, and I don't know why! From my logic, it should remain in the same position and draw the bar in the right position. I attach an image so you can see what is going on.

enter image description here

As you can see the blue bar has taken the place of the red bar at the absence of a red bar (because it has 0 value). Does anyone know why is this happening and, is there any way I can fix this?

Thanks!



Solution 1:[1]

One work-around is to count the number of observations before ggplot, and plot the count information.

Note I have swapped X3 and X4 in your first Group vector so that red is on the left and blue is on the right.

library(tidyverse)
X2 <- c(4,4,3,5,4,4,2,3,4,3,5,5,4,3,3,4,2,3,3,4,3,5,3,3,4,4,3,3,5,4,5,4,4,3,5,5,3,5,4,5,5,4,4,2,3,3,3,4,4,4,2,4,4,4,4,4,2,4,4,3,3,3,5,3,4,3,3,4,4,4,4,1,3,3,4,3,3,2,4,1)
X3 <- rep("I",40)
X4 <- rep("C",40)
Group <- c(X4,X3)
dat2 <- data.frame(X2,Group)
dat2$Group <- factor(dat2$Group)
levels(dat2$Group) = c("I","C")
Group <- c("C","I")
grp.mean <- c(3.8,3.375)
mu2 <- data.frame(Group,grp.mean)

dat2 %>% group_by(X2, Group) %>% summarize(n = n()) %>% complete(Group, fill = list(n = 0)) %>% 
  ggplot(aes(x=X2, n))+
  geom_bar(aes(color=Group,fill=Group),alpha=0.4, position= position_dodge(), stat = "identity")+
  geom_vline(data=mu2, aes(xintercept=grp.mean, color=Group), linetype="dashed")+
  xlab("Density in Responses") + 
  ylab("Levels")+
  theme_gray() +
  theme_grey(base_size = 30)+
  theme(axis.text=element_text(size=22),
        axis.title=element_text(size=20,face="bold"),
        legend.title=element_text(size=16),
        legend.text=element_text(size=14))+
  theme(plot.title = element_text(hjust = 0.5,size=19,face="bold"))
#> `summarise()` has grouped output by 'X2'. You can override using the `.groups`
#> argument.

Created on 2022-05-13 by the reprex package (v2.0.1)

Solution 2:[2]

Like the others commenters i also got the opposite bar color so i changed the I/C values and it matched yours. I am not sure if my result would satisfy you, as i managed to make the blue bar to fill the whole space of X=1

plot

Anyway, I also used a cleaner code to generate the table:

X2 <- c(4,4,3,5,4,4,2,3,4,3,5,5,4,3,3,4,2,3,3,4,3,5,3,3,4,4,3,3,5,4,5,4,4,3,5,5,3,5,4,5, #40
        5,4,4,2,3,3,3,4,4,4,2,4,4,4,4,4,2,4,4,3,3,3,5,3,4,3,3,4,4,4,4,1,3,3,4,3,3,2,4,1)
# First col is the unique X2 values.
# Second col is Group, which is a factor. It is a repetition of I/C, each 40 times (40*I and then 40*C)
# grp.mean is a grouped mean (by each Group[I/C]) of X2.
dat2 <- data.frame(
  X2,
  Group=factor(rep(c("C","I"),each=40))
) %>% group_by(Group) %>% mutate(grp.mean=mean(X2)) %>% ungroup()

dat2 %>% ggplot(aes(X2,fill=Group))+
  geom_bar(position="dodge")+
  geom_vline(xintercept = dat2$grp.mean)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 benson23
Solution 2 benson23