首页 文章

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

提问于
浏览
193

我有一个我要合并的许多data.frames的列表 . 这里的问题是每个data.frame在行数和列数方面都有所不同,但它们都共享关键变量(我在下面的代码中称之为 "var1""var2" ) . 如果data.frames在列方面是相同的,我只能 rbind ,plyr的rbind.fill可以完成这项工作,但这些数据并非如此 .

因为 merge 命令仅适用于2个data.frames,所以我转向Internet寻求创意 . 我从here获得了这个,它在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

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

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

6 回答

  • 204

    另一个问题具体问了how to perform multiple left joins using dplyr in R . 这个问题被标记为这个问题的副本,所以我在这里回答,使用下面的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)
    

    Update June 2018 :我将答案分为三个部分,分别代表三种不同的方式来执行合并 . 如果您已经在使用 tidyverse 包,则可能需要使用 purrr 方式 . 为了进行比较,您将找到使用相同样本数据集的基本R版本 .

    使用purrr包中的reduce加入它们

    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_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
    

    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
    

    Base R merge()与base R Reduce()

    为了进行比较,这里是左连接的基本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
    
  • 47

    减少使这相当容易:

    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)
    #    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
    

    这是一个使用these data复制 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 将正常工作 .

  • 98

    您可以使用 reshape 包中的 merge_all 来执行此操作 . 您可以使用 ... 参数将参数传递给 merge

    reshape::merge_all(list_of_dataframes, ...)
    

    Here is an excellent resource on different methods to merge data frames .

  • 0

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

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

    我将重用@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)
    

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

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

    我有一个没有公共id列的数据帧列表 .
    我在许多dfs上丢失了数据 . 有Null值 . 数据帧是使用表函数生成的 . Reduce,Merging,rbind,rbind.fill等他们无法帮助我实现目标 . 我的目标是产生一个可理解的合并数据帧,与缺失的数据和常见的id列无关 .

    因此,我做了以下功能 . 也许这个功能可以帮助别人 .

    ##########################################################
    ####             Dependencies                        #####
    ##########################################################
    
    # Depends on Base R only
    
    ##########################################################
    ####             Example DF                          #####
    ##########################################################
    
    # Example df
    ex_df           <- cbind(c( seq(1, 10, 1), rep("NA", 0), seq(1,10, 1) ), 
                             c( seq(1, 7, 1),  rep("NA", 3), seq(1, 12, 1) ), 
                             c( seq(1, 3, 1),  rep("NA", 7), seq(1, 5, 1), rep("NA", 5) ))
    
    # Making colnames and rownames
    colnames(ex_df) <- 1:dim(ex_df)[2]
    rownames(ex_df) <- 1:dim(ex_df)[1]
    
    # Making an unequal list of dfs, 
    # without a common id column
    list_of_df      <- apply(ex_df=="NA", 2, ( table) )
    

    它遵循的功能

    ##########################################################
    ####             The function                        #####
    ##########################################################
    
    
    # The function to rbind it
    rbind_null_df_lists <- function ( list_of_dfs ) {
      length_df     <- do.call(rbind, (lapply( list_of_dfs, function(x) length(x))))
      max_no        <- max(length_df[,1])
      max_df        <- length_df[max(length_df),]
      name_df       <- names(length_df[length_df== max_no,][1])
      names_list    <- names(list_of_dfs[ name_df][[1]])
    
      df_dfs <- list()
      for (i in 1:max_no ) {
    
        df_dfs[[i]]            <- do.call(rbind, lapply(1:length(list_of_dfs), function(x) list_of_dfs[[x]][i]))
    
      }
    
      df_cbind               <- do.call( cbind, df_dfs )
      rownames( df_cbind )   <- rownames (length_df)
      colnames( df_cbind )   <- names_list
    
      df_cbind
    
    }
    

    运行示例

    ##########################################################
    ####             Running the example                 #####
    ##########################################################
    
    rbind_null_df_lists ( list_of_df )
    

相关问题