将数据与r中的部分匹配合并
Posted
技术标签:
【中文标题】将数据与r中的部分匹配合并【英文标题】:merge data with partial match in r 【发布时间】:2012-05-23 22:43:51 【问题描述】:我有两个数据集
datf1 <- data.frame (name = c("regular", "kklmin", "notSo", "Jijoh",
"Kish", "Lissp", "Kcn", "CCCa"),
number1 = c(1, 8, 9, 2, 18, 25, 33, 8))
#-----------
name number1
1 regular 1
2 kklmin 8
3 notSo 9
4 Jijoh 2
5 Kish 18
6 Lissp 25
7 Kcn 33
8 CCCa 8
datf2 <- data.frame (name = c("reGulr", "ntSo", "Jijoh", "sean", "LiSsp",
"KcN", "CaPN"),
number2 = c(2, 8, 12, 13, 20, 18, 13))
#-------------
name number2
1 reGulr 2
2 ntSo 8
3 Jijoh 12
4 sean 13
5 LiSsp 20
6 KcN 18
7 CaPN 13
我想按名称列合并它们,但是允许部分匹配(以避免妨碍合并大型数据集中的拼写错误,甚至检测此类拼写错误),例如
(1) 如果在任意位置连续四个字母(如果字母数小于 4 则全部) - 匹配即可
ABBCD = BBCDK = aBBCD = ramABBBCD = ABB
(2) 匹配中不区分大小写例如ABBCD = aBbCd
(3) 新数据集将保留两个名称(来自 datf1 和 datf2 的名称)。这样我们就可以检测该字母是否匹配(可以单独一列显示匹配多少个字母)
这样的合并可能吗?
编辑:
datf1 <- data.frame (name = c("xxregular", "kklmin", "notSo", "Jijoh",
"Kish", "Lissp", "Kcn", "CCCa"),
number1 = c(1, 8, 9, 2, 18, 25, 33, 8))
datf2 <- data.frame (name = c("reGulr", "ntSo", "Jijoh", "sean",
"LiSsp", "KcN", "CaPN"),
number2 = c(2, 8, 12, 13, 20, 18, 13))
uglyMerge(datf1, datf2)
name1 name2 number1 number2 matches
1 xxregular <NA> 1 NA 0
2 kklmin <NA> 8 NA 0
3 notSo <NA> 9 NA 0
4 Jijoh Jijoh 2 12 5
5 Kish <NA> 18 NA 0
6 Lissp LiSsp 25 20 5
7 Kcn KcN 33 18 3
8 CCCa <NA> 8 NA 0
9 <NA> reGulr NA 2 0
10 <NA> ntSo NA 8 0
11 <NA> sean NA 13 0
12 <NA> CaPN NA 13 0
【问题讨论】:
尝试修复一些格式。我看到您添加了一份似乎来自@sgibb 回复的“uglyMerge”副本。 'xxregular' 与 'reGulr' 的不匹配对您来说可能很明显,但您可能需要向我们解释,因为它似乎符合您的规范 看fuzzyjoin
包
【参考方案1】:
也许有一个简单的解决方案,但我找不到任何解决方案。 恕我直言,您必须自己实施这种合并。 请在下面找到一个丑陋的例子(有很大的改进空间):
uglyMerge <- function(df1, df2)
## lower all strings to allow case-insensitive comparison
lowerNames1 <- tolower(df1[, 1]);
lowerNames2 <- tolower(df2[, 1]);
## split strings into single characters
names1 <- strsplit(lowerNames1, "");
names2 <- strsplit(lowerNames2, "");
## create the final dataframe
mergedDf <- data.frame(name1=as.character(df1[,1]), name2=NA,
number1=df1[,2], number2=NA, matches=0,
stringsAsFactors=FALSE);
## store names of dataframe2 (to remember which strings have no match)
toMerge <- df2[, 1];
for (i in seq(along=names1))
for (j in seq(along=names2))
## set minimal match to 4 or to string length
minMatch <- min(4, length(names2[[j]]));
## find single matches
matches <- names1[[i]] %in% names2[[j]];
## look for consecutive matches
r <- rle(matches);
## any matches found?
if (any(r$values))
## find max consecutive match
possibleMatch <- r$value == TRUE;
maxPos <- which(which.max(r$length[possibleMatch]) & possibleMatch)[1];
## store max conscutive match length
maxMatch <- r$length[maxPos];
## to remove FALSE-POSITIVES (e.g. CCC and kcn) find
## largest substring
start <- sum(r$length[0:(maxPos-1)]) + 1;
stop <- start + r$length[maxPos] - 1;
maxSubStr <- substr(lowerNames1[i], start, stop);
## all matching criteria fulfilled
isConsecutiveMatch <- maxMatch >= minMatch &&
grepl(pattern=maxSubStr, x=lowerNames2[j], fixed=TRUE) &&
nchar(maxSubStr) > 0;
if (isConsecutiveMatch)
## merging
mergedDf[i, "matches"] <- maxMatch
mergedDf[i, "name2"] <- as.character(df2[j, 1]);
mergedDf[i, "number2"] <- df2[j, 2];
## don't append this row to mergedDf because already merged
toMerge[j] <- NA;
## stop inner for loop here to avoid possible second match
break;
## append not matched rows to mergedDf
toMerge <- which(df2[, 1] == toMerge);
df2 <- data.frame(name1=NA, name2=as.character(df2[toMerge, 1]),
number1=NA, number2=df2[toMerge, 2], matches=0,
stringsAsFactors=FALSE);
mergedDf <- rbind(mergedDf, df2);
return (mergedDf);
输出:
> uglyMerge(datf1, datf2)
name1 name2 number1 number2 matches
1 xxregular reGulr 1 2 5
2 kklmin <NA> 8 NA 0
3 notSo <NA> 9 NA 0
4 Jijoh Jijoh 2 12 5
5 Kish <NA> 18 NA 0
6 Lissp LiSsp 25 20 5
7 Kcn KcN 33 18 3
8 CCCa <NA> 8 NA 0
9 <NA> ntSo NA 8 0
10 <NA> sean NA 13 0
11 <NA> CaPN NA 13 0
【讨论】:
感谢您提供的出色解决方案。它适用于我提供的示例。但是我可能需要进一步调整,因为如果它不是前四个连续字母,它似乎不起作用,只需查看我的编辑,我在常规之前添加了附加 xxx,不匹配。不过,它会给我一个很好的开始,谢谢!! @hijo 抱歉,我的子字符串计算中有一些错误。请使用我的修改版本。 使用edit distance 来进行字符串匹配可能对您也很有价值。它在 R 中有implementation。【参考方案2】:agrep
将帮助您入门。
类似:
lapply(tolower(datf1$name), function(x) agrep(x, tolower(datf2$name)))
然后你可以调整max.distance
参数,直到你得到合适的匹配量。然后随意合并。
【讨论】:
以上是关于将数据与r中的部分匹配合并的主要内容,如果未能解决你的问题,请参考以下文章
在 Shiny 中使用部分 textInput 作为 R 中的变量