когда использовать функцию map () и когда использовать summarise_at () / mutate_at () - PullRequest
0 голосов
/ 04 мая 2018

Может кто-нибудь дать совет относительно того, когда использовать map() (все функции map _ .. ()) и когда использовать summarise_at() / mutate_at()?

например. если мы делаем какую-то модификацию столбца векторов, тогда нам не нужно думать map()? Если у нас есть df /, у столбца есть список, тогда нам нужно использовать map()?

Всегда ли необходимо использовать функцию map() с функцией nest()? Любой может предложить несколько обучающих видео по этому поводу. А также как поместить списки в df и моделировать несколько списков одновременно, а затем сохранить результаты модели в другом столбце?

Большое спасибо!

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Колин дает отличный самостоятельный ответ. Поскольку вы запросили дополнительные ресурсы по использованию нескольких моделей с таблицами, я также хотел бы добавить главу «Много моделей» в R 4 Data Science, которая дает широкий обзор создания, упрощения и моделирования с помощью столбцов списка. http://r4ds.had.co.nz/many-models.html

0 голосов
/ 04 мая 2018

Самое большое различие между {dplyr} и {purrr} заключается в том, что {dplyr} предназначен для работы только с data.frames, а {purrr} предназначен для работы со всеми видами списков. Data.frames являются списками, вы также можете использовать {purrr} для итерации data.frame.

map_chr(iris, class)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
   "numeric"    "numeric"    "numeric"    "numeric"     "factor" 

summarise_at и map_at не ведут себя точно так же: summarise_at просто возвращает искомую сводку, map_at возвращает все данные. Фрейм в виде списка, с изменениями, выполненными там, где вы просили это:

> library(purrr)
> library(dplyr)
> small_iris <- sample_n(iris, 5)
> map_at(small_iris, c("Sepal.Length", "Sepal.Width"), mean)
$Sepal.Length
[1] 6.58

$Sepal.Width
[1] 3.2

$Petal.Length
[1] 6.7 1.3 5.7 4.3 4.7

$Petal.Width
[1] 2.0 0.4 2.1 1.3 1.5

$Species
[1] virginica  setosa     virginica  versicolor versicolor
Levels: setosa versicolor virginica

> summarise_at(small_iris, c("Sepal.Length", "Sepal.Width"), mean)
  Sepal.Length Sepal.Width
1         6.58         3.2

map_at всегда возвращает список, mutate_at всегда data.frame:

> map_at(small_iris, c("Sepal.Length", "Sepal.Width"), ~ .x / 10)
$Sepal.Length
[1] 0.77 0.54 0.67 0.64 0.67

$Sepal.Width
[1] 0.28 0.39 0.33 0.29 0.31

$Petal.Length
[1] 6.7 1.3 5.7 4.3 4.7

$Petal.Width
[1] 2.0 0.4 2.1 1.3 1.5

$Species
[1] virginica  setosa     virginica  versicolor versicolor
Levels: setosa versicolor virginica

> mutate_at(small_iris, c("Sepal.Length", "Sepal.Width"), ~ .x / 10)
  Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1         0.77        0.28          6.7         2.0  virginica
2         0.54        0.39          1.3         0.4     setosa
3         0.67        0.33          5.7         2.1  virginica
4         0.64        0.29          4.3         1.3 versicolor
5         0.67        0.31          4.7         1.5 versicolor

Итак, подведем итоги по первому вопросу: если вы думаете о выполнении операции «по столбцам» для не вложенного df и хотите в результате получить data.frame, вам нужно перейти к {dplyr}.

Что касается вложенного столбца, вы должны объединить group_by(), nest() из {tidyr}, mutate() и map(). Здесь вы создаете уменьшенную версию вашего фрейма данных, который будет содержать столбец, представляющий собой список фреймов данных. Затем вы будете использовать map() для перебора элементов внутри этого нового столбца.

Вот пример с нашим любимым ирисом:

