dandrews
dandrews

Reputation: 1074

Parse an annotation across multiple lines in ggplot

Closest I've come is here: Annotate ggplot plot with a multiline expression with objects? but I cannot seem to get the solution working. I cannot use the ggpmisc package because I am forcing the regression through the origin. Here's some example data to create a plot and the exact expressions I am working with.

library(tidyverse)
df <- tibble(x=rnorm(10,10,3),
             y=rnorm(10,10,3)+exp(seq(1,10,1)))

eqn <- "italic(y) == \"53\" * italic(x) * \",\" ~ ~italic(r)^2 ~ \"=\" ~ \"0.732\""
a <- "italic(y) == \"53\" * italic(x)"
r2 <- "italic(r)^2 == \"0.732\""

This works for a single line:

ggplot(df) +
  geom_point(aes(x=x,y=y)) +
  ggplot2::annotate('text',
                    x = 0, y = 15000,
                    label=a,
                    parse = T,
                    hjust=-1)

This does not work:

ggplot(df) +
  geom_point(aes(x=x,y=y)) +
  ggplot2::annotate('text',
                    x = 0, y = 15000,
                    label=paste(a,r2),
                    parse = T,
                    hjust=-1) # throws error

ggplot(df) +
  geom_point(aes(x=x,y=y)) +
  ggplot2::annotate('text',
                    x = 0, y = 15000,
                    label=paste(a,r2,sep='\n'),
                    parse = T,
                    hjust=-1) # no error but no R2 value

Per the linked solution I should be able to use atop() inside the expression? This throws an error:

# from solution linked that I'm trying to adapt
# paste0('atop(Q[10] ==', q10, ', M[O[2]] ==', a, '*e^(', b, '*T))')

ggplot(df) +
  geom_point(aes(x=x,y=y)) +
  ggplot2::annotate('text',
                    x = 0, y = 15000,
                    label=paste0('atop(y ==', a, ', r^2 ==', r2, '*e^2)'),
                    parse = T)
                    hjust=-1)

Upvotes: 0

Views: 703

Answers (1)

Jon Spring
Jon Spring

Reputation: 66415

Here's a ggtext alternative:

  ggplot(df) +
  geom_point(aes(x=x,y=y)) +
  ggtext::geom_richtext(aes(x,y, label = label),
                        hjust = 0,
    data = data.frame(x = 0, y = 15000,
                      label = "53*x*<br>*r<sup>2</sup>* = 0.732"))

enter image description here


We could automate it like this:

set.seed(42)
df <- tibble(x=rnorm(10,10,3),
             y=rnorm(10,10,3)+exp(seq(1,10,1)))
fit <- lm(y~x, df)
r2 <- formatC(summary(fit)$r.squared, digits = 2)
slope <- formatC(fit$coefficients[2], digits = 2)

ggplot(df) +
  geom_point(aes(x=x,y=y)) +
  ggtext::geom_richtext(aes(x,y, label = label),
                        hjust = 0,
    data = data.frame(x = 0, y = 15000,
                      label = paste0(slope, "*x*<br>*r<sup>2</sup>* = ", r2)))

enter image description here


Suggested edit from the original poster:

To account for forcing the regression through the origin, first with the full formula when it is not forced through (0,0), then with just the slope when it is. Because the above would not make sense to someone else (why only provide the slope of the regression?

set.seed(42)
df <- tibble(x=rnorm(10,10,3),
             y=rnorm(10,10,3)+exp(seq(1,10,1)))
fit <- lm(y~x, df)
r2 <- formatC(summary(fit)$r.squared, digits = 2)
slope <- formatC(fit$coefficients[2], digits = 2)
int <- formatC(fit$coefficients[1], digits = 2)
eqn <- formatC(paste(int,"+",slope))

ggplot(df) +
  geom_point(aes(x=x,y=y)) +
  ggtext::geom_richtext(aes(x,y, label = label),
                        hjust = 0,
                        data = data.frame(x = 0, y = 15000,
                                          label = paste0(eqn, "*x*<br>*r<sup>2</sup>* = ", r2)))
# Force regression through origin
fit0 <- lm(y~0+x,df)
r20 <- formatC(summary(fit)$r.squared, digits = 2)
slope0 <- formatC(fit0$coefficients[1], digits = 2) # only a slope coefficient
# int0 <- 0
# eqn0 <- slope0 # these are not necessary because the regression goes through the origin

ggplot(df) +
  geom_point(aes(x=x,y=y)) +
  ggtext::geom_richtext(aes(x,y, label = label),
                        hjust = 0,
                        data = data.frame(x = 0, y = 15000,
                                          label = paste0(slope0, "*x*<br>*r<sup>2</sup>* = ", r2)))

Upvotes: 3

Related Questions