Edit Strip Size Ggplot2

edit strip size ggplot2

A simple solution is to specify the margins of your strip.text elements appropriately:

ggplot(mtcars, aes(x=gear)) + 
geom_bar(aes(y=gear), stat="identity", position="dodge") +
facet_wrap(~cyl) +
theme(strip.text.x = element_text(margin = margin(2,0,2,0, "cm")))

Sample Image

How can I change the size of the strip on facets in a ggplot?

converting the plot to a gtable manually lets you tweak the strip height,

library(ggplot2)
library(gtable)

d <- ggplot(mtcars, aes(x=gear)) +
geom_bar(aes(y=gear), stat="identity", position="dodge") +
facet_wrap(~cyl)

g <- ggplotGrob(d)
g$heights[[3]] = unit(1,"in")

grid.newpage()
grid.draw(g)

Resize strip.background to match strip.text in ggplot facet_wrap

The result you want is achievable modifying the grobs created by ggplot. The process is somewhat messy and bound to need some manual fine tuning:

Setting aside the loop, that is uninfluential on the problem, we can start by reducing the spacing between facet. That is easily done by adding the panel.spacing argument to the theme.

library(ggplot2)

d <- ggplot(subset(df, id %in% unique(df$id)[1:(100)]), aes(x=xcat)) +
annotate("line", y=yvalue1, x=xcat, colour="cornflowerblue") +
annotate("line", y=yvalue2, x=xcat, colour="grey20") +
theme(axis.text.x = element_text(size=5),
axis.text.y = element_text(size=5),
strip.text = element_text(size=5),
panel.spacing = unit(.5, 'pt')) +
facet_wrap(~id, ncol=10, nrow=10) +
expand_limits(y=0)

We then have to generate a ggplot2 plot grob. This create a list of individual elements composing the plot.

g <- ggplotGrob(d)

head(g, 5)
# TableGrob (5 x 43) "layout": 13 grobs
# z cells name grob
# 1 3 ( 5- 5, 4- 4) axis-t-1-1 zeroGrob[NULL]
# 2 3 ( 5- 5, 8- 8) axis-t-2-1 zeroGrob[NULL]
# 3 3 ( 5- 5,12-12) axis-t-3-1 zeroGrob[NULL]
# 4 3 ( 5- 5,16-16) axis-t-4-1 zeroGrob[NULL]
# 5 3 ( 5- 5,20-20) axis-t-5-1 zeroGrob[NULL]
# 6 3 ( 5- 5,24-24) axis-t-6-1 zeroGrob[NULL]
# 7 3 ( 5- 5,28-28) axis-t-7-1 zeroGrob[NULL]
# 8 3 ( 5- 5,32-32) axis-t-8-1 zeroGrob[NULL]
# 9 3 ( 5- 5,36-36) axis-t-9-1 zeroGrob[NULL]
# 10 3 ( 5- 5,40-40) axis-t-10-1 zeroGrob[NULL]
# 11 4 ( 4- 4, 4-40) xlab-t zeroGrob[NULL]
# 12 8 ( 3- 3, 4-40) subtitle zeroGrob[plot.subtitle..zeroGrob.77669]
# 13 9 ( 2- 2, 4-40) title zeroGrob[plot.title..zeroGrob.77668]

Our objective is then to find and fix the height of the ones relative to the strips.

for(i in 1:length(g$grobs)) {
if(g$grobs[[i]]$name == 'strip') g$grobs[[i]]$heights <- unit(5, 'points')
}

Unfortunately the strip grobs are also affected by the different heights the the main plot is divided into, so we have to reduce that ones also. I haven't found a way to identify programmatically which heights refers to the strip, but inspecting g$heights is quite fast to figure out which we need.

g$heights
# [1] 5.5pt 0cm 0cm 0cm 0cm
# [6] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [11] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [16] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [21] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [26] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [31] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [36] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [41] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [46] 0.518897450532725cm 1null 0cm 0.5pt 0cm
# [51] 0.518897450532725cm 1null 0.306264269406393cm 1grobheight 0cm
# [56] 5.5pt

strip_grob_position <- seq(6, length(g$heights) - 5, 5)
for(i in strip_grob_position) {
g$heights[[i]] = unit(.05, 'cm')
}

We can finally draw the result:

library(grid)
grid.newpage()
grid.draw(g)

Sample Image

I am not an expert at all in the subject, so I may have said or explained something wrongly or inaccurately. Moreover there may be better (and simpler and more ggplot-ish) ways to obtain the result.

How to modify the width of a facet_wrap strip?

