13

I've created a faceted scatterplot with ggplot but I'm struggling to add the regression line equation to each of the facets. The simple case where there is no faceting has been answered here but this method won't extend to a faceted plot.

Any ideas how to accomplish this in a clean fashion?

Community
  • 1
  • 1
TheComeOnMan
  • 12,535
  • 8
  • 39
  • 54
  • 2
    Better to give some reproducible data and provide what you have tried... – agstudy Oct 31 '13 at 08:53
  • 5
    The suggested duplicate points to a link that I have already provided in my question. One of the comments on that link, which wasn't answered, also asks the same question of how to add the equation on a faceted plot. The trick seems to be to pass a `vector`/`data.frame` for the labels. I don't believe this is a duplicate. – TheComeOnMan Oct 31 '13 at 14:54

3 Answers3

34

Here is an example starting from this answer

require(ggplot2)
require(plyr)

df <- data.frame(x = c(1:100))
df$y <- 2 + 3 * df$x + rnorm(100, sd = 40)


lm_eqn = function(df){
    m = lm(y ~ x, df);
    eq <- substitute(italic(y) == a + b %.% italic(x)*","~~italic(r)^2~"="~r2, 
         list(a = format(coef(m)[1], digits = 2), 
              b = format(coef(m)[2], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3)))
    as.character(as.expression(eq));                 
}

Create two groups on which you want to facet

df$group <- c(rep(1:2,50))

Create the equation labels for the two groups

eq <- ddply(df,.(group),lm_eqn)

And plot

p <- ggplot(data = df, aes(x = x, y = y)) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x) +
            geom_point()
p1 = p + geom_text(data=eq,aes(x = 25, y = 300,label=V1), parse = TRUE, inherit.aes=FALSE) + facet_grid(group~.)
p1 

enter image description here

Community
  • 1
  • 1
Jonas Tundo
  • 6,137
  • 2
  • 35
  • 45
  • @agstudy, I'm trying to use `annotate` instead of `geom_text` but I'm unable to pass the `eq` dataframe or the column `eq$V1` to it. Could you help me with that please? – TheComeOnMan Oct 31 '13 at 14:50
  • @JT85, irrespective of the above comment, your answer works perfectly well for my current problem which is why I've accepted it already. I'd still like to use `annotate` instead though. – TheComeOnMan Oct 31 '13 at 14:53
  • No problem. However I don't think `annotate` works like that with `facet_grid`. agstudy's approach is just completely different. But I could be wrong. – Jonas Tundo Oct 31 '13 at 15:12
  • 3
    @JT85 This is a **great work around**, but it leaves one major flaw, IMHO. The line is plotted ***using different code*** than the actual equation. For a simple linear least squares, big deal. But what if I, for instance, wanted to use the nice `loess` polynomial fit. I would have to recreate `loess`, then presume I have set all my parameters / assumptions correctly. Isn't there a simple `show.eqn` flag or something like that? If not, why? That seems so fundamental to a regression fit! (BTW, I know it's not your code. Just asking the general crowd here...) – Mike Williamson Feb 26 '16 at 22:35
  • It appears that `, inherit.aes=FALSE` is not necessary to get the same output, but perhaps this was inserted for an older version of R or ggplot. I have R 3.2.3 and ggplot 2.1.0 – Mark Lakata Mar 04 '16 at 02:33
  • 2
    As of R 3.5 at least, `format()` does not discard anymore the `names` attribute of `coef(m)[1]` and the `lm_eqn(df)` function returns `"italic(y) == c(\`(Intercept)\` = \"-0.83\")…` instead of somehing like `"italic(y) == \"-0.83\" …`. This can be corrected by discarding name attributes with `as.numeric(coef(m)[1])`, etc. – Charles Plessy Apr 15 '19 at 02:34
5

Does this do what you want?

library(ggplot2); library(gridExtra)
ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
  geom_point() +
  geom_smooth(method="lm") +
  facet_wrap(~ Species)

grid.newpage()
vpa_ <- viewport(width = 1, height = 1)
print(p, vp = vpa_)
grid.text("y ~ mx + b", x=0.3, y=0.8)
grid.text("y ~ mx + b", x=0.5, y=0.8)
grid.text("y ~ mx + b", x=0.8, y=0.8)

enter image description here

sckott
  • 5,755
  • 2
  • 26
  • 42
  • Thanks, but I need the equation more importantly. – TheComeOnMan Oct 31 '13 at 06:51
  • I think you can pass a vector/list/etc. to `annotate` , like this http://stackoverflow.com/questions/11889625/annotating-text-on-individual-facet-in-ggplot2 – sckott Oct 31 '13 at 06:54
  • You're probably right. In the link you've put, the answered isn't passing a vector to `annotate` though. Do you know how to do that? – TheComeOnMan Oct 31 '13 at 14:51
  • Hmm, I guess that doesn't work does it. Editing answer above with a gridextra solution that I use often – sckott Oct 31 '13 at 15:17
4

Using gridExtra you can arrange yours plots like this.

enter image description here

library(ggplot2)

library(ggplot2)
iris$x = iris$Sepal.Length 
iris$y = iris$Sepal.Width
xx <- range(iris$x)
yy <- range(iris$y)

ll <- by(iris,iris$Species,function(df){
  x.eq <- max(xx)-mean(xx)/2
  y.eq <- max(yy)*0.95
  p <- ggplot(df, aes(x, y)) +
    geom_point() +
    geom_smooth(method="lm") +
    annotate(x=x.eq, y =y.eq ,  geom='text',
              label = lm_eqn(df), size=5,parse=TRUE) +
    xlim(xx[1],xx[2])+ylim(yy[1],yy[2])
})

library(gridExtra)
do.call(grid.arrange,ll)
agstudy
  • 119,832
  • 17
  • 199
  • 261
  • Thanks. `grid.arrange` seems to work more like `facet_wrap` than `facet_grid`. Is that right? If so, I would like to get the `facet_grid` sort of solution to work. – TheComeOnMan Oct 31 '13 at 15:15