将R中的不完整数据帧合并到矩阵中

将R中的不完整数据帧合并到矩阵中,r,list,matrix,dataframe,R,List,Matrix,Dataframe,我在R中有一个数据帧列表,每个数据帧从包含列组的不同文件加载。例如,这些文件可能包含不同比赛中不同运动员的终点位置 同一元素(运动员)可以出现在多个数据帧(比赛)中,但没有一个数据帧必须包含所有元素 我想用运动员作为行,种族作为列来填充排名矩阵。如果某个运动员在某一特定比赛中没有排名,则应为0 例如,如果我有: [[1]] name rank 1 Alice 1 2 Bob 2 3 Carla 3 4 Diego 4 [[2]] name rank 1

我在R中有一个数据帧列表,每个数据帧从包含列组的不同文件加载。例如,这些文件可能包含不同比赛中不同运动员的终点位置

同一元素(运动员)可以出现在多个数据帧(比赛)中,但没有一个数据帧必须包含所有元素

我想用运动员作为行,种族作为列来填充排名矩阵。如果某个运动员在某一特定比赛中没有排名,则应为0

例如,如果我有:

[[1]]
   name rank
1 Alice    1
2   Bob    2
3 Carla    3
4 Diego    4

[[2]]
   name rank
1 Alice    2
2 Carla    1
3  Eric    3
4 Frank    4
5  Gary    5

[[3]]
   name rank
1   Bob    5
2 Carla    4
3 Diego    3
4  Eric    1
5  Gary    2
我想生成一个矩阵:

      1 2 3
Alice 1 2 0
Bob   2 0 5
Carla 3 1 4
Diego 4 0 3
Eric  0 3 1
Frank 0 4 0
Gary  0 5 2

我正在寻找一种有效的方法来实现这一点:我的数据更像是200个数据帧和每个数据帧10000个排序元素(总共15000个唯一元素),因此最终的矩阵大约为15000x200

require(reshape2)
dcast(do.call(rbind, lapply(seq_along(ll), function(ix) 
         transform(ll[[ix]], id = ix))), name ~ id, value.var="rank", fill=0)

   name 1 2 3
1 Alice 1 2 0
2   Bob 2 0 5
3 Carla 3 1 4
4 Diego 4 0 3
5  Eric 0 3 1
6 Frank 0 4 0
7  Gary 0 5 2
其中
ll
data.frame
s的列表


或相当于:

dcast(transform(do.call(rbind, ll), id = rep(seq_along(ll), sapply(ll, nrow))), 
    name ~ id, value.var = "rank", fill = 0)

A
数据表
解决方案:

require(data.table)
pp <- rbindlist(ll)[, id := rep(seq_along(ll), sapply(ll, nrow))]
setkey(pp, "name", "id")
pp[CJ(unique(name), 1:3)][is.na(rank), rank := 0L][, as.list(rank), by = name]

    name V1 V2 V3
1: Alice  1  2  0
2:   Bob  2  0  5
3: Carla  3  1  4
4: Diego  4  0  3
5:  Eric  0  3  1
6: Frank  0  4  0
7:  Gary  0  5  2
library(reshape2)

dcast(melt(ll, id.vars = 'name'), name ~ L1, fill = 0)
#   name 1 2 3
#1 Alice 1 2 0
#2   Bob 2 0 5
#3 Carla 3 1 4
#4 Diego 4 0 3
#5  Eric 0 3 1
#6 Frank 0 4 0
#7  Gary 0 5 2
require(data.table)

pp这里的数据,因为OP没有给出一个可重复的例子:

