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")))
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)
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
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
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))
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))
P + theme(strip.text = element_text(size=9, lineheight=3.0))
Related Topics
How to Expand Axis Asymmetrically with Ggplot2 Without Setting Limits Manually
Sort a List of Nontrivial Elements in R
How to Italicize One Category in a Legend in Ggplot2
How to Pass Multiple Arguments to a Function as a Single Vector
Warning in Install.Packages: Unable to Move Temporary Installation
How to Merge Two Data Frames on Common Columns in R with Sum of Others
In R, Using Scientific Notation 10^ Rather Than E+
Move Nas to the End of Each Column in a Data Frame
R: Find Vector in List of Vectors
Determine Level of Nesting in R
Devtools::Install_Github() - Ignore Ssl Cert Verification Failure
How to Produce a Heatmap with Ggplot2
Split Character Columns and Get Names of Field in String
Format Date-Time as Seasons in R
Using the Geosphere Distm Function on a Data.Table to Calculate Distances