Doug Fir
Doug Fir

Reputation: 21204

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

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?

Upvotes: 7

Views: 165

Answers (4)

TimTeaFan
TimTeaFan

Reputation: 18551

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)

Upvotes: 1

tmfmnk
tmfmnk

Reputation: 39858

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>  

Upvotes: 3

Doug Fir
Doug Fir

Reputation: 21204

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.

Upvotes: 3

PKumar
PKumar

Reputation: 11128

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  

Upvotes: 6

Related Questions