Cettt
Cettt

Reputation: 11981

repeated mutate in tidyverse

consider the following tibble and the follwing vector:

library(tidyverse)
a <- tibble(val1 = 10:15, val2 = 20:25)
params <- 1:3

Also I have a function myfun which takes a vector of arbitrary length and an integer as input and returns an vector of the same length. For demonstration purposes you can think of

myfun <- function(x, k) dplyr::lag(x, k)

I want to create the follwing: for each column in a and for each element in params I want to create a new column given by myfun(col, params[i]) . In the toy example above this could for example be achieved like this:

a %>% mutate_at(1:2, funs(run1 = myfun), k = params[1]) %>% 
  mutate_at(1:2, funs(run2 = myfun), k = params[2]) %>% 
  mutate_at(1:2, funs(run3 = myfun), k = params[3]) 

Is there a more elegant approach to do this? If params is very long then this solution becomes infeasible. Of course one could do this with a for loop, but I thought that there might be a solution within the tidyverse (maybe using purrr::map?)

Thank you!

Upvotes: 2

Views: 1095

Answers (2)

Wimpel
Wimpel

Reputation: 27732

Here is a solution using tidyverse:

library(tidyverse)
a <- tibble(val1 = 10:15, val2 = 20:25)
params <- 1:3

#set the column names, add leading zeroes based om max(params)
run_names <- paste0("run", formatC(params, width = nchar(max(params)), flag = "0"))

#what functions to perform
lag_functions <- setNames(paste("dplyr::lag( ., ", params, ")"), run_names)
#perfporm functions 
a %>% mutate_at(vars(1:2), funs_(lag_functions ))

# # A tibble: 6 x 8
#    val1  val2 val1_run1 val2_run1 val1_run2 val2_run2 val1_run3 val2_run3
#   <int> <int>     <int>     <int>     <int>     <int>     <int>     <int>
# 1    10    20        NA        NA        NA        NA        NA        NA
# 2    11    21        10        20        NA        NA        NA        NA
# 3    12    22        11        21        10        20        NA        NA
# 4    13    23        12        22        11        21        10        20
# 5    14    24        13        23        12        22        11        21
# 6    15    25        14        24        13        23        12        22

Upvotes: 2

akrun
akrun

Reputation: 887048

The repeated lags is easier to do in data.table as shift can take a vector of ns

library(data.table)
# create a vector of new column names
nm1 <- paste0(rep(names(a), each = length(params)),  '_run', params) 
# get the `shift` of the Subset of Data.table (`.SD`)
# by default type is "lag"
# assign the output to the column names created earlier
setDT(a)[, (nm1)  := shift(.SD, n = params)]    a
#   val1 val2 val1_run1 val1_run2 val1_run3 val2_run1 val2_run2 val2_run3
#1:   10   20        NA        NA        NA        NA        NA        NA
#2:   11   21        10        NA        NA        20        NA        NA
#3:   12   22        11        10        NA        21        20        NA
#4:   13   23        12        11        10        22        21        20
#5:   14   24        13        12        11        23        22        21
#6:   15   25        14        13        12        24        23        22

Or using tidyverse with parse_exprs

library(tidyverse)
library(rlang)
# create a string with `rep` and `paste`
nm2 <- glue::glue('lag({rep(names(a), each = length(params))}, n = {rep(params, length(a))})') %>% paste(., collapse=";")
# convert string to expression with parse_exprs and evaluate (`!!!`)
a %>% 
   mutate(!!! parse_exprs(nm2)) %>%
   rename_at(-(1:2), ~nm1)
# A tibble: 6 x 8
#   val1  val2 val1_run1 val1_run2 val1_run3 val2_run1 val2_run2 val2_run3
#  <int> <int>     <int>     <int>     <int>     <int>     <int>     <int>
#1    10    20        NA        NA        NA        NA        NA        NA
#2    11    21        10        NA        NA        20        NA        NA
#3    12    22        11        10        NA        21        20        NA
#4    13    23        12        11        10        22        21        20
#5    14    24        13        12        11        23        22        21
#6    15    25        14        13        12        24        23        22

Upvotes: 1

Related Questions