【问题标题】:R function for a function to be repeated based on column values基于列值重复的函数的 R 函数
【发布时间】:2017-08-05 06:02:36
【问题描述】:

我有一个需要进行字符串匹配的大型数据集。我从这个站点获得了一些非常有用的帖子,并引用它们我创建了一个函数来为我的数据集进行字符串匹配。我正在粘贴我的示例数据和代码。

样本数据

Address1 <- c("786, GALI NO 5, XYZ","rambo, 45, strret 4, atlast, pqr","23/4, 23RD FLOOR, STREET 2, ABC-E, PQR","45-B, GALI NO5, XYZ","HECTIC, 99 STREET, PQR")
AREACODE <- c('10','10','14','20','30')
Year1 <- c(2001:2005)

Address2 <- c("abc, pqr, xyz","786, GALI NO 4 XYZ","45B, GALI NO 5, XYZ","del, 546, strret2, towards east, pqr","23/4, STREET 2, PQR","abc, pqr, xyz","786, GALI NO 4 XYZ","45B, GALI NO 5, XYZ","del, 546, strret2, towards east, pqr","23/4, STREET 2, PQR")
Year2 <- c(2001:2010)
AREA_CODE <- c('10','10','10','20','30','40','50','61','64', '99')

data1 <- data.table(Address1, Year1, AREACODE)
data2 <- data.table(Address2, Year2, AREA_CODE)
data2[, unique_id := sprintf("%06d", 1:nrow(data2))]

代码

fn.fuzzymatch<-function(dat1,dat2,string1,string2,meth){

  dist.name<-stringdistmatrix(dat1[[string1]],dat2[[string2]],method = meth)

  min.name<-apply(dist.name, 1, min)

  match.s1.s2<-NULL
  for(i in 1:nrow(dist.name))
  {
    s2.i<-match(min.name[i],dist.name[i,])
    s1.i<-i
    match.s1.s2<-rbind(data.frame(s1_row=s1.i,s2_row=s2.i,s1name=dat1[s1.i,][[string1]],s2name=dat2[s2.i,][[string2]], dist=min.name[i]),match.s1.s2)
  }
  output <- (match.s1.s2)[order(match.s1.s2$s1_row),]
  return(output)
}


match_50 <- fn.fuzzymatch(data1,data2,"Address1","Address2","dl")

这适用于国家/地区级别的数据,但是我在区域级别有多个数据文件,并且每个区域都有多个区域。每个区域的区号可通过 data1 中的 AREACODE 变量和 data2 中的 AREA_CODE 变量获得。我想更新我的功能,以便

  1. 对每个区域进行字符串匹配,输出具有该区域代码
  2. 为每个区域返回输出,为该区域中的所有区号合并。

我试图使用 split 并将数据文件转换为列表并使用,然后使用 rbindlist 将它们组合但无法成功并且遇到了不同类型的错误。我确信有办法做到这一点,但无法做到。希望能给点建议。

【问题讨论】:

  • 你能分享你到目前为止尝试过的代码吗? Data1 和 Data2 代表您的数据在区域级别的外观?
  • @JuanBosco,是的 dat1 和 dat2 代表数据在区域级别的样子。像这样,我有 10 个地区的多个数据文件。我调用了这个当前函数 10 次。但现在我需要检查每个区域并获取该区域的输出。每个区域可以有 10-40 个不同的区域,我总共有 190 个独特的区域。
  • 我看到您的函数需要成对的数据集并使用它们执行操作。检查每个区域是什么意思?第一个数据集中的每个区域,第二个数据集中的所有区域组合?简而言之,您的预期输出是什么?我认为您可以使用当前功能实现一些可能的输出,但我不完全确定您需要什么。
  • @JuanBosco,是的,我的意思是在两个数据集中存在的区域之间。例如,区域 10 我们在 data1 中有 2 个地址,在 data2 中有 3 个地址,因此区域 10 的匹配应该只发生在这些记录之间。来自data1的区域10的2地址有3个选择来匹配data2中的区域10。我希望这个区域变量成为一个参数,因为目前我们想要在区域之间进行匹配,明天我们可能想要根据其他一些变量(比如年份)进行测试。因此考虑在当前功能上使用另一个功能。希望您能理解并能提供帮助!!谢谢!!
  • 这个答案有帮助吗?

