Додайте рівняння лінії регресії та R ^ 2 на графіку


227

Цікаво, як додати рівняння регресійної лінії та R ^ 2 на ggplot. Мій код:

library(ggplot2)

df <- data.frame(x = c(1:100))
df$y <- 2 + 3 * df$x + rnorm(100, sd = 40)
p <- ggplot(data = df, aes(x = x, y = y)) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x) +
            geom_point()
p

Будь-яка допомога буде високо оцінена.


1
Щодо грати грати , див latticeExtra::lmlineq().
Josh O'Brien

Відповіді:


234

Ось одне рішення

# GET EQUATION AND R-SQUARED AS STRING
# SOURCE: https://groups.google.com/forum/#!topic/ggplot2/1TgH-kG5XMA

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

p1 <- p + geom_text(x = 25, y = 300, label = lm_eqn(df), parse = TRUE)

EDIT. Я з'ясував джерело, звідки я вибрав цей код. Ось посилання на початкову публікацію в групах goggle ggplot2

Вихідні дані


1
@ Коментар JonasRaedle про те, щоб краще виглядати тексти, annotateбув правильним на моїй машині.
IRTFM

2
Це не схоже на розміщений вихід на моїй машині, де мітка перезаписується стільки разів, скільки викликаються дані, в результаті чого густий і розмитий текст мітки. Передача міток у data.frame спочатку працює (дивіться мою пропозицію в коментарі нижче.
PatrickT

@PatrickT: видаліть aes(і відповідне ). aesпризначений для відображення змінних фреймів даних у візуальні змінні - це тут не потрібно, оскільки є лише один екземпляр, тому ви можете помістити все в основний geom_textвиклик. Я відредагую це у відповідь.
naught101

Проблема з цим рішенням полягає в тому, що якщо набір даних більший (у мене було 370000 спостережень), функція, здається, не працює. Я б рекомендував рішення від @kdauria, яке робить те саме, але набагато швидше.
Бенджамін

3
для тих, хто хоче значення r і p замість R2 та рівняння: eq <- заміна (курсив (r) ~ "=" ~ rvalue * "," ~ курсив (p) ~ "=" ~ pvalue, список (rvalue = sprintf ("% .2f", знак (coef (m) [2]) * sqrt (резюме (м) $ r.squared)), pvalue = формат (підсумки (m) $ коефіцієнти [2,4], цифри = 2 )))
Джеррі Т

135

Я включив статистику stat_poly_eq()в свій пакет, ggpmiscякий дозволяє відповісти:

library(ggplot2)
library(ggpmisc)
df <- data.frame(x = c(1:100))
df$y <- 2 + 3 * df$x + rnorm(100, sd = 40)
my.formula <- y ~ x
p <- ggplot(data = df, aes(x = x, y = y)) +
   geom_smooth(method = "lm", se=FALSE, color="black", formula = my.formula) +
   stat_poly_eq(formula = my.formula, 
                aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                parse = TRUE) +         
   geom_point()
p

введіть тут опис зображення

Ця статистика працює з будь-яким поліном без пропущених термінів, і, сподіваємось, має достатню гнучкість, щоб бути загалом корисною. Мітки R ^ 2 або відрегульовані R ^ 2 можуть використовуватися з будь-якою формулою моделі, забезпеченою lm (). Будучи статистикою ggplot, вона поводиться так, як і очікувалося, як з групами, так і з аспектами.

Пакет 'ggpmisc' доступний через CRAN.

Версія 0.2.6 щойно була прийнята до CRAN.

Він стосується коментарів @shabbychef та @ MYaseen208.

@ MYaseen208 це показує, як додати шапку .

library(ggplot2)
library(ggpmisc)
df <- data.frame(x = c(1:100))
df$y <- 2 + 3 * df$x + rnorm(100, sd = 40)
my.formula <- y ~ x
p <- ggplot(data = df, aes(x = x, y = y)) +
   geom_smooth(method = "lm", se=FALSE, color="black", formula = my.formula) +
   stat_poly_eq(formula = my.formula,
                eq.with.lhs = "italic(hat(y))~`=`~",
                aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
                parse = TRUE) +         
   geom_point()
p

введіть тут опис зображення

@shabbychef Тепер можна змінити змінні рівняння з тими, що використовуються для осей-міток. Для заміни x на say z і y на h можна використовувати:

p <- ggplot(data = df, aes(x = x, y = y)) +
   geom_smooth(method = "lm", se=FALSE, color="black", formula = my.formula) +
   stat_poly_eq(formula = my.formula,
                eq.with.lhs = "italic(h)~`=`~",
                eq.x.rhs = "~italic(z)",
                aes(label = ..eq.label..), 
                parse = TRUE) + 
   labs(x = expression(italic(z)), y = expression(italic(h))) +          
   geom_point()
p

введіть тут опис зображення

Будучи цими нормальними виразами R, грецькими літерами, тепер також можна використовувати як в lhs, так і rhs рівняння.

[2017-03-08] @elarry Редагувати, щоб точніше вирішити початкове запитання, показавши, як додати кому між мітками рівняння та R2.

p <- ggplot(data = df, aes(x = x, y = y)) +
  geom_smooth(method = "lm", se=FALSE, color="black", formula = my.formula) +
  stat_poly_eq(formula = my.formula,
               eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse = TRUE) +         
  geom_point()
p

введіть тут опис зображення

[2019-10-20] @ helen.h Я наводжу нижче приклади використання stat_poly_eq()з групуванням.

library(ggpmisc)
df <- data.frame(x = c(1:100))
df$y <- 20 * c(0, 1) + 3 * df$x + rnorm(100, sd = 40)
df$group <- factor(rep(c("A", "B"), 50))
my.formula <- y ~ x
p <- ggplot(data = df, aes(x = x, y = y, colour = group)) +
  geom_smooth(method = "lm", se=FALSE, formula = my.formula) +
  stat_poly_eq(formula = my.formula, 
               aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
               parse = TRUE) +         
  geom_point()
p

p <- ggplot(data = df, aes(x = x, y = y, linetype = group)) +
  geom_smooth(method = "lm", se=FALSE, formula = my.formula) +
  stat_poly_eq(formula = my.formula, 
               aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
               parse = TRUE) +         
  geom_point()
p

введіть тут опис зображення

введіть тут опис зображення

[2020-01-21] @ Герман На перший погляд це може бути трохи протиінтуїтивно, але для отримання єдиного рівняння при використанні групування потрібно дотримуватися граматики графіки. Або обмежте відображення, яке створює групування, окремими шарами (показано нижче), або збережіть відображення за замовчуванням і замініть його постійним значенням у шарі, де ви не хочете групування (наприклад, colour = "black").

Продовжуючи з попереднього прикладу.

p <- ggplot(data = df, aes(x = x, y = y)) +
  geom_smooth(method = "lm", se=FALSE, formula = my.formula) +
  stat_poly_eq(formula = my.formula, 
               aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
               parse = TRUE) +         
  geom_point(aes(colour = group))
p

введіть тут опис зображення

[2020-01-22] Для повноти приклад з гранями, демонструючи, що і в цьому випадку очікування граматики графіки виконані.

library(ggpmisc)
df <- data.frame(x = c(1:100))
df$y <- 20 * c(0, 1) + 3 * df$x + rnorm(100, sd = 40)
df$group <- factor(rep(c("A", "B"), 50))
my.formula <- y ~ x

p <- ggplot(data = df, aes(x = x, y = y)) +
  geom_smooth(method = "lm", se=FALSE, formula = my.formula) +
  stat_poly_eq(formula = my.formula, 
               aes(label = paste(..eq.label.., ..rr.label.., sep = "~~~")), 
               parse = TRUE) +         
  geom_point() +
  facet_wrap(~group)
p

введіть тут опис зображення


1
Слід зазначити, що формула xі yу формулі посилається на xта yдані в шарах сюжету, а не обов'язково на ті, які мають на той час обсяг my.formula. Таким чином, формула завжди повинна використовувати змінні x і y?
shabbychef

Це дуже вірно, що xі yпосилання на всі змінні відображаються на цій естетиці. Це очікування і для geom_smooth (), і як граматика графіки працює. Можливо було б зрозуміліше використовувати різні імена в кадрі даних, але я просто зберігав їх, як у первісному питанні.
Педро Афало

Це стане можливим у наступній версії ggpmisc. Дякую за пропозицію!
Педро Афало

3
Хороший момент @elarry! Це пов’язано з тим, як працює функція розбору () R. Через спроби та помилки я виявив, що aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~"))робить цю роботу.
Педро Афало

1
@HermanToothrot Зазвичай R2 є кращим для регресії, тому в даних, що повертаються, немає заздалегідь визначених знаків r stat_poly_eq(). Ви можете також використовувати stat_fit_glance()пакет "ggpmisc", який повертає R2 як числове значення. Перегляньте приклади на сторінці довідки та замініть stat(r.squared)на sqrt(stat(r.squared)).
Педро Афало

99

Я змінив кілька рядків джерела stat_smoothта пов'язаних з ними функцій, щоб створити нову функцію, яка додає рівняння придатності та значення R у квадрат. Це також буде працювати на фасетних ділянках!

library(devtools)
source_gist("524eade46135f6348140")
df = data.frame(x = c(1:100))
df$y = 2 + 5 * df$x + rnorm(100, sd = 40)
df$class = rep(1:2,50)
ggplot(data = df, aes(x = x, y = y, label=y)) +
  stat_smooth_func(geom="text",method="lm",hjust=0,parse=TRUE) +
  geom_smooth(method="lm",se=FALSE) +
  geom_point() + facet_wrap(~class)

введіть тут опис зображення

Я використовував код у відповіді @ Ramnath для форматування рівняння. stat_smooth_funcФункція не надто надійна, але це не повинно бути важко грати з ним.

https://gist.github.com/kdauria/524eade46135f6348140 . Спробуйте оновити, ggplot2якщо ви отримали помилку.


2
Велике дякую. Цей не працює лише для граней, але навіть для груп. Я вважаю це дуже корисним для кускових регресій, наприклад stat_smooth_func(mapping=aes(group=cut(x.val,c(-70,-20,0,20,50,130))),geom="text",method="lm",hjust=0,parse=TRUE), у поєднанні з EvaluateSmooths від stackoverflow.com/questions/19735149/…
Julian

1
@aelwan, змінити ці рядки: gist.github.com/kdauria/… як завгодно. Потім sourceвесь файл у вашому сценарії.
kdauria

1
@kdauria Що робити, якщо у кожного з facet_wraps у мене є кілька рівнянь, і я маю різні значення y_ у кожному з facet_wrap. Будь-які пропозиції, як виправити положення рівнянь? Я спробував кілька варіантів hjust, vjust та angle на цьому прикладі dropbox.com/s/9lk9lug2nwgno2l/R2_facet_wrap.docx?dl=0, але я не зміг звести всі рівняння на одному рівні в кожному з facet_wrap
блискучий

3
@aelwan, положення рівняння визначається цими рядками: gist.github.com/kdauria/… . Я зробив xposі yposаргументи функції в «Гісті». Тож якщо ви хотіли, щоб усі рівняння перетиналися, просто встановіть xposі ypos. В іншому випадку xposі yposобчислюються з даних. Якщо ви хочете чогось вигадливішого, не слід надто важко додати деяку логіку всередині функції. Наприклад, можливо, ви могли б написати функцію, щоб визначити, яка частина графіка має найбільш порожній простір, і помістити туди функцію.
kdauria

6
Я зіткнувся з помилкою з source_gist: Помилка r_files [[котрий]]: недійсний тип підпису 'закриття'. Дивіться цей пост для вирішення: stackoverflow.com/questions/38345894/r-source-gist-not-working
Matifou

73

Я змінив пост Рамната на а) зробити більш загальним, щоб він прийняв лінійну модель як параметр, а не кадр даних, і б) відображає мінуси більш доцільно.

