Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change aesthetics of nested facet in ggplot2

I want to use nested panels in ggplot2 but the names of the two panels must be on opposite sides of the plot. Here is a reproducible example:

library(ggplot2)
library(data.table)

# data for reproducible example
dt <- data.table(
  value = c("East", "West","East", "West", "NY", "LA","NY", "LA"),
  year = c(2008, 2008, 2013, 2013, 2008, 2008, 2013, 2013),
  index = c(12, 10, 18, 15, 10, 8, 12 , 14),
  var = c("Region","Region","Region","Region", "Metro","Metro","Metro","Metro"))

# change order or plot facets
dt[, var := factor(var, levels=c( "Region", "Metro"))]

# plot
ggplot(data=dt) +
  geom_point( aes(x=index, y= factor(year), color=index)) +
  facet_grid(value + var ~., scales = "free_y", space="free") 

enter image description here

Note that in this example I'm using columns value + var to create the facets, but the title of the two panels are plotted together.

Expected output: What I would like to achieve is this:

enter image description here

like image 763
rafa.pereira Avatar asked Jul 16 '16 15:07

rafa.pereira


People also ask

How do I rearrange facets in ggplot2?

To reorder the facets accordingly of the given ggplot2 plot, the user needs to reorder the levels of our grouping variable accordingly with the help of the levels function and required parameter passed into it, further it will lead to the reordering of the facets accordingly in the R programming language.

How do I change a facet label in R?

Change the text of facet labels Facet labels can be modified using the option labeller , which should be a function. In the following R code, facets are labelled by combining the name of the grouping variable with group levels. The labeller function label_both is used.

Can you facet wrap by 2 variables?

Note that you can add as many (categorical) variables as you'd like in your facet wrap, however, this will result in a longer loading period for R.

What is the function of Facet_grid () in Ggplot ()?

facet_grid() forms a matrix of panels defined by row and column faceting variables. It is most useful when you have two discrete variables, and all combinations of the variables exist in the data. If you have only one variable with many levels, try facet_wrap() .


2 Answers

A possible solution using labeller = label_bquote(rows = .(var1)), two calls to geom_text and some further customizations:

ggplot(dt, aes(x = index, y = factor(year), color = index)) +
  geom_point() +
  geom_text(aes(x = 6, y = 1.5, label = value), color = 'black', hjust = 0) +
  geom_text(aes(x = 7, label = year), color = 'black') +
  geom_segment(aes(x = 7.5, xend = 7.5, y = 0.7, yend = 2.3), color = 'black') +
  geom_segment(aes(x = 7.45, xend = 7.5, y = 1, yend = 1), color = 'black') +
  geom_segment(aes(x = 7.45, xend = 7.5, y = 2, yend = 2), color = 'black') +
  scale_x_continuous(breaks = seq(8,18,2)) +
  facet_grid(value + var1 ~., scales = "free_y", space="free", labeller = label_bquote(rows = .(var1))) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        axis.text.y = element_blank(),
        strip.background = element_rect(color = 'darkgrey', fill = 'lightgrey'),
        panel.grid.major.y = element_blank(),
        panel.grid.minor = element_blank())

which gives:

enter image description here

Note: I used var1 instead of var because the latter is also a function name.


Another possibility is to make use of the gridExtra package to create the additional labels and put them in front of the y-axis labels with grid.arrange:

# create the main plot
mainplot <- ggplot(dt, aes(x = index, y = factor(year), color = index)) +
  geom_point(size = 2) +
  scale_x_continuous(breaks = seq(8,18,2)) +
  facet_grid(value + var1 ~., scales = "free_y", space="free", labeller = label_bquote(rows = .(var1))) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        strip.background = element_rect(color = 'darkgrey', fill = 'lightgrey'))

# create a 2nd plot with everything besides the labels set to blank or NA
lbls <- ggplot(dt, aes(x = 0, y = factor(year))) +
  geom_point(color = NA) +
  geom_text(aes(x = 0, y = 1.5, label = value), color = 'black') +
  scale_x_continuous(limits = c(0,0), breaks = 0) +
  facet_grid(value + var1 ~.) +
  theme_minimal() +
  theme(axis.title = element_blank(),
        axis.text.x = element_text(color = NA),
        axis.text.y = element_blank(),
        strip.background = element_blank(),
        strip.text = element_blank(),
        panel.grid = element_blank(),
        legend.position = 'none')

# plot with 'grid.arrange' and give the 'lbls'-plot a small width
library(gridExtra)
grid.arrange(lbls, mainplot, ncol = 2, widths = c(1,9))

which gives:

enter image description here

like image 67
Jaap Avatar answered Oct 05 '22 13:10

Jaap


Adding this mostly to show some grob/gtable manipulations:

library(ggplot2)
library(data.table)
library(gtable)
library(gridExtra)

# data for reproducible example
dt <- data.table(
  value = c("East", "West","East", "West", "NY", "LA","NY", "LA"),
  year = c(2008, 2008, 2013, 2013, 2008, 2008, 2013, 2013),
  index = c(12, 10, 18, 15, 10, 8, 12 , 14),
  var = c("Region","Region","Region","Region", "Metro","Metro","Metro","Metro"))

