Line Spacing for Wrapped Text in Ggplot

Line spacing for wrapped text in ggplot

Just use lineheight, e.g.:

ggplot(data = NULL, aes(x = 1:3, y = 1:3, 
label = str_wrap(txt, width = 3))) +
geom_text(lineheight = .5) +
expand_limits(x = c(0.5, 3.5), y = c(0.5, 3.5))

(s. ?geom_text)

Sample Image

Reduce line-height in R ggplot in geom_label() and geom_text(), when line-breaking with \n

Try the lineheight argument in geom_label:

data.frame(x = c(1,2,3,4), y = c(1,2,3,4)) %>% 
ggplot() +
geom_point(aes(x = x, y = y)) +
geom_label(aes(x = 2.5, y = 2.5, label = 'label here \n with break'),
fill = '#dddddd', lineheight = 0.5)

Sample Image

How to reduce space when using geom_text, paste and \n

geom_text takes a lineheight argument, which defaults to 1.2. Here it is at 0.9:

ggplot(Plot_DF.2.2, aes(x = SN, y = Proportion, fill = SN)) +
facet_wrap(V ~ .) +
geom_col() +
labs(x = "Number of Vaccinated Discussants in Respondents' Social Network",
title = "Distribution of Vaccination in Social Networks",
subtitle = "Conditional on Vaccination Status",
caption = paste("Note: Numbers represent estimated percentage with",
"estimates' standard errors in parentheses.",
"\nError bar represents confidence interval",
"around the estimate.")) +
theme_minimal() +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
theme(legend.position = "none",
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
plot.caption= element_text(hjust = 0),
axis.title.y=element_blank()) +
geom_text(aes(label = paste0(round(Percent,0), "%", "\n(", round(SE*100, 2), ")")),
position = position_dodge(width = 0.9), vjust = -0.25,
lineheight = 0.9)

Sample Image

Wrapping text and increasing the space between factors on the y-axis in a ggplot bar chart in R?

Details on increasing space between bars in second part of answer.

I think this is really an exercise in your own preferences in what would look best: options include:

  1. Moving the legend to the bottom to give you more width for y axis text. You could shrink the legend a bit by reducing the key sizes to give you a bit more vertical space for the axis labels.
  2. Reduce bar width so there is more white space between axis text.
  3. The size of the final plot, I've assumed that it needs to be presented on an A4 portrait page with allowance for margins and a figure title. This is probably the defining criteria for what is possible.
  4. The criteria and trade-offs you have clearly identified in the question.

Personally I would go for a much reduced bar width: following Tufte’s principle of high data-ink ratio: what is important is the relative length of each stacked bar not the thickness. I've reduced the bar width a tad from the default 0.9 to 0.8.

library(tibble)
library(ggplot2)
library(viridis)
library(forcats)
library(stringr)

df |>
ggplot(aes(x=subgr_count, y=fct_reorder(sjr_categories, subgr_count),
fill = labels)) +
geom_col(width = 0.8) +
scale_fill_viridis(discrete = TRUE) +
scale_y_discrete(labels = function(x) str_wrap(x, width = 35)) +
guides(fill = guide_legend(nrow = 2))+
theme_bw() +
theme(legend.position = "bottom",
legend.direction = "horizontal",
legend.justification = "left",
legend.text = element_text(size = 10),
legend.key.size = unit(4, "mm"),
axis.text = element_text(size = 10)) +

labs(x = "count", y = NULL)

Sample Image


ggsave(filename="wrap35_font10.png", width = 180, height = 200, units = "mm")

Created on 2022-10-16 with reprex v2.0.2

Personal preference: smaller plotting area, reduce width to 0.4 otherwise all the same as above:

 ggsave(filename="wrap35_font10_width_0.4.png", width = 150, height = 180, units = "mm")

Sample Image

Addendum: increasing the separation between bars

With regard to the core question as pointed out in the OP comment: How to increase the gap between the bars (to allow for better separation between the y axis labels). This can be achieved by the adjusting bar widths and output height of the plot:

To illustrate the point here is a minimal example:

For a discrete scale, discrete values, in ggplot, are placed apart by intervals of 1 unit.
To increase the gap between bars you have a couple of options for horizontal bars:

a) reduce the bar width, but this retains the absolute distance between bars for a given plot, it just increases the white space between the bars.
b) increase the overall plot height. For a given number of bars this increases the relative thickness of each bar but also the relative distance between each bar.

So to increase space between bars (and hence allow more room for wrapped text) increase the height of the plot and decrease the width of the bars.

Hopefully this is illustrated by these side by side plots where every thing is the same except the plot height.

df1 <- data.frame(var = c("Discrete value 1", "Discrete value 2 description", "Discrete value 3 even longer description"),
val = 1:3)

p1 <-
ggplot(df1, aes(val, var))+
geom_col(width = 0.1)+
scale_y_discrete(labels = function(x) str_wrap(x, width = 15))+
labs(x = NULL,
y = NULL)+
theme_bw()+
theme(axis.text.y = element_text(size = 10))

