Reputation: 11981
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
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
Reputation: 887048
The repeated lags is easier to do in data.table
as shift
can take a vector of n
s
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