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

前端 未结 2 1282
说谎
说谎 2020-12-12 02:43
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
funErro         


        
相关标签:
2条回答
  • 2020-12-12 03:27

    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)?

    0 讨论(0)
  • 2020-12-12 03:40

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

    0 讨论(0)
提交回复
热议问题