结果证明这比我预期的要快(尽管仍然没有@akrun 采用的明显方法那么快),所以我将发布这个(像大卫一样)“只是为了一般知识”。(另外,“data.table”所有的东西。):-)
创建一个data.table
包含三列的:
- 单行的未列出值。
- 一个分组变量,用于指示应在最终输出中将值分配给哪一行。
- 一个分组变量,用于指示应在最终输出中将值分配给哪一列。
一旦你有了它,你就可以dcast.data.table
用来获取你提到的输出(加上一个奖励栏)。
对于上面的第 2 点,我们可以很容易地定义如下函数来简化创建组的过程:
groupMaker <- function(vecLen, perGroup) {
(0:(vecLen-1) %/% perGroup) + 1
}
然后我们可以按如下方式使用它:
dcast.data.table(
data.table(value = unlist(df, use.names = FALSE),
row = groupMaker(ncol(df), 3),
col = 1:3),
row ~ col)
# row 1 2 3
# 1: 1 1 2 3
# 2: 2 4 5 6
# 3: 3 7 8 9
# 4: 4 10 11 12
现在,您提到您实际上正在处理单行 ~ 40K 列data.frame
(我假设它是 39,999 列,因为它可以很好地被 3 整除,我不想破坏其他答案)。
记住这一点,这里有一些(无用的)基准(没用,因为我们在这里谈论的是毫秒,真的)。
set.seed(1)
S <- sample(20, 39999, TRUE)
S <- data.frame(t(S))
funAM <- function(indf) {
dcast.data.table(
data.table(value = unlist(indf, use.names = FALSE),
row = groupMaker(ncol(indf), 3),
col = 1:3),
row ~ col)
}
funDA <- function(indf) {
as.data.frame(t(`dim<-`(unlist(indf), c(3, ncol(indf)/3))))
}
funAK <- function(indf) as.data.frame(matrix(indf, ncol=3, byrow=TRUE))
library(microbenchmark)
microbenchmark(funAM(S), funDA(S), funAK(S))
# Unit: milliseconds
# expr min lq mean median uq max neval
# funAM(S) 18.487001 18.813297 22.105766 18.999891 19.455812 50.25876 100
# funDA(S) 37.187177 37.450893 40.393893 37.870683 38.869726 94.20128 100
# funAK(S) 5.018571 5.149758 5.929944 5.271679 5.536449 26.93281 100
在所需列的数量和您的输入列的数量不能很好地相互整除的情况下,这可能很有用。
例如,尝试以下示例数据:
set.seed(1)
S2 <- sample(20, 40000, TRUE)
S2 <- data.frame(t(S))
使用此示例数据:
funAM
会给你一个warning
但会正确地给你最后一行的最后两列作为NA
.
funAK
会给你一个warning
但会(大概)错误地回收最后一行中的值。
funDA
只会给你一个error
.
我仍然认为你应该从源头上解决问题:-)