построение линии регрессии для каждой записи во вложенном списке

#r #ggplot2 #nested #tidyverse #purrr

#r #ggplot2 #вложенный #tidyverse #мурлыканье

Вопрос:

У меня есть пример, который помещает данные в список, добавляет линейную модель к каждому элементу в списке, добавляет коэффициенты регрессии к каждой записи в списке и добавляет 2 разных объекта ggplot к каждой записи в списке. Я хочу построить линию регрессии на отдельном рисунке для каждой записи. Я могу заставить geom_smooth делать именно то, что мне нужно, но geom_abline, похоже, добавляет регрессию для каждой записи в списке по одной на каждую цифру (в моем примере три записи, следовательно, три строки на каждой фигуре вместо желаемой отдельной строки для каждой записи.

 library(tidyverse)
library(purrr)
library(broom)
library(ggplot2)

iris_species <- iris %>%  
  group_by(Species) %>%  
  nest()

# build model functions
model <- function(iris) {
  lm(Sepal.Length ~ Sepal.Width, data = iris)
}

# map models to the tibble
iris_species <- iris_species %>% 
  mutate(model = map(data, model))

iris_species # look at the structure

# add glance and tidy results to list
iris_species <- iris_species %>% 
  mutate(t = map(model, tidy)
         )

# unnest tidy list
iris_species_g <- iris_species %>% 
  unnest(t) %>% 
  select(Species, term, estimate) %>% 
  spread(key = term, value = estimate) %>%
  select(Species, `(Intercept)`, Sepal.Width) 

# pain down a list for species and data
iris_species_list <- iris_species %>% 
  select(Species, data, model)

# join  
iris_species_coeffs <- left_join(iris_species_list, iris_species_g, by = 'Species')

# add figures to list
iris_species_figs <- iris_species_coeffs %>% 
  mutate(plot1 = map(data, ~ ggplot(., aes(x = Sepal.Width, y = Sepal.Length))  
                       geom_point()   
                       geom_smooth(se = TRUE, size = 1, color = 'grey')
                     ) 
         ) 

iris_species_figs <- iris_species_figs %>% 
  mutate(plot2 = map(data, ~ ggplot(., aes(x = Sepal.Width, y = Sepal.Length))  
                      geom_point()  
                      geom_abline(intercept = `(Intercept)`, slope = Sepal.Width, color = 'blue')
                    )
         ) 

iris_species_figs

# make figures
iris_species_figs$plot1 # works as expected

iris_species_figs$plot2 # does not
  

Вот конечный результат из приведенного выше кода:

 # A tibble: 3 x 7
  Species    data              model    `(Intercept)` Sepal.Width plot1    plot2   
  <fct>      <list>            <list>           <dbl>       <dbl> <list>   <list>  
1 setosa     <tibble [50 × 4]> <S3: lm>          2.64       0.690 <S3: gg> <S3: gg>
2 versicolor <tibble [50 × 4]> <S3: lm>          3.54       0.865 <S3: gg> <S3: gg>
3 virginica  <tibble [50 × 4]> <S3: lm>          3.91       0.902 <S3: gg> <S3: gg>
  

запуск последних двух строк показывает проблему. Код geom_smooth в plot1 создает 1 фигуру для каждой записи с данными из каждой записи и применяет линию сглаживания к каждой фигуре. Однако goem_abline в plot2 этого не делает. Похоже, что на каждой из трех фигур отображаются все 3 линии (по одной из каждой записи). Любые предложения относительно того, как заставить goem_abline вести себя как geom_smooth, были бы высоко оценены.

Комментарии:

1. purrr и ggplot2 являются частью tidyverse , поэтому вам не нужно загружать их, опять же, по отдельности.

Ответ №1:

map Функция, для plot 2 которой вы используете, отображает ТОЛЬКО data , а для перехвата и наклона она видит вектор из 3 элементов. Вот почему вы видите 3 строки на каждом графике.

Вы должны использовать pmap для того, чтобы map получить всю информацию / столбцы, которые вам нужно использовать для каждого графика.

Попробуйте это:

 iris_species_figs <- iris_species_figs %>% 
  mutate(plot2 = pmap(list(data,`(Intercept)`,Sepal.Width), 
                      function(a,b,c) ggplot(a, aes(x = Sepal.Width, y = Sepal.Length))  
                       geom_point()  
                       geom_abline(intercept = b, slope = c, color = 'blue')
  )
  ) 
  

Комментарии:

1. Работает отлично. Однако мне любопытно лучше понять, как вы используете pmap и функцию (a, b, c). Я вижу, что feed отображает список (каждая запись данных, перехват и наклон), затем использует эти термины в качестве аргументов для geom_abline. Это необходимо или для наглядности? На первый взгляд это выглядит как метод переименования неудобного имени аргумента, (Intercept) например. Независимо от того, я ценю ответ. Я всегда находил документацию для семейства map немного бестолковой.

2. map2 принимает 2 входных параметра, и вы можете использовать либо что-то вроде ~ .x .y , либо что-то вроде function(a,b) a b . Однако pmap принимает более 3 входных данных, поэтому вам придется использовать что-то вроде function(a,b,c) a b c , потому что после .x и .y использовать нечего. И, как вы сказали, вам нужно создать список входных данных для передачи pmap в качестве входных данных.