Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 619
astrsk Avatar asked Dec 24 '22 15:12

astrsk


2 Answers

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.

like image 158
Z.Lin Avatar answered Dec 28 '22 08:12

Z.Lin


Looking over SO I encountered a solution which might be a bit tricky - from here

The idea is to create a second fake dataset which would plot a single point at each facet. This point will be drawn in the position, corresponding to the highest desired value for y scale in every case. So heights of scales can be manually adjusted for each facet. Here is the solution for the dataset in question. We want y scale (maximum y value) 5 for the first row, and 17 for the second row. So create

df3=data.frame(newvar = rep("xx",8),    
               period = c("A","B","C","D","E","F","G","H"),
               newvalue = c(5,5,5,5,17,17,17,17))

And now superimpose the new data on our graph using geom_point() .

ggplot(trial.facets.tidied,aes(x=newvar,y=newvalue,position="dodge"))+
   geom_bar(stat ="identity") +
   facet_wrap(~period,nrow=2,scales="free_y")+
   geom_point(data=df3,aes(x=newvar,y=newvalue),alpha=1)

Here what we get:

barplot with fake points, fixing the scale

Here I intentionally draw this extra point to make things clear. Next we need to make it invisible, which can be achieved by setting alpha=0 instead of 1 in the last command.

like image 40
astrsk Avatar answered Dec 28 '22 08:12

astrsk