Multirow axis labels with nested grouping variables Multirow axis labels with nested grouping variables r r

Multirow axis labels with nested grouping variables


The strip.position argument in facet_wrap() and switch argument in facet_grid() since ggplot2 2.2.0 now makes the creation of a simple version of this plot fairly straightforward via faceting. To give the plot the uninterrupted look, set the panel.spacing to 0.

Here's the example using the dataset with a different number of Groups per Category from @agtudy's answer.

  • I used scales = "free_x" to drop the extra Group from the Categories that don't have it, although this won't always be desirable.
  • The strip.position = "bottom" argument moves the facet labels to the bottom. I removed the strip background all together with strip.background, but I could see that leaving the strip rectangle would be useful in some situations.
  • I used width = 1 to make the bars within each Category touch - they'd have spaces between them by default.

I also use strip.placement and strip.background in theme to get the strips on the bottom and remove the strip rectangle.

The code for versions of ggplot2_2.2.0 or newer:

ggplot(data = data, aes(x = Group, y = Value, fill = Group)) +     geom_bar(stat = "identity", width = 1) +    geom_text(aes(label = paste(Value, "%")), vjust = -0.25) +    facet_wrap(~Category, strip.position = "bottom", scales = "free_x") +    theme(panel.spacing = unit(0, "lines"),          strip.background = element_blank(),         strip.placement = "outside")

enter image description here

You could use space= "free_x" in facet_grid() if you wanted all the bars to be the same width regardless of how many Groups per Category. Note that this uses switch = "x" instead of strip.position. You also might want to change the label of the x axis; I wasn't sure what it should be, maybe Category instead of Group?

ggplot(data = data, aes(x = Group, y = Value, fill = Group)) +     geom_bar(stat = "identity", width = 1) +    geom_text(aes(label = paste(Value, "%")), vjust = -0.25) +    facet_grid(~Category, switch = "x", scales = "free_x", space = "free_x") +    theme(panel.spacing = unit(0, "lines"),          strip.background = element_blank(),         strip.placement = "outside") +     xlab("Category")

enter image description here

Older code versions

The code for ggplot2_2.0.0, when this feature was first introduced, was a little different. I've saved it below for posterity:

ggplot(data = data, aes(x = Group, y = Value, fill = Group)) +     geom_bar(stat = "identity") +    geom_text(aes(label = paste(Value, "%")), vjust = -0.25) +    facet_wrap(~Category, switch = "x", scales = "free_x") +    theme(panel.margin = unit(0, "lines"),          strip.background = element_blank())


You can create a custom element function for axis.text.x.

enter image description here

library(ggplot2)library(grid)## create some data with asymmetric fill aes to generalize solution data <- read.table(text = "Group Category Value                   S1 A   73                   S2 A   57                   S3 A   57                   S4 A   57                   S1 B   7                   S2 B   23                   S3 B   57                   S1 C   51                   S2 C   57                   S3 C   87", header=TRUE)# user-level interface axis.groups = function(groups) {  structure(    list(groups=groups),    ## inheritance since it should be a element_text    class = c("element_custom","element_blank")    )}# returns a gTree with two children: # the categories axis# the groups axiselement_grob.element_custom <- function(element, x,...)  {  cat <- list(...)[[1]]  groups <- element$group  ll <- by(data$Group,data$Category,I)  tt <- as.numeric(x)  grbs <- Map(function(z,t){    labs <- ll[[z]]    vp = viewport(             x = unit(t,'native'),              height=unit(2,'line'),             width=unit(diff(tt)[1],'native'),             xscale=c(0,length(labs)))    grid.rect(vp=vp)    textGrob(labs,x= unit(seq_along(labs)-0.5,                                'native'),             y=unit(2,'line'),             vp=vp)  },cat,tt)  g.X <- textGrob(cat, x=x)  gTree(children=gList(do.call(gList,grbs),g.X), cl = "custom_axis")}## # gTrees don't know their size grobHeight.custom_axis =   heightDetails.custom_axis = function(x, ...)  unit(3, "lines")## the final plot callggplot(data=data, aes(x=Category, y=Value, fill=Group)) +   geom_bar(position = position_dodge(width=0.9),stat='identity') +  geom_text(aes(label=paste(Value, "%")),            position=position_dodge(width=0.9), vjust=-0.25)+  theme(axis.text.x = axis.groups(unique(data$Group)),        legend.position="none")


An alternative to agstudy's method is to edit the gtable and insert an "axis" calculated by ggplot2,

p <- ggplot(data=data, aes(x=Category, y=Value, fill=Group)) +   geom_bar(position = position_dodge(width=0.9),stat='identity') +  geom_text(aes(label=paste(Value, "%")),            position=position_dodge(width=0.9), vjust=-0.25)axis <- ggplot(data=data, aes(x=Category, y=Value, colour=Group)) +  geom_text(aes(label=Group, y=0),            position=position_dodge(width=0.9))annotation <- gtable_filter(ggplotGrob(axis), "panel", trim=TRUE)annotation[["grobs"]][[1]][["children"]][c(1,3)] <- NULL #only keep textGroblibrary(gtable)g <- ggplotGrob(p)gtable_add_grobs <- gtable_add_grob # let's use this aliasg <- gtable_add_rows(g, unit(1,"line"), pos=4)g <- gtable_add_grobs(g, annotation, t=5, b=5, l=4, r=4)grid.newpage()grid.draw(g)

enter image description here