Oska Fentem
Oska Fentem

Reputation: 145

Purrr map multiple functions and two inputs

I am trying to use purrr to map multiple functions to two inputs. An example is given below but ideally i'd like to extend this to more functions. When trying to do this i'm getting an error that the input is not found, however, even when I try and name inputs in the list of functions this doesn't rectify the problem.

library(yardstick)
library(tidyverse)

funcs <- list(accuracy = yardstick::accuracy_vec,
              recall = yardstick::recall_vec)

n <- 1000
x <- as.factor(rbinom(n, 1, 0.5))
y <- as.factor(rbinom(n, 1, 0.5))

df <- tibble(true = rep(list(y), 3),
             preds = rep(list(x), 3))

df
#> # A tibble: 3 x 2
#>   true          preds        
#>   <list>        <list>       
#> 1 <int [1,000]> <int [1,000]>
#> 2 <int [1,000]> <int [1,000]>
#> 3 <int [1,000]> <int [1,000]>

df %>% map2_df(.x = true, .y = preds, .f = funcs)
#> Error in map2(.x, .y, .f, ...): object 'true' not found

funcs <- list(accuracy = ~yardstick::accuracy_vec(truth = .x, estimate = .y),
              recall = ~yardstick::recall_vec(truth = .x, estimate = .y))

df %>% map2_df(.x = true, .y = preds, .f = funcs)
#> Error in map2(.x, .y, .f, ...): object 'true' not found

Ideally I would end up with something like this:

# A tibble: 3 x 4
  true          preds         accuracy recall
  <list>        <list>           <dbl>  <dbl>
1 <int [1,000]> <int [1,000]>      0.7    0.8
2 <int [1,000]> <int [1,000]>      0.7    0.8
3 <int [1,000]> <int [1,000]>      0.7    0.8

Any help is much appreciated, TIA

Upvotes: 3

Views: 1008

Answers (2)

Ronak Shah
Ronak Shah

Reputation: 388982

I get errors when I pass numeric values to function accuracy_vec and recall_vec. I get

Error: truth should be a factor but a integer was supplied..

So I change the data to factor.

library(tidyverse)

n <- 1000
x <- rbinom(n, 1, 0.5)
y <- rbinom(n, 1, 0.5)

df <- tibble(true = rep(list(factor(y)), 3),
             preds = rep(list(factor(x)), 3))

Secondly, pipes pass value from left-hand side (LHS) as first argument to the function on right-hand side. So when you use df %>% map2_df(.x = true, .y = preds, .f = funcs) df is being passed implicitly.

You may write a custom function to return a tibble.

funcs <- function(.x, .y) {
  tibble(accuracy = yardstick::accuracy_vec(truth = .x, estimate = .y), 
         recall =   yardstick::recall_vec(truth = .x, estimate = .y))
}

and then use map2_df to get one dataframe as output.

map2_df(df$true, df$preds, funcs)

Upvotes: 2

Robin Gertenbach
Robin Gertenbach

Reputation: 10776

You can use nested maps:

df %>% 
  mutate(map2_dfr(true, preds, ~map_dfc(funcs, do.call, list(.x, .y))))

Upvotes: 2

Related Questions