简体   繁体   中英

“dims [product xx] do not match the length of object [xx]” error in using R function `outer`

x <- 1:9
names(x) <- paste0("x",x)
y <- 2:5
names(y) <- paste0("y",y)

fun1      <-function(a, b) {paste(class(a),b, sep = "**")} #works
funError  <-function(a, b) {paste(class(a),class(b), sep = "**")} #does not work with outer
funNoError<-function(a, b) {paste(a,class(a),class(b),b, sep = "**")}  #works with outer  

funError(1,2) #is a valid function
outer(x, y, "funError") # fails
outer(x, y, "funNoError") # works

Q1 : Why does outer(x, y, "funError") not work ?

Error in dim(robj) <- c(dX, dY) : dims [product 36] do not match the length of object [1]

Q2 : Why does outer(x, y, "funNoError") work ? Its pretty similar.

  • The only difference I can see is that every "result" of funError is identical ( "numeric**numeric" ).

  • If having the same value all the time is the problem: Why does this here work?

outer(rep(0,7), 1:10, "^")


Okay, I get it:

lol  <- function(a,b) {"lol"}
lol_v<- Vectorize(lol)

outer(x, y, "lol")   # fails with same Error
outer(x, y, "lol_v") # works as expected

I often explain outer(x, y, FUN) when both x and y are vectors with the following:

xx <- rep(x, times = length(y))
yy <- rep(y, each = length(x))
zz <- FUN(xx, yy)
stopifnot(length(zz) == length(x) * length(y))  ## length = product?
z <- matrix(zz, length(x), length(y))

funError fails because zz has length 1, while funNoError does not because "recycling rule" has been applied when you paste a (a vector with length > 1) and class(a) (a length-1 vector).

This is illustrative as you will see why outer(1:5, 1:5, "+") works but outer(1:5, 1:5, sum) fails. Basically, FUN must be able to process xx and yy element-wise . Otherwise, wrap FUN with a sugar function called Vectorize . More details are given later.

Note that "list" is also a valid mode of a vector. So outer could be used to some non-standard things like How to perform pairwise operation like `%in%` and set operations for a list of vectors .


You can pass matrices / arrays to outer , too. Given that they are just vectors with an "dim" attribute (optionally with "dimnames"), how outer works does not change.

x <- matrix(1:4, 2, 2)  ## has "dim"
y <- matrix(1:9, 3, 3)  ## has "dim"

xx <- rep(x, times = length(y))  ## xx <- rep(c(x), times = length(y))
yy <- rep(y, each = length(x))  ## yy <- rep(c(y), each = length(x))
zz <- "*"(xx, yy)
stopifnot(length(zz) == length(x) * length(y))  ## length = product?
z <- "dim<-"( zz, c(dim(x), dim(y)) )

z0 <- outer(x, y, "*")
all.equal(z, z0)
#[1] TRUE

?outer explains the code above in plain words.

 ‘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).

 Where they exist, the [dim]names of ‘X’ and ‘Y’ will be copied to
 the answer, and a dimension assigned which is the concatenation of
 the dimensions of ‘X’ and ‘Y’ (or lengths if dimensions do not
 exist).

The word "vectorized" is NOT the most discussed one in R on performance . It means "vectorizing the action of a function":

## for FUN with a single argument
FUN( c(x1, x2, x3, x4) ) = c( FUN(x1), FUN(x2), FUN(x3), FUN(x4) )

## for FUN with two arguments
  FUN( c(x1, x2, x3, x4), c(y1, y2, y3, y4) )
= c( FUN(x1, y1), FUN(x2, y2), FUN(x3, y3), FUN(x4, y4) )

Some functions say "+" , "*" , paste behave like this, but many others don't, say class , sum , prod . The *apply family functions in R are there to help you to vectorize function action, or you can write your own loop to achieve the same effect.


Another worth reading good-quality Q & A: Why doesn't outer work the way I think it should (in R)?

I think this comes because the resulting matrix from outer expects to be the same dimensions as your inputs, however, class(a) is only length 1, therefor the matrix dimensions do not match. Try

funError2 <- function(a,b){paste(rep(class(a), length(a)),rep(class(b), length(b)), sep = "**")}
outer(x,y, "funError2")
#>    y2                 y3                 y4                
#> x1 "integer**integer" "integer**integer" "integer**integer"
#> x2 "integer**integer" "integer**integer" "integer**integer"
#> x3 "integer**integer" "integer**integer" "integer**integer"
#> x4 "integer**integer" "integer**integer" "integer**integer"
#> x5 "integer**integer" "integer**integer" "integer**integer"
#> x6 "integer**integer" "integer**integer" "integer**integer"
#> x7 "integer**integer" "integer**integer" "integer**integer"
#> x8 "integer**integer" "integer**integer" "integer**integer"
#> x9 "integer**integer" "integer**integer" "integer**integer"
#>    y5                
#> x1 "integer**integer"
#> x2 "integer**integer"
#> x3 "integer**integer"
#> x4 "integer**integer"
#> x5 "integer**integer"
#> x6 "integer**integer"
#> x7 "integer**integer"
#> x8 "integer**integer"
#> x9 "integer**integer"

Created on 2018-09-13 by the reprex package (v0.2.0).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM