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":
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;
it must return a vector of the same length as
x
(andy
); andyou must expect the output as a
matrix
of dimensionslength(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:
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 todata.frame
) createfactor
s by default. It does accept thestringsAsFactors=FALSE
argument.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, thendo.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) withdo.call("mapply", c(myfunc, eg, SIMPLIFY=FALSE))
Related Topics
Visualizing Distance Between Nodes According to Weights - with R
Rselenium on Docker: Where Are Files Downloaded
R -Apply- Convert Many Columns from Numeric to Factor
Aggregating Rows for Multiple Columns in R
How to Extract Coefficients' Standard Error from an "Aov" Model
R: How to Prompt The User for Input from The Console
Convert 12Hour Time to 24Hour Time
Standard Deviation on Dataframe Does Not Work
Encrypt Password in R - to Connect to an Oracle Db Using Rodbc
How to Create a Continuous Legend (Color Bar Style) for Scale_Alpha
Passing Ellipsis Arguments to Map Function Purrr Package, R
How to Give Numbers to Each Group of a Dataframe with Dplyr::Group_By
Extract Coefficients from Ggplot2-Created Nls Fit