I am making a plot where variable descriptions appear in the left “strip” text of facet_wrap()
facets. Sometimes when variable descriptions are long enough to be wrapped, the last line comes out very short. This can make the plot look weird, especially if the text is right-justified.
Here’s a reproducible example:
library('ggplot2')
dat <- data.frame(variable=rep(c('Frogs','Kittens wearing tiny hats','Mars',
'Really long name that will wrap to three lines','Tacos',
'Your local public library'),each=2))
dat$stratify <- rep(c('Yes','No'),6)
dat$beta <- 1:12
dat$lower_ci <- dat$beta - (1:12)/10
dat$upper_ci <- dat$beta + (1:12)/10
dat$is_oddvar <- grepl('Frogs|Mars|Tacos',dat$variable) # Every other variable in the plot has a grey stripe
ggplot(dat,aes(x=beta,xmin=lower_ci,xmax=upper_ci,y=stratify,color=stratify)) +
# Gray/white stripes
geom_rect(aes(fill=is_oddvar),color='grey90',xmin = -Inf,xmax = Inf,ymin = -Inf,ymax = Inf) +
scale_fill_manual(breaks=c(T,F),values=c('grey90','white'),guide=NULL) +
facet_wrap('variable',strip.position='left',ncol=1,scales='fixed',labeller=label_wrap_gen(width=24)) +
# Points and confidence intervals
geom_point() + geom_linerange(linewidth=1) +
# Themes/general plot appearance
theme_bw() +
theme(panel.spacing=unit(0,'lines'),panel.border=element_blank(),
axis.ticks.y=element_blank(),axis.text.y=element_blank(),
axis.title.y=element_blank(),axis.title.x=element_blank(),
strip.text.y.left=element_text(angle=0,hjust=1),strip.background=element_blank(),
legend.position='bottom',legend.title=element_blank())
Is there a way to make the labeller split lines more evenly when they’re long enough that they need to be wrapped? For example, to wrap “Kittens wearing tiny hats” as “Kittens wearing | tiny hats” instead of “Kittens wearing tiny | hats”?
1
I found out that nchar()
can be used to control the length of the strings of the labeller
. Since nchar()
returns the number of characters of the input strings, we can use facet_wrap()
variable as its input strings, and then divide its return with a fraction to get a numeric vector of lengths. Then, we use this vector as the value of width
in label_wrap_gen()
.
In your example, by trial and error I found 0.51
is a good choice. I then changed your facet_wrap(...)
line to the following:
facet_wrap(vars(variable),strip.position='left',ncol=1,scales='fixed',
labeller=label_wrap_gen(width = nchar(vars(variable))/0.51))
Here is the result:
3
One approach with {ggtext}:
library(ggplot2)
library(ggtext)
data.frame(variable = c('Frogs','Kittens wearing tiny hats','Mars',
'Really long name that will wrap to three lines','Tacos',
'Your local public library')
) |>
ggplot() +
geom_point(aes(1, 1)) +
facet_wrap(~ variable, strip.position = 'left', ncol = 1) +
theme(strip.text = element_textbox_simple(maxwidth = unit(1, 'in'),
halign = 1,
margin = margin(0, 5, 0, 0)
),
strip.background=element_blank(),
axis.ticks = element_blank(),
axis.text = element_blank()
) +
labs(x = '', y = '')
Note that {ggtext} also allows to render markdown, incl. HTML. So one should be able to use the whole works of CSS text wrapping and aligning (haven’t tried though).