Replace the nested loop with expand.grid and call the inner function with several arguments

I want to use the rollapply function with various combinations of the width , by and FUN arguments ( width and by must have the same value). I inspired here and created the following code that works, but it has nothing to do with rollapply so far, it just demonstrates how to pass a few arguments to a function inside apply :

 > dframe <- expand.grid(c(1,2,3), c(1,2,3)) > testFunc <- function(a, b) a^2 + b > apply(dframe, 1, function(x) testFunc(x[1], x[2])) [1] 2 5 10 3 6 11 4 7 12 > apply(dframe, 1, function(x) x[1]^2 + x[2]) [1] 2 5 10 3 6 11 4 7 12 > apply(dframe, 1, function(x) (x[1]^2 + x[2])) [1] 2 5 10 3 6 11 4 7 12 > apply(dframe, 1, function(x) {x[1]^2 + x[2]}) [1] 2 5 10 3 6 11 4 7 12 

My final solution is here, but this does not work:

 > dframe <- expand.grid(c(1,2,3), c(median, mean)) > testFunc <- function(a, b) rollapply(mtcars, width = a, by = a, FUN = b, align="left") > apply(dframe, 1, function(x) testFunc(x[1], x[2])) Error in get(as.character(FUN), mode = "function", envir = envir) : object 'b' of mode 'function' was not found > apply(dframe, 1, function(x) rollapply(mtcars, width = x[1], by = x[1], FUN = x[2], align="left")) Error in match.fun(FUN) : 'x[2]' is not a function, character or symbol 

When I call testFunc , everything works fine, so I think the problem is that apply cannot somehow collect the results:

 > testFunc(10,mean) mpg cyl disp hp drat wt qsec vs am gear carb [1,] 20.37 5.8 208.61 122.8 3.538 3.1280 18.581 0.6 0.3 3.6 2.5 [2,] 19.89 6.6 259.25 149.6 3.552 3.6689 18.301 0.4 0.3 3.4 2.9 [3,] 20.39 6.2 228.25 152.6 3.654 2.8633 16.914 0.3 0.5 3.9 2.6 > class(testFunc(10,mean)) [1] "matrix" 

I also tried debugging testFunc and calling it from apply and it seems that the arguments passed correctly:

 > debug(testFunc) > apply(dframe, 1, function(x) testFunc(x[1], x[2])) debugging in: testFunc(x[1], x[2]) debug: rollapply(mtcars, width = a, by = a, FUN = b, align = "left") Browse[2]> print(a) $Var1 [1] 1 Browse[2]> print(b) $Var2 function (x, na.rm = FALSE) UseMethod("median") <bytecode: 0x08244ffc> <environment: namespace:stats> Browse[2]> n Error in get(as.character(FUN), mode = "function", envir = envir) : object 'b' of mode 'function' was not found 

Questions:

  • What is a mistake and what am I doing wrong?
  • How to replace a nested loop with expand.grid and call an internal function with several arguments?
  • How to return a list of matrices using *apply function families?

PS: I think it would be easy to achieve using two nested loops, but I wonder if there is an R-way.

PPS: Here is the answer to a similar error ( object 'b' of mode 'function' was not found ) with the conclusion that b (in my case) conflicts with named arguments with another function, But I do not see this problem in my code.

+5
source share
1 answer

Suppose that df <- data.frame(a = 1:2, b = 3:4) and apply(df, 1, function(x) fun(x)) . Then the two arguments passed x are the vectors c(1, 3) and c(2, 4) .

However, when df <- expand.grid(c(1,2,3), c(median, mean)) and apply(df, 1, function(x) fun(x)) are executed, we can no longer store, for example, 1 and median , to one vector, because they have too different types. Then x is a list, for example, x <- list(1, median) . Then executing x[1] or x[2] does not give 1 and median as desired; instead, they are single-item lists (hence the error object 'b' of mode 'function' was not found ). This can really be seen in your debugging example.

So, here are some ways to use apply in your case:

1) do not modify testFunc , but acknowledge that the list is passed to apply ; do.call helps in this case, but it also takes care of the df column names, so I also use unname :

 apply(unname(df), 1, do.call, what = testFunc) 

2) the same as 1), but without do.call :

 apply(dframe, 1, function(x) testFunc(x[[1]], x[[2]])) 

3) testFunc redefined to have one argument:

 testFunc <- function(a) rollapply(mtcars, width = a[[1]], by = a[[1]], FUN = a[[2]], align="left") apply(dframe, 1, testFunc) 
+6
source

Source: https://habr.com/ru/post/1245105/


All Articles