【问题标题】:Join multiple columns in data.frame to single column in reference data.frame将 data.frame 中的多列连接到参考 data.frame 中的单列
【发布时间】:2018-05-25 17:35:48
【问题描述】:

我正在尝试加入两个数据框。然而,与普通连接不同,我想将第一列中的一系列列与第二列相匹配。 基本上我有一个站点列表,其中引用了最近的周围站点。我需要在单独的数据框中查找最近的站点完整仪表和 LTA ID。我提供了一些示例数据框,包括示例输出,但实际情况并非如此整洁(并且有更多的列和行),这就是为什么我需要在 TestRefList 中查找 Surrogate 仪表的原因,而不是在下面的方法中创建。

library(plyr)
library(tidyverse)

TestRefList <- data.frame(Site = paste0("sl",1:10,".1"), Gauge = paste0(1:10,".1","/110.00/1"), LTA = paste0(1:10,".1","/110.99/1"), stringsAsFactors = F)
Surrogates <- data.frame(Primary = paste0("sl",c(2,4,6),".1"), nearest1=paste0("sl",1:3,".1"), nearest2=paste0("sl",7:9,".1"), stringsAsFactors = F)
HopefulOutput <- data.frame(Primary = paste0("sl",c(2,4,6),".1"), nearest1=paste0("sl",1:3,".1"), nearest2=paste0("sl",7:9,".1"), 
                    nearest1Gauge = paste0(1:3,".1","/110.00/1"), nearest1LTA = paste0(1:3,".1","/110.99/1"), 
                    nearest2Gauge = paste0(7:9,".1","/110.00/1"), nearest2LTA = paste0(7:9,".1","/110.99/1"), stringsAsFactors = F)

我认为我可以使用 plyr::ldply 和 dplyr::left_join 的某种组合,例如: 输出

但是,我无法使用列表中的名称加入工作。我已经在列表之外尝试过,在等号周围有一些 " 和 ' 的排列,例如:

left_join(Surrogates,TestRefList, by = c(paste0('"',names(Surrogates)[2],'"' , '="Site"')))

即使我可以让这部分工作,我也不确定它在 ldply 中是如何工作的。

有什么想法吗?如果有必要,我很高兴以一种完全不同的方式来解决这个问题,尽管我对 data.frames 和 tidyverse 比 data.table 选项更舒服

【问题讨论】:

  • 如果有任何答案满足您的需求,请“采纳”!谢谢。

标签: r dplyr left-join


【解决方案1】:
Reduce(function(x, fld) merge(x, TestRefList, by.x=fld, by.y="Site"),
       c("nearest1", "nearest2"), init = Surrogates)
#   nearest2 nearest1 Primary      Gauge.x        LTA.x      Gauge.y        LTA.y
# 1    sl7.1    sl1.1   sl2.1 1.1/110.00/1 1.1/110.99/1 7.1/110.00/1 7.1/110.99/1
# 2    sl8.1    sl2.1   sl4.1 2.1/110.00/1 2.1/110.99/1 8.1/110.00/1 8.1/110.99/1
# 3    sl9.1    sl3.1   sl6.1 3.1/110.00/1 3.1/110.99/1 9.1/110.00/1 9.1/110.99/1

您可以根据需要重命名列。这也可以通过dplyr::left_join 完成,只需稍作改动:

Reduce(function(x, fld) left_join(x, TestRefList, by = setNames("Site", fld)),
       c("nearest1", "nearest2"), init = Surrogates)

或在管道内:

Surrogates %>% 
  Reduce(function(x, fld) left_join(x, TestRefList, by = setNames("Site", fld)),
         c("nearest1", "nearest2"), init = .)

