代码之家  ›  专栏  ›  技术社区  ›  dan

连接数据帧而不返回所有匹配组合

  •  2
  • dan  · 技术社区  · 7 年前

    我有一个 list 属于 data.frame S(本例中仅2个):

    set.seed(1)
    
    df1 <- data.frame(id = sample(LETTERS,50,replace=T), val = rnorm(50), val1 = rnorm(50), stringsAsFactors = F)
    df2 <- data.frame(id = sample(LETTERS,30,replace=T), val = rnorm(30), val2 = rnorm(30), stringsAsFactors = F)
    
    df.list <- list(df1,df2)
    

    我想 join 它们变成了一个单曲 数据帧 只有共享列名的子集,在本例中 id .

    如果我使用:

    library(dplyr)
    df <- df.list %>% purrr::reduce(dplyr::inner_join,by="id")
    

    我没有加入的共享列名称会随着 x y 足够的:

      id       val.x       val1     val.y       val2
    1  G -0.05612874  0.2914462  2.087167  0.7876396
    2  G -0.05612874  0.2914462 -0.255027  1.4411577
    3  J -0.15579551 -0.4432919 -1.286301  1.0273924
    

    实际上,对于我没有加入的共享列名,从列表中的一个data.frame中选择它们就足够了—只要它们存在于wrt中,就可以选择加入的列。 身份证件 .

    我不知道这些共享的列名,但不难发现:

    例如。:

    df.list.colnames <- unlist(lapply(df.list,function(l) colnames(l %>% dplyr::select(-id))))
    df.list.colnames <- table(df.list.colnames)
    repeating.colnames <- names(df.list.colnames)[which(df.list.colnames > 1)]
    

    这样我就可以把它们和 数据帧 S在 列表 :

    repeating.colnames.df <- do.call(rbind,lapply(df.list,function(r) r %>% dplyr::select_(.dots = c("id",repeating.colnames)))) %>%
      unique()
    

    我可以加入 数据帧 s不包括这些列:

    然后如上所述加入他们:

    for(r in 1:length(df.list)) df.list[[r]] <- df.list[[r]] %>% dplyr::select_(.dots = paste0("-",repeating.colnames))
    df <- df.list %>% purrr::reduce(dplyr::inner_join,by="id")
    

    现在我要添加 repeating.colnames.df 对此。我什么都不知道 参加 在里面 dplyr 不会返回所有组合 df 重复.colname.df 所以我能做的就是 apply 超过每一个 df$id ,选择第一场比赛 重复.colname.df 并将结果与 东风 .

    在这种情况下有没有什么比这更麻烦的?

    2 回复  |  直到 7 年前
        1
  •  2
  •   Chase    7 年前

    如果我正确地遵循了,我认为您可以通过编写一个自定义函数来处理这个问题。 reduce 它标识公共列名称(不包括联接列),并从合并中的“第二个”表中排除这些列。AS 减少 通过列表,函数将“累计”唯一列,默认为“最左边”表中的列。

    像这样:

    library(dplyr)
    library(purrr)
    set.seed(1)
    df1 <- data.frame(id = sample(LETTERS,50,replace=T), val = rnorm(50), val1 = rnorm(50), stringsAsFactors = F)
    df2 <- data.frame(id = sample(LETTERS,30,replace=T), val = rnorm(30), val2 = rnorm(30), stringsAsFactors = F)
    df.list <- list(df1,df2)
    
    fun <- function(df1, df2, by_col = "id"){
      df1_names <- names(df1)
      df2_names <- names(df2)
      dup_cols <- intersect(df1_names[!df1_names %in% by_col], df2_names[!df2_names %in% by_col])
      out <- dplyr::inner_join(df1, df2[, !(df2_names %in% dup_cols)], by = by_col)
      return(out)
    }
    
    df_chase <- df.list %>% reduce(fun,by_col="id")
    

    创建日期:2019-01-15 reprex package (v0.2.1)

    如果我比较 df_chase 对于您的最终解决方案,我给出了相同的答案:

    > all.equal(df_chase, df_orig)
    [1] TRUE
    
        2
  •  0
  •   aljabadi    7 年前

    如果您说您不真正关心这些列,只需使用 base::merge :

    set.seed(1)
    
    df1 <- data.frame(id = sample(LETTERS,50,replace=T), val = rnorm(50), val1 = rnorm(50), stringsAsFactors = F)
    df2 <- data.frame(id = sample(LETTERS,30,replace=T), val = rnorm(30), val2 = rnorm(30), stringsAsFactors = F)
    
    
    duplicates = names(df1) == names(df2) & names(df1) !="id"
    
    df2 = df2[,!duplicates]
    df12 = base::merge.data.frame(df1, df2, by = "id")
    head(df12)