【问题标题】:Merging through fuzzy matching of variables in R通过R中变量的模糊匹配进行合并
【发布时间】:2011-08-19 10:31:20
【问题描述】:

我有两个数据框 (x & y),其中 ID 为 student_name、father_name 和 mother_name。由于印刷错误(“n”而不是“m”、随机空格等),我有大约 60% 的值没有对齐,尽管我可以观察数据并看到它们应该对齐。有没有办法以某种方式降低不匹配的水平,以便手动编辑至少可行?数据框有大约 700K 的观测值。

R 最好。我知道一点 python 和一些基本的 unix 工具。附:我阅读了agrep(),但不明白它如何在实际数据集上工作,尤其是当匹配超过一个变量时。


更新(已发布赏金的数据):

Here 是两个示例数据帧,sites_a 和 sites_b。它们可以在数字列 lat 和 lon 以及 sitename 列上匹配。了解如何在 a) 仅 lat + lon、b) sitename 或 c) 两者上完成此操作会很有用。

您可以获取作为 gist 发布的文件 test_sites.R。

理想情况下,答案应该以

结尾
merge(sites_a, sites_b, by = **magic**)

【问题讨论】:

  • 您能否提供一小部分数据(或向我们提供一些虚假数据)?
  • @RomanLuštrik 虽然这不是我最初的问题,但我遇到了类似的问题,创建了一些示例数据,并提供了赏金。
  • @David 你试过merge(sites_a, sites_b, by = c("lon", "lat"))吗?在您的情况下,如果您想按名称合并,则必须投入更多精力使两个 data.frames 中的名称匹配(祝您好运,呵呵)。
  • @RomanLuštrik 在示例中,坐标具有不同的精度级别。因此,round(sites_b[,c('lat','lon')],2) 上的匹配将接近 - 除非出现符号错误。

标签: linux r unix


【解决方案1】:

agrep 函数(基本 R 的一部分)使用 Levenshtein edit distance 进行近似字符串匹配可能值得一试。在不知道您的数据是什么样子的情况下,我无法真正提出可行的解决方案。但这是一个建议......它将匹配记录在一个单独的列表中(如果有多个同样好的匹配,那么这些也会被记录)。假设您的 data.frame 被称为df:

l <- vector('list',nrow(df))
matches <- list(mother = l,father = l)
for(i in 1:nrow(df)){
  father_id <- with(df,which(student_name[i] == father_name))
  if(length(father_id) == 1){
    matches[['father']][[i]] <- father_id
  } else {
    old_father_id <- NULL
    ## try to find the total                                                                                                                                 
    for(m in 10:1){ ## m is the maximum distance                                                                                                             
      father_id <- with(df,agrep(student_name[i],father_name,max.dist = m))
      if(length(father_id) == 1 || m == 1){
        ## if we find a unique match or if we are in our last round, then stop                                                                               
        matches[['father']][[i]] <- father_id
        break
      } else if(length(father_id) == 0 && length(old_father_id) > 0) {
        ## if we can't do better than multiple matches, then record them anyway                                                                              
        matches[['father']][[i]] <- old_father_id
        break
      } else if(length(father_id) == 0 && length(old_father_id) == 0) {
        ## if the nearest match is more than 10 different from the current pattern, then stop                                                                
        break
      }
    }
  }
}

mother_name 的代码基本相同。您甚至可以将它们放在一个循环中,但此示例仅用于说明目的。

【讨论】:

  • 谢谢,nullglob。您能否解释一下使用数据框(x,y)和变量(学生姓名等)的语法。
  • 对不起@user702432,我没有仔细阅读你的问题。你已经找到agrep。我添加了一个关于如何将它与 data.frame 结合使用的建议
【解决方案2】:

这需要一个常见列名列表,基于所有这些列组合的agrep 进行匹配,然后如果all.x 或all.y 等于TRUE,它会附加不匹配的记录,用NA 填充缺失的列。与merge 不同,要匹配的列名在每个数据帧中需要相同。挑战似乎是正确设置 agrep 选项以避免虚假匹配。

  agrepMerge <- function(df1, df2, by, all.x = FALSE, all.y = FALSE, 
    ignore.case = FALSE, value = FALSE, max.distance = 0.1, useBytes = FALSE) {

    df1$index <- apply(df1[,by, drop = FALSE], 1, paste, sep = "", collapse = "")
    df2$index <- apply(df2[,by, drop = FALSE], 1, paste, sep = "", collapse = "")

    matches <- lapply(seq_along(df1$index), function(i, ...) {
      agrep(df1$index[i], df2$index, ignore.case = ignore.case, value = value,
            max.distance = max.distance, useBytes = useBytes)
    })

    df1_match <- rep(1:nrow(df1), sapply(matches, length))
    df2_match <- unlist(matches)

    df1_hits <- df1[df1_match,]
    df2_hits <- df2[df2_match,]

    df1_miss <- df1[setdiff(seq_along(df1$index), df1_match),]
    df2_miss <- df2[setdiff(seq_along(df2$index), df2_match),]

    remove_cols <- colnames(df2_hits) %in% colnames(df1_hits)

    df_out <- cbind(df1_hits, df2_hits[,!remove_cols])

    if(all.x) {
      missing_cols <- setdiff(colnames(df_out), colnames(df1_miss))
      df1_miss[missing_cols] <- NA
      df_out <- rbind(df_out, df1_miss)
    }
    if(all.x) {
      missing_cols <- setdiff(colnames(df_out), colnames(df2_miss))
      df2_miss[missing_cols] <- NA
      df_out <- rbind(df_out, df2_miss)
    }
    df_out[,setdiff(colnames(df_out), "index")]
}

【讨论】:

猜你喜欢
  • 2019-01-17
  • 2021-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多