【讨论】:

    【解决方案2】:

    我提供基于data.table 的解决方案。当然,您的任务可以按照您的要求使用dplyr 完成。但是我不太了解 dplyr 来解决它。另外,我认为下面的 data.table 解决方案非常优雅和快速,只要您愿意在您的工作流程中添加另一个包。此外,此代码已经适用于数据中任意数量的“最近 n”列。

    library(data.table)
    
    # Melt the Surrogate data, providing useful column names.
    surrogate_dat = melt(data.table(Surrogates), 
                    id.vars="Primary", 
                    value.name="Site", 
                    variable.name="nearest_site_group")
    #    Primary nearest_site_group  Site
    # 1:   sl2.1           nearest1 sl1.1
    # 2:   sl4.1           nearest1 sl2.1
    # 3:   sl6.1           nearest1 sl3.1
    # 4:   sl2.1           nearest2 sl7.1
    # 5:   sl4.1           nearest2 sl8.1
    # 6:   sl6.1           nearest2 sl9.1
    
    # Merge melted surrogate data with reference list data.
    merged_dat = merge(x=surrogate_dat, 
                       y=data.table(TestRefList), 
                       by="Site")
    #     Site Primary nearest_site_group        Gauge          LTA
    # 1: sl1.1   sl2.1           nearest1 1.1/110.00/1 1.1/110.99/1
    # 2: sl2.1   sl4.1           nearest1 2.1/110.00/1 2.1/110.99/1
    # 3: sl3.1   sl6.1           nearest1 3.1/110.00/1 3.1/110.99/1
    # 4: sl7.1   sl2.1           nearest2 7.1/110.00/1 7.1/110.99/1
    # 5: sl8.1   sl4.1           nearest2 8.1/110.00/1 8.1/110.99/1
    # 6: sl9.1   sl6.1           nearest2 9.1/110.00/1 9.1/110.99/1
    
    # 'Cast' merged data back to wide form, specifying 3 value variables.
    results= dcast(data=merged_dat, 
                   formula=Primary ~ nearest_site_group, 
                   value.var=c("Site", "Gauge", "LTA"))
    #    Primary Site_nearest1 Site_nearest2 Gauge_nearest1 Gauge_nearest2
    # 1:   sl2.1         sl1.1         sl7.1   1.1/110.00/1   7.1/110.00/1
    # 2:   sl4.1         sl2.1         sl8.1   2.1/110.00/1   8.1/110.00/1
    # 3:   sl6.1         sl3.1         sl9.1   3.1/110.00/1   9.1/110.00/1
    #    LTA_nearest1 LTA_nearest2
    # 1: 1.1/110.99/1 7.1/110.99/1
    # 2: 2.1/110.99/1 8.1/110.99/1
    # 3: 3.1/110.99/1 9.1/110.99/1
    

    【讨论】:

      【解决方案3】:

      这是Surrogates 中任意数量的“最近”列的通用解决方案。它首先获取“最近”列的向量,然后从那里开始。

      # get list of columns matching "nearest"
      nearestCols <- colnames(Surrogates) %>%
        `[`(grepl("nearest", .))
      
      # output data.frame
      out <- Surrogates
      
      # for each "nearest" column, merge Gauge and LTA
      for (n in nearestCols) {
        out <- merge(out, TestRefList, by.x = n, by.y = "Site", all.x = TRUE)
        colnames(out)[(ncol(out)-1):ncol(out)] <- paste0(n, c("Gauge", "LTA"))
      }
      
      # re-order the columns
      out <- out[, c(length(nearestCols) + 1, length(nearestCols):1, (length(nearestCols)+2):ncol(out))]
      

      输出:

      > out
        Primary nearest1 nearest2 nearest1Gauge  nearest1LTA nearest2Gauge  nearest2LTA
      1   sl2.1    sl1.1    sl7.1  1.1/110.00/1 1.1/110.99/1  7.1/110.00/1 7.1/110.99/1
      2   sl4.1    sl2.1    sl8.1  2.1/110.00/1 2.1/110.99/1  8.1/110.00/1 8.1/110.99/1
      3   sl6.1    sl3.1    sl9.1  3.1/110.00/1 3.1/110.99/1  9.1/110.00/1 9.1/110.99/1
      > identical(out, HopefulOutput)
      [1] TRUE
      

      【讨论】:

      • 嗯,谢谢,我希望避免这种解决方案,因为我真正的 data.frame 有十几个“最近”的列,如果你一次做一个,这会让整个事情看起来很荒谬
      • 很公平。我同意,在某个时候最好寻找其他解决方案。如果您需要 100 条 merge() 语句,那么它看起来有点多,如果有 10 条左右,并且它有效,我会选择它而不是花费额外的时间进行优化。我的个人意见。
      • 已将我的答案更新为通用解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-20
      相关资源
      最近更新 更多