根据两列的 matching/fuzzy 匹配值选择列

Selecting columns based on matching/fuzzy matching value of two columns

我有包含列 "Year""Agent" 的数据框 df1:

df1 <- structure(list(Year = c(1999, 1999, 1998), Agent = list(c("abn-amro-nv", "suntrust banks", "wachovia"), c("jp morgan", "abn-amro-nv"), c("ba-corp", "boston bks", "nbd"))), .Names = c("Year", "Agent"), row.names = c(NA, -3L), class = "data.frame")

#df1
Year                                   Agent
1999   abn-amro-nv, suntrust banks, wachovia
1999                  jp morgan, abn-amro-nv
1998                ba-corp, boston bks, nbd

我有另一个数据框 df2,它有 5 列 "Rank""Arrangers""Share""Issues""Year",如以下:

df2 <- structure(list(Rank = 1:3, Arranger = c("jp morgan", "boston-bank", "suntrust bk"), Share = c(1.2, 1.8, 2.1), Issues = c(7L, 4L, 3L), Year = c(1999L, 1998L, 1999L)), .Names = c("Rank", "Arranger", "Share", "Issues", "Year"), class = "data.frame", row.names = c(NA, -3L))

#df2
Rank    Arranger         Share    Issues    Year
 1      jp morgan        1.2       7        1999
 2      boston-bank      1.8       4        1998
 3      suntrust bk      2.1       3        1999

我需要将 df1"Agent""Year"df2 和 select 的 "Arranger""Year" 匹配df2 中的 3 列,例如 "Rank""Share""Issues"df1"Agent"df2"Arranger"的匹配为模糊匹配。这是因为它们并不完全相同。

我的原始数据框很大,仅供参考。

以下是我的代码:

library(stringdist)

leadrep <- matrix(ncol=3, nrow=length(df1$Agent))

for (i in 1:length(df1$Agent)) {

  for (j in 1:length(df2$Arrangers)) {

      if ((ain(df2$Arrangers[j], df1$Agent[[i]], maxDist=0.3, 
      method="jw")) == 'TRUE' & (df1$Year[i] == df2$Year[j])){             

         leadrep[i,] <-  df2[j, c('Rank', 'Mkt.Share', 'NumberofIssues')]

      }

  }
} 

在代码中,leadrep是我创建的矩阵。这样我就可以填充 for loop。我使用 stringdist 包和 ain 函数来进行模糊匹配。我用两个 for loopif 来比较字符串和年份。

上面的代码可以工作,但是由于我的数据帧很大,所以需要很长时间才能得到结果。我真的认为我上面的方法效率不高。如果有人为我提供一个更好的替代现有代码的方法,那将是一个很大的帮助。

感谢您的帮助。

以下方法使用 foreach 而不是两个嵌套的 for 循环,这将使您在大型数据帧上的计算速度更快。请参阅 this 了解该软件包的概览。你还应该看看小插曲。

library(foreach)
library(stringdist)

match.cond <- function(ij, df1, df2) {                                   ## 1.
  i = floor((ij-1) / nrow(df2)) + 1
  j = ij - (i-1) * nrow(df2)
  if ((ain(df2$Arranger[j], df1$Agent[[i]], maxDist=0.3, 
       method="jw")) == 'TRUE' & (df1$Year[i] == df2$Year[j])){
    return(df2[j, c('Rank', 'Share', 'Issues')])
  }
}

leadrep <- foreach(ij = 1:(nrow(df1)*nrow(df2)), .combine=rbind) %do%    ## 2.
  match.cond(ij, df1, df2)

备注:

  1. match.cond 是你的匹配条件封装到一个 kernel 函数中,它将被 foreach vectorized来自 df1df2 的所有行对。它的输入是 ij,这是配对的索引,以及两个数据帧。 match.cond 以内:
    • ij 转换为 df1 的行索引 idf2
    • j
    • 你的条件被评估,如果满足条件,
    • 返回 df2 中匹配行的列。
  2. 这是 foreach 通话。
    • 我们遍历从 1nrow(df1)*nrow(df2) 的索引 ij,它枚举了来自 df1df2 和 [=33] 的所有行对=] match.cond 函数。请注意,这都是一行。
    • .combine=rbind 参数声明我们要从 match.cond 收集所有结果并将它们绑定为行。
    • 这个returns数据框leadrep

我已经对你的数据进行了测试,dput:

df1 <- structure(list(Year = c(1999, 1999, 1998), Agent = list(c("abn-amro-nv", 
"suntrust banks", "wachovia"), c("jp morgan", "abn-amro-nv"), 
c("ba-corp", "boston bks", "nbd"))), .Names = c("Year", "Agent"
), row.names = c(NA, -3L), class = "data.frame")

df2 <- structure(list(Rank = 1:3, Arranger = c("jp morgan", "boston-bank", 
"suntrust bk"), Share = c(1.2, 1.8, 2.1), Issues = c(7L, 4L, 
3L), Year = c(1999L, 1998L, 1999L)), .Names = c("Rank", "Arranger", 
"Share", "Issues", "Year"), class = "data.frame", row.names = c(NA, 
-3L))

这给了我想要的结果:

print(leadrep)
##   Rank Share Issues
##3     3   2.1      3
##2     1   1.2      7
##21    2   1.8      4

希望这对您有所帮助。