#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
в качестве входных данных.