Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'Labels on top' with facet_grid, or 'space option' with facet_wrap

Tags:

r

ggplot2

facet

facet_grid allow me to size each facet width according to number of items on y axis (space argument):

df <- data.frame(label = c("Variable one", rep("Variable two", 2), rep("Variable three", 3)), item = c("A", "B", "C", "D", "E", "F"), value = rnorm(6))
ggplot(df, aes(x = value, y = item)) + 
geom_point() + 
facet_grid(label ~ ., scales = "free_y", space = "free_y") + 
ylab("") + 
theme(strip.text.y = element_text(angle=0))

enter image description here

But I would like facet labels on top, so I switch to facet_wrap, and lost the space argument (facets have all the same width):

ggplot(df, aes(x = value, y = item)) + 
geom_point() + 
facet_wrap(~ label, scales = "free_y", ncol = 1) + 
ylab("")

enter image description here

Is it possible to obtain the best of both worlds?

Thank you in advance for your help.

like image 583
eusebe Avatar asked Jan 22 '15 16:01

eusebe


2 Answers

I don't think this feature exists or is planning on being implemented in ggplot2 (see here for the closed issue discussing this feature)

However the ggforce package supports what you're looking for

library(ggplot2)
library(ggforce)
df <- data.frame(label = c("Variable one", rep("Variable two", 2), rep("Variable three", 3)), item = c("A", "B", "C", "D", "E", "F"), value = rnorm(6))
  
ggplot(df, aes(x = value, y = item)) + 
  geom_point() + 
  ggforce::facet_col(facets = vars(label), 
                     scales = "free_y", 
                     space = "free") +
  ylab("") + 
  theme(strip.text.y = element_text(angle=0))

Created on 2020-06-28 by the reprex package (v0.3.0)

like image 170
John-Henry Avatar answered Sep 27 '22 23:09

John-Henry


It can be done manually. The ratio of the heights of the three panels in the desired plot is roughly 1:3:2. The heights of the three panels can be adjusted by changing the grobs:

library(ggplot2)
library(grid)
df <- data.frame(label = c("Variable one", rep("Variable two", 2), rep("Variable three", 3)), item = c("A", "B", "C", "D", "E", "F"), value = rnorm(6))

p1 = ggplot(df, aes(x = value, y = item)) + 
geom_point() + 
facet_wrap(~ label, scales = "free_y", ncol = 1) + 
ylab("")

g1 = ggplotGrob(p1) 

g1$heights[[7]] = unit(1, "null")
g1$heights[[12]] = unit(3, "null")
g1$heights[[17]] = unit(2, "null")

grid.newpage()
grid.draw(g1)

Or, the heights can be set to be the same as those in the original plot:

p2 = ggplot(df, aes(x = value, y = item)) + 
geom_point() + 
facet_grid(label ~ ., scales = "free_y", space = "free_y") + 
ylab("") + 
theme(strip.text.y = element_text(angle=0))

g2 = ggplotGrob(p2) 

g1$heights[[7]] = g2$heights[[6]]
g1$heights[[12]] = g2$heights[[8]]
g1$heights[[17]] = g2$heights[[10]]

grid.newpage()
grid.draw(g1)

Or, the heights can be set without reference to the original plot. They can be set according to the number of items for each label in df. And borrowing some code from @baptiste's answer here to select the items from the layout corresponding to the panels:

# From 'df', get the number of 'items' for each 'label'.
# That is, the number y-breaks in each panel.
library(plyr)
N = dlply(df, .(label), function(x) length(row.names(x)))

# Get the items in the g1 layout corresponding to the panels.
panels1 <- g1$layout$t[grepl("panel", g1$layout$name)]

# Replace the default panel heights with relative heights
g1$heights[panels1] <- unit(N, "null")

## Draw g1
grid.newpage()
grid.draw(g1)

enter image description here

like image 24
Sandy Muspratt Avatar answered Sep 27 '22 22:09

Sandy Muspratt