Ggplot2 Two-Line Label With Expression

ggplot2 two-line label with expression

I think this is a bug. (Or a consequence of the fact that "multi-line expressions are not supported", as stated in the conversation you linked to).

The workaround that Gavin Simpson alluded to is:

#For convenience redefine p as the unlabeled plot
p <- ggplot(mtcars,aes(x=wt,y=mpg))+geom_point()

#Use atop to fake a line break
p + xlab(expression(atop("A long string of text for the purpose", paste("of illustrating my point" [reported]))))

Sample Image

It is possible to use true line breaks with subscripts. In the short example below, which has the same form as your example, the subscript is correctly placed adjacent to the rest of the text but the two lines of text are not centered correctly:

p + xlab(expression(paste("line1 \n line2 a" [b])))

Sample Image

I think that in both cases, the subscript is placed wrong when the upper line of text is longer than the lower line of text. Compare

p + xlab(expression(paste("abc \n abcd" [reported])))

Sample Image

p + xlab(expression(paste("abc \n ab" [reported])))

Sample Image

The subscript always ends up aligned just to the right of the right end of the upper line.

p + xlab(expression(paste("abcdefghijklmnop \n ab" [reported])))

Sample Image

ggplot2 - two or more lines on y axis title

Play with plot.margin in theme to change the whitespace around your plot

ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_line() +
labs(y = expression(paste("line 1\nline2"))) +
theme(plot.margin = margin(1, 1, 1, 1, "cm")
)

Sample Image

How to make an expression go onto multiple lines for axis labels in ggplot?

To make a label into a new line use "\r\n" would create that. This is an example

library(ggplot2)

group_name = sprintf("A[%i*x This is a long string and I would like it to look nicely wrapped on the x axis label]", rep(1:4,each=2)) #added long string from code solution
group_name3 = sprintf("A[%i*y]", rep(1:4,each=2))

make_labels2 <- function(value) {
x <- as.character(value)
#do.call(expression, lapply(x, function(y) bquote(atop(bold(.(y)), "this"~italic("degree")~x)) ))
x <- lapply(x, function(x) { paste(strwrap(x, width = 10), collapse = "\r\n") })
x <- do.call(expression, lapply(x, function(y) bquote(atop(bold(.(strsplit(y,split="_")[[1]][[1]]))~"_"~italic(.(strsplit(y,split="_")[[1]][[2]])), "this"~italic("degree")~x)) ))
x
}

mydata2 <- data.frame(mygroup = group_name,
mygroup3 = group_name3,
mysubgroup = factor(c("Yes", "No"),
levels = c("Yes", "No")),
value = c(60,40,90,10,55,45,88,12))
mydata2$mygrp2 <- paste0(mydata2$mygroup,"_",mydata2$mygroup3)

ggplot(mydata2, aes(mygrp2, value, fill = mysubgroup)) +
geom_bar(position = "dodge", width = 0.5, stat = "identity")+
coord_flip() +
scale_x_discrete(labels = make_labels2)

Output graph

Sample Image

properly formatting a two-line caption in ggplot2

How about this:

# needed libraries
library(ggplot2)

# custom function to prepare a caption
caption_maker <- function(caption) {

# prepare the caption with additional info
caption <- base::substitute(
atop(y,
paste(
"In favor of null: ",
"log"["e"],
"(BF"["01"],
") = ",
bf
)),
env = base::list(
bf = 123,
y = caption
)
)

# return the message
return(caption)
}

# custom function to add labels to the plot
plot_maker <-
function(xlab = NULL,
ylab = NULL,
title = NULL,
caption = NULL) {
caption.text <- caption_maker(caption = caption)

plot <- ggplot(mtcars, aes(wt, mpg)) + geom_point() +
ggplot2::labs(
x = xlab,
y = ylab,
title = title,
caption = caption.text)

# return the plot
return(plot)
}


plot_maker(caption = NULL)
plot_maker(caption = "This is mtcars:")
plot_maker(xlab = "x Axis Title",
caption = substitute(paste(italic("Note"), ": This is mtcars dataset"))
)

I got the atop idea from this question

Line-break and superscript in axis label ggplot2 R

You could use expression() and atop() to autoformat white space.

ggplot(data,aes(x=x,y=y)) + 
geom_point() +
xlab( expression(atop("Density of mobile",paste("invertebrates (ind.~",m^{2},")"))))

Sample Image

Data

set.seed(1) 
data <- data.frame(x = 1:10, y = 1:10 + runif(-1,1,n=10))

Expressions dynamically positioned on multiple lines in ggplot

I don't use math notation that often. That's probably why I always struggle with using it and also with the differing ways to add them to a ggplot. What worked the best for me is to set up the plotmath strings using paste0. Therefore I have rewritten your bquotes using paste0. I also made two small changes to your fun linEq(replaced = by ==, added a * before x).

To put your four labels on the plot I have put them in a vector and also set the y positions as a vector.

However, my approach does not automatically pick the right x and y positions for your labels. And I don't think that there is an easy approach to achieve that and which will work for each and every case. That's why I opted for putting the labels on top of the plot.