For cases where you need to stack two separate plots, here's a way to match the strip widths:

  • Load the ggtext package, which allows us to have multiple text colors within a single strip label (using html tags).
  • Get the full text of the longest strip label. Add that as a second line to the short strip labels (we'll add an html tag to do this). This text will ensure that the strip width is exactly the same in both plots.
  • However, we don't want the extra line of text to be visible. So we'll add some html tags to set the color of this text to be the same as the background fill color of the strip.
library(tidyverse)
library(patchwork) # For plot layout
library(ggtext) # For multiple text colors in strip labels
theme_set(theme_bw())

set.seed(2)
df <- data.frame(shortCat = sample(c('a','b'), 10, replace=TRUE),
longCat = sample(c('a really long label','another really long label'), 10, replace=TRUE),
x = sample(seq(as.Date('2020/01/01'), as.Date('2020/12/31'), by="day"), 10),
y = sample(0:25, 10, replace = TRUE) )

# Test method's robustness by making labels of different lengths
df = df %>%
mutate(shortCat2 = gsub("b", "medium label", shortCat))

# Get text of longest label
pad = df$longCat[which.max(nchar(df$longCat))]

# Get colour of strip background
txt.col = theme_get()$strip.background$fill

# Set padding text to same colour as background, so it will be invisible
# (you can set the color to "white" for a visual confirmation of what this does)
df$shortCat2 = paste0(df$shortCat2, "<span style = 'color:",txt.col,";'><br>", pad ,"</span>")

figA = df %>%
ggplot( aes(x=x,y=y) ) +
geom_line() +
facet_wrap(vars(shortCat2), ncol=1, strip.position ="right", scales="free_y") +
theme(axis.title.y=element_blank(),
axis.title.x=element_blank(),
axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
strip.text.y=element_textbox())

figA / figB

Sample Image

Here's another approach in which we add whitespace to expand the strip labels to the desired width. It requires a monospace font, which makes it less flexible, but it doesn't require the use of html tags as in the previous method.

# Identify monospaced fonts on your system
fonts = systemfonts::system_fonts()
fonts %>% filter(monospace) %>% pull(name)
#> [1] "Menlo-Bold" "Courier-Oblique"
#> [3] "Courier-BoldOblique" "AppleBraille"
#> [5] "AppleBraille-Pinpoint8Dot" "AndaleMono"
#> [7] "Menlo-BoldItalic" "Menlo-Regular"
#> [9] "CourierNewPS-BoldMT" "AppleBraille-Outline6Dot"
#> [11] "GB18030Bitmap" "Monaco"
#> [13] "AppleBraille-Outline8Dot" "PTMono-Regular"
#> [15] "PTMono-Bold" "AppleColorEmoji"
#> [17] "Menlo-Italic" "CourierNewPS-ItalicMT"
#> [19] "Courier" "Courier-Bold"
#> [21] "CourierNewPSMT" "AppleBraille-Pinpoint6Dot"
#> [23] "CourierNewPS-BoldItalicMT"

# Set theme to use a monospace font
theme_set(theme_bw() +
theme(text=element_text(family="Menlo-Regular")))

figA <- df %>%
mutate(shortCat = paste0(shortCat,
paste(rep(" ", max(nchar(longCat)) - 1), collapse=""))
) %>%
ggplot( aes(x=x,y=y) ) +
geom_line() +
facet_wrap(vars(shortCat), ncol=1, strip.position ="right", scales="free_y") +
theme(axis.title.y=element_blank(),
axis.title.x=element_blank(),
axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
strip.text.y.right = element_text(angle = 0, hjust=0))

figB <- df %>% ggplot( aes(x=x,y=y) ) +
geom_line() +
facet_wrap(vars(longCat), ncol=1, strip.position ="right", scales="free_y") +
theme( axis.title.y=element_blank(),
strip.text.y.right = element_text(angle = 0, hjust=0) )

figA / figB

Sample Image

If you can reshape your data to long format and make a single plot, that's the most straightforward approach:

theme_set(theme_bw())

df %>%
pivot_longer(matches("Cat")) %>%
mutate(value = fct_relevel(value, "a", "b")) %>%
ggplot(aes(x,y)) +
geom_line() +
facet_wrap(~value, ncol=1, strip.position="right") +
theme(strip.text.y=element_text(angle=0, hjust=0))

Sample Image

Changing the Appearance of Facet Labels size

Use margins

From about ggplot2 ver 2.1.0: In theme, specify margins in the strip_text element (see here).

library(ggplot2)
library(gcookbook) # For the data set

p = ggplot(cabbage_exp, aes(x=Cultivar, y=Weight)) + geom_bar(stat="identity") +
facet_grid(. ~ Date) +
theme(strip.text = element_text(face="bold", size=9),
strip.background = element_rect(fill="lightblue", colour="black",size=1))

p +
theme(strip.text.x = element_text(margin = margin(.1, 0, .1, 0, "cm")))



The original answer updated to ggplot2 v2.2.0

Your facet_grid chart

This will reduce the height of the strip (all the way to zero height if you want). The height needs to be set for one strip and three grobs. This will work with your specific facet_grid example.

library(ggplot2)
library(grid)
library(gtable)
library(gcookbook) # For the data set

p = ggplot(cabbage_exp, aes(x=Cultivar, y=Weight)) + geom_bar(stat="identity") +
facet_grid(. ~ Date) +
theme(strip.text = element_text(face="bold", size=9),
strip.background = element_rect(fill="lightblue", colour="black",size=1))

g = ggplotGrob(p)

g$heights[6] = unit(0.4, "cm") # Set the height

for(i in 13:15) g$grobs[[i]]$heights = unit(1, "npc") # Set height of grobs

grid.newpage()
grid.draw(g)

Your Facet_wrap chart

There are three strips down the page. Therefore, there are three strip heights to be changed, and the three grob heights to be changed.

The following will work with your specific facet_wrap example.

p = ggplot(cabbage_exp, aes(x=Cultivar, y=Weight)) + geom_bar(stat="identity") +
facet_wrap(~ Date,ncol = 1) +
theme(strip.text = element_text(face="bold", size=9),
strip.background = element_rect(fill="lightblue", colour="black",size=1))

g = ggplotGrob(p)

for(i in c(6,11,16)) g$heights[[i]] = unit(0.4,"cm") # Three strip heights changed
for(i in c(17,18,19)) g$grobs[[i]]$heights <- unit(1, "npc") # The height of three grobs changed

grid.newpage()
grid.draw(g)

How to find the relevant heights and grobs?

g$heights returns a vector of heights. The 1null heights are the plot panels. The strip heights are one before - that is 6, 11, 16.

g$layout returns a data frame with the names of the grobs in the last column. The grobs that need their heights changed are those with names beginning with "strip". They are in rows 17, 18, 19.

To generalise a little

p = ggplot(cabbage_exp, aes(x=Cultivar, y=Weight)) + geom_bar(stat="identity") +
facet_wrap(~ Date,ncol = 1) +
theme(strip.text = element_text(face="bold", size=9),
strip.background = element_rect(fill="lightblue", colour="black",size=1))

g = ggplotGrob(p)

# The heights that need changing are in positions one less than the plot panels
pos = c(subset(g$layout, grepl("panel", g$layout$name), select = t))
for(i in pos) g$heights[i-1] = unit(0.4,"cm")

# The grobs that need their heights changed:
grobs = which(grepl("strip", g$layout$name))
for(i in grobs) g$grobs[[i]]$heights <- unit(1, "npc")
grid.newpage()
grid.draw(g)

Multiple panels per row

Nearly the same code can be used, even with a title and a legend positioned on top. There is a change in the calculation of pos, but even without that change, the code runs.

library(ggplot2)
library(grid)

# Some data
df = data.frame(x= rnorm(100), y = rnorm(100), z = sample(1:12, 100, T), col = sample(c("a","b"), 100, T))

# The plot
p = ggplot(df, aes(x = x, y = y, colour = col)) +
geom_point() +
labs(title = "Made-up data") +
facet_wrap(~ z, nrow = 4) +
theme(legend.position = "top")

g = ggplotGrob(p)

# The heights that need changing are in positions one less than the plot panels
pos = c(unique(subset(g$layout, grepl("panel", g$layout$name), select = t)))
for(i in pos) g$heights[i-1] = unit(0.2, "cm")

# The grobs that need their heights changed:
grobs = which(grepl("strip", g$layout$name))
for(i in grobs) g$grobs[[i]]$heights <- unit(1, "npc")

grid.newpage()
grid.draw(g)

Edit distance between the facet / strip and the plot

There is a much simpler solution

theme(strip.placement = "outside")

Is there a way to increase the height of the strip.text bar in a facet?

First, modify the levels so that they include a linebreak:

levels(diamonds$color) <- paste0(" \n", levels(diamonds$color) , "\n ")

Then adjust as necessary. eg:

P <- ggplot(diamonds, aes(carat, price, fill = ..density..)) +
xlim(0, 2) + stat_binhex(na.rm = TRUE)+
facet_wrap(~ color)

P + theme(strip.text = element_text(size=9, lineheight=0.5))

lineheight=0.5

P +  theme(strip.text = element_text(size=9, lineheight=3.0))

lineheight=3.0



Related Topics



Leave a reply



Submit