2

I have a working custom function but not sure how to allow it to loop with a list of inputs. Looks like I need to understand apply() and the such but I'm not quite there with my current setup. The function uses rollapply() to find the largest metric for a given time frame.

library(zoo)
library(dplyr)

# Data
set.seed(1)
df <- tibble(player = rep(LETTERS[1:2], each = 10),
             minute = rep(1:10, times = 2),
             tdc = sample(100:200,size = 20),
             sumad = sample(1:10, size = 20, replace = TRUE))

# Custom function
x_min_roll <- function(df, metric, n_minutes, fun){
  metric <- ensym(metric)
  newname <- glue::glue("{rlang::as_string(metric)}_x{as.character(n_minutes)}")
  df %>% 
    # dynamically create new column name based on input
    mutate("{newname}" := rollapply(!!metric, n_minutes, fun, align='left', fill=NA)) %>% 
    group_by(player) %>% 
    slice_max(.data[[newname]]) %>% 
    select(player, .data[[newname]])
}

# This works
df %>% 
  x_min_roll(metric = tdc, n_minutes = 2, fun = sum)

# A tibble: 2 x 2
# Groups:   player [2]
  player tdc_x2
  <chr>   <int>
1 A         339
2 B         380

I would like to be able to do this:

metric_list <- c('tdc', 'sumad')
minutes_list <- c(2,5)

df %>% 
  x_min_roll(metric = metric_list, n_minutes = minutes_list, fun = sum) %>% 
  # maybe a few more steps here.... to get this

# A tibble: 2 x 5
  player tdc_x2 tdc_x5 sumad_x2 sumad_x5
  <chr>   <dbl>  <dbl>    <dbl>    <dbl>
1 A         339    793       20       36
2 B         380    866       19       41

1 Answer 1

2

We can use map2 to loop over the corresponding elements of both vectors

library(purrr)
library(dplyr)
map2(metric_list, minutes_list, 
  ~ df %>%
    x_min_roll(metric = !!.x, n_minutes = .y, fun = sum))

-output

[[1]]
# A tibble: 2 × 2
# Groups:   player [2]
  player tdc_x2
  <chr>   <int>
1 A         339
2 B         380

[[2]]
# A tibble: 3 × 2
# Groups:   player [2]
  player sumad_x5
  <chr>     <int>
1 A            36
2 B            41
3 B            41

EDIT: Based on @Onyambu's comments


If we want for each combination, then use crossing to create the combination

library(tidyr)
crossing(metric_list, minutes_list) %>% 
 pmap(~ df %>% 
      x_min_roll(metric = !!.x, n_minutes = .y, fun = sum))

Based on the comments from the OP, if we want to combine the datasets

crossing(metric_list, minutes_list) %>% 
 pmap(~ df %>% x_min_roll(metric = !!.x, n_minutes = .y, fun = sum)) %>%
    reduce(inner_join, by = 'player')
Sign up to request clarification or add additional context in comments.

10 Comments

Since .y is numeric, I believe you could ignore the bang bang operator on it. in that case, the naming will be as required. Great solution
@Onyambu thanks, I overlooked that part.
If I add another value the minutes list like this minutes_list <- c(2,3,5). I receive and error message Error: Mapped vectors must have consistent lengths: *.x has length 2 * .y has length 3
@seansteele then what about metric_list it is only of length 2. I am not sure what you are trying to do with that function when one of the input is of length greater different than the other
@seansteele your function is constructed in such a way to take a single value for metric and n_minutes (if I understand correctly)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.