为什么sapply返回一个需要转置的矩阵,然后转置后的矩阵无法附加到数据框中?

10

我希望了解为什么会出现这种情况,以及如何更加优雅地处理。使用sapply时,希望返回一个3x2的矩阵,但实际上返回的是一个2x3的矩阵。为什么会这样?而且为什么很难将其附加到另一个数据框中?

a <- data.frame(id=c('a','b','c'), var1 = c(1,2,3), var2 = c(3,2,1))
out <- sapply(a$id, function(x) out = a[x, c('var1', 'var2')])
#out is 3x2, but I would like it to be 2x3
#I then want to append t(out) (out as a 2x3 matrix) to b, a 1x3 dataframe
b <- data.frame(var3=c(0,0,0))

当我尝试附加它们时,

b[,c('col2','col3')] <- t(out)

我遇到的错误是:
Warning message:
In `[<-.data.frame`(`*tmp*`, , c("col2", "col3"), value = list(1,  :
  provided 6 variables to replace 2 variables

尽管以下内容似乎能够得到所需的结果:
rownames(out) <- c('col1', 'col2')
b <- cbind(b, t(out))

我无法操作这些变量:

b$var1/b$var2

返回值

Error in b$var1/b$var2 : non-numeric argument to binary operator

谢谢!


1
你想用这些数据做什么?你的示例并没有实现有意义的操作。 - hadley
3
@hadley:这个示例遵循 R 发布指南,提供了一个最小的可行示例。实际情况相当复杂,复杂性会分散注意力,不利于中心问题的解答。我编写的函数使用 Taylor 级数展开法估算模型对 20 个不同参数的灵敏度,并接受一个 20x8 的数据框作为输入。如果您愿意,我可以发送完整的可重现示例,尽管它还没有准备好公开。 - David LeBauer
1
你需要在易于理解和捕捉你正在努力解决的问题本质之间寻求一个平衡点。在你目前的例子中,似乎你正在尝试让b等于a - hadley
@hadley:谢谢你的提示,我会牢记的。 - David LeBauer
3个回答

6

在DWin的回答基础上进行补充:查看一下您的out对象的结构会有所帮助。这可以解释为什么b$var1/b$var2不会产生您期望的结果。

> out <- sapply(a$id, function(x) out = a[x, c('var1', 'var2')])
> str(out)  # this isn't a data.frame or a matrix...
List of 6
 $ : num 1
 $ : num 3
 $ : num 2
 $ : num 2
 $ : num 3
 $ : num 1
 - attr(*, "dim")= int [1:2] 2 3
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:2] "var1" "var2"
  ..$ : NULL
<代码>apply函数族被设计用于向量和数组,因此在使用它们与数据框(data.frames)时需要小心(数据框通常是向量列表)。可以利用数据框是列表的事实来使用<代码>lapply。
> out <- lapply(a$id, function(x) a[x, c('var1', 'var2')])  # list of data.frames
> out <- do.call(rbind, out) # data.frame
> b <- cbind(b,out)
> str(b)
'data.frame':   3 obs. of  4 variables:
 $ var3: num  0 0 0
 $ var1: num  1 2 3
 $ var2: num  3 2 1
 $ var3: num  0 0 0
> b$var1/b$var2
[1] 0.3333333 1.0000000 3.0000000

3

首先介绍一下R语言的记号。如果您查看sapply代码,就可以找到问题的答案。 sapply函数会检查列表长度是否都相等,如果是,则首先使用"unlist()"函数将它们展开,然后将这些列表作为数据参数传递给array()函数。由于array(类似于matrix())默认按列主序排列值,所以你得到的是这样的结果。这些列表被转向了。如果您不喜欢这种方式,那么可以定义一个新的函数tsapply,返回转置后的值:

> tsapply <- function(...) t(sapply(...))
> out <- tsapply(a$id, function(x) out = a[x, c('var1', 'var2')])
> out
     var1 var2
[1,] 1    3   
[2,] 2    2   
[3,] 3    1 

...一个 3 x 2 的矩阵。


1
从技术上讲,“out”不是矩阵。它是一个带有“dim”和“dimnames”属性的列表。例如,“out%*%t(out)”会失败。 - Joshua Ulrich
1
除了 R 认为它是一个矩阵:> is.matrix(out) [1] TRUE - IRTFM

1

请查看plyr包中的ddply函数

a <- data.frame(id=c('a','b','c'), var1 = c(1,2,3), var2 = c(3,2,1))

library(plyr)
ddply(a, "id", function(x){
    out <- cbind(O1 = rnorm(nrow(x), x$var1), O2 = runif(nrow(x)))
    out
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接