我有一个列表,里面有很多我想合并的数据帧。这里的问题是,每个data.frame的行数和列数不同,但它们都共享关键变量(在下文中我将其称为“var1”和“var2”)。如果data.frames在列方面是相同的,我可以只rbind,对于plyr的rbind。填充可以完成这项工作,但这些数据不是这样的。

因为merge命令只适用于2 data.frames,所以我求助于互联网。我从这里得到了这个,它在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

(顺便说一句,我在其他地方看到了这个错误的其他引用,但没有解决方案)。

有办法解决这个问题吗?


当前回答

下面是一个通用包装器,可用于将二进制函数转换为多参数函数。这种解决方案的好处是它非常通用,可以应用于任何二进制函数。你只需要做一次,然后你可以把它应用到任何地方。

为了演示这个想法,我使用简单的递归来实现。当然,它可以用更优雅的方式实现,这得益于R对函数范式的良好支持。

fold_left <- function(f) {
return(function(...) {
    args <- list(...)
    return(function(...){
    iter <- function(result,rest) {
        if (length(rest) == 0) {
            return(result)
        } else {
            return(iter(f(result, rest[[1]], ...), rest[-1]))
        }
    }
    return(iter(args[[1]], args[-1]))
    })
})}

然后,您可以简单地用它包装任何二进制函数,并在第一个括号中调用位置参数(通常是data.frames),在第二个括号中调用命名参数(例如by =或后缀=)。如果没有命名参数,则将第二个括号保留为空。

merge_all <- fold_left(merge)
merge_all(df1, df2, df3, df4, df5)(by.x = c("var1", "var2"), by.y = c("var1", "var2"))

left_join_all <- fold_left(left_join)
left_join_all(df1, df2, df3, df4, df5)(c("var1", "var2"))
left_join_all(df1, df2, df3, df4, df5)()

其他回答

我们可以使用{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

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


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

你可以使用递归来做到这一点。我还没有验证以下内容,但它应该会给你一个正确的想法:

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

另一个问题是如何在R中使用dplyr执行多个左连接。这个问题被标记为这个问题的重复,所以我在这里回答,使用下面的3个样本数据帧:

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)

答案分为三个部分,代表执行合并的三种不同方式。如果您已经在使用tidyverse包,那么您可能希望使用purrr方式。出于下面的比较目的,您将找到一个使用相同示例数据集的基本R版本。


1)加入他们的减少从purrr包:

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

你也可以执行其他的连接,比如full_join或者inner_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

2) dplyr::left_join() with base 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

3) Base R merge()与Base R Reduce():

为了比较,这是一个基于Charles答案的左连接的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

你可以在重塑包中使用merge_all。您可以使用…来传递参数进行合并。论点

reshape::merge_all(list_of_dataframes, ...)

这里有一个关于合并数据帧的不同方法的优秀资源。

下面是一个通用包装器,可用于将二进制函数转换为多参数函数。这种解决方案的好处是它非常通用,可以应用于任何二进制函数。你只需要做一次,然后你可以把它应用到任何地方。

为了演示这个想法,我使用简单的递归来实现。当然,它可以用更优雅的方式实现,这得益于R对函数范式的良好支持。

fold_left <- function(f) {
return(function(...) {
    args <- list(...)
    return(function(...){
    iter <- function(result,rest) {
        if (length(rest) == 0) {
            return(result)
        } else {
            return(iter(f(result, rest[[1]], ...), rest[-1]))
        }
    }
    return(iter(args[[1]], args[-1]))
    })
})}

然后,您可以简单地用它包装任何二进制函数,并在第一个括号中调用位置参数(通常是data.frames),在第二个括号中调用命名参数(例如by =或后缀=)。如果没有命名参数,则将第二个括号保留为空。

merge_all <- fold_left(merge)
merge_all(df1, df2, df3, df4, df5)(by.x = c("var1", "var2"), by.y = c("var1", "var2"))

left_join_all <- fold_left(left_join)
left_join_all(df1, df2, df3, df4, df5)(c("var1", "var2"))
left_join_all(df1, df2, df3, df4, df5)()