合并列表

王致远

2018/10/10

今天在工作中遇到一个问题:当我用lapply处理完一个列表后,会得到结果列表。结果列表的某一列都是相同的索引,我需要把列表中每一个元素都根据索引合并到一个DataFrame中,该怎么操作?

比如结果列表是l。

l1 <- replicate(5,round(runif(10),3),simplify = F)
l2 <- replicate(5,sample(1:10,10),simplify = F)
l <- vector(mode = "list",length = 5)
for(i in 1:5){
  l[[i]][[1]] <- l2[[i]]
  l[[i]][[2]] <- l1[[i]]
  l[[i]] <- data.frame(l[[i]])
  names(l[[i]]) <- c("v1","v2")
}
l
## [[1]]
##    v1    v2
## 1  10 0.515
## 2   9 0.398
## 3   7 0.822
## 4   3 0.347
## 5   4 0.293
## 6   8 0.937
## 7   6 0.270
## 8   1 0.256
## 9   2 0.649
## 10  5 0.607
## 
## [[2]]
##    v1    v2
## 1   5 0.053
## 2  10 0.977
## 3   4 0.115
## 4   8 0.887
## 5   6 0.175
## 6   3 0.991
## 7   7 0.915
## 8   2 0.815
## 9   1 0.558
## 10  9 0.071
## 
## [[3]]
##    v1    v2
## 1   6 0.284
## 2   7 0.838
## 3   2 0.423
## 4   1 0.846
## 5  10 0.442
## 6   8 0.241
## 7   3 0.690
## 8   5 0.020
## 9   4 0.671
## 10  9 0.096
## 
## [[4]]
##    v1    v2
## 1   5 0.645
## 2   9 0.156
## 3   4 0.286
## 4  10 0.373
## 5   2 0.029
## 6   7 0.204
## 7   1 0.342
## 8   6 0.796
## 9   8 0.913
## 10  3 0.714
## 
## [[5]]
##    v1    v2
## 1   9 0.511
## 2   5 0.266
## 3   7 0.809
## 4   3 0.897
## 5   8 0.900
## 6   6 0.441
## 7   4 0.689
## 8   1 0.954
## 9  10 0.950
## 10  2 0.334

比较常规的操作是写一个类似于Reduce的函数,用循环完成。

merge_reduce <- function(l,atts){
  num <- length(l)
  k <- l[[1]]
  for(i in 2:num){
    k <- merge(k,l[[i]],by=atts)
  }
  return(k)
}
merge_reduce(l,"v1")
##    v1  v2.x  v2.y  v2.x  v2.y    v2
## 1   1 0.256 0.558 0.846 0.342 0.954
## 2   2 0.649 0.815 0.423 0.029 0.334
## 3   3 0.347 0.991 0.690 0.714 0.897
## 4   4 0.293 0.115 0.671 0.286 0.689
## 5   5 0.607 0.053 0.020 0.645 0.266
## 6   6 0.270 0.175 0.284 0.796 0.441
## 7   7 0.822 0.915 0.838 0.204 0.809
## 8   8 0.937 0.887 0.241 0.913 0.900
## 9   9 0.398 0.071 0.096 0.156 0.511
## 10 10 0.515 0.977 0.442 0.373 0.950

但是用循环完成,仍不是最优美的方式。

考虑使用泛函,写一个mege_list的闭包,可以提供一个bywhat的参数,然后再用Reduce递归使用merge_list。

merge_list <- function(bywhat){
  function(df1,df2){
    merge(df1,df2,by=bywhat)
  }
}
Reduce(merge_list(bywhat = "v1"),l)
##    v1  v2.x  v2.y  v2.x  v2.y    v2
## 1   1 0.256 0.558 0.846 0.342 0.954
## 2   2 0.649 0.815 0.423 0.029 0.334
## 3   3 0.347 0.991 0.690 0.714 0.897
## 4   4 0.293 0.115 0.671 0.286 0.689
## 5   5 0.607 0.053 0.020 0.645 0.266
## 6   6 0.270 0.175 0.284 0.796 0.441
## 7   7 0.822 0.915 0.838 0.204 0.809
## 8   8 0.937 0.887 0.241 0.913 0.900
## 9   9 0.398 0.071 0.096 0.156 0.511
## 10 10 0.515 0.977 0.442 0.373 0.950

在简单的几行代码中,运用到了闭包和Reduce的泛函,非常简洁优美。