R:搜索字符串SIMILAR并使用条件符号返回

时间:2011-06-29 17:00:34

标签: r similarity

我的df有以下条目:

A
xxx
xxx
xxx1
xx1x
yyyy
gggg

我想基于以下条件,基于A列的相似性将符号添加到我的df的B列。

  • 我将阈值设置为=或> 75% 类似。

  • 列A已经排序。所以, 检查以上ONE的相似性 需要。

  • 如果上面的一个是相似的,那么符号 将从上一栏的B栏复制。

  • 如果上一个不同,那么 符号将从同一行的A列复制。

例如,第1行和第2行是相同的。它们的符号与A列相同。第3行是(4个字母中的3个字母具有相同的字母且序列相同)75%与row1和row2相似。 B列中的sybmol将从上面复制,即xxx。由于xx1x(row4)只是4个字母中的2个,类似于row3,因此它只使用自己的符号,即xx1x。由于yyyy和gggg完全不同,他们将保留自己的sybmol,如A列所示。

因此,我的最终结果应该是这样的:

A      B
xxx    xxx
xxx    xxx
xxx1   xxx
xx1x   xx1x
yyyy   yyyy
gggg   gggg

我通过猜测得出这个相似度%(如果有正式的字符串相似性搜索方法则不需要使用),如果有任何形式的方法来检查R中的字符串相似性,那么它可能很好用。

您能否介意如何使用R?

有效地添加此符号列

2 个答案:

答案 0 :(得分:6)

设置数据:

x=c("xxx", "xxx", "xxx1", "xx1x", "yyyy", "gggg")

代码:

same <- sapply(seq(length(x)-1), 
  function(i)any(agrep(x[i+1], x[1], max.distance=0.25)))
ex <- embed(x, 2)
cbind(A=x, B=c(x[1], ifelse(same, ex[, 2], ex[, 1])))

结果:

     A      B     
[1,] "xxx"  "xxx" 
[2,] "xxx"  "xxx" 
[3,] "xxx1" "xxx" 
[4,] "xx1x" "xxx1"
[5,] "yyyy" "yyyy"
[6,] "gggg" "gggg"

为什么会这样?

一些关键概念和真正有用的功能:

首先,agrep使用Levenshtein edit distance测试字符串的相似程度,它有效地计算将一个字符串转换为另一个字符串所需的单个字符更改的数量。参数max.distance=0.25表示允许25%的模式字符串不同。

例如,测试任何原始字符串是否与“xxx”类似:返回1:4:

agrep("xxx", x, max.distance=0.25)
[1] 1 2 3 4

其次,embed提供了测试滞后变量的有用方法。例如,embed(x, 2) turns x`为滞后数组。这使得比较x [1]和x [2]变得容易,因为它们现在位于数组的同一行:

embed(x, 2)
     [,1]   [,2]  
[1,] "xxx"  "xxx" 
[2,] "xxx1" "xxx" 
[3,] "xx1x" "xxx1"
[4,] "yyyy" "xx1x"
[5,] "gggg" "yyyy"

最后,我使用cbind和矢量子集将原始矢量和新矢量拼接在一起。


为了使这个工作在数据框而不是矢量上,我将代码转换为如下函数:

df <- data.frame(A=c("xxx", "xxx", "xxx1", "xx1x", "yyyy", "gggg"))

f <- function(x){
  x <- as.vector(x)
  same <- sapply(seq(length(x)-1), 
      function(i)any(agrep(x[i+1], x[1], max.distance=0.25)))
  ex <- embed(x, 2)
  c(x[1], ifelse(same, ex[, 2], ex[, 1]))
}
df$B <- f(df$A)
df

     A    B
1  xxx  xxx
2  xxx  xxx
3 xxx1  xxx
4 xx1x xxx1
5 yyyy yyyy
6 gggg gggg

答案 1 :(得分:0)

这是一个更“基本”的解决方案(编辑以解决评论中提出的一些问题):

dat <- data.frame(A=c('xxx','xxx','xxx1','xx1x','yyyy','gggg'))
dat$B <- rep(NA,nrow(dat))

tmp <- strsplit(as.character(dat$A),"")
dat$B[1] <- dat$A[1]
for (i in 2:length(tmp)){
    n <- min(length(tmp[[i]]),length(tmp[[i-1]]))
    x <- sum(tmp[[i]][1:n] == tmp[[i-1]][1:n]) / length(tmp[[i]])
    if (x >= 0.75){
        dat$B[i] <- paste(tmp[[i-1]],collapse="")
    }
    else{ dat$B[i] <- paste(tmp[[i]],collapse="")}
}