Verifica se i caratteri sono in una stringa


279

Sto cercando di determinare se una stringa è un sottoinsieme di un'altra stringa. Per esempio:

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

Voglio restituire VERO se "valore" appare come parte della stringa "caratteri". Nel seguente scenario, vorrei restituire false:

chars <- "test"
value <- "et"

12
La risposta accettata è sbagliata, devi aggiungere fixed=TRUE, altrimenti la stai trattando come una regex invece che una stringa. Vedi la mia risposta da ottobre 2016.
Joshua Cheek,

@JoshuaCheek A meno che tu non abbia caratteri speciali nella sequenza, regex restituirà lo stesso risultato di fixed.
user3932000,

1
Certo, ma lo sai solo se lo passi letteralmente. Altrimenti, non saprai quali caratteri sono presenti nel modello, quindi usi fixed=TRUEo hai un bug che confonderà silenziosamente e sottilmente i tuoi dati.
Joshua Cheek,

Risposte:


388

Usa la greplfunzione

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

Usa ?greplper saperne di più.


8
Per questo semplice caso l'aggiunta di fixed = TRUE può migliorare le prestazioni (supponendo che si effettueranno molti di questi calcoli).
Greg Snow,

1
@Josh O'brien, quel post accertamento rispetto (conteggio) tutte le partite di una singola stringa lunga, provare a trovare 1 partita in un mucchio di stringhe più brevi: vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') ).
Greg Snow,

2
@GregSnow - Provato system.time(a <- grepl("abc", vec))e system.time(a <- grepl("abc", vec, fixed=TRUE)), ed fixed=TRUEè ancora, semmai leggermente più lento. La differenza non è apprezzabile con queste stringhe corte, ma fixed=TRUEnon sembra essere ancora più veloce. Grazie per aver sottolineato, tuttavia, che è su lunghe corde che fixed=TRUEprende il vero successo.
Josh O'Brien,

2
grepl (pattern, x) almeno nel 2017
JMR,

2
Questa non dovrebbe essere la risposta accettata, perché il valore verrà interpretato come un modello regex. fixed = TRUE dovrebbe sempre essere usato a meno che tu non sappia che la stringa che stai cercando non sembrerà un modello regex. La risposta di Joshua Creek di seguito ha una spiegazione molto chiara di ciò e dovrebbe essere la risposta accettata.
bhaller,

159

Risposta

Sospiro, mi ci sono voluti 45 minuti per trovare la risposta a questa semplice domanda. La risposta è: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

Interpretazione

grepprende il nome del file eseguibile Linux, che è essa stessa un acronimo di " G lobal R egular E Xpression P tampa", sarebbe leggere le linee di input e quindi stampare loro se abbinati gli argomenti hai dato. "Globale" significa che la corrispondenza potrebbe verificarsi in qualsiasi punto della riga di input, spiegherò di seguito "Espressione regolare", ma l'idea è che sia un modo più intelligente per abbinare la stringa (R chiama questo "carattere", ad esempio class("abc")) e "Stampa "poiché è un programma da riga di comando, l'emissione dell'output significa che stampa sulla sua stringa di output.