标签: r function split data.table


【解决方案1】:

虽然您可能可以使用 apply 函数对不同区域的单独数据文件进行重复,但这里有一个 fuzzyjoin 解决方案,基于我对您的 previous question 的回答。

它会寻找地址的最佳stringdist 匹配,并且区域代码必须完全匹配 (==)。我还指定 year2 必须是 &gt;= year1,仅用于演示。

最后,我使用dplyr::group_bydplyr::top_n 来获得最小距离匹配,并且我必须假设在匹配关系中要做什么(选择最大年份的匹配)。您还可以使用 slice_min 替换旧的 top_n,如果原始顺序很重要且不按字母顺序排列,请使用 mutate(rank = row_number(dist)) %&gt;% filter(rank == 1)

数据:

Address1 <- c("786, GALI NO 5, XYZ","rambo, 45, strret 4, atlast, pqr","23/4, 23RD FLOOR, STREET 2, ABC-E, PQR","45-B, GALI NO5, XYZ","HECTIC, 99 STREET, PQR")
AREACODE <- c('10','10','14','20','30')
Year1 <- c(2001:2005)

Address2 <- c("abc, pqr, xyz","786, GALI NO 4 XYZ","45B, GALI NO 5, XYZ","del, 546, strret2, towards east, pqr","23/4, STREET 2, PQR","abc, pqr, xyz","786, GALI NO 4 XYZ","45B, GALI NO 5, XYZ","del, 546, strret2, towards east, pqr","23/4, STREET 2, PQR")
Year2 <- c(2001:2010)
AREA_CODE <- c('10','10','10','20','30','40','50','61','64', '99')

data1 <- data.table(Address1, Year1, AREACODE)
data2 <- data.table(Address2, Year2, AREA_CODE)
data2[, unique_id := sprintf("%06d", 1:nrow(data2))]

解决方案:

library(fuzzyjoin, quietly = TRUE); library(dplyr, quietly = TRUE)

# First, need to define match_fun_stringdist 
# Code from stringdist_join from https://github.com/dgrtwo/fuzzyjoin
match_fun_stringdist <- function(v1, v2) {
  
  # Can't pass these parameters in from fuzzy_join because of multiple incompatible match_funs, so I set them here.
  ignore_case = FALSE
  method = "dl"
  max_dist = 99
  distance_col = "dist"
  
  if (ignore_case) {
    v1 <- stringr::str_to_lower(v1)
    v2 <- stringr::str_to_lower(v2)
  }
  
  # shortcut for Levenshtein-like methods: if the difference in
  # string length is greater than the maximum string distance, the
  # edit distance must be at least that large
  
  # length is much faster to compute than string distance
  if (method %in% c("osa", "lv", "dl")) {
    length_diff <- abs(stringr::str_length(v1) - stringr::str_length(v2))
    include <- length_diff <= max_dist
    
    dists <- rep(NA, length(v1))
    
    dists[include] <- stringdist::stringdist(v1[include], v2[include], method = method)
  } else {
    # have to compute them all
    dists <- stringdist::stringdist(v1, v2, method = method)
  }
  ret <- dplyr::data_frame(include = (dists <= max_dist))
  if (!is.null(distance_col)) {
    ret[[distance_col]] <- dists
  }
  ret
}

# Finally, call fuzzy_join
fuzzy_join(data1, data2, 
           by = list(x = c("Address1", "AREACODE", "Year1"), y = c("Address2", "AREA_CODE", "Year2")), 
           match_fun = list(match_fun_stringdist, `==`, `<=`),
           mode = "left"
           ) %>%
  group_by(Address1, Year1, AREACODE) %>%
  top_n(1, -Address1.dist) %>%
  top_n(1, Year2) %>%
  select(unique_id, Address1.dist, everything())

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-28
    • 1970-01-01
    • 2017-12-22
    • 1970-01-01
    相关资源
    最近更新 更多