ggplot in R to add significance asterisk vs control group over multiple variables

Viewed 388

I have barplots, but would like to run a Wilcox.test within each "grp1" comparing the bars to the control for that group, and then putting an asterix if it is significant.

I've seen "compare_means" to get the comparisons, but I'm trying to make it automated and not so manual. Would "geom_signif" or "stat_compare_means" do this? Can someone help with this? Thank you very much.

I need the comparison to be made using the full dataset, not just the means (which is only one value per bar). I added a line at the end of the code running one of the comparisons so you can see where I need the p-values from.

y <- c(runif(100,0,4.5),runif(100,3,6),runif(100,4,7))
grp1 <- sample(c("A","B","C","D"),size = 300, replace = TRUE)
grp2 <- rep(c("High","Med","Contrl"),each=100)
dataset <- data.frame(y,grp1,grp2)

means <- aggregate(y~grp1+grp2,data=dataset,mean)
sd <- aggregate(y~grp1+grp2,data=dataset,function(x){sd(x)})

means.all <- merge(sd,means,by=c("grp1","grp2"))
names(means.all)[3:4] <- c("sd","y.mean")

library(ggplot2)
p<- ggplot(means.all, aes(x=grp1, y=y.mean, fill=grp2))+ 
  geom_bar(stat="identity", color="black", 
           position=position_dodge()) +
  geom_errorbar(aes(ymin=y.mean-sd, ymax=y.mean+sd), width=.2,
                position=position_dodge(.9))
p

compare_means(y~grp2,data = dataset[dataset$grp1=="A",],method="wilcox.test")
1 Answers

Maybe this is not the optimal way but you can create a list splitting the data and applying the stat_compare_means() function individually at each level of your data. After that you can arrange the plots in one using patchwork:

library(ggplot2)
library(ggpubr)
library(patchwork)
#Split data
List <- split(means.all,means.all$grp1)
#Function for plot
myfun <- function(x)
{
  #Ref group
  rg <- paste0(unique(x$grp1),'.','Contrl')
  #Plot
  G <- ggplot(x, aes(x=interaction(grp1,grp2), y=y.mean, fill=grp2))+ 
    geom_bar(stat="identity", color="black", 
             position=position_dodge()) +
    geom_errorbar(aes(ymin=y.mean-sd, ymax=y.mean+sd), width=.2,
                  position=position_dodge(.9))+
    stat_compare_means(ref.group = rg,label =  "p.signif",method = "wilcox.test",label.y = 7)+
    theme(axis.text.x = element_blank())+
    xlab(unique(x$grp1))
  return(G)
}
#Apply
Lplot <- lapply(List, myfun)
#Wrap plots
wrap_plots(Lplot,nrow = 1)+plot_layout(guides = 'collect')

Output:

enter image description here

Consider this update that takes the values for asterisks stored in a new dataframe:

#Create p-vals dataset
List2 <- split(dataset,dataset$grp1)
#p-val function
mypval <- function(x)
{
  y <- compare_means(y~grp2,data = x,method="wilcox.test")
  y <- y[,c('group2', 'group1','p.signif')]
  names(y)<-c('grp2','grp1','p.signif')
  y <- y[y$grp2=='Contrl',]
  y$grp2 <- y$grp1
  y <- rbind(y,data.frame(grp2='Contrl',grp1='',p.signif=''))
  y$grp1 <- unique(x$grp1)
  y$y.mean=7
  return(y)
}
#Apply
dfpvals <- lapply(List2, mypval)
df <- do.call(rbind,dfpvals)
#Plot
ggplot(means.all, aes(x=grp1, y=y.mean, fill=grp2,group=grp2))+ 
  geom_bar(stat="identity", color="black", 
           position=position_dodge()) +
  geom_errorbar(aes(ymin=y.mean-sd, ymax=y.mean+sd), width=.2,
                position=position_dodge(.9))+
  geom_text(data=df,aes(x=grp1, y=y.mean,group=grp2,label=p.signif),
            position=position_dodge(0.9))

Output:

enter image description here

Related