Prompted by @hadley's article on functionals referenced in an answer today, I decided to revisit a persistent puzzle about how the outer
function works (or doesn't). Why does this fail:
outer(0:5, 0:6, sum) # while outer(0:5, 0:6, "+") succeeds
This shows how I think outer
should handle a function like sum
:
Outer <- function(x,y,fun) {
mat <- matrix(NA, length(x), length(y))
for (i in seq_along(x)) {
for (j in seq_along(y)) {mat[i,j] <- fun(x[i],y[j])} }
mat}
> Outer(0:5, 0:6, `+`)
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 0 1 2 3 4 5 6
[2,] 1 2 3 4 5 6 7
[3,] 2 3 4 5 6 7 8
[4,] 3 4 5 6 7 8 9
[5,] 4 5 6 7 8 9 10
[6,] 5 6 7 8 9 10 11
OK, I don't have my indices exactly aligned for that example, but it wouldn't be that hard to fix. The question is why a function like sum
that should be able to accept two arguments and return an (atomic) value suitable for a matrix element, cannot do so when passed to the base::outer
function?
So @agstudy has given inspiration for a more compact version of Outer
and his is even more compact:
Outer <- function(x,y,fun) {
mat <- matrix(mapply(fun, rep(x, length(y)),
rep(y, each=length(x))),
length(x), length(y))
However, the question remains. The term "vectorized" is somewhat ambiguous here and I think "dyadic" is more correct, since sin
and cos
are "vectorized" in the usual sense of the term. Is there a fundamental logical barrier to expecting outer
to expand its arguments in a manner that non-dyadic functions can be used.
And here's another outer
-error that is probably similarly connected to my lack of understanding of this issue:
> Vectorize(sum)
function (..., na.rm = FALSE) .Primitive("sum")
> outer(0:5, 0:6, function(x,y) Vectorize(sum)(x,y) )
Error in outer(0:5, 0:6, function(x, y) Vectorize(sum)(x, y)) :
dims [product 42] do not match the length of object [1]
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With