library(tidyr)

iris_n <- iris %>% 
  group_by(Species) %>% 
  nest()
iris_n
# A tibble: 3 x 2
  Species    data             
  <fct>      <list>           
1 setosa     <tibble [50 × 4]>
2 versicolor <tibble [50 × 4]>
3 virginica  <tibble [50 × 4]>

Здесь новый объект - это data.frame с столбцом data, представляющим собой список меньших data.frames, один по виду (коэффициент, который мы указали в group_by()). Затем мы можем перебрать этот столбец, выполнив:

map(iris_n$data, ~ lm(Sepal.Length ~ Sepal.Width, data = .x))
[[1]]

Call:
lm(formula = Sepal.Length ~ Sepal.Width, data = .x)

Coefficients:
(Intercept)  Sepal.Width  
     2.6390       0.6905  


[[2]]

Call:
lm(formula = Sepal.Length ~ Sepal.Width, data = .x)

Coefficients:
(Intercept)  Sepal.Width  
     3.5397       0.8651  


[[3]]

Call:
lm(formula = Sepal.Length ~ Sepal.Width, data = .x)

Coefficients:
(Intercept)  Sepal.Width  
     3.9068       0.9015  

Но идея состоит в том, чтобы хранить все внутри data.frame, поэтому мы можем использовать mutate для создания столбца, в котором будет храниться этот новый список lm результатов:

iris_n %>%
  mutate(lm = map(data, ~ lm(Sepal.Length ~ Sepal.Width, data = .x)))
# A tibble: 3 x 3
  Species    data              lm      
  <fct>      <list>            <list>  
1 setosa     <tibble [50 × 4]> <S3: lm>
2 versicolor <tibble [50 × 4]> <S3: lm>
3 virginica  <tibble [50 × 4]> <S3: lm>

Таким образом, вы можете запустить несколько mutate(), чтобы получить r.squared, например:

iris_n %>%
  mutate(lm = map(data, ~ lm(Sepal.Length ~ Sepal.Width, data = .x)), 
         lm = map(lm, summary), 
         r_squared = map_dbl(lm, "r.squared")) 
# A tibble: 3 x 4
  Species    data              lm               r_squared
  <fct>      <list>            <list>               <dbl>
1 setosa     <tibble [50 × 4]> <S3: summary.lm>     0.551
2 versicolor <tibble [50 × 4]> <S3: summary.lm>     0.277
3 virginica  <tibble [50 × 4]> <S3: summary.lm>     0.209

Но более эффективный способ - использовать compose() из {purrr} для создания функции, которая сделает это один раз, вместо того, чтобы повторять mutate().

get_rsquared <- compose(as_mapper("r.squared"), summary, lm)

iris_n %>%
  mutate(lm = map_dbl(data, ~ get_rsquared(Sepal.Length ~ Sepal.Width, data = .x)))
# A tibble: 3 x 3
  Species    data                 lm
  <fct>      <list>            <dbl>
1 setosa     <tibble [50 × 4]> 0.551
2 versicolor <tibble [50 × 4]> 0.277
3 virginica  <tibble [50 × 4]> 0.209

Если вы знаете, что всегда будете использовать Sepal.Length ~ Sepal.Width, вы можете даже предварительно заполнить lm() с помощью partial():

pr_lm <- partial(lm, formula = Sepal.Length ~ Sepal.Width)
get_rsquared <- compose(as_mapper("r.squared"), summary, pr_lm)

iris_n %>%
  mutate(lm = map_dbl(data, get_rsquared))
# A tibble: 3 x 3
  Species    data                 lm
  <fct>      <list>            <dbl>
1 setosa     <tibble [50 × 4]> 0.551
2 versicolor <tibble [50 × 4]> 0.277
3 virginica  <tibble [50 × 4]> 0.209

Что касается ресурсов, я написал серию постов на {purrr}, которые вы можете проверить: https://colinfay.me/tags/#purrr

...