regex - Partidos superpuestos en R
string dna-sequence (6)
He buscado y pude encontrar esta discusión en el foro para lograr el efecto de coincidencias superpuestas.
También encontré la siguiente pregunta SO que habla de encontrar índices para realizar esta tarea, pero no pude encontrar nada conciso sobre agarrar coincidencias superpuestas en el lenguaje R.
Puedo realizar esta tarea en la mayoría de los lenguajes que admiten ( PCRE ) mediante el uso de una aserción de Lookahead positivo al implementar un grupo de captura dentro del lookahead para capturar las coincidencias superpuestas.
Pero, si bien realizo esto de la misma manera que lo haría en otros idiomas, usando
perl=T
en R, no se obtienen resultados.
> x <- ''ACCACCACCAC''
> regmatches(x, gregexpr(''(?=([AC]C))'', x, perl=T))[[1]]
[1] "" "" "" "" "" "" ""
Lo mismo ocurre con el uso del paquete
stringi
y
stringr
.
> library(stringi)
> library(stringr)
> stri_extract_all_regex(x, ''(?=([AC]C))'')[[1]]
[1] "" "" "" "" "" "" ""
> str_extract_all(x, perl(''(?=([AC]C))''))[[1]]
[1] "" "" "" "" "" "" ""
Los resultados correctos que se deben devolver al ejecutar esto son:
[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
Editar
-
Soy consciente de que las
regmatches
no funcionan bien con las coincidencias capturadas, pero ¿ qué causa exactamente este comportamiento en las coincidencias y por qué no se devuelven resultados? Estoy buscando una respuesta algo detallada . -
¿
stringr
paquetestringi
ystringr
no es capaz de realizar esto sobre lasregmatches
? -
Siéntase libre de agregar a mi respuesta o proponer una solución alternativa diferente a la que he encontrado.
En cuanto a una solución, esto es lo que se me ocurrió para extraer las coincidencias superpuestas.
> x <- ''ACCACCACCAC''
> m <- gregexpr(''(?=([AC]C))'', x, perl=T)
> mapply(function(X) substr(x, X, X+1), m[[1]])
[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
No dude en agregar o comentar una mejor manera de realizar esta tarea.
Las
regmatches
estándar no funcionan bien con coincidencias capturadas (específicamente, varias coincidencias capturadas en la misma cadena).
Y en este caso, dado que está "haciendo coincidir" una mirada hacia adelante (ignorando la captura), la coincidencia en sí es de longitud cero.
También hay una función
regmatches()<-
que puede ilustrar esto.
Obtener
x <- ''ACCACCACCAC''
m <- gregexpr(''(?=([AC]C))'', x, perl=T)
regmatches(x, m) <- "~"
x
# [1] "~A~CC~A~CC~A~CC~AC"
Observe cómo se conservan todas las letras, acabamos de reemplazar las ubicaciones de las coincidencias de longitud cero con algo que podemos observar.
He creado una función regcapturedmatches() que a menudo uso para tales tareas. Por ejemplo
x <- ''ACCACCACCAC''
regcapturedmatches(x, gregexpr(''(?=([AC]C))'', x, perl=T))[[1]]
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
gregexpr
está tomando todos los datos muy bien para que pueda extraerlos de ese objeto de todos modos si prefiere no usar esta función auxiliar.
No es una solución de expresiones regulares, y realmente no responde a ninguna de sus preguntas más importantes, pero también puede obtener el resultado deseado utilizando las subcadenas de dos caracteres a la vez y luego eliminando los elementos de
CA
no deseados.
x <- ''ACCACCACCAC''
y <- substring(x, 1:(nchar(x)-1), 2:nchar(x))
y[y != "CA"]
# [1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
Otra forma indirecta de extraer la misma información que he hecho en el pasado es reemplazar
"match.length"
por
"capture.length"
:
x <- c("ACCACCACCAC","ACCACCACCAC")
m <- gregexpr(''(?=([AC]C))'', x, perl=TRUE)
m <- lapply(m, function(i) {
attr(i,"match.length") <- attr(i,"capture.length")
i
})
regmatches(x,m)
#[[1]]
#[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
#
#[[2]]
#[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
Una respuesta adicional, basada en la propia respuesta de @ hwnd (el original no permitía regiones capturadas de longitud variable), usando solo funciones R incorporadas:
> x <- ''ACCACCACCAC''
> m <- gregexpr(''(?=([AC]C))'', x, perl=T)[[1]]
> start <- attr(m,"capture.start")
> end <- attr(m,"capture.start") + attr(m,"capture.length") - 1
> sapply(seq_along(m), function(i) substr(x, start[i], end[i]))
[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
Bastante feo, por eso existen los paquetes
stringr
etc.
Una solución
stringi
que usa un grupo de captura en la parte de anticipación:
> stri_match_all_regex(''ACCACCACCAC'', ''(?=([AC]C))'')[[1]][,2]
## [1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"