1

I've been stuck on this issue for a long time. Basically I need to generate stacked bar plots of bacterial taxa in a 2 dimensional facet_grid. This is not a problem - however, I do need to use 2 different x-axis labels plots.

I've looked at these threads which help, but still not satisfying:

Force x-axis labels on facet_grid ggplot: drop lables

Force x-axis labels on facet_grid ggplot: x-axis labels differ per row

Force X axis text on for all facets of a facet_grid plot

My first data frame:

structure(list(variable = structure(c(1L, 1L, 1L, 15L, 15L, 15L, 
8L, 8L, 8L, 14L, 14L, 14L, 11L, 11L, 11L, 6L, 6L, 6L, 9L, 9L, 
9L, 2L, 2L, 2L, 4L, 4L, 4L, 13L, 13L, 13L, 12L, 12L, 12L, 10L, 
10L, 10L, 3L, 3L, 3L, 7L, 7L, 7L, 5L, 5L, 5L), .Label = c("Control-5C10-7F", 
"Control-5C20-7M", "Treatment-5T16-7M", "Treatment-5T10-7F", 
"Treatment-5T20-7F", "Control-5C18-7F", "Treatment-5T17-7F", 
"Control-5C11-7F", "Control-5C19-7M", "Treatment-5T16-7F", "Control-5C17-7F", 
"Treatment-5T11-7M", "Treatment-5T10-7M", "Control-5C11-7M", 
"Control-5C10-7M"), class = "factor"), value = c(0.331838128419, 
0.352404886374, 0.0718854601356, 0.354207630618, 0.294475795902, 
0.0531792027053, 0.29236332445, 0.468273675353, 0.0607016856192, 
0.293872306838, 0.362196681338, 0.066892937885, 0.365593390631, 
0.307405564259, 0.0440421535284, 0.355757725414, 0.344565656834, 
0.0385728714381, 0.321804993859, 0.357477936005, 0.0189435852408, 
0.29485205349, 0.323279812948, 0.0613479971185, 0.330899082108, 
0.0950818222943, 0.00477515444166, 0.382399029908, 0.118943005246, 
0.00476898392234, 0.369726433283, 0.163932867784, 0.00531688661114, 
0.309218704951, 0.146836936202, 0.196198995646, 0.395112160831, 
0.185072972204, 0.00267743643869, 0.306483893184, 0.330640754636, 
0.0715509081212, 0.301275805191, 0.388917814857, 0.0356065705157
), Type = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L), .Label = c("Control", "Treatment"), class = "factor"), 
    Time.Sex = structure(c(2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 
    1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 
    2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 
    2L, 2L, 2L, 2L, 2L, 2L), .Label = c("7d.M", "7d.F"), class = "factor"), 
    IndividualID = c("5C10", "5C10", "5C10", "5C10", "5C10", 
    "5C10", "5C11", "5C11", "5C11", "5C11", "5C11", "5C11", "5C17", 
    "5C17", "5C17", "5C18", "5C18", "5C18", "5C19", "5C19", "5C19", 
    "5C20", "5C20", "5C20", "5T10", "5T10", "5T10", "5T10", "5T10", 
    "5T10", "5T11", "5T11", "5T11", "5T16", "5T16", "5T16", "5T16", 
    "5T16", "5T16", "5T17", "5T17", "5T17", "5T20", "5T20", "5T20"
    ), Sex = c("F", "F", "F", "M", "M", "M", "F", "F", "F", "M", 
    "M", "M", "F", "F", "F", "F", "F", "F", "M", "M", "M", "M", 
    "M", "M", "F", "F", "F", "M", "M", "M", "M", "M", "M", "F", 
    "F", "F", "M", "M", "M", "F", "F", "F", "F", "F", "F"), Rep = c(1, 
    1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 3, 3, 
    3, 4, 4, 4, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 
    3, 3, 3, 4, 4, 4), IndividualID.Sex = c("5C10.F", "5C10.F", 
    "5C10.F", "5C10.M", "5C10.M", "5C10.M", "5C11.F", "5C11.F", 
    "5C11.F", "5C11.M", "5C11.M", "5C11.M", "5C17.F", "5C17.F", 
    "5C17.F", "5C18.F", "5C18.F", "5C18.F", "5C19.M", "5C19.M", 
    "5C19.M", "5C20.M", "5C20.M", "5C20.M", "5T10.F", "5T10.F", 
    "5T10.F", "5T10.M", "5T10.M", "5T10.M", "5T11.M", "5T11.M", 
    "5T11.M", "5T16.F", "5T16.F", "5T16.F", "5T16.M", "5T16.M", 
    "5T16.M", "5T17.F", "5T17.F", "5T17.F", "5T20.F", "5T20.F", 
    "5T20.F"), Genus = c("o__Bacteroidales;g__Parabacteroides", 
    "o__Lactobacillales;g__Lactobacillus", "o__Clostridiales;g__Lachnospiraceae NK4A136 group", 
    "o__Bacteroidales;g__Parabacteroides", "o__Lactobacillales;g__Lactobacillus", 
    "o__Clostridiales;g__Lachnospiraceae NK4A136 group", "o__Bacteroidales;g__Parabacteroides", 
    "o__Lactobacillales;g__Lactobacillus", "o__Clostridiales;g__Lachnospiraceae NK4A136 group", 
    "o__Bacteroidales;g__Parabacteroides", "o__Lactobacillales;g__Lactobacillus", 
    "o__Clostridiales;g__Lachnospiraceae NK4A136 group", "o__Bacteroidales;g__Parabacteroides", 
    "o__Lactobacillales;g__Lactobacillus", "o__Clostridiales;g__Lachnospiraceae NK4A136 group", 
    "o__Bacteroidales;g__Parabacteroides", "o__Lactobacillales;g__Lactobacillus", 
    "o__Clostridiales;g__Lachnospiraceae NK4A136 group", "o__Bacteroidales;g__Parabacteroides", 
    "o__Lactobacillales;g__Lactobacillus", "o__Clostridiales;g__Lachnospiraceae NK4A136 group", 
    "o__Bacteroidales;g__Parabacteroides", "o__Lactobacillales;g__Lactobacillus", 
    "o__Clostridiales;g__Lachnospiraceae NK4A136 group", "o__Bacteroidales;g__Parabacteroides", 
    "o__Lactobacillales;g__Lactobacillus", "o__Clostridiales;g__Lachnospiraceae NK4A136 group", 
    "o__Bacteroidales;g__Parabacteroides", "o__Lactobacillales;g__Lactobacillus", 
    "o__Clostridiales;g__Lachnospiraceae NK4A136 group", "o__Bacteroidales;g__Parabacteroides", 
    "o__Lactobacillales;g__Lactobacillus", "o__Clostridiales;g__Lachnospiraceae NK4A136 group", 
    "o__Bacteroidales;g__Parabacteroides", "o__Lactobacillales;g__Lactobacillus", 
    "o__Clostridiales;g__Lachnospiraceae NK4A136 group", "o__Bacteroidales;g__Parabacteroides", 
    "o__Lactobacillales;g__Lactobacillus", "o__Clostridiales;g__Lachnospiraceae NK4A136 group", 
    "o__Bacteroidales;g__Parabacteroides", "o__Lactobacillales;g__Lactobacillus", 
    "o__Clostridiales;g__Lachnospiraceae NK4A136 group", "o__Bacteroidales;g__Parabacteroides", 
    "o__Lactobacillales;g__Lactobacillus", "o__Clostridiales;g__Lachnospiraceae NK4A136 group"
    ), variable2 = c("7d.F-1", "7d.F-1", "7d.F-1", "7d.M-1", 
    "7d.M-1", "7d.M-1", "7d.F-2", "7d.F-2", "7d.F-2", "7d.M-2", 
    "7d.M-2", "7d.M-2", "7d.F-3", "7d.F-3", "7d.F-3", "7d.F-4", 
    "7d.F-4", "7d.F-4", "7d.M-3", "7d.M-3", "7d.M-3", "7d.M-4", 
    "7d.M-4", "7d.M-4", "7d.F-1", "7d.F-1", "7d.F-1", "7d.M-1", 
    "7d.M-1", "7d.M-1", "7d.M-2", "7d.M-2", "7d.M-2", "7d.F-2", 
    "7d.F-2", "7d.F-2", "7d.M-3", "7d.M-3", "7d.M-3", "7d.F-3", 
    "7d.F-3", "7d.F-3", "7d.F-4", "7d.F-4", "7d.F-4"), pos = c(0.1, 
    0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 
    0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 
    0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 
    0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2)), .Names = c("variable", 
"value", "Type", "Time.Sex", "IndividualID", "Sex", "Rep", "IndividualID.Sex", 
"Genus", "variable2", "pos"), row.names = c(NA, -45L), class = "data.frame")

