An Error in R: When I Try to Apply Outer Function:

Why doesn't outer work the way I think it should (in R)?

outer(0:5, 0:6, sum) don't work because sum is not "vectorized" (in the sense of returning a vector of the same length as its two arguments). This example should explain the difference:

 sum(1:2,2:3)
8
1:2 + 2:3
[1] 3 5

You can vectorize sum using mapply for example:

identical(outer(0:5, 0:6, function(x,y)mapply(sum,x,y)),
outer(0:5, 0:6,'+'))
TRUE

PS: Generally before using outer I use browser to create my function in the debug mode:

outer(0:2, 1:3, function(x,y)browser())
Called from: FUN(X, Y, ...)
Browse[1]> x
[1] 0 1 2 0 1 2 0 1 2
Browse[1]> y
[1] 1 1 1 2 2 2 3 3 3
Browse[1]> sum(x,y)
[1] 27 ## this give an error
Browse[1]> x+y
[1] 1 2 3 2 3 4 3 4 5 ## this is vectorized

Simple question regarding the use of outer() and user-defined functions?

The answer becomes obvious if you read ?outer:

Details:

‘X’ and ‘Y’ must be suitable arguments for ‘FUN’. Each will be
extended by ‘rep’ to length the products of the lengths of ‘X’ and
‘Y’ before ‘FUN’ is called.

‘FUN’ is called with these two extended vectors as arguments.
Therefore, it must be a vectorized function (or the name of one),
expecting at least two arguments.

Think about what you are doing, you are concatenating two vectors into one vector, then sum the first and second elements of this vector. fun1() on the other hand does the vectorised sum of the inputs, so the returned object is of the same length as the individual lengths of the inputs. In fun2(), the output is a vector of length 1 and it was expecting 25.

The way to make the idea behind fun2() work is to cbind() not c() the two inputs:

> fun3 <- function(x, y) { z <- cbind(x, y); z[,1] + z[,2]}
> outer(seq(1,5,length=5),seq(6,10,length=5),fun3)
[,1] [,2] [,3] [,4] [,5]
[1,] 7 8 9 10 11
[2,] 8 9 10 11 12
[3,] 9 10 11 12 13
[4,] 10 11 12 13 14
[5,] 11 12 13 14 15

Issues in passing variable names as arguments from outer function to inner function in R?

When you use enquo() you also need to use !! when you call the variable in question within the function. This works:

fn_run_all <-function(bench_country = India, year_start = 1952, year_end = 2007){

bench_country = enquo(bench_country)
year_start = year_start
year_end = year_end

fn_benchmark_country(!! bench_country)
}

fn_run_all()

You can also just substitute enquo() and !! with the tunnel {{ }}:

fn_run_all <-function(bench_country = India, year_start = 1952, year_end = 2007){

year_start = year_start
year_end = year_end

fn_benchmark_country({{ bench_country }})
}

fn_run_all()

dim() Error in outer

You are right that your Rab function is wrong. The documentation of outer says

X and Y must be suitable arguments for FUN. Each will be extended by rep to length the products of the lengths of X and Y before FUN is called.

FUN is called with these two extended vectors as arguments (plus any arguments in ...). It must be a vectorized function (or the name of one) expecting at least two arguments and returning a value with the same length as the first (and the second).

So in your example a and b are extended to both have length length(a) * length(b), which happens to be 420 in your case. your function Rab should then return a vector of the same length.

In Rab you compute a vector g that has the correct length and would be suitable as a return value. Instead of returning this vector you try to assign it to an entry in the matrix r. Note that this matrix is defined as

r <- matrix(ncol = 1, nrow = 4)

and can't hold vectors of length 420 in either its rows or columns (this is what the warning messages are about). You lose all but the first element of your vector g in the process. You then go on to re-compute g with a slightly different set of parameters, which brings us to the next problem. These computations happen in a loop that is defined like this:

for (p in seq(from=0, to=4,by=1)){
## compute g ...
r[p] <- g
}

You seem to expect this loop to be executed four times but it is actually run five times for values of p equalling 0, 1, 2, 3 and 4. This means that the first g is assigned to r[0], which R silently ignores. Of course when you then try to return r none of this really matters because it only has length 4 (rather than 420) and this triggers an error.

I'm not convinced that I really understand what you are trying to do but the following might be a step in the right direction.

Rab <- function(a, b, p){
ifelse(a>=0 & a<1-1/p & p >b, a*p,
ifelse(a>=0 & a<1-1/b & p< b, -1+(a+1/b),
ifelse(a > 1-1/max(p,b),-1+p,NA)))
}

This will compute the g from your function once for a fixed value of p and return the result. You'd call this like so (for p=0):

q <- outer(a, b, Rab, 0)

If you want to call it for a number of different p you can do something like

q <- lapply(0:3, function(x,y,f, p) outer(x, y, f, p), x=a, y=b, f=Rab)

This would call Rab with p = 0, 1, 2 and 3 (I'm guessing that's what you want, adjust as required).

R: Use outer() on user-defined function

To use outer, some basic "requirements":

  1. the function will take two vectors all at once (as shown below); whether it chooses to do vectorized work on them or work on them individually is up to you;

  2. it must return a vector of the same length as x (and y); and

  3. you must expect the output as a matrix of dimensions length(x),length(y).

Interpreting that these are not all true for you, we move on. "The right function" depends on how you want the model to be run. A companion function to outer is expand.grid (and tidyr::crossing, the tidyverse version), in that it creates the same combinations of the supplied vectors. For instance:

outer(c(30,60,90), c(30, 60, 100), function(x,y) {browser();1;})
# Called from: FUN(X, Y, ...)
# Browse[2]>
x
# [1] 30 60 90 30 60 90 30 60 90
# Browse[2]>
y
# [1] 30 30 30 60 60 60 100 100 100

and

eg <- expand.grid(x=c(30,60,90), y=c(30, 60, 100))
eg
# x y
# 1 30 30
# 2 60 30
# 3 90 30
# 4 30 60
# 5 60 60
# 6 90 60
# 7 30 100
# 8 60 100
# 9 90 100

(which you can then access as eg$x and eg$y).

Some options:

  1. if you want your function to be called once (as with outer) with two arguments, and it will figure out what to do:

    eg <- expand.grid(x=c(30,60,90), y=c(30, 60, 100))
    do.call("myfunc", eg)

    Note that if given character arguments, it will (similar to data.frame) create factors by default. It does accept the stringsAsFactors=FALSE argument.

  2. if you want your function to be called for each pair of the vectors (so 9 times in this example), then do one either

    myfunc(eg$x, eg$y)

    if the number of vectors is known. If not, then using eg from above, then

    do.call("mapply", c(myfunc, eg))

    should work. Depending on the output, you can preclude it from "simplifying" the output (i.e., force a list output) with

    do.call("mapply", c(myfunc, eg, SIMPLIFY=FALSE))


Related Topics



Leave a reply



Submit