lm_eqn = function(m) {

  l <- list(a = format(coef(m)[1], digits = 2),
      b = format(abs(coef(m)[2]), digits = 2),
      r2 = format(summary(m)$r.squared, digits = 3));

  if (coef(m)[2] >= 0)  {
    eq <- substitute(italic(y) == a + b %.% italic(x)*","~~italic(r)^2~"="~r2,l)
  } else {
    eq <- substitute(italic(y) == a - b %.% italic(x)*","~~italic(r)^2~"="~r2,l)    
  }

  as.character(as.expression(eq));                 
}

Використання зміниться на:

p1 = p + geom_text(aes(x = 25, y = 300, label = lm_eqn(lm(y ~ x, df))), parse = TRUE)

17
Це виглядає чудово! Але я будую geom_points на кількох гранях, де df відрізняється на основі змінної фасети. Як це зробити?
bshor

24
Рішення Джейдена працює досить добре, але шрифт виглядає дуже некрасиво. Я рекомендую змінити використання на це: p1 = p + annotate("text", x = 25, y = 300, label = lm_eqn(lm(y ~ x, df)), colour="black", size = 5, parse=TRUE)edit: це також вирішує будь-які проблеми, які можуть виникнути з листами, що відображаються у вашій легенді.
Jonas Raedle