The second data frame used in the internal function of ggplot.

structure(list(variable2 = c("7d.F-1", "7d.M-1", "7d.F-2", "7d.M-2", 
"7d.F-3", "7d.F-4", "7d.M-3", "7d.M-4", "7d.F-1", "7d.M-1", "7d.M-2", 
"7d.F-2", "7d.M-3", "7d.F-3", "7d.F-4"), IndividualID = c("5C10", 
"5C10", "5C11", "5C11", "5C17", "5C18", "5C19", "5C20", "5T10", 
"5T10", "5T11", "5T16", "5T16", "5T17", "5T20"), Type = structure(c(1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("Control", 
"Treatment"), class = "factor")), .Names = c("variable2", "IndividualID", 
"Type"), row.names = c(1L, 4L, 7L, 10L, 13L, 16L, 19L, 22L, 25L, 
28L, 31L, 34L, 37L, 40L, 43L), class = "data.frame")

My code:

index_x <<-  0
index_k <<-  0
p <- ggplot(data=df, aes(x=variable2, y=value, fill=Genus)) +
   facet_grid("Type ~ Time.Sex", scales="free_x", space="free_x") +
   geom_bar(stat="identity", size=0.0) +
   geom_bar( size=0.0, stat="identity", show.legend=FALSE) +
##I tried with some level of success to include the labels I want inside the stacked bars themselves, but 
   ## would ideally like them to be on the x-axis.
   geom_text(aes(y=pos, label=IndividualID), vjust=0, angle=90, size=3) +
   xlab("Category") +
   ylab("Abundance %") +
   ggtitle(paste0("Taxa abundance")) +
   theme(
      panel.border=element_rect(fill=NA, linetype="solid", colour = "black", size=0.25),
      axis.text.x=element_text(size=9, colour="black", angle=as.numeric(90)),
      axis.text.y=element_text(size=9, colour="black"),
      axis.title=element_text(family="Helvetica", size=10),
      plot.title = element_text(lineheight=1.2, face="bold", size=25),
      panel.grid.major=element_blank(),
      panel.grid.minor=element_blank(),
      panel.background=element_blank(),
      legend.key.size = unit(0.35, "cm"),
      legend.text = element_text(size=12, face="bold"),
      legend.title = element_text(size=12, face="bold"),
      legend.spacing = unit(1, "cm"),
      legend.position="right",
      strip.text.x = element_text(angle=90, vjust=0, size=7, face="bold"),
      strip.text.y = element_text(hjust=0, angle=0, size=7, face="bold"),
      strip.background =  element_blank(),
      panel.spacing.y = unit(0.3, "lines")
   ) +  scale_fill_manual(values=vColors) +
   ## I wrote an internal function to force the x labels
   scale_x_discrete(name="IndividualID", labels = function(x){

      final_labels = c()

      if(index_x %% length(curr_order_x) == 0){
         index_k <<- index_k + 1
      }
      df_labels2 = df_labels_orig[df_labels_orig[[ facets[1] ]] == rev(curr_order_y)[index_k], ]

      for(i in 1:length(x)){
         final_labels = c(final_labels, df_labels2[df_labels2$variable2 == x[i],]$IndividualID)
      }
      index_x <<- index_x + 1

      print(final_labels)
      return(final_labels)
   }  
   )
print(p)

For which I get the following: Obtained stacked barplot But what I'd like to have are the Individual IDs that are in the stacked bars themselves just below on the x-axis. So here is what I did, thinking I could display the forced x-axis labels from the internal function above using gTable.

# Then following the references I found on SO, I tried the following:
index_x <<- 0 
index_k <<- 0

g <- ggplotGrob(p)

panels <- grep("panel", g$layout$name)
top <- unique(g$layout$t[panels])

top.row <- gtable:::rbind_gtable(
   g[seq.int(min(top)), ],
   g[max(top)+1,], "first"
)
bottom.row <- g[(max(top)-1):nrow(g), ]
all <- gtable:::rbind_gtable(top.row, bottom.row, "first")
grid.newpage()
grid.draw(all)

For which I obtain: stacked barplot with gTable The x-axis is the same on the two rows and I'd like them to correspond to what they actually represent. It seems to work well for the bottom panels, but the upper panels x-axis labels contain the same labels as the ones on the lower panels. I've also lost the legend.

Edit: Here is what I'd like to have for final result: Desired figure

julio514
  • 181
  • 1
  • 1
  • 13

1 Answers1

1

Rather than adjusting to the settings of facet_grid, consider building plots individually according to the needed groupings. Specifically, consider base R's by (that slices a dataframe by factor(s)) to build a list of plots according to your levels, Type and Time.Sex.

However, adjustments need to be made to fit your requirements:

  1. Place Control plots above Treatment plots;
  2. Adjust axes to avoid redundancy (tailored to actual number of plots, here being 4):
    • Remove the y-axis title/text/tick marks on 2nd and 4th;
    • Remove the x-axis title on 1st and 2nd plots;
  3. Avoid multiple legends by using @baptist's share a legend method.

Share a Legend (adjusted to read in a list of plots as first argument and adds a title at top)

# CREDIT: @Baptist: https://github.com/tidyverse/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs
grid_arrange_shared_legend <- function(plots, ncol = length(list(...)), nrow = 1, position = c("bottom", "right")) {

  position <- match.arg(position)
  g <- ggplotGrob(plots[[1]] + theme(legend.position = position))$grobs
  legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
  lheight <- sum(legend$height)
  lwidth <- sum(legend$width)
  gl <- lapply(plots, function(x) x + theme(legend.position="none"))
  gl <- c(gl, ncol = ncol, nrow = nrow)

  title <- textGrob("Taxa Abundance",gp=gpar(fontsize=20))

  combined <- switch(position,
                     "bottom" = arrangeGrob(do.call(arrangeGrob, gl),
                                            legend,
                                            ncol = 1,
                                            top = title,
                                            heights = unit.c(unit(0.95, "npc") - lheight, lheight)),
                     "right" = arrangeGrob(do.call(arrangeGrob, gl),
                                           legend,
                                           ncol = 2,
                                           widths = unit.c(unit(1, "npc") - lwidth, lwidth)))

  grid.newpage()
  grid.draw(combined)

  # return gtable invisibly
  invisible(combined)

}

R (using by() plots)

vColors <- c("red", "green", "blue")

by_plots <- by(df, df[,c("Type", "Time.Sex")], FUN=function(sub)       
  ggplot(sub, aes(x=IndividualID, y=value, fill=factor(Genus))) +
    geom_bar(stat="identity", size=0.0) + 
    labs(title=paste(sub$Type[[1]], "-", sub$Time.Sex[[1]]), x="Category", y="Abundance %") + 
    guides(fill=guide_legend(title="Genus", nrow=3)) + ylim(0, 0.9) +
    scale_fill_manual(values=vColors) +
    theme(plot.title = element_text(hjust = 0.5))
)

# RE-ORDER FOR CONTROL PLOTS ABOVE TREATMENT PLOTS
by_plots <- by_plots[c(1,3,2,4)]

# CONDITIONALLY REMOVE Y-AXIS AND X-AXIS TITLE    
by_plots <- lapply(seq_along(by_plots), function(i) {
  if ((i %% 2) == 0)
    by_plots[[i]] <- by_plots[[i]] + theme(axis.title.y = element_blank(),
                                           axis.text.y = element_blank(),
                                           axis.ticks.y = element_blank())

  if (i < 3)
    by_plots[[i]] <- by_plots[[i]] + theme(axis.title.x = element_blank())      

  return(by_plots[[i]])
})

grid_arrange_shared_legend(by_plots, ncol=2, nrow=2)

Output

Plot Output

Parfait
  • 104,375
  • 17
  • 94
  • 125