ggsave(p1, filename="wrap15_font10_100x100.png", height = 100, width = 100, units = "mm")

ggsave(p1, filename="wrap15_font10_150x100.png", height = 150, width = 100, units = "mm")

Sample Image

A hack

Not really recommending this but you could insert dummy discrete values between your actual values to force separation between bars...

library(ggplot2)
library(stringr)
library(forcats)

df1 <- data.frame(var = fct_inorder(c("Discrete value 1", "gap1", "Discrete value 2 description", "gap2", "Discrete value 3 even longer description")),
val = c(1, 0, 2, 0, 3))

ggplot(df1, aes(val, var))+
geom_col(width = 0.9)+
scale_y_discrete(labels = function(x) ifelse(str_detect(x, "gap"), "", str_wrap(x, width = 15)))+
labs(title = "A hack: include dummy discrete values between actual values",
x = NULL,
y = NULL)+
theme_bw()+
theme(axis.text.y = element_text(size = 10))

Sample Image

Created on 2022-10-16 with reprex v2.0.2

ggplot: How to wrap title text according to margins that are relative to plot's width

Not a 100% satisfactory answer to the question but it might still be helpful. Instead of aligning to the entire plot's width, we can align to the panel's width with ggtext::element_textbox(). You can vary the margin argument depending on the size of the title, unit(15, "pt") seemed to work in this particular case.

library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.0.2
library(ggtext)
#> Warning: package 'ggtext' was built under R version 4.0.3

my_title <- c("reltively long sentences that normally isn't appropriate as a title but it is what it is")

ggplot(ToothGrowth, aes(x = factor(dose), y = len)) +
geom_boxplot() +
labs(title = my_title) +
theme(
plot.title = element_textbox(hjust = 0.5,
width = unit(0.5, "npc"),
margin = margin(b = 15))
)

Sample Image

Created on 2020-12-25 by the reprex package (v0.3.0)

EDIT: Example with plot.title.position = "plot". If you set element_textbox(..., halign = 0.5), then the distances from the text to the borders is equal, but this will center-align the text.

ggplot(ToothGrowth, aes(x = factor(dose), y = len)) + 
geom_boxplot() +
labs(title = my_title) +
theme(
plot.title = element_textbox(hjust = 0.5,
width = unit(0.5, "npc"),
margin = margin(b = 15)),
plot.title.position = "plot"
)

Sample Image

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

wrapping long geom_text labels

Use a Unicode zero-width space and substitute all the _'s with _ + it:

library(stringi)
library(ggplot2)

my_label <- "Some_arbitrarily_larger_text"
my_label <- stri_replace_all_fixed(my_label, "_", "_\U200B")

To the naked eye it's continuous:

my_label
## [1] "Some_​arbitrarily_​larger_​text"

But, programmatically, it's a word break/wrap opportunity:

wrapper <- function(x, ...) paste(stri_wrap(x, ...), collapse = "\n")

ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
annotate("text", x = 4, y = 25, label = wrapper(my_label, width = 5))

Sample Image

Wrap long axis labels via labeller=label_wrap in ggplot2

You don't need the label_wrap function. Instead use the str_wrap function from the stringr package.

You do not provide your df data frame, so I create a simple data frame, one that contains your labels. Then, apply the str_wrap function to the labels.

library(ggplot2)
library(stringr)

df = data.frame(x = c("label", "long label", "very, very long label"),
y = c(10, 15, 20))
df

df$newx = str_wrap(df$x, width = 10)
df

Now to apply the labels to a ggplot chart: The first chart uses the original labels; the second chart uses the modified labels; and for the third chart, the labels are modified in the call to ggplot.

ggplot(df, aes(x, y)) + 
xlab("") + ylab("Number of Participants") +
geom_bar(stat = "identity")

ggplot(df, aes(newx, y)) +
xlab("") + ylab("Number of Participants") +
geom_bar(stat = "identity")

ggplot(df, aes(x, y)) +
xlab("") + ylab("Number of Participants") +
geom_bar(stat = "identity") +
scale_x_discrete(labels = function(x) str_wrap(x, width = 10))

Sample Image

Wrap the axis text by the delimiter in ggplot

I think str_wrap needs a space to trigger a new line. Here I add one to each semicolon:

new_df$Group = stringr::str_replace_all(new_df$Group, ";", "; ")

ggplot(data = new_df,
aes(x = stringr::str_wrap(Group, 20),
group = Type, fill = Type)) +
....
# clearer formatting for posting purposes
theme(axis.text.x=element_text(size=rel(1), angle=0))+
....

Sample Image

ggplot: How to increase spacing between faceted plots?

Use the theme function:

library(grid)

p + theme(panel.spacing = unit(2, "lines"))

See also here: Slicing plots generated by ggplot2



Related Topics



Leave a reply



Submit