我有一个字符串变量可以解析成两部分。我想我会使用str_match
包中的stringr
来解决这个问题,该包会返回第一列中包含原始字符串的矩阵,而其他列中的每个提取部分都会返回。
我找到了十几个正则表达式来提取这两个部分。 (这些部分是一个阶梯,并按工资计划进行调整,而且非常混乱。我已经通过使用一堆嵌套的ifelse
语句定义函数来验证我的正则表达式是有效的。)
library(stringr)
library(data.table)
my_strs <- c("A 01","G 00","A 2")
mydt <- data.table(strs = my_strs)
rx1 <- '^([[:alpha:]] )([[:digit:]]{2})$'
rx2 <- '(A) ([[:digit:]])'
我想按顺序检查正则表达式,并使用第一个检出的部分提取部分。如果我只有一个正则表达式,我可以这样做:
myfun <- function(x){
y <- str_match(x,rx1)
return(y)
}
mydt[,myfun(strs)]
# [,1] [,2] [,3]
# [1,] "A 01" "A " "01"
# [2,] "G 00" "G " "00"
# [3,] NA NA NA
(我花了很长时间才开始工作,尝试Vectorize
和as.list
的所有组合以及电话中的*apply
。
我按顺序检查正则表达式的最佳尝试是这个相当难看的kludge:
myfun2 <- function(x){
y <- str_match(x,rx1)
ifelse(!is.na(y[1]),"",(y <- str_match(x,rx2))[1])
return(y)
}
mydt[1:2,myfun2(strs)]
# [,1] [,2] [,3]
# [1,] "A 01" "A " "01"
# [2,] "G 00" "G " "00"
mydt[3,myfun2(strs)]
# [,1] [,2] [,3]
# [1,] "A 2" "A" "2"
mydt[1:3,myfun2(strs)]
# [,1] [,2] [,3]
# [1,] "A 01" "A " "01"
# [2,] "G 00" "G " "00"
# [3,] NA NA NA
如你所见,它还没有完全奏效。
您是否有更好的方法来解决这个问题?我的数据集中有大约3.5米的行,但是这个字符串只有大约2000个唯一值,所以我并不担心效率。
答案 0 :(得分:1)
使用gsubfn package中的strapply
尝试此操作。我们定义一个接受匹配的函数,并返回前两个非空的函数。然后将其与正则表达式paste(rx1, rx2, sep = "|")
一起用于my_str
的每个组件:
library(gsubfn)
# test data
# there was an addition to the question in the comments. It asked to be able to handle
# one regular expression which has only a single capture. Make sure its at the end.
rx3 <- "^([[:digit:]]{2})$"
my_strs2 <- c(my_strs, "99")
# code
first2 <- function(...) { x <- c(..., NA); head(x[x != ""], 2) }
strapply(my_strs2, paste(rx1, rx2, rx3, sep = "|"), first2, simplify = TRUE)
最后一行返回:
[,1] [,2] [,3] [,4]
[1,] "A " "G " "A" "99"
[2,] "01" "00" "2" NA
(如果有my_strs
的组件根本不匹配,那么将返回一个列表,其中这些组件为NULL。在这种情况下,您可能更愿意放弃simplify = TRUE
并始终拥有它返回一个列表。)
注意:同一个软件包中的 strapplyc
比strapply
多快,因为它的内容是用tcl编写的(字符串处理)语言)而strapply
是用R编写的。因此你可能希望以这种方式将其分解以利用更快的例程:
L <- strapplyc(my_strs2, paste(rx1, rx2, rx3, sep = "|"))
sapply(L, first2)
答案 1 :(得分:0)
对于子孙后代,这是我今天发现的另一种解决方案:
mydt[,{
i_rx <- min(which(unlist(sapply(rx_list,function(x)grepl(x,strs)))))
as.list(str_match(strs,rx_list[[i_rx]]))
},by=1:nrow(mydt)]
我对正则表达式进行了一些小的修改并将它们放在一个列表中。
rx1 <- '^([[:alpha:]] )([[:digit:]]{2})$'
rx2a <- "^(A) ([[:digit:]])$"
rx3a <- "^()([[:digit:]]{2})$"
rx_list <- list(rx1,rx2a,rx3a)