25

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]
Community
  • 1
  • 1
IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • your functions are ok, but I'm guessing much slower, so not that great for an R implementation of `outer`; had outer been implemented in C++ instead then the `sum` version would've likely worked – eddi Aug 07 '13 at 18:28
  • 3
    you're looking for this in your last example: `outer(0:5, 0:6, Vectorize(function(x,y)sum(x,y)))` – eddi Aug 07 '13 at 18:35
  • @eddi: I would have upvoted that if offered as an answer. – IRTFM Aug 07 '13 at 18:47
  • Check http://stackoverflow.com/questions/15601359/an-error-in-r-when-i-try-to-apply-outer-function/15751908#15751908 – Ferdinand.kraft Aug 08 '13 at 02:54
  • Your reporter function is possibly helpful, but just wrapping "Vectorize" around a function is not enough to ensure success. @eddi's version is better. – IRTFM Aug 08 '13 at 03:01

1 Answers1

34

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
IRTFM
  • 258,963
  • 21
  • 364
  • 487
agstudy
  • 119,832
  • 17
  • 199
  • 261
  • A subtlety from `?sum`: `... numeric or complex or logical vectors`. So `sum(c(1,2,3)) = sum(1,2,3) = sum(c(1,2),3)` – Señor O Aug 07 '13 at 18:17
  • I like the browser example. I'll try that with my Vectorize(sum) follow-on Q. (But I don't think that just saying "it's vectorized" is specific enough.) – IRTFM Aug 07 '13 at 18:33
  • 1
    @DWin I agree for for the terminology here. "Vectorized" is not the good term here. `dyadic` maybe? feel free to edit my answer. – agstudy Aug 07 '13 at 18:35
  • Just add a link to a new explanatory Q & A on `outer` issue: [“dims \[product xx\] do not match the length of object \[xx\]” error in using R function `outer`](https://stackoverflow.com/q/52317124/4891738) – Zheyuan Li Sep 14 '18 at 16:50