[SOLVED] Problem with 'match.arg()' inside 'outer()' function

Issue

Background/Problem

I am trying to use the outer() function to apply a function on all the pairwise combinations of two vectors as arguments. In my case one of the inputs is a character vector that should serve as arguments to an inner function (cor()) that will need to run match.arg() on it.

Example 1 (it works)

This works fine when the inner function doesn’t require this such as with paste().

# define function to test
f1 <- function(method, vec_len){
  paste(method, vec_len)
}

# define inputs to test
methods <- c("pearson", "spearman", "kendall")
vec_lens <- 33:35

# outer runs fine
outer(methods, vec_lens, f1)
#>      [,1]          [,2]          [,3]         
#> [1,] "pearson 33"  "pearson 34"  "pearson 35" 
#> [2,] "spearman 33" "spearman 34" "spearman 35"
#> [3,] "kendall 33"  "kendall 34"  "kendall 35"

Example 2 (it doesn’t work)

However when the inner function needs to run match.arg() on the input this fails:

# function that parses string with match.arg fails
f2 <- function(method, vec_len){
  x <- runif(vec_len, 0, 10)
  y <- x + rnorm(vec_len)
  
  cor(x, y, method = method)
}

# outer fails on match.arg within cor()
outer(methods, vec_lens, f2)
#> Error in match.arg(method): 'arg' must be of length 1

My research

From the research I’ve done (1, 2, 3) it looks like this might be related to the scoping of the inner function being unable to access the global environment but I’m not sure and don’t know how to fix it.


BTW

I know the code is fine because I can get the result with a loop:

# desired result by loop
out <- list()
for (method in methods) {
  for (j in seq_along(vec_lens)) {
    out[[method]][[j]] <- f2(method, vec_lens[j])
    names(out[[method]])[j] <- vec_lens[j]
  }
}
do.call(rbind, out)
#>          33        34        35       
#> pearson  0.9351946 0.9491004 0.9516057
#> spearman 0.9067513 0.9315508 0.9456583
#> kendall  0.8371212 0.8146168 0.7983193

Solution

We can wrap with Vectorize

set.seed(24)
outer(methods, vec_lens, FUN = Vectorize(f2))
       [,1]      [,2]      [,3]
[1,] 0.9060101 0.9661931 0.9572807
[2,] 0.9542112 0.9495798 0.9490196
[3,] 0.8863636 0.8680927 0.7983193

which gives the same output as for loop (except for the dimnames)

> set.seed(24)
> out <- list()
> for (method in methods) {
+   for (j in seq_along(vec_lens)) {
+     out[[method]][[j]] <- f2(method, vec_lens[j])
+     names(out[[method]])[j] <- vec_lens[j]
+   }
+ }
> do.call(rbind, out)
         33        34        35       
pearson  0.9060101 0.9309473 0.9637686
spearman 0.9532086 0.9254393 0.9526611
kendall  0.8257576 0.8074866 0.7983193

Answered By – akrun

Answer Checked By – Robin (BugsFixing Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *