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:为什么outer(x, y, "funError")
不起作用?
dim(robj)中的错误< - c(dX,dY):dims [product 36]与object [1]的长度不匹配
Q2:为什么outer(x, y, "funNoError")
有效?它非常相似。
funError
的每个“结果”都是相同的("numeric**numeric"
)。outer(rep(0,7), 1:10, "^")
好的,我明白了:
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
当outer(x, y, FUN)
和x
都是以下的向量时,我经常解释y
:
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
失败,因为zz
的长度为1,而funNoError
不会因为粘贴a
(长度> 1的向量)和class(a)
(长度为1的向量)时应用了“回收规则”。
这是说明性的,因为你会看到为什么outer(1:5, 1:5, "+")
工作,但outer(1:5, 1:5, sum)
失败。基本上,FUN
必须能够处理xx
和yy
元素。否则,用一个名为FUN
的糖函数包裹Vectorize
。稍后将给出更多细节。
请注意,“list”也是向量的有效模式。所以outer
可以用于一些非标准的东西,如How to perform pairwise operation like `%in%` and set operations for a list of vectors。
您也可以将矩阵/数组传递给outer
。鉴于它们只是具有“暗淡”属性的向量(可选地带有“dimnames”),outer
的工作方式不会改变。
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
用简单的词语解释了上面的代码。
‘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 most discussed one in R on performance。它意味着“矢量化函数的动作”:
## 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) )
一些功能说"+"
,"*"
,paste
表现得像这样,但许多其他人没有,比如class
,sum
,prod
。 R中的*apply
族函数可以帮助您矢量化函数动作,或者您可以编写自己的循环来实现相同的效果。
另一个值得阅读的优质问答:Why doesn't outer work the way I think it should (in R)?
我认为这是因为外部的结果矩阵期望与输入的尺寸相同,但是,类(a)只有长度1,因此矩阵尺寸不匹配。尝试
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"
由reprex package创建于2018-09-13(v0.2.0)。