Prueba si los caracteres están en una cadena

279

Estoy tratando de determinar si una cadena es un subconjunto de otra cadena. Por ejemplo:

chars <- "test"
value <- "es"

Quiero devolver VERDADERO si "valor" aparece como parte de la cadena "caracteres". En el siguiente escenario, me gustaría devolver falso:

chars <- "test"
value <- "et"
Miguel
fuente
12
La respuesta aceptada es incorrecta, debe agregarla fixed=TRUE, de lo contrario, la trata como una expresión regular en lugar de una cadena. Vea mi respuesta de octubre de 2016.
Joshua Cheek
@JoshuaCheek A menos que tenga caracteres especiales en su patrón, regex devolverá el mismo resultado que el fijo.
user3932000
1
Claro, pero solo puedes saber eso si lo estás pasando literalmente. De lo contrario, no sabrá qué caracteres hay en el patrón, por lo que puede usar fixed=TRUEo tiene un error que desordenará sus datos de manera silenciosa y sutil.
Joshua Cheek

Respuestas:

388

Usa la greplfunción

grepl(value, chars, fixed = TRUE)
# TRUE

Use ?greplpara descubrir más.

smu
fuente
8
Para este caso simple, agregar fixed = TRUE puede mejorar el rendimiento (suponiendo que va a hacer muchos de estos cálculos).
Greg Snow
1
@ Josh O'Brien, puesto que en comparación hallazgo (contando) todos los partidos en una sola cadena larga, trate de encontrar 1 partido en un montón de cadenas más cortas: vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') ).
Greg Snow
2
@GregSnow - Probado system.time(a <- grepl("abc", vec))y system.time(a <- grepl("abc", vec, fixed=TRUE)), y fixed=TRUEsigue siendo, si algo algo más lento. La diferencia no es apreciable con estas cadenas cortas, pero fixed=TRUEaún así no parece ser más rápida. Gracias por señalar, sin embargo, que son las cuerdas largas las que fixed=TRUEtoman el golpe real.
Josh O'Brien el
2
grepl (patrón, x) al menos en 2017
JMR
2
Esta no debería ser la respuesta aceptada, porque el valor se interpretará como un patrón regex. fixed = TRUE siempre debe usarse a menos que sepa que la cadena que está buscando no se verá como un patrón regex. La respuesta de Joshua Creek a continuación tiene una explicación muy clara de esto, y debería ser la respuesta aceptada.
bhaller
159

Responder

Suspiro, me tomó 45 minutos encontrar la respuesta a esta simple pregunta. La respuesta es:grepl(needle, haystack, fixed=TRUE)

# Correct
> grepl("1+2", "1+2", fixed=TRUE)
[1] TRUE
> grepl("1+2", "123+456", fixed=TRUE)
[1] FALSE

# Incorrect
> grepl("1+2", "1+2")
[1] FALSE
> grepl("1+2", "123+456")
[1] TRUE

Interpretación

greplleva el nombre del ejecutable de Linux, que en sí es un acrónimo de " G lobal R egular E Xpression P rint", se leería las líneas de entrada y luego imprimirlos si coincide con los argumentos que diste. "Global" significa que la coincidencia podría ocurrir en cualquier lugar de la línea de entrada, explicaré "Expresión regular" a continuación, pero la idea es que es una forma más inteligente de hacer coincidir la cadena (R llama a este "carácter", por ejemplo class("abc")) e "Imprimir "debido a que es un programa de línea de comando, la emisión de salida significa que se imprime en su cadena de salida.

Ahora, el grepprograma es básicamente un filtro, desde líneas de entrada hasta líneas de salida. Y parece que la grepfunción de R de manera similar tomará una serie de entradas. Por razones completamente desconocidas para mí (solo comencé a jugar con R hace aproximadamente una hora), devuelve un vector de los índices que coinciden, en lugar de una lista de coincidencias.

Pero, volviendo a su pregunta original, lo que realmente queremos es saber si encontramos la aguja en el pajar, un valor verdadero / falso. Al parecer, decidieron llamar a esta función grepl, como en "grep" pero con una " L valor de retorno gico" (que ellos llaman valores lógicos verdadero y falso, por ejemplo class(TRUE)).

