Search code examples
rggplot2facetfacet-wrapfacet-grid

ggplot2: specifying different scales for rows in facet layout for bar plots


My data are visualized in the package ggplot2 via bar plots with several (~10) facets. I want first to split these facets in several rows. I can use function facet_grid() or facet_wrap() for this. In the minimal example data here I build 8 facets in two rows (4x2). However I need to adjust scales for different facets, namely: first row contains data on small scale, and in the second row values are bigger. So I need to have same scale for all data in the first row to compare them along the row, and another scale for the second row.

Here is the minimal example and possible solutions.

#loading necessary libraries and example data
library(dplyr)
library(tidyr)
library(ggplot2)

trial.facets<-read.csv(text="period,xx,yy
A,2,3
B,1.5,2.5
C,3.2,0.5
D,2.5,1.5
E,11,13
F,16,14
G,8,5
H,5,4")

#arranging data to long format with omission of the "period" variable
trial.facets.tidied<-trial.facets %>% gather(key=newvar,value=newvalue,-period)

And now plotting itself:

#First variant
ggplot(trial.facets.tidied,aes(x=newvar,y=newvalue,position="dodge"))+geom_bar(stat ="identity") +facet_grid(.~period)

#Second variant:
ggplot(trial.facets.tidied,aes(x=newvar,y=newvalue,position="dodge"))+geom_bar(stat ="identity") +facet_wrap(~period,nrow=2,scales="free")

The results for the first and second variants are as follows:

enter image description here

In both examples we have either free scales for all graphs, or fixed for all graphs. Meanwhile the first row (first 4 facets) needs to be scaled somewhat to 5, and the second row - to 15.

As a solution to use facet_grid() function I can add a fake variable "row" which specifies, to what row should the corresponding letter belong. The new dataset, trial.facets.row (three lines shown only) would look like as follows:

period,xx,yy,row
C,3.2,0.5,1
D,2.5,1.5,1
E,11,13,2

Then I can perform the same rearrangement into long format, omitting variables "period" and "row":

trial.facets.tidied.2<-trial.facets.row %>% gather(key=newvar,value=newvalue,-period,-row)

Then I arrange facets along variables "row" and "period" in the hope to use the option scales="free_y" to adjust scales only across rows:

ggplot(trial.facets.tidied.2,aes(x=newvar,y=newvalue,position="dodge"))+geom_bar(stat ="identity") +facet_grid(row~period,scales="free_y")

and - surprise: the problem with scales is solved, however, I get two groups of empty bars, and whole data is again stretched across a long strip:

enter image description here

All discovered manual pages and handbooks (usually using the mpg and mtcars dataset) do not consider such situation of such unwanted or dummy data


Solution

  • I used a combination of your first method (facet_wrap) & second method (leverage on dummy variable for different rows):

    # create fake variable "row"
    trial.facets.row <- trial.facets %>% mutate(row = ifelse(period %in% c("A", "B", "C", "D"), 1, 2))
    # rearrange to long format
    trial.facets.tidied.2<-trial.facets.row %>% gather(key=newvar,value=newvalue,-period,-row)
    # specify the maximum height for each row
    trial.facets.tidied.3<-trial.facets.tidied.2 %>%
      group_by(row) %>%
      mutate(max.height = max(newvalue)) %>%
      ungroup()
    
    ggplot(trial.facets.tidied.3,
           aes(x=newvar, y=newvalue,position="dodge"))+
      geom_bar(stat = "identity") +
      geom_blank(aes(y=max.height)) + # add blank geom to force facets on the same row to the same height
      facet_wrap(~period,nrow=2,scales="free")
    

    resulting plot

    Note: based on this reproducible example, I'm assuming that all your plots already share a common ymin at 0. If that's not the case, simply create another dummy variable for min.height & add another geom_blank to your ggplot.