Regex 向量化模式匹配返回R中的模式

Regex 向量化模式匹配返回R中的模式,regex,r,Regex,R,我的问题主要是效率问题 我有一个要与向量x匹配的模式向量 最终结果应该返回与向量的每个元素匹配的模式。第二个标准是,如果为向量x的特定元素匹配了许多模式,则返回匹配的第一个模式 例如,假设模式向量为: patterns <- c("[0-9]{2}[a-zA-Z]", "[0-9][a-zA-Z] ", " [a-zA-Z]{3} ") 最终结果将是: customeRExp(patterns, x) [1] "[0-9]{2}[a-zA-Z]" " [a-zA-Z]{3} " [3]

我的问题主要是效率问题

我有一个要与向量
x
匹配的模式向量

最终结果应该返回与向量的每个元素匹配的模式。第二个标准是,如果为向量
x
的特定元素匹配了许多模式,则返回匹配的第一个模式

例如,假设模式向量为:

patterns <- c("[0-9]{2}[a-zA-Z]", "[0-9][a-zA-Z] ", " [a-zA-Z]{3} ")
最终结果将是:

customeRExp(patterns, x)
[1] "[0-9]{2}[a-zA-Z]" " [a-zA-Z]{3} "
[3]  NA                "[0-9]{2}[a-zA-Z]"
[5] "[0-9][a-zA-Z] "
这就是我到目前为止所做的:

customeRExp <- function(pattern, x){
                        m <- matrix(NA, ncol=length(x), nrow=length(pattern))
                        for(i in 1:length(pattern)){
                            m[i, ] <- grepl(pattern[i], x)}
                        indx <- suppressWarnings(apply(m, 2, function(y) min(which(y, TRUE))))
                        pattern[indx]
}

customeRExp(patterns, x)
问题是我的数据集很大,模式列表也很大

有没有更有效的方法来做同样的事情

library(purrr) 
library(stringr)
bool_results <- x %>% map(str_detect, patterns)
要提取与哪个布尔值关联的模式,可以

lapply(bool_results, function(x) patterns[which(x == TRUE)])

[[1]]
[1] "[0-9]{2}[a-zA-Z]"

[[2]]
character(0)

[[3]]
character(0)

[[4]]
[1] "[0-9]{2}[a-zA-Z]" "[0-9][a-zA-Z] "  

[[5]]
[1] "[0-9][a-zA-Z] "
要提取与哪个布尔值关联的模式,可以

lapply(bool_results, function(x) patterns[which(x == TRUE)])

[[1]]
[1] "[0-9]{2}[a-zA-Z]"

[[2]]
character(0)

[[3]]
character(0)

[[4]]
[1] "[0-9]{2}[a-zA-Z]" "[0-9][a-zA-Z] "  

[[5]]
[1] "[0-9][a-zA-Z] "

<>我的加速方法类似于上面的方法,通常只是在C++中改写。下面是使用Boost Xpression的快速尝试:

// [[Rcpp::depends(BH)]]
#include <Rcpp.h>
#include <boost/xpressive/xpressive.hpp>

namespace xp = boost::xpressive;

// [[Rcpp::export]]
Rcpp::CharacterVector
first_match(Rcpp::CharacterVector x, Rcpp::CharacterVector re) {
    R_xlen_t nx = x.size(), nre = re.size(), i = 0, j = 0;
    Rcpp::CharacterVector result(nx, NA_STRING);
    std::vector<xp::sregex> vre(nre);

    for ( ; j < nre; j++) {
        vre[j] = xp::sregex::compile(std::string(re[j]));
    }

    for ( ; i < nx; i++) {
        for (j = 0; j < nre; j++) {
            if (xp::regex_search(std::string(x[i]), vre[j])) {
                result[i] = re[j];
                break;
            }
        }
    }

    return result;
} 


另一个选择是考虑使用
stringi
包,它通常比base R好很多

> P> >我的加速循环方法类似于通常只在C++中重写。下面是使用Boost Xpression的快速尝试:

// [[Rcpp::depends(BH)]]
#include <Rcpp.h>
#include <boost/xpressive/xpressive.hpp>

namespace xp = boost::xpressive;

// [[Rcpp::export]]
Rcpp::CharacterVector
first_match(Rcpp::CharacterVector x, Rcpp::CharacterVector re) {
    R_xlen_t nx = x.size(), nre = re.size(), i = 0, j = 0;
    Rcpp::CharacterVector result(nx, NA_STRING);
    std::vector<xp::sregex> vre(nre);

    for ( ; j < nre; j++) {
        vre[j] = xp::sregex::compile(std::string(re[j]));
    }

    for ( ; i < nx; i++) {
        for (j = 0; j < nre; j++) {
            if (xp::regex_search(std::string(x[i]), vre[j])) {
                result[i] = re[j];
                break;
            }
        }
    }

    return result;
} 


