0

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)
dandrews
  • 967
  • 5
  • 18
  • I'm not sure how to do this as markdown, but you might consider using `ggtext`, based on html, which might allow you more flexibility. – Jon Spring Mar 22 '23 at 18:26

1 Answers1

3

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)))
Jon Spring
  • 55,165
  • 4
  • 35
  • 53
  • This will do for now, thanks it's a good workaround. I'd love if we could get parsing working so I could use object based expressions as to not have to recode the formulas each time I fit a new regression. Hence the vote up but not accepting as final answer yet! – dandrews Mar 22 '23 at 18:45
  • Great! I made an edit to include both the case where the regression passes through the origin (as that's what I needed) and one where it does not so that anyone viewing this in the future is not led astray. – dandrews Mar 22 '23 at 19:32