data.table中的str_match序列

时间:2013-05-06 20:22:35

标签: r data.table stringr

我有一个字符串变量可以解析成两部分。我想我会使用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  

(我花了很长时间才开始工作,尝试Vectorizeas.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个唯一值,所以我并不担心效率。

2 个答案:

答案 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并始终拥有它返回一个列表。)

注意:同一个软件包中的 strapplycstrapply 快,因为它的内容是用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)