r语言 - 为什么rbind和cbind用sapply产生相同的输出



包含以下列表:

> lll = list(list(5,3,4), list(5,3,7), list(6,2,1), list(6,1,3), list(5,2,1))

我期望输出用rbind转置,用sapply转置,但它们是相同的:

> sapply(lll, rbind)
     [,1] [,2] [,3] [,4] [,5]
[1,] 5    5    6    6    5   
[2,] 3    3    2    1    2   
[3,] 4    7    1    3    1   
> sapply(lll, cbind)
     [,1] [,2] [,3] [,4] [,5]
[1,] 5    5    6    6    5   
[2,] 3    3    2    1    2   
[3,] 4    7    1    3    1   
> 
> identical(sapply(lll, cbind), sapply(lll, rbind))
[1] TRUE

为什么会这样,什么代码会产生cbind和rbind的转置输出?

您编写它的方式是,rbind分别应用于每个子列表,因此它实际上并没有将它们与任何东西组合在一起。在这种情况下,它所做的只是向子列表添加维度属性,将其从具有length=3的列表更改为具有dim=c(1,3)的矩阵。cbind做同样的事情,除了你会得到dim=c(3,1)。关键是,正如您所写的那样,rbindcbind实际上都没有用于将子列表组合在一起。这些都是由sapply完成的,sapply不关心它们作为矩阵的维数。它把它们当作向量,并把它们组合成列。

考虑这个稍微简单一些的例子:

> sapply(list(list(1,2,3),list(4,5,6)),rbind)
     [,1] [,2]
[1,] 1    4   
[2,] 2    5   
[3,] 3    6   

等价于:

> sapply(list(rbind(list(1,2,3)),rbind(list(4,5,6))),identity)

等价于:

> sapply(list(matrix(list(1,2,3),c(1,3)),matrix(list(4,5,6),c(1,3))),identity)

等价于这个,因为sapply关心的是它们的长度,而不是它们的维度:

> sapply(list(c(1,2,3),c(4,5,6)),identity)

基本上就是这样(因为sapply将它们合并为列):

> cbind(c(1,2,3),c(4,5,6))

你真正想要的是用每个子列表作为参数调用rbind一次,而不是在每个子列表上分别调用它。换句话说,您希望它像这样工作:

> rbind(list(1,2,3),list(4,5,6))
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6

但是由于您已经将子列表存储在单个列表中,您可以通过使用do.call来实现这一点,它允许您将rbind的所有参数提供为单个list:

> list_of_lists <- list(list(1,2,3),list(4,5,6))
> do.call(rbind, list_of_lists)
     [,1] [,2] [,3]
[1,] 1    2    3   
[2,] 4    5    6   

通常,cbindrbind适用于矢量。例如:

v <- c(3,1,2)
cbind(v)
#     v
#[1,] 3
#[2,] 1
#[3,] 2
rbind(v)
#  [,1] [,2] [,3]
#v    3    1    2

但是sapply有一个名为simplify的参数,默认情况下是TRUE,这个参数在代码中中和了rbindcbind的效果。参数simplify?sapply声明:

逻辑或字符串;如果可能的话,结果应该简化为向量、矩阵或高维数组吗?

实际上你的代码和下面的代码是一样的:

sapply(lll, function(x) x)

为了让它起作用,我会这样做(虽然其他人已经建议):

x <- do.call(rbind, lll)
     # [,1] [,2] [,3]
# [1,] 5    3    4   
# [2,] 5    3    7   
# [3,] 6    2    1   
# [4,] 6    1    3   
# [5,] 5    2    1   
t(x)
     # [,1] [,2] [,3] [,4] [,5]
# [1,] 5    5    6    6    5   
# [2,] 3    3    2    1    2   
# [3,] 4    7    1    3    1   

或使用data.table

x <- rbindlist(lll)
t(x)

x <- matrix(unlist(lll), ncol = 3, byrow = T)
t(x)

一旦我们得到一种形式的输出(例如rbind),我们将结果转置以得到另一种形式(以完成cbind的情况)。

要得到您想要的输出,您可能想尝试使用do.calllapply:

rbind:

do.call(rbind, lapply(lll, rbind))

cbind:

do.call(cbind, lapply(lll, cbind))

正如@zx8754所指出的,实际上可以跳过apply的使用,只使用do.call来完成它(我不知道的事情,对于列表中的列表)。谢谢!):

cbind and rbind:

do.call(cbind, lll)
do.call(rbind, lll)

谢谢!

最新更新