同时合并列表中的多个data.frames

我有我想要合并的许多data.frames的列表。 这里的问题是,每个data.frame在行数和列数方面有所不同,但它们都共享关键variables(在下面的代码中我称之为"var1""var2" )。 如果数据框架在列方面是相同的,那么我只能用rbind来表示,plyr的rbind.fill会做这个工作,但是这些数据并不是这样。

由于merge命令只适用于2个data.frames,所以我转向Internet寻求想法。 我从这里得到了这个,在R 2.7.2中完美的工作,这正是我当时所做的:

 merge.rec <- function(.list, ...){ if(length(.list)==1) return(.list[[1]]) Recall(c(list(merge(.list[[1]], .list[[2]], ...)), .list[-(1:2)]), ...) } 

我会这样调用函数:

 df <- merge.rec(my.list, by.x = c("var1", "var2"), by.y = c("var1", "var2"), all = T, suffixes=c("", "")) 

但是在2.7.2之后的任何R版本中,包括2.11和2.12,这段代码都会失败,并出现以下错误:

 Error in match.names(clabs, names(xi)) : names do not match previous names 

(可以肯定的是,我在其他地方看到其他引用,没有解决scheme)。

有什么办法可以解决这个问题吗?

另一个问题具体问到如何在R中使用dplyr执行多个左连接 。 这个问题被标记为这个重复的,所以我在这里回答:

 library(dplyr) x <- data_frame(i = c("a","b","c"), j = 1:3) y <- data_frame(i = c("b","c","d"), k = 4:6) z <- data_frame(i = c("c","d","a"), l = 7:9) list(x,y,z) %>% Reduce(function(dtf1,dtf2) left_join(dtf1,dtf2,by="i"), .) # ijkl #1 a 1 NA 9 #2 b 2 4 NA #3 c 3 5 7 

你也可以执行full_join()和inner_join()

 list(x,y,z) %>% Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2,by="i"), .) #Source: local data frame [4 x 4] # ijkl #1 a 1 NA 9 #2 b 2 4 NA #3 c 3 5 7 #4 d NA 6 8 list(x,y,z) %>% Reduce(function(dtf1,dtf2) inner_join(dtf1,dtf2,by="i"), .) #Source: local data frame [1 x 4] # ijkl #1 c 3 5 7 

为了完整起见,这里是完整连接的基本R版本

 Reduce(function(dtf1, dtf2) merge(dtf1, dtf2, by = "i", all = TRUE), list(x,y,z)) # ijkl #1 a 1 NA 9 #2 b 2 4 NA #3 c 3 5 7 #4 d NA 6 8 

减less使这相当容易:

 merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames) 

这是一个使用一些模拟数据的完整例子:

 set.seed(1) list.of.data.frames = list(data.frame(x=1:10, a=1:10), data.frame(x=5:14, b=11:20), data.frame(x=sample(20, 10), y=runif(10))) merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames) tail(merged.data.frame) # xaby #12 12 NA 18 NA #13 13 NA 19 NA #14 14 NA 20 0.4976992 #15 15 NA NA 0.7176185 #16 16 NA NA 0.3841037 #17 19 NA NA 0.3800352 

以下是使用这些数据复制my.list的示例:

 merged.data.frame = Reduce(function(...) merge(..., by=match.by, all=T), my.list) merged.data.frame[, 1:12] # matchname party st district chamber senate1993 name.x v2.x v3.x v4.x senate1994 name.y #1 ALGIERE 200 RI 026 S NA <NA> NA NA NA NA <NA> #2 ALVES 100 RI 019 S NA <NA> NA NA NA NA <NA> #3 BADEAU 100 RI 032 S NA <NA> NA NA NA NA <NA> 

注意:看起来这可能是一个merge错误。 问题是没有检查添加后缀(处理重叠的不匹配的名称)实际上使它们唯一。 在某个时候,它使用[.data.frame它使 make.unique的名字,导致rbind失败。

 # first merge will end up with 'name.x' & 'name.y' merge(my.list[[1]], my.list[[2]], by=match.by, all=T) # [1] matchname party st district chamber senate1993 name.x # [8] votes.year.x senate1994 name.y votes.year.y #<0 rows> (or 0-length row.names) # as there is no clash, we retain 'name.x' & 'name.y' and get 'name' again merge(merge(my.list[[1]], my.list[[2]], by=match.by, all=T), my.list[[3]], by=match.by, all=T) # [1] matchname party st district chamber senate1993 name.x # [8] votes.year.x senate1994 name.y votes.year.y senate1995 name votes.year #<0 rows> (or 0-length row.names) # the next merge will fail as 'name' will get renamed to a pre-existing field. 

最简单的方法就是不要将字段重命名为重复字段(这里有很多字段) merge 。 例如:

 my.list2 = Map(function(x, i) setNames(x, ifelse(names(x) %in% match.by, names(x), sprintf('%s.%d', names(x), i))), my.list, seq_along(my.list)) 

merge / Reduce将工作正常。

你可以在reshape包中使用merge_all来完成。 您可以传递参数以使用...参数进行merge

 reshape::merge_all(list_of_dataframes, ...) 

这是合并数据框的不同方法的优秀资源 。

你可以使用recursion来做到这一点。 我没有validation以下内容,但它应该给你正确的想法:

 MergeListOfDf = function( data , ... ) { if ( length( data ) == 2 ) { return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) ) } return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) ) } 

purrr包中可能有更新的解决scheme。 对于你确切的问题,你可以使用reduce()注意与base::Reduce相比较小的r,但是你可以完全避免使用map_dfr()或者map_dfc这个问题,它可以通过map和reduce来避免问题一步到位。

这里的其他解决scheme在小数据方面做得很好,但是它们recursion地创build和销毁很多variables。 为了避免N ^ 2做类似的复杂性

 X = A X = merge(X,B) X = merge(X,C) ... X = merge(X,Z) 

可以使用rbind。 你只需要自己pipe理列名,这是一个痛苦。 我不认为有什么好的办法来做到这一点,除了推出一些像下面的代码。

 allnames <- unique(unlist(sapply(myBigDataframeList,names))) for(i in 1:length(myBigDataframeList)){ columnmap <- match(allnames,names(myBigDataframeList[[i]])) columnmap <- ifelse(is.na(columnmap),1,columnmap+1) myBigDataframeList[[i]] <- cbind(data.frame(dummycolumn=NA),myBigDataframeList[[i]])[,columnmap] names(myBigDataframeList[[i]]) <- allnames } myBiggerDataframe <- do.call(rbind,myBigDataframeList) 

唯一要注意的是最后一列的数据types。 任何有NAs的东西都会转换成正确的types,但是也可能需要自己pipe理两个同名但types不同的列。

我将重用@PaulRougieux中的数据示例

 x <- data_frame(i = c("a","b","c"), j = 1:3) y <- data_frame(i = c("b","c","d"), k = 4:6) z <- data_frame(i = c("c","d","a"), l = 7:9) 

这里是一个简短而甜蜜的解决scheme,使用purrrtidyr

 library(tidyverse) list(x, y, z) %>% map_df(gather, key=key, value=value, -i) %>% spread(key, value)