R中的部分字符串匹配并修剪字符
这是一个数据帧和一个向量R中的部分字符串匹配并修剪字符,r,string-matching,fuzzy-search,agrep,fuzzyjoin,R,String Matching,Fuzzy Search,Agrep,Fuzzyjoin,这是一个数据帧和一个向量 df1 <- tibble(var1 = c("abcd", "efgh", "ijkl", "mnopqr", "qrst")) vec <- c("ab", "mnop", "ijk") 这里有一个两步解决方案。首先是一个函数,它对前n个字符进行模糊匹配和替换。它运行agrepl,将输入模式与提供的向量相匹配,并保持第一个n字符(如果匹配)。如果没有匹配项,则返回NA。这是为了通过lappy应用于模式向量,因此第二个函数用于Reduce将其转换为
df1 <- tibble(var1 = c("abcd", "efgh", "ijkl", "mnopqr", "qrst"))
vec <- c("ab", "mnop", "ijk")
这里有一个两步解决方案。首先是一个函数,它对前n个字符进行模糊匹配和替换。它运行
agrepl
,将输入模式与提供的向量相匹配,并保持第一个n
字符(如果匹配)。如果没有匹配项,则返回NA
。这是为了通过lappy
应用于模式向量,因此第二个函数用于Reduce
将其转换为一个向量reducer
接受两个长度相同的向量,并用第二个向量的非缺失值替换第一个向量的所有实例,其中第二个向量不是NA
这一切都会在几个调用中完成,并根据需要返回向量
fuzzy_match_and_replace = function(pattern, vector, n = 3){
n = min(c(n,nchar(pattern)))
match = agrepl(pattern,vector)
pattern_first_n = substr(pattern,1,n)
vector_first_n = substr(vector,1,n)
output = rep(NA,length(vector))
output[match & pattern_first_n == vector_first_n] = pattern_first_n
return(output)
}
reducer = function(a,b){
a[!is.na(b)] = b[!is.na(b)]
return(a)
}
df1 <- data.frame(var1 = c("abcd", "efgh", "ijkl", "mnopqr", "qrst"), stringsAsFactors = FALSE)
vec <- c("ab", "mnop", "ijk")
Reduce(reducer,lapply(vec,fuzzy_match_and_replace,vector=df1$var1),init=df1$var1)
#> [1] "ab" "efgh" "ijk" "mno" "qrst"
更新
这里有一个更简单的函数(1步),它利用了Onyanbu答案中的adist
,但不依赖max.col
,而是使用vapply
遍历矩阵,识别匹配项并进行替换
fuzzy_match_and_replace = function(pattern, vector, n = 3, ...){
matches = adist(pattern,vector,partial=T,...) == 0
replace = vapply(apply(matches,2,which)
,function(x){
if(length(x) > 0) return(substr(pattern,1,n)[x]) else return(NA_character_)
}
,FUN.VALUE = c(""))
vector[!is.na(replace)] = replace[!is.na(replace)]
return(vector)
}
library(dplyr)
df1 <- tibble(var1 = c("abcd", "efgh", "ijkl", "mnopqr", "qrst","mnopr"))
vec <- c("ab", "mnop", "ijk")
df1%>%
mutate(var1=fuzzy_match_and_replace(vec,var1))
#> # A tibble: 6 x 1
#> var1
#> <chr>
#> 1 ab
#> 2 efgh
#> 3 ijk
#> 4 mno
#> 5 qrst
#> 6 mno
模糊匹配和替换=函数(模式,向量,n=3,…){
匹配=adist(模式,向量,部分=T,…)=0
替换=vapply(应用(匹配,2,其中)
,函数(x){
if(length(x)>0)返回(substr(pattern,1,n)[x]),否则返回(NA_字符)
}
,FUN.VALUE=c(“”)
向量[!is.na(替换)]=替换[!is.na(替换)]
返回(向量)
}
图书馆(dplyr)
df1#A tible:6 x 1
#>var1
#>
#>1 ab
#>2 efgh
#>3 ijk
#>4 mno
#>5QRST
#>6 mno
df1%
突变(var1=replace(var1,a[,2],substr(vec[a[,1]],1,3)))
#一个tibble:6x1
var1
1 ab
2 efgh
3 ijk
4 mno
5QRST
6 mno
如果值“tmnop”在var1中,它会返回“mno”还是什么都不返回呢?如果“nope”在var1中,它会返回nop
还是什么都不返回呢?我想匹配前n个字符。所以tmnop和nope应该像efgh一样返回tmnop和nope。感谢您的详细解释和创建如此复杂的函数来解决这个问题!伟大的您还可以使其不区分大小写吗?在第二个函数中,您可以通过..
将参数传递给adist
,因此只需添加ignore.case=TRUEIt就可以使其更加灵活。我无法再现真实数据,但上面的方法给了我以下错误:列var1的长度必须为2(组大小)或1,而不是4。知道问题出在哪里吗?知道怎么解决吗?真有趣!您是否也可以将其设置为不区分大小写和另一个变量,例如var2?因此,AB应该与AB匹配,依此类推。@Geet在函数中adist(…,ignore.case=TRUE)
包括ignore.case=TRUE
甚至在grep
函数中,您可以包括ignore.case=TRUE
这将使其不区分大小写此解决方案适用于给定示例,但在df1中有更多匹配项时会出现故障,例如df1@Onyambu:我将无法再现真实数据,但上述方法给了我以下错误:列var1必须是长度2(组大小)或1,不是1166955。知道问题出在哪里以及如何解决吗?@Geet您的数据已分组。你需要把它解组。iedf1%%>%ungroup()%%>%mutate(…)
fuzzy_match_and_replace = function(pattern, vector, n = 3){
n = min(c(n,nchar(pattern)))
match = agrepl(pattern,vector)
pattern_first_n = substr(pattern,1,n)
vector_first_n = substr(vector,1,n)
output = rep(NA,length(vector))
output[match & pattern_first_n == vector_first_n] = pattern_first_n
return(output)
}
reducer = function(a,b){
a[!is.na(b)] = b[!is.na(b)]
return(a)
}
df1 <- data.frame(var1 = c("abcd", "efgh", "ijkl", "mnopqr", "qrst"), stringsAsFactors = FALSE)
vec <- c("ab", "mnop", "ijk")
Reduce(reducer,lapply(vec,fuzzy_match_and_replace,vector=df1$var1),init=df1$var1)
#> [1] "ab" "efgh" "ijk" "mno" "qrst"
wrapper = function(pattern, vector, n = 3){
Reduce(reducer,lapply(pattern,fuzzy_match_and_replace,vector=vector,n=n),init=vector)
}
fuzzy_match_and_replace = function(pattern, vector, n = 3, ...){
matches = adist(pattern,vector,partial=T,...) == 0
replace = vapply(apply(matches,2,which)
,function(x){
if(length(x) > 0) return(substr(pattern,1,n)[x]) else return(NA_character_)
}
,FUN.VALUE = c(""))
vector[!is.na(replace)] = replace[!is.na(replace)]
return(vector)
}
library(dplyr)
df1 <- tibble(var1 = c("abcd", "efgh", "ijkl", "mnopqr", "qrst","mnopr"))
vec <- c("ab", "mnop", "ijk")
df1%>%
mutate(var1=fuzzy_match_and_replace(vec,var1))
#> # A tibble: 6 x 1
#> var1
#> <chr>
#> 1 ab
#> 2 efgh
#> 3 ijk
#> 4 mno
#> 5 qrst
#> 6 mno
df1 <- tibble(var1 = c("abcd", "efgh", "ijkl", "mnopqr", "qrst","mnopr"))
a = which(adist(vec,df1$var1,partial = T,ignore.case = T)==0,T)
df1%>%
mutate(var1=replace(var1,a[,2],substr(vec[a[,1]],1,3)))
# A tibble: 6 x 1
var1
<chr>
1 ab
2 efgh
3 ijk
4 mno
5 qrst
6 mno