# change order or plot facets
dt[, var := factor(var, levels=c( "Region", "Metro"))]

# plot
ggplot(data=dt) +
  geom_point( aes(x=index, y= factor(year), color=index)) +
  facet_grid(value + var ~., scales = "free_y", space="free") +
  theme_bw() +
  theme(panel.grid=element_blank()) +
  theme(panel.border=element_blank()) +
  theme(axis.line.x=element_line()) +
  theme(axis.line.y=element_line()) -> gg

gb <- ggplot_build(gg)
gt <- ggplot_gtable(gb)

Here's what that looks like:

gt
## TableGrob (14 x 8) "layout": 24 grobs
##     z         cells        name                                    grob
## 1   0 ( 1-14, 1- 8)  background        rect[plot.background..rect.5201]
## 2   5 ( 4- 4, 3- 3)      axis-l    absoluteGrob[GRID.absoluteGrob.5074]
## 3   6 ( 6- 6, 3- 3)      axis-l    absoluteGrob[GRID.absoluteGrob.5082]
## 4   7 ( 8- 8, 3- 3)      axis-l    absoluteGrob[GRID.absoluteGrob.5090]
## 5   8 (10-10, 3- 3)      axis-l    absoluteGrob[GRID.absoluteGrob.5098]
## 6   1 ( 4- 4, 4- 4)       panel                  gTree[GRID.gTree.5155]
## 7   2 ( 6- 6, 4- 4)       panel                  gTree[GRID.gTree.5164]
## 8   3 ( 8- 8, 4- 4)       panel                  gTree[GRID.gTree.5173]
## 9   4 (10-10, 4- 4)       panel                  gTree[GRID.gTree.5182]
## 10  9 ( 4- 4, 5- 5) strip-right   absoluteGrob[strip.absoluteGrob.5104]
## 11 10 ( 6- 6, 5- 5) strip-right   absoluteGrob[strip.absoluteGrob.5110]
## 12 11 ( 8- 8, 5- 5) strip-right   absoluteGrob[strip.absoluteGrob.5116]
## 13 12 (10-10, 5- 5) strip-right   absoluteGrob[strip.absoluteGrob.5122]
## 14 13 ( 4- 4, 6- 6) strip-right   absoluteGrob[strip.absoluteGrob.5128]
## 15 14 ( 6- 6, 6- 6) strip-right   absoluteGrob[strip.absoluteGrob.5134]
## 16 15 ( 8- 8, 6- 6) strip-right   absoluteGrob[strip.absoluteGrob.5140]
## 17 16 (10-10, 6- 6) strip-right   absoluteGrob[strip.absoluteGrob.5146]
## 18 17 (11-11, 4- 4)      axis-b    absoluteGrob[GRID.absoluteGrob.5066]
## 19 18 (12-12, 4- 4)        xlab titleGrob[axis.title.x..titleGrob.5185]
## 20 19 ( 4-10, 2- 2)        ylab titleGrob[axis.title.y..titleGrob.5188]
## 21 20 ( 4-10, 7- 7)   guide-box                       gtable[guide-box]
## 22 21 ( 3- 3, 4- 4)    subtitle  zeroGrob[plot.subtitle..zeroGrob.5198]
## 23 22 ( 2- 2, 4- 4)       title     zeroGrob[plot.title..zeroGrob.5197]
## 24 23 (13-13, 4- 4)     caption   zeroGrob[plot.caption..zeroGrob.5199]

We can manipulate those components in a straightforward manner:

# make a copy of the gtable (not rly necessary but I think it helps simplify things since 
# I'll usually forget to offset the column positions at some point if the
# manipulations get too involved)
gt2 <- gt

# add a new column after the axis title
gt2 <- gtable_add_cols(gt2, unit(3.0, "lines"), 2)

# these are those pesky strips of yours
for_left <- gt[c(4,6,8,10),5]

# let's copy them over into our new column
gt2 <- gtable_add_grob(gt2, for_left$grobs[[1]], t=4, l=3, b=4, r=3)
gt2 <- gtable_add_grob(gt2, for_left$grobs[[2]], t=6, l=3, b=6, r=3)
gt2 <- gtable_add_grob(gt2, for_left$grobs[[3]], t=8, l=3, b=8, r=3)
gt2 <- gtable_add_grob(gt2, for_left$grobs[[4]], t=10, l=3, b=10, r=3)

# then get rid of the original ones
gt2 <- gt2[, -6]

# now we'll change the background color, border color and text rotation of each strip text 
for (gi in 21:24) {
  gt2$grobs[[gi]]$children[[1]]$gp$fill <- "white"
  gt2$grobs[[gi]]$children[[1]]$gp$col <- "white"
  gt2$grobs[[gi]]$children[[2]]$children[[1]]$rot <- 0
}

grid.arrange(gt2)

enter image description here

IMO the custom labeller & geom_text approach in the first answer is far more readable and repeatable.

like image 35
hrbrmstr Avatar answered Oct 05 '22 12:10

hrbrmstr