1
@ Jonas, чомусь я отримую "cannot coerce class "lm" to a data.frame". Ця альтернатива працює: df.labs <- data.frame(x = 25, y = 300, label = lm_eqn(df))і p <- p + geom_text(data = df.labs, aes(x = x, y = y, label = label), parse = TRUE)
PatrickT

1
@PatrickT - Це повідомлення про помилку, яке ви отримаєте, якби зателефонували lm_eqn(lm(...))з рішенням Рамната. Ви, мабуть, спробували цю, спробувавши цю, але забули переконатися, що ви переосмислилиlm_eqn
Хамі,

@PatrickT: ви могли б зробити свою відповідь окремою відповіддю? Я був би радий проголосувати за це!
JelenaČuklina

11

дуже люблю рішення @Ramnath. Щоб дозволити використовувати для налаштування формули регресії (замість фіксованих як y і x як літеральні назви змінних), а також додав p-значення до роздруківки (як @Jerry T коментує), ось мод:

lm_eqn <- function(df, y, x){
    formula = as.formula(sprintf('%s ~ %s', y, x))
    m <- lm(formula, data=df);
    # formating the values into a summary string to print out
    # ~ give some space, but equal size and comma need to be quoted
    eq <- substitute(italic(target) == a + b %.% italic(input)*","~~italic(r)^2~"="~r2*","~~p~"="~italic(pvalue), 
         list(target = y,
              input = x,
              a = format(as.vector(coef(m)[1]), digits = 2), 
              b = format(as.vector(coef(m)[2]), digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             # getting the pvalue is painful
             pvalue = format(summary(m)$coefficients[2,'Pr(>|t|)'], digits=1)
            )
          )
    as.character(as.expression(eq));                 
}

geom_point() +
  ggrepel::geom_text_repel(label=rownames(mtcars)) +
  geom_text(x=3,y=300,label=lm_eqn(mtcars, 'hp','wt'),color='red',parse=T) +
  geom_smooth(method='lm')

введіть тут опис зображення На жаль, це не працює з facet_wrap або facet_grid.


Дуже акуратно, я тут посилався . ggplot(mtcars, aes(x = wt, y = mpg, group=cyl))+Пояснення - ваш код відсутній перед geom_point ()? Напівпов'язане запитання - якщо ми посилаємось на hp та wt у aes()для ggplot, чи можемо ми потім захопити їх для використання у виклику до lm_eqn, тож нам залишиться кодувати лише в одному місці? Я знаю, що ми могли б налаштуватись до xvar = "hp"виклику ggplot () і використовувати xvar в обох місцях для заміни hp , але це здається, що це повинно бути непотрібним.
Марк Ніл

9

Використання ggpubr :

library(ggpubr)

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

# By default showing Pearson R
ggscatter(df, x = "x", y = "y", add = "reg.line") +
  stat_cor(label.y = 300) +
  stat_regline_equation(label.y = 280)

введіть тут опис зображення

# Use R2 instead of R
ggscatter(df, x = "x", y = "y", add = "reg.line") +
  stat_cor(label.y = 300, 
           aes(label = paste(..rr.label.., ..p.label.., sep = "~`,`~"))) +
  stat_regline_equation(label.y = 280)

## compare R2 with accepted answer
# m <- lm(y ~ x, df)
# round(summary(m)$r.squared, 2)
# [1] 0.85

введіть тут опис зображення


Ви бачили акуратний програмний спосіб вказати число label.y?
Марк Ніл

@MarkNeal, можливо, отримаємо макс y, а потім помножимо на 0,8. label.y = max(df$y) * 0.8
zx8754

1
@MarkNeal хороші бали, можливо, надішліть питання як запит на функцію на GitHub ggpubr.
zx8754

1
Випуск про автоматичне місцезнаходження, поданий тут
Марк Ніл

1
@ zx8754, у вашому сюжеті зображено rho, а не R², будь-який простий спосіб показати R²?
матмар

5

Ось найпростіший код для всіх

Примітка: Показано Rho Пірсона, а не R ^ 2.

library(ggplot2)
library(ggpubr)

df <- data.frame(x = c(1:100)
df$y <- 2 + 3 * df$x + rnorm(100, sd = 40)
p <- ggplot(data = df, aes(x = x, y = y)) +
        geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x) +
        geom_point()+
        stat_cor(label.y = 35)+ #this means at 35th unit in the y axis, the r squared and p value will be shown
        stat_regline_equation(label.y = 30) #this means at 30th unit regresion line equation will be shown

p

Один такий приклад із моїм власним набором даних


Та ж проблема, що і вище, у вашому сюжеті показано rho, а не R²!
матмар

3

Натхненний стилем рівнянь, наданим у цій відповіді , більш загальним підходом (більше ніж один предиктор + вихід латексу як варіант) може бути:

print_equation= function(model, latex= FALSE, ...){
    dots <- list(...)
    cc= model$coefficients
    var_sign= as.character(sign(cc[-1]))%>%gsub("1","",.)%>%gsub("-"," - ",.)
    var_sign[var_sign==""]= ' + '

    f_args_abs= f_args= dots
    f_args$x= cc
    f_args_abs$x= abs(cc)
    cc_= do.call(format, args= f_args)
    cc_abs= do.call(format, args= f_args_abs)
    pred_vars=
        cc_abs%>%
        paste(., x_vars, sep= star)%>%
        paste(var_sign,.)%>%paste(., collapse= "")

    if(latex){
        star= " \\cdot "
        y_var= strsplit(as.character(model$call$formula), "~")[[2]]%>%
            paste0("\\hat{",.,"_{i}}")
        x_vars= names(cc_)[-1]%>%paste0(.,"_{i}")
    }else{
        star= " * "
        y_var= strsplit(as.character(model$call$formula), "~")[[2]]        
        x_vars= names(cc_)[-1]
    }

    equ= paste(y_var,"=",cc_[1],pred_vars)
    if(latex){
        equ= paste0(equ," + \\hat{\\varepsilon_{i}} \\quad where \\quad \\varepsilon \\sim \\mathcal{N}(0,",
                    summary(MetamodelKdifEryth)$sigma,")")%>%paste0("$",.,"$")
    }
    cat(equ)
}

modelАргумент очікує lmоб'єкт, то latexаргумент є булевим задати для простого символу або рівняння латексу відформатованих, і ...аргумент передати свої значення в formatфункцію.

Я також додав можливість вивести його як латекс, щоб ви могли використовувати цю функцію в rmarkdown, як це:


```{r echo=FALSE, results='asis'}
print_equation(model = lm_mod, latex = TRUE)
```

Тепер використовуємо:

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

print_equation(model = lm_mod, latex = FALSE)

Цей код дає: y = 11.3382963933174 + 2.5893419 * x + 0.1002227 * z

І якщо ми попросимо рівняння латексу, округлюючи параметри до 3 цифр:

print_equation(model = lm_mod, latex = TRUE, digits= 3)

Це дає: рівняння латексу


0

У мене є сумніви, як поставити в рівняння знакову статистику t.test для бхети ggpmisc::stat_poly_eq()?

колишній: expression(hat(Y)== 0000*"**"+0000*"x"*"*"-0000*"x"^2*"**"~~~~"R"^2*":"~~0.000)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.