Use a function name that's a string in map loop?
Asked Answered
C

4

7

Some code:

mymtcars <- mtcars %>% head %>% rownames_to_column('model') %>% group_by(vs) %>% nest
mymtcars
     vs data             
  <dbl> <list>           
1     0 <tibble [3 × 11]>
2     1 <tibble [3 × 11]>

I can fit a linear model on this list column df like so:

mymtcars %>% 
+   mutate(mod = map(.x = data, ~ lm(.x$mpg ~ .x$cyl)))
# A tibble: 2 x 3
# Groups:   vs [2]
     vs data              mod   
  <dbl> <list>            <list>
1     0 <tibble [3 × 11]> <lm>  
2     1 <tibble [3 × 11]> <lm>  

What if my function name is a field?

mymtcars2 <- mtcars %>% head %>% rownames_to_column('model') %>% group_by(vs) %>% nest %>% crossing(func = c('lm'))
> mymtcars2
# A tibble: 2 x 3
     vs data              func 
  <dbl> <list>            <chr>
1     0 <tibble [3 × 11]> lm   
2     1 <tibble [3 × 11]> lm

I gave it a try with:

mymtcars2 %>% 
+   mutate(mod = map2(.x = data, .y = func, ~ .y(.x$mpg ~ .x$cyl)))
Error: Problem with `mutate()` input `mod`.
x could not find function ".y"
ℹ Input `mod` is `map2(.x = data, .y = func, ~.y(.x$mpg ~ .x$cyl))`.

How can I pass the function to call in map and then call it in the above block?

Cabana answered 26/3, 2021 at 14:7 Comment(0)
C
6

May be using match.fun inside map2 like below:

   models <-  mymtcars2 %>% 
       mutate(mod = map2(.x = data, .y = func, ~ match.fun(.y)(.x$mpg ~ .x$cyl)))

Output:

[[1]]

Call:
match.fun(.y)(formula = .x$mpg ~ .x$cyl)

Coefficients:
(Intercept)       .x$cyl  
  36.926733    -2.728218  


[[2]]

Call:
match.fun(.y)(formula = .x$mpg ~ .x$cyl)

Coefficients:
(Intercept)       .x$cyl  
    41.9400      -3.8025  
Cowled answered 26/3, 2021 at 14:14 Comment(0)
C
3

I also found that I can use get:

mymtcars2 %>% 
  mutate(mod = map2(.x = data, .y = func, ~ get(.y)(.x$mpg ~ .x$cyl)))

Am unsure of when to use one over the other.

Cabana answered 26/3, 2021 at 14:28 Comment(0)
F
3

A different option could be:

mymtcars2 %>%
    mutate(mod = map2(.x = data,
                      .y = func,
                      ~ exec(.y, mpg ~ cyl, data = .x)))

     vs data              func  mod   
  <dbl> <list>            <chr> <list>
1     0 <tibble [3 × 11]> lm    <lm>  
2     1 <tibble [3 × 11]> lm    <lm>  
Faithless answered 26/3, 2021 at 15:9 Comment(1)
This looks interesting. I read the documentation for exec but didn't really follow or understand it? Does it simply translate a string into it's function equivalent like it reads above or is it more nuanced than that?Cabana
M
1

Since {dplyr} >= 1.0 this kind of problems can be solved with dplyr::rowwise. We can use it either with a classic do.call, in which case we have to wrap the arguments in list(), or with rlang::exec. With dlpyr::rowwise we don't need map2 which makes things more readable since there is no lambda function with .x .y. However, since the output column stores lm objects (and not an atomic vector), the result has to be wrapped in mod = list(...).

library(tidyverse)

mymtcars2 %>% 
  rowwise %>% 
  mutate(mod = list(do.call(func, list(mpg ~ cyl, data = data))))
#> # A tibble: 2 x 4
#> # Rowwise: 
#>      vs data              func  mod   
#>   <dbl> <list>            <chr> <list>
#> 1     0 <tibble [3 × 11]> lm    <lm>  
#> 2     1 <tibble [3 × 11]> lm    <lm>

mymtcars2 %>% 
  rowwise %>% 
  mutate(mod = list(exec(func, mpg ~ cyl, data = data)))
#> # A tibble: 2 x 4
#> # Rowwise: 
#>      vs data              func  mod   
#>   <dbl> <list>            <chr> <list>
#> 1     0 <tibble [3 × 11]> lm    <lm>  
#> 2     1 <tibble [3 × 11]> lm    <lm>

Created on 2021-08-28 by the reprex package (v0.3.0)

Mccauley answered 28/8, 2021 at 19:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.