Ora, il grepprogramma è fondamentalmente un filtro, dalle linee di input alle linee di output. E sembra che la grepfunzione di R in modo simile prenderà una serie di input. Per motivi che mi sono del tutto sconosciuti (ho iniziato a giocare con R solo un'ora fa), restituisce un vettore degli indici corrispondenti, anziché un elenco di corrispondenze.

Ma, tornando alla tua domanda originale, quello che vogliamo veramente è sapere se abbiamo trovato l'ago nel pagliaio, un valore vero / falso. Apparentemente hanno deciso di nominare questa funzione grepl, come in "grep" ma con un valore di ritorno " L ogical" (chiamano valori logici veri e falsi, ad es class(TRUE).).

Quindi, ora sappiamo da dove viene il nome e cosa dovrebbe fare. Torniamo alle espressioni regolari. Gli argomenti, anche se sono stringhe, sono usati per costruire espressioni regolari (d'ora in poi: regex). Una regex è un modo per abbinare una stringa (se questa definizione ti irrita, lasciala andare). Ad esempio, la regex acorrisponde al personaggio "a", la regex a*corrisponde al carattere "a"0 o più volte e la regex a+corrisponderebbe al carattere "a"1 o più volte. Quindi nell'esempio sopra, l'ago che stiamo cercando 1+2, quando trattato come regex, significa "uno o più 1 seguito da un 2" ... ma il nostro è seguito da un plus!

1 + 2 come regex

Quindi, se si usasse il greplsenza impostazione fixed, i tuoi aghi sarebbero casualmente dei pagliai e ciò avrebbe accidentalmente funzionato abbastanza spesso, possiamo vedere che funziona anche per l'esempio del PO. Ma questo è un bug latente! Dobbiamo dirgli che l'input è una stringa, non una regex, che a quanto pare fixedè ciò che serve. Perché riparato Nessun indizio, aggiungi questa risposta ai segnalibri in b / c probabilmente dovrai cercarla altre 5 volte prima di memorizzarla.

Alcuni pensieri finali

Migliore è il codice, minore è la cronologia che devi sapere per dargli un senso. Ogni argomento può avere almeno due valori interessanti (altrimenti non dovrebbe essere un argomento), i documenti elencano 9 argomenti qui, il che significa che ci sono almeno 2 ^ 9 = 512 modi per invocarlo, c'è molto lavoro da fare scrivere, testare e ricordare ... disaccoppiare tali funzioni (dividerle, rimuovere le dipendenze le une dalle altre, le cose della stringa sono diverse dalle cose regex sono diverse dalle cose vettoriali). Alcune delle opzioni si escludono a vicenda, non offrono agli utenti modi errati di utilizzare il codice, vale a dire che l'invocazione problematica dovrebbe essere strutturalmente priva di senso (come passare un'opzione che non esiste), non logicamente priva di senso (dove devi emettere un avvertimento per spiegarlo). Metti metaforicamente: sostituire la porta d'ingresso sul lato del decimo piano con un muro è meglio che appendere un cartello che mette in guardia contro il suo uso, ma uno dei due è meglio di nessuno dei due. In un'interfaccia, la funzione definisce come dovrebbero apparire gli argomenti, non il chiamante (perché il chiamante dipende dalla funzione, deducendo tutto ciò che chiunque potrebbe desiderare di chiamarlo fa dipendere anche la funzione dai chiamanti, e questo tipo di dipendenza ciclica ostruirà rapidamente un sistema e non fornirà mai i benefici che ci si aspetta). Diffidare di tipi equivoci, è un difetto di progettazione che piace alle cose dedurre tutto ciò che tutti potrebbero desiderare di chiamarlo fa sì che la funzione dipenda anche dai chiamanti e questo tipo di dipendenza ciclica ostruirà rapidamente un sistema e non fornirà mai i benefici che ci si aspetta). Diffidare di tipi equivoci, è un difetto di progettazione che piace alle cose dedurre tutto ciò che tutti potrebbero desiderare di chiamarlo fa sì che la funzione dipenda anche dai chiamanti e questo tipo di dipendenza ciclica ostruirà rapidamente un sistema e non fornirà mai i benefici che ci si aspetta). Diffidare di tipi equivoci, è un difetto di progettazione che piace alle coseTRUEe 0e "abc"sono tutti vettori.


6
Saluti per la tua spiegazione! Sembra che R si sia evoluto per un lungo periodo di tempo ed è bloccato da alcune strane scelte di design (vedi ad es. Risposte a questa domanda sui tipi di valore ). Tuttavia, in questo caso la restituzione di un vettore di indici di corrispondenza sembra appropriata, così come il grepfiltraggio delle righe, non delle celle.
Krevelen,

4
"fisso" si riferisce ai caratteri che corrispondono a una sequenza "fissa".
Will Beason il

32

Tu vuoi grepl:

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

27

Utilizzare questa funzione dal stringipacchetto:

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

Alcuni parametri di riferimento:

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

22

Inoltre, può essere fatto usando la libreria "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

20

Nel caso in cui desideri anche verificare se una stringa (o un insieme di stringhe) contiene (s) più sottostringhe, puoi anche usare '|' tra due sottostringhe.

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

Otterrete

[1]  TRUE FALSE FALSE  TRUE

poiché la prima parola ha la sottostringa "as" e l'ultima parola contiene la sottostringa "at"


L'operatore OR era esattamente quello di cui avevo bisogno! +1
Sam,

10

Utilizzare grepo grepl ma essere consapevoli se si desidera o meno utilizzare espressioni regolari .

Per impostazione predefinita, grepe relativi prendono un'espressione regolare da abbinare, non una sottostringa letterale. Se non te lo aspetti, e cerchi di abbinare una regex non valida, non funziona:

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

Per eseguire un vero test di sottostringa, utilizzare fixed = TRUE.

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

Se vuoi regex, ottimo, ma non è quello che sembra che l'OP stia chiedendo.


7

Puoi usare grep

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

0

Problema simile qui: data una stringa e un elenco di parole chiave, rileva quali eventuali parole chiave sono contenute nella stringa.

I consigli di questa discussione suggeriscono stringrdi str_detecte grepl. Ecco i benchmark dal microbenchmarkpacchetto:

utilizzando

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))
}

e poi

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

noi troviamo

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

Come puoi vedere, oltre 5.000 iterazioni della ricerca di parole chiave utilizzando str_detecte greplsu una stringa pratica e un vettore di parole chiave, hanno greplprestazioni leggermente migliori rispetto a str_detect.

Il risultato è il vettore booleano rche identifica le eventuali parole chiave contenute nella stringa.

Pertanto, ti consiglio di utilizzare greplper determinare se ci sono parole chiave in una stringa.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.