dput(ll)
list(structure(list(name = structure(1:4, .Label = c("Alice", 
"Bob", "Carla", "Diego"), class = "factor"), rank = 1:4), .Names = c("name", 
"rank"), class = "data.frame", row.names = c("1", "2", "3", "4"
)), structure(list(name = structure(1:5, .Label = c("Alice", 
"Carla", "Eric", "Frank", "Gary"), class = "factor"), rank = c(2L, 
1L, 3L, 4L, 5L)), .Names = c("name", "rank"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5")), structure(list(name = structure(1:5, .Label = c("Bob", 
"Carla", "Diego", "Eric", "Gary"), class = "factor"), rank = c(5L, 
4L, 3L, 1L, 2L)), .Names = c("name", "rank"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5")))
与@Arun one几乎相同的解决方案,但分为两个步骤:

## add race column
ll <- lapply(seq_along(ll),function(x){
  ll[[x]]$race <- x
  ll[[x]]
  })
## create a long data.frame
dd <- do.call(rbind,ll)
## transform to the wide format
library(reshape2)

dcast(name~race,data=dd,fill=0,value.var='rank')

   name 1 2 3
1 Alice 1 2 0
2   Bob 2 0 5
3 Carla 3 1 4
4 Diego 4 0 3
5  Eric 0 3 1
6 Frank 0 4 0
7  Gary 0 5 2
##添加种族栏

ll另一个
Reduce
用例,似乎

merge.all <- function(x, y)
merge(x, y, all=TRUE, by="name")

# to avoid problems with merged name clashes
for(i in seq_along(ll))
    names(ll[[i]])[2] <- paste0("rank", i)

out <- Reduce(merge.all, ll)

merge.all这里有一个更简单的
restrape2
解决方案:

require(data.table)
pp <- rbindlist(ll)[, id := rep(seq_along(ll), sapply(ll, nrow))]
setkey(pp, "name", "id")
pp[CJ(unique(name), 1:3)][is.na(rank), rank := 0L][, as.list(rank), by = name]

    name V1 V2 V3
1: Alice  1  2  0
2:   Bob  2  0  5
3: Carla  3  1  4
4: Diego  4  0  3
5:  Eric  0  3  1
6: Frank  0  4  0
7:  Gary  0  5  2
library(reshape2)

dcast(melt(ll, id.vars = 'name'), name ~ L1, fill = 0)
#   name 1 2 3
#1 Alice 1 2 0
#2   Bob 2 0 5
#3 Carla 3 1 4
#4 Diego 4 0 3
#5  Eric 0 3 1
#6 Frank 0 4 0
#7  Gary 0 5 2

Arun的基准测试非常有趣,而且看起来像是
数据。table
做得非常好的是融化的部分,而
Reforme2
做得非常好的是
dcast
,所以这里是两个世界中最好的:

library(reshape2)
library(data.table)

pp = rbindlist(ll)[, id := rep(seq_along(ll), sapply(ll, nrow))]
dcast(pp, name ~ id, fill = 0, value.var = 'rank')
使用Arun的基准数据:

names <- tapply(sample(letters, 1e4, replace=TRUE), rep(1:(1e4/5), each=5), paste, collapse="")
names <- unique(names)

dd_create <- function() {
    nrow <- sample(c(100:500), 1)
    ncol <- 3
    data.frame(name = sample(names, nrow, replace=FALSE), rank = sample(nrow))
}

ll <- replicate(1e3, dd_create(), simplify = FALSE)

Arun_data.table <- function(ll) {
    pp <- rbindlist(ll)[, id := rep(seq_along(ll), sapply(ll, nrow))]
    setkey(pp, "name", "id")
    pp[CJ(unique(name), 1:1000)][is.na(rank), rank := 0L][, as.list(rank), by = name]
}

mix_of_both = function(ll) {
    pp = rbindlist(ll)[, id := rep(seq_along(ll), sapply(ll, nrow))]
    dcast(pp, name ~ id, fill = 0, value.var = 'rank')
}

require(microbenchmark)
microbenchmark(Arun_data.table(ll), mix_of_both(ll), times = 10)
# Unit: milliseconds
#                expr      min        lq    median        uq       max neval
# Arun_data.table(ll) 2568.333 2586.0079 2626.7704 2832.8076 2911.1314    10
#     mix_of_both(ll)  615.166  739.9383  766.8994  788.5822  821.0478    10

names@Arun因为我错过了value.var='rank':)我编辑我的答案并添加数据。我认为
Reduce
在很多列表元素上会非常慢。Hong,你知道为什么你的答案会以这个错误结尾吗(请参阅我在基准测试中的编辑)?这是因为合并结果中有重复的列名。您需要将每个数据帧中的名称
rank
更改为不重复的名称,例如
rank1
rank2
,等等。谢谢,基准测试结果现在也包括您的函数。eddi,非常有趣!我只是读了它的要点。看起来真不错!(我已经对你说过了)。我很快会详细检查并回信。