Thanks to the comment by @teunbrand: At least for the x-axis position of the labels you could do x=Inf to align the labels on the right of the plot.

library(ggplot2)

set.seed(42)

dat <- data.frame(
"x" = sample(1:100, 800, replace = T),
"y" = sample(1:100, 800, replace = T)
)

mod <- lm(y ~ poly(x, 1, raw = T), dat)

linEq <- function(lmObj, dig) {
paste0(c("", "y == "),
c("", signif(lmObj$coef[1], dig)),
c("", ifelse(sign(lmObj$coef)[2] == 1, " + ", " - ")),
c("", signif(abs(lmObj$coef[2]), dig)), c("", "* x"),
collapse = ""
)
}

lmp <- function(modelobject) {
if (class(modelobject) != "lm") stop("Not an object of class 'lm' ")
f <- summary(modelobject)$fstatistic
p <- pf(f[1], f[2], f[3], lower.tail = F)
attributes(p) <- NULL
return(p)
}


eq <- linEq(mod, 3)
r2 <- paste0("italic(R)^{2} == ", round(summary(mod)$adj.r.squared, 2))
f.value <- round(summary(mod)$fstatistic[[1]], 2)
df1 <- summary(mod)$fstatistic[[2]]
df2 <- summary(mod)$fstatistic[[3]]
f.text <- paste0("italic(F)['", df1, ",", df2, "'] == ", f.value)
p.value <- paste0("italic(p) ", ifelse(lmp(mod) < 0.001, "<", "=="), ifelse(lmp(mod) < 0.001, "0.001", round(lmp(mod), 3)))

ggplot(data = dat, aes(x = x, y = y)) +
geom_point() +
geom_smooth(method = "lm", formula = y ~ x, se = F) +
xlab("X") +
ylab("Y") +
annotate(
geom = "text", x = (1 * max(dat$x)), y = c(2, 1.8, 1.6, 1.4) * max(dat$y),
label = c(eq, r2, f.text, p.value),
parse = TRUE,
hjust = "inward"
) +
theme_classic()

Sample Image

EDIT Maybe this fits your needs. Not sure whether this works for all of your plots, but ... The approach below first makes on object from the labels via the grid package. Second, making use of the patchwork package you could add them as an inset in the top right corner. One caveat of using patchwork is that is does not work with grid.arrange (at least I got an error), i.e. you have to glue your plots together using patchwork too. As an example I added a second plot by simply scaling x and y by 10:

Note The picture does not show the sub-/superscripts. But they show up in the plot window as well as when saving the plot.

library(patchwork)

make_grob <- function(labels, y, size = unit(8, "pt")) {
grob_list <- purrr::map2(labels, y,
~ grid::textGrob(scales::parse_format()(.x), hjust = 1,
x = grid::unit(1, "npc"), y = grid::unit(.y, "npc"),
gp = grid::gpar(fontsize = size))

)

grid::grobTree(grob_list[[1]], grob_list[[2]], grob_list[[3]], grob_list[[4]])
}


gt <- make_grob(list(eq, r2, f.text, p.value), y = seq(.2, .8, .2))
gg1 <- ggplot(data = dat, aes(x = x, y = y)) +
geom_point() +
geom_smooth(method = "lm", formula = y ~ x, se = F) +
xlab("X") +
ylab("Y") +
theme_classic() +
patchwork::inset_element(gt, left = .7, bottom = .7, right = 1, top = 1, align_to = "plot", on_top = TRUE)

gg2 <- ggplot(data = dat, aes(x = 10 * x, y = 10 * y)) +
geom_point() +
geom_smooth(method = "lm", formula = y ~ x, se = F) +
xlab("X") +
ylab("Y") +
theme_classic() +
patchwork::inset_element(gt, left = .7, bottom = .7, right = 1, top = 1, align_to = "plot", on_top = TRUE)

gg1 + gg2

Sample Image

How to fit the axis title with two lines in R?

One way would be to adjust the margins giving more space to the left.

library(ggplot2)

ggplot(data=DataA, aes(x=Genotype, y=mean, fill=Category))+
geom_bar(stat="identity",position="dodge", width = 0.7) +
geom_errorbar(aes(ymin= mean-se, ymax=mean + se), position=position_dodge(0.7),
width=0.2, size=1) +
scale_fill_manual(values= c ("Dark gray", "Cadetblue")) +
scale_y_continuous(breaks = seq(0,2,0.2), labels = scales::percent, limits = c(0,2)) +
geom_hline(yintercept=1, linetype="dashed", color = "Dark red", size=1.5) +
xlab("Cultivar") +
ylab(expression(paste("Value of thinning treatment \n relative to unthinning treatment"))) +
theme(axis.title = element_text (face = "plain", size = 15, color = "black"),
axis.text.x = element_text(size= 15),
axis.text.y = element_text(size= 15),
axis.line = element_line(size = 0.5, colour = "black")) +
theme(plot.margin=unit(c(1,0.5,0.5,1.2),"cm"))

Sample Image



Related Topics



Leave a reply



Submit