Entonces, ahora sabemos de dónde viene el nombre y qué se supone que debe hacer. Volvamos a las expresiones regulares. Los argumentos, aunque son cadenas, se usan para construir expresiones regulares (en adelante: regex). Una expresión regular es una forma de hacer coincidir una cadena (si esta definición te irrita, déjalo ir). Por ejemplo, la expresión regular acoincide con el carácter "a", la expresión regular a*coincide con el carácter "a"0 o más veces, y la expresión regular a+coincidirá con el carácter "a"1 o más veces. Por lo tanto, en el ejemplo anterior, la aguja que estamos buscando 1+2, cuando se trata como una expresión regular, significa "uno o más 1 seguido de un 2" ... ¡pero al nuestro le sigue un signo más!

1 + 2 como una expresión regular

Entonces, si usó la greplconfiguración sin configuración fixed, sus agujas accidentalmente serían pajares, y eso funcionaría accidentalmente con bastante frecuencia, podemos ver que incluso funciona para el ejemplo del OP. Pero eso es un error latente! Necesitamos decirle que la entrada es una cadena, no una expresión regular, que aparentemente fixedes para lo que sirve. ¿Por qué arreglado? No tengo idea, marque esta respuesta b / c, probablemente tendrá que buscarla 5 veces más antes de memorizarla.

Algunas reflexiones finales

Cuanto mejor sea su código, menos historia tendrá que saber para darle sentido. Cada argumento puede tener al menos dos valores interesantes (de lo contrario, no necesitaría ser un argumento), los documentos enumeran 9 argumentos aquí, lo que significa que hay al menos 2 ^ 9 = 512 formas de invocarlo, eso es mucho trabajo para escribir, probar y recordar ... desacoplar tales funciones (dividirlas, eliminar dependencias entre sí, las cosas de cadena son diferentes a las cosas de expresiones regulares son diferentes a las cosas de vectores). Algunas de las opciones también son mutuamente excluyentes, no brindan a los usuarios formas incorrectas de usar el código, es decir, la invocación problemática debe ser estructuralmente sin sentido (como pasar una opción que no existe), no lógicamente sin sentido (donde debe emitir una advertencia para explicarlo). Poner metafóricamente: reemplazar la puerta de entrada en el costado del décimo piso con una pared es mejor que colgar un letrero que advierte contra su uso, pero cualquiera de los dos es mejor que ninguno. En una interfaz, la función define cómo deberían verse los argumentos, no la persona que llama (porque la persona que llama depende de la función, inferir todo con lo que todos quieran llamarla hace que la función también dependa de las personas que llaman, y este tipo de dependencia cíclica obstruirá rápidamente un sistema y nunca proporcionará los beneficios que espera). Tenga mucho cuidado con los tipos equívocos, es un defecto de diseño que cosas como inferir todo con lo que todos quieran llamarlo hace que la función también dependa de las personas que llaman, y este tipo de dependencia cíclica obstruirá rápidamente un sistema y nunca proporcionará los beneficios que espera). Tenga mucho cuidado con los tipos equívocos, es un defecto de diseño que cosas como inferir todo con lo que todos quieran llamarlo hace que la función también dependa de las personas que llaman, y este tipo de dependencia cíclica obstruirá rápidamente un sistema y nunca proporcionará los beneficios que espera). Tenga mucho cuidado con los tipos equívocos, es un defecto de diseño que cosas comoTRUEy 0y "abc"son todos vectores.

Joshua Cheek
fuente
66
Saludos por su explicación! Parece que R evolucionó durante un largo período de tiempo y está atascado con algunas elecciones de diseño extrañas (ver, por ejemplo, respuestas a esta pregunta sobre tipos de valores ). Sin embargo, devolver un vector de índices de coincidencia parece apropiado en este caso, al igual grepque filtrar filas, no celdas.
krevelen
44
"fijo" se refiere a los caracteres que coinciden con una secuencia "fija".
Will Beason
32

Tu quieres grepl:

> chars <- "test"
> value <- "es"
> grepl(value, chars)
[1] TRUE
> chars <- "test"
> value <- "et"
> grepl(value, chars)
[1] FALSE
Justin
fuente
27

Use esta función del stringipaquete:

> stri_detect_fixed("test",c("et","es"))
[1] FALSE  TRUE

