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

bsh*_*hor 233 merge r list dataframe r-faq

我有一个我要合并的许多data.frames的列表.这里的问题是每个data.frame在行数和列数方面都不同,但它们都共享关键变量(我已经调用过"var1","var2"在下面的代码中).如果data.frames在列方面是相同的,我只能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)]), ...)
}
Run Code Online (Sandbox Code Playgroud)

我会像这样调用函数:

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

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

Error in match.names(clabs, names(xi)) : 
  names do not match previous names
Run Code Online (Sandbox Code Playgroud)

(很明显,我在其他地方看到了其他对此错误的引用而没有解决方案).

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

Cha*_*les 216

减少使这相当容易:

merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)
Run Code Online (Sandbox Code Playgroud)

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

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)
#    x  a  b         y
#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
Run Code Online (Sandbox Code Playgroud)

以下是使用这些数据进行复制的示例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>
Run Code Online (Sandbox Code Playgroud)

注意:看起来这可能是一个错误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.
Run Code Online (Sandbox Code Playgroud)

最简单的修复方法是不要为重复字段(其中有很多字段)保留字段重命名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))
Run Code Online (Sandbox Code Playgroud)

merge/ Reduce然后将正常工作.


Pau*_*eux 150

另一个问题具体询问如何在R中使用dplyr执行多个左连接.这个问题被标记为这个问题的副本,所以我在这里回答,使用下面的3个示例数据框:

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)
Run Code Online (Sandbox Code Playgroud)

更新2018年6月:我将答案分为三个部分,分别代表三种不同的合并方式.purrr如果您已经在使用tidyverse软件包,那么您可能希望使用这种方式.为了进行比较,您将找到使用相同样本数据集的基本R版本.

reducepurrr包中加入他们

purrr包提供了一个reduce具有简洁语法的函数:

library(tidyverse)
list(x, y, z) %>% reduce(left_join, by = "i")
#  A tibble: 3 x 4
#  i       j     k     l
#  <chr> <int> <int> <int>
# 1 a      1    NA     9
# 2 b      2     4    NA
# 3 c      3     5     7
Run Code Online (Sandbox Code Playgroud)

您还可以执行其他连接,例如a full_joininner_join:

list(x, y, z) %>% reduce(full_join, by = "i")
# A tibble: 4 x 4
# i       j     k     l
# <chr> <int> <int> <int>
# 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(inner_join, by = "i")
# A tibble: 1 x 4
# i       j     k     l
# <chr> <int> <int> <int>
# 1 c     3     5     7
Run Code Online (Sandbox Code Playgroud)

dplyr::left_join() 与基地R Reduce()

list(x,y,z) %>%
    Reduce(function(dtf1,dtf2) left_join(dtf1,dtf2,by="i"), .)

#   i j  k  l
# 1 a 1 NA  9
# 2 b 2  4 NA
# 3 c 3  5  7
Run Code Online (Sandbox Code Playgroud)

基础R merge()与基础RReduce()

为了进行比较,这里是左连接的基本R版本

 Reduce(function(dtf1, dtf2) merge(dtf1, dtf2, by = "i", all.x = TRUE),
        list(x,y,z))
#   i j  k  l
# 1 a 1 NA  9
# 2 b 2  4 NA
# 3 c 3  5  7
Run Code Online (Sandbox Code Playgroud)


Ram*_*ath 50

您可以merge_allreshape包中使用它.您可以传递参数以merge使用...参数

reshape::merge_all(list_of_dataframes, ...)
Run Code Online (Sandbox Code Playgroud)

这是合并数据帧的不同方法的优秀资源.

  • 是.每当我有一个想法,我总是检查@hadley是否已经完成它,并且他有大多数时间:-) (16认同)
  • 我想我从reshape2中删除了这个.Reduce + merge就是这么简单. (2认同)
  • @Ramnath,链接死了,有镜子吗? (2认同)

Moo*_*per 8

我们可以使用{powerjoin}。

从已接受的答案中借用示例数据:

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

library(powerjoin)
power_full_join(list(x,y,z), by = "i")
#>   i  j  k  l
#> 1 a  1 NA  9
#> 2 b  2  4 NA
#> 3 c  3  5  7
#> 4 d NA  6  8

power_left_join(list(x,y,z), by = "i")
#>   i j  k  l
#> 1 a 1 NA  9
#> 2 b 2  4 NA
#> 3 c 3  5  7
Run Code Online (Sandbox Code Playgroud)

您也可以从数据框开始并加入数据框列表,以获得相同的结果


power_full_join(x, list(y,z), by = "i")
#>   i  j  k  l
#> 1 a  1 NA  9
#> 2 b  2  4 NA
#> 3 c  3  5  7
#> 4 d NA  6  8
Run Code Online (Sandbox Code Playgroud)


SFu*_*n28 6

您可以使用递归来执行此操作。我还没有验证以下内容,但它应该给你正确的想法:

MergeListOfDf = function( data , ... )
{
    if ( length( data ) == 2 ) 
    {
        return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) )
    }    
    return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) )
}
Run Code Online (Sandbox Code Playgroud)


dmi*_*kno 5

我将重用@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)
Run Code Online (Sandbox Code Playgroud)

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

library(tidyverse)

 list(x, y, z) %>% 
  map_df(gather, key=key, value=value, -i) %>% 
  spread(key, value)
Run Code Online (Sandbox Code Playgroud)