This is a problem that puzzles me. What is the easiest (and most elegant) way to program a function to use in a mutation section that returns several vectors.
I'll give an example. Let's say I have such a function.
f1 = function(x, n){
y1 = rep(NA, length(x))
y2 = rep(NA, length(x))
y3 = rep(NA, length(x))
y4 = rep(NA, length(x))
for(i in (n+1):(length(x)-n)){
idx = (i-n):(i+n)
y1[i] = sin(mean(x[idx])/max(x[idx]))
y2[i] = cos(mean(x[idx])/max(x[idx]))
y3[i] = tan(mean(x[idx])/max(x[idx]))
y4[i] = 1/tan(mean(x[idx])/max(x[idx]))
}
data.frame(
y1 = y1,
y2 = y2,
y3 = y3,
y4 = y4
)
}
Please do not analyze its mathematical sense, this is just an example. As you can see, this function takes one vector and returns four vectors of the same length. If I would like to use this function in the mutation section, the function will be called four times. Unfortunately, with a long input vector, it will take a long time.
Here is an example.
n = 10000
df = tibble(
key = rep(c("a", "b", "c", "d"), n),
val = rep(rnorm(n), 4)
)
f1test = function(df) df %>%
group_by(key) %>%
mutate(
y1 = f1(val, 100) %>% pull(y1),
y2 = f1(val, 100) %>% pull(y2),
y3 = f1(val, 100) %>% pull(y3),
y4 = f1(val, 100) %>% pull(y4)
)
f1test(df)
When looking for a solution, I had a slightly different idea to return all four vectors at once and then separate them somehow. So I created a second example function that does the same computation, it only differs in the way it returns the result.
f2 = function(x, n){
ret = rep(NA, length(x))
for(i in (n+1):(length(x)-n)){
idx = (i-n):(i+n)
ret[i] = paste(
sin(mean(x[idx])/max(x[idx])),
cos(mean(x[idx])/max(x[idx])),
tan(mean(x[idx])/max(x[idx])),
1/tan(mean(x[idx])/max(x[idx])), sep = ";")
}
ret
}
Using such functions may look like this:
f2test = function(df) df %>%
group_by(key) %>%
mutate(ret = f2(val, 100)) %>%
separate(ret, paste0("y", 1:4), sep=";", convert = TRUE)
f2test(df)
You can see right away that the latter should be faster. And it really is for n = 1000, the version with f2 is approximate 2 times faster.
For n = 10000, it is four times faster.
Now my question. Does anyone know a better (and more elegant) way to solve this problem?