Algunos puntos de referencia:

library(stringi)
set.seed(123L)
value <- stri_rand_strings(10000, ceiling(runif(10000, 1, 100))) # 10000 random ASCII strings
head(value)

chars <- "es"
library(microbenchmark)
microbenchmark(
   grepl(chars, value),
   grepl(chars, value, fixed=TRUE),
   grepl(chars, value, perl=TRUE),
   stri_detect_fixed(value, chars),
   stri_detect_regex(value, chars)
)
## Unit: milliseconds
##                               expr       min        lq    median        uq       max neval
##                grepl(chars, value) 13.682876 13.943184 14.057991 14.295423 15.443530   100
##  grepl(chars, value, fixed = TRUE)  5.071617  5.110779  5.281498  5.523421 45.243791   100
##   grepl(chars, value, perl = TRUE)  1.835558  1.873280  1.956974  2.259203  3.506741   100
##    stri_detect_fixed(value, chars)  1.191403  1.233287  1.309720  1.510677  2.821284   100
##    stri_detect_regex(value, chars)  6.043537  6.154198  6.273506  6.447714  7.884380   100
bartektartanus
fuente
22

Además, se puede hacer usando la biblioteca "stringr":

> library(stringr)
> chars <- "test"
> value <- "es"
> str_detect(chars, value)
[1] TRUE

### For multiple value case:
> value <- c("es", "l", "est", "a", "test")
> str_detect(chars, value)
[1]  TRUE FALSE  TRUE FALSE  TRUE
Surya
fuente
20

En caso de que también desee comprobar si una cadena (o un conjunto de cadenas) contiene varias subcadenas, también puede usar el '|' entre dos subcadenas.

>substring="as|at"
>string_vector=c("ass","ear","eye","heat") 
>grepl(substring,string_vector)

Conseguirás

[1]  TRUE FALSE FALSE  TRUE

ya que la primera palabra tiene una subcadena "como", y la última palabra contiene la subcadena "en"

C. Zeng
fuente
¡El operador OR era exactamente lo que necesitaba! +1
Sam
10

Use grepo grepl pero tenga en cuenta si desea o no usar expresiones regulares .

Por defecto, grepy relacionado, toma una expresión regular para que coincida, no una subcadena literal. Si no espera eso e intenta hacer coincidir una expresión regular no válida, no funciona:

> grep("[", "abc[")
Error in grep("[", "abc[") : 
  invalid regular expression '[', reason 'Missing ']''

Para hacer una verdadera prueba de subcadena, use fixed = TRUE.

> grep("[", "abc[", fixed = TRUE)
[1] 1

Si quieres expresiones regulares, genial, pero eso no es lo que parece estar preguntando el OP.

Chris
fuente
7

Puedes usar grep

grep("es", "Test")
[1] 1
grep("et", "Test")
integer(0)
nico
fuente
0

Problema similar aquí: dada una cadena y una lista de palabras clave, detecta cuál de las palabras clave, si las hay, está contenida en la cadena.

Las recomendaciones de este hilo sugieren stringr's str_detecty grepl. Aquí están los puntos de referencia de lamicrobenchmark paquete:

Utilizando

map_keywords = c("once", "twice", "few")
t = "yes but only a few times"

mapper1 <- function (x) {
  r = str_detect(x, map_keywords)
}

mapper2 <- function (x) {
  r = sapply(map_keywords, function (k) grepl(k, x, fixed = T))
}

y entonces

microbenchmark(mapper1(t), mapper2(t), times = 5000)

encontramos

Unit: microseconds
       expr    min     lq     mean  median      uq      max neval
 mapper1(t) 26.401 27.988 31.32951 28.8430 29.5225 2091.476  5000
 mapper2(t) 19.289 20.767 24.94484 23.7725 24.6220 1011.837  5000

Como puede ver, más de 5,000 iteraciones de la búsqueda de palabras clave usando str_detecty greplsobre una cadena práctica y un vector de palabras clave, se grepldesempeña bastante mejor questr_detect .

El resultado es el vector booleano rque identifica cuál de las palabras clave, si las hay, está contenida en la cadena.

Por lo tanto, recomiendo usar greplpara determinar si alguna palabra clave está en una cadena.

Alex L
fuente