why doesn't nest_by replicate this typical group_by & nest pipeline?

Viewed 136

A typical way to use group_by and then nest is to estimate a series of models--

library(tidyverse)

mpg %>% 
  group_by(
    manufacturer
  ) %>%
  nest %>% 
  mutate(
    mods = data %>% 
      map(
        \(i) 
          lm(cty ~ displ, data = i)
      )
  )

returns

# A tibble: 15 x 3
# Groups:   manufacturer [15]
   manufacturer data               mods  
   <chr>        <list>             <list>
 1 audi         <tibble [18 x 10]> <lm>  
 2 chevrolet    <tibble [19 x 10]> <lm>  
 3 dodge        <tibble [37 x 10]> <lm>  
 4 ford         <tibble [25 x 10]> <lm>  

but trying to be concise by using nest_by results in an error:

mpg %>% 
  nest_by(
    manufacturer
  ) %>% 
  mutate(
    mods = data %>% 
      map(
        \(i) 
        lm(cty ~ displ, data = i)
      )
  )

the error:

 Error: Problem with `mutate()` column `mods`.
i `mods = data %>% map(function(i) lm(cty ~ displ, data = i))`.
x 'data' must be a data.frame, environment, or list
i The error occurred in row 1.

how can I use nest_by to replicate the sequential use of group_by and nest?

1 Answers

We can add ungroup in between as the nest_by returns with rowwise attribute which clashes with the map

library(dplyr)
library(purrr)
out1 <- mpg %>% 
  nest_by(
    manufacturer
  ) %>% 
  ungroup %>%
  mutate(
    mods = data %>% 
      map(
        \(i) 
        lm(cty ~ displ, data = i)
      )
  )

-output

out1
# A tibble: 15 x 3
   manufacturer                data mods  
   <chr>        <list<tibble[,10]>> <list>
 1 audi                   [18 × 10] <lm>  
 2 chevrolet              [19 × 10] <lm>  
 3 dodge                  [37 × 10] <lm>  
 4 ford                   [25 × 10] <lm>  
 5 honda                   [9 × 10] <lm>  
 6 hyundai                [14 × 10] <lm>  
 7 jeep                    [8 × 10] <lm>  
 8 land rover              [4 × 10] <lm>  
 9 lincoln                 [3 × 10] <lm>  
10 mercury                 [4 × 10] <lm>  
11 nissan                 [13 × 10] <lm>  
12 pontiac                 [5 × 10] <lm>  
13 subaru                 [14 × 10] <lm>  
14 toyota                 [34 × 10] <lm>  
15 volkswagen             [27 × 10] <lm>  

Also, when we have nest_by, there is no need for map i.e.

out2 <- mpg %>% 
  nest_by(
    manufacturer
  ) %>%
  mutate(mods = list(lm(cty ~ displ, data = data)))

-output

out2
# A tibble: 15 x 3
# Rowwise:  manufacturer
   manufacturer                data mods  
   <chr>        <list<tibble[,10]>> <list>
 1 audi                   [18 × 10] <lm>  
 2 chevrolet              [19 × 10] <lm>  
 3 dodge                  [37 × 10] <lm>  
 4 ford                   [25 × 10] <lm>  
 5 honda                   [9 × 10] <lm>  
 6 hyundai                [14 × 10] <lm>  
 7 jeep                    [8 × 10] <lm>  
 8 land rover              [4 × 10] <lm>  
 9 lincoln                 [3 × 10] <lm>  
10 mercury                 [4 × 10] <lm>  
11 nissan                 [13 × 10] <lm>  
12 pontiac                 [5 × 10] <lm>  
13 subaru                 [14 × 10] <lm>  
14 toyota                 [34 × 10] <lm>  
15 volkswagen             [27 × 10] <lm>  

The output are the same except for the call argument

out1$mods[[1]]

Call:
lm(formula = cty ~ displ, data = i)

Coefficients:
(Intercept)        displ  
     22.066       -1.751  

> out2$mods[[1]]

Call:
lm(formula = cty ~ displ, data = data)

Coefficients:
(Intercept)        displ  
     22.066       -1.751  
> all.equal(out1$mods[[1]], out2$mods[[1]], check.attributes = FALSE)
[1] "Component “call”: target, current do not match when deparsed"
Related