另一个选择是考虑使用
stringi
包,它通常比base R好很多

在概念上类似于nrussell的方法,我们可以丢弃从以下
grep
s匹配的“x”元素:

ff = function(x, p)
{
    ans = rep_len(NA_integer_, length(x))
    for(i in seq_along(p)) {
        nas = which(is.na(ans))
        ans[nas[grepl(p[i], x[nas])]] = i
    }
    p[ans]    
}
ff(x, patterns)
#[1] "[0-9]{2}[a-zA-Z]" " [a-zA-Z]{3} "    NA                 "[0-9]{2}[a-zA-Z]" "[0-9][a-zA-Z] "
在每次迭代中对“x”进行子集设置可能比看起来更昂贵,特别是如果子集设置最终只忽略了少量“x”元素,在这种情况下,我们最终复制了一个大“x”(少部分元素更短),但仍然是一个大“x”。但是,如果(1)“x”的很大一部分确实存在匹配,并且(2)如果“x”的很大一部分在每次(可能是早期)迭代中都匹配,那么它可能会更有效。使用nrussell的例子,我们有这样一种情况,实际上,“x”的许多元素在每次迭代中都沿着“模式”被丢弃:


即使在边缘情况下,nrussell的方法仍能完成所需的最小工作(其他两种方法将增加更多的计算时间)。

概念上与nrussell的方法类似,我们可以丢弃从以下
grep
s匹配的“x”元素:

ff = function(x, p)
{
    ans = rep_len(NA_integer_, length(x))
    for(i in seq_along(p)) {
        nas = which(is.na(ans))
        ans[nas[grepl(p[i], x[nas])]] = i
    }
    p[ans]    
}
ff(x, patterns)
#[1] "[0-9]{2}[a-zA-Z]" " [a-zA-Z]{3} "    NA                 "[0-9]{2}[a-zA-Z]" "[0-9][a-zA-Z] "
在每次迭代中对“x”进行子集设置可能比看起来更昂贵,特别是如果子集设置最终只忽略了少量“x”元素,在这种情况下,我们最终复制了一个大“x”(少部分元素更短),但仍然是一个大“x”。但是,如果(1)“x”的很大一部分确实存在匹配,并且(2)如果“x”的很大一部分在每次(可能是早期)迭代中都匹配,那么它可能会更有效。使用nrussell的例子,我们有这样一种情况,实际上,“x”的许多元素在每次迭代中都沿着“模式”被丢弃:


nrussell的方法即使在边缘情况下也能完成所需的最小工作量(其他两种方法会增加更多的计算时间)。

类似的情况<代码>库(purrr);图书馆(stringr);x%>%map(str_detect,patterns)将输出一个
长度(x)
列表,每个列表的布尔向量长度(patterns)。结果的第二个元素不应该是
NA
?也就是说,
“[a-zA-Z]{3}”
“abc 123 abc”
@nrussell如果你是对的,我会更正这个问题。thnx如果你真的能在内存中保存
length(x)*length(patterns)
结构,你就可以避免
length(x)
调用
apply
,方法稍有不同,比如
patterns[do.call(pmin,c(na.rm=TRUE,Map(“*”,seq\u-along(patterns),lappy(patterns,function(p){g=grepl(p,x);is.na(g)=!g;g}))]
。像这样的吗<代码>库(purrr);图书馆(stringr);x%>%map(str_detect,patterns)将输出一个
长度(x)
列表,每个列表的布尔向量长度(patterns)。结果的第二个元素不应该是
NA
?也就是说,
“[a-zA-Z]{3}”
“abc 123 abc”
@nrussell如果你是对的,我会更正这个问题。thnx如果你真的能在内存中保存
length(x)*length(patterns)
结构,你就可以避免
length(x)
调用
apply
,方法稍有不同,比如
patterns[do.call(pmin,c(na.rm=TRUE,Map(“*”,seq\u-along(patterns),lappy(patterns,function(p){g=grepl(p,x);is.na(g)=!g;g}))]
microbenchmark::microbenchmark(ff(x2, p2), first_match(x2, p2), customeRExp(p2, x2), times = 25)
#Unit: milliseconds
                expr       min        lq      mean    median        uq       max neval cld
#          ff(x2, p2)  299.7235  306.0875  312.9303  308.0544  320.6126  333.9144    25 a  
# first_match(x2, p2) 1581.4085 1606.3984 1642.4471 1643.0671 1661.9499 1734.9066    25  b 
# customeRExp(p2, x2) 3464.4267 3515.7499 3623.0920 3611.0809 3694.3931 3849.0399    25   c

all.equal(ff(x2, p2), customeRExp(p2, x2))
#[1] TRUE
all.equal(ff(x2, p2), first_match(x2, p2))
#[1] TRUE