我有如下排列的数据:
- 一个列表
- 在列表中,每个元素都是一个数据框(50 个数据框) 每个数据框
- 都包含 5 行数字,其中 9 个命名列(所有 50 个数据框的名称相同 9 个)
我的目标是有效地重新排列这些数据,以便:
- "第二维度"(在上述描述中从 1 到 5 的范围)成为第一个维度。
- "第一维度"(在上述描述中从 1 到 50 的范围)成为第二个维度。
- 我只保留 9 个命名列中的一些(其余的可以丢弃),按名称选择
- 我希望所有数字都存储在一个数组中(或者另一个更有效的数据结构也可以),而不是这些低效的列表和数据帧。
可以使用以下代码生成示例数据(简化为只有 2 个数据框,每个数据框 5 行 3 列):
example_list<-lapply(X=1:2, FUN=function(X){setNames(data.frame(X*c(1:5), -X*c(1:5), X*100*c(1:5)), c("C1", "C2", "C3"))})
这将创建以下两个数据框的列表:
> example_list[1]
C1 C2 C3
1 1 -1 100
2 2 -2 200
3 3 -3 300
4 4 -4 400
5 5 -5 500
> example_list[2]
C1 C2 C3
1 2 -2 200
2 4 -4 400
3 6 -6 600
4 8 -8 800
5 10 -10 1000
我当前的解决方案(示例数据使用硬编码数字)如下所示。在这种情况下,我假设我们只关心名为"C1"和"C2"的列:
important_cols <- c("C1", "C2")
result <- array(0, c(5, 2, length(important_cols)))
for(i in 1:5){
for(j in 1:2){
result[i,j,] <- c(example_list[[j]][i,important_cols], recursive=T)
}
}
这给出了以下输出:
> result
, , 1
[,1] [,2]
[1,] 1 2
[2,] 2 4
[3,] 3 6
[4,] 4 8
[5,] 5 10
, , 2
[,1] [,2]
[1,] -1 -2
[2,] -2 -4
[3,] -3 -6
[4,] -4 -8
[5,] -5 -10
例如,result[5,2,] = [10, -10]
对应于原始数据的第2
个数据帧的第5
行(删除了第三列)。
上面的解决方案有效,但我不禁怀疑应该有一个比双手动实现的 for 循环和逐个设置所有元素更有效的解决方案。
您可以使用一些lapply
和purrr::transpose
来避免循环:
# Example
N <- 1e5
example_list <-
lapply(
X = 1:2,
FUN = function(X) {
setNames(data.frame(X * c(1:N), -X * c(1:N), X * 100 * c(1:N)), c("C1", "C2", "C3"))
}
)
important_cols <- c("C1", "C2")
# Your solution -> 58 seconds :O
system.time({
result <- array(0, c(N, 2, length(important_cols)))
for(i in 1:N){
for(j in 1:2){
result[i,j,] <- c(example_list[[j]][i,important_cols], recursive=T)
}
}
})
# Solution with purrr::transpose -> 0 sec
library(magrittr) ## for the %>%
system.time({
result2 <- example_list %>%
lapply(function(df) df[important_cols]) %>%
purrr::transpose() %>%
sapply(function(l) do.call(cbind, l))
})
dim(result2) <- c(nrow(example_list[[1]]),
length(example_list),
length(important_cols))
# Verification
all.equal(result, result2)