2016-01-10 14 views
5

Ho un dataframe:gruppi Assegnazione utilizzando Grepl con più ingressi

df <- data.frame(name=c("john", "david", "callum", "joanna", "allison", "slocum", "lisa"), id=1:7) 
df 

    name id 
1 john 1 
2 david 2 
3 callum 3 
4 joanna 4 
5 allison 5 
6 slocum 6 
7 lisa 7 

Ho un vettore contenente regex che desidero di trovare nella variabile df nome $:

vec <- c("lis", "^jo", "um$") 

L'uscita I vuole ottenere è la seguente:

 name id group 
1 john 1  2 
2 david 2 NA 
3 callum 3  3 
4 joanna 4  2 
5 allison 5  1 
6 slocum 6  3 
7 lisa 7  1 

ho potuto fare questo nel modo seguente:

df$group <- ifelse(grepl("lis", df$name), 1, 
       ifelse(grepl("^jo", df$name), 2, 
       ifelse(grepl("um$", df$name), 3, 
       NA) 

Tuttavia, voglio farlo direttamente da "vec". Sto generando diversi valori in vec in modo reattivo in un'app lucida. Posso assegnare gruppi basati su indice in vec?

Inoltre, se succede qualcosa di simile a quanto segue, il gruppo dovrebbe essere il primo che appare. per esempio. 'Callum' è TRUE per 'all' e 'um $' ma dovrebbe ottenere un gruppo 1 qui.

vec <- c("all", "^jo", "um$") 
+1

Come si vuole affrontare con la possibilità di molteplici modelli che corrispondono a un determinato nome? –

+1

Dovrebbero avere il primo indice per cui sono VERO. Ad esempio, Callum dovrebbe essere 1 come 'all' è in vec prima 'um $' – jalapic

+1

Qualcosa come 'apply (df, 1, function (row) che (sapply (vec, function (regex) grepl (regex, row [1 ])))) 'dovrebbe iniziare. Passa attraverso il frame di dati riga per riga e per ogni elemento controlla quali espressioni regolari corrispondono. Sono interessato a vedere quello che gli altri inventano però. – brittenb

risposta

3

Qui ci sono diverse opzioni:

df$group <- apply(Vectorize(grepl, "pattern")(vec, df$name), 
        1, 
        function(ii) which(ii)[1]) 
#  name id group 
#1 john 1  2 
#2 david 2 NA 
#3 callum 3  3 
#4 joanna 4  2 
#5 allison 5  1 
#6 slocum 6  3 
#7 lisa 7  1 

utilizzare un vettore di nome e fondere su di esso:

names(vec) <- seq_along(vec) 

df <- merge(df, stack(Vectorize(grep, "pattern", SIMPLIFY=FALSE)(vec, df$name)), 
by.x="id", by.y="values", all.x = TRUE) 

df[!duplicated(df$id),] # to keep only the first match 
# id name ind 
#1 1 john 2 
#2 2 david <NA> 
#3 3 callum 3 
#4 4 joanna 2 
#5 5 allison 1 
#6 6 slocum 3 
#7 7 lisa 1 

Un ciclo for:

df$group <- NA 

for (i in rev(seq_along(vec))) { 
    TFvec <- grepl(vec[i], df$name) 
    df$group[TFvec] <- i 
} 

df 
#  name id group 
#1 john 1  2 
#2 david 2 NA 
#3 callum 3  3 
#4 joanna 4  2 
#5 allison 5  1 
#6 slocum 6  3 
#7 lisa 7  1 

Oppure si può utilizzare outer con stri_match_first_regex da stringi

library(stringi) 
match.mat <- outer(df$name, vec, stri_match_first_regex) 
df$group <- apply(match.mat, 1, function(ii) which(!is.na(ii))[1]) 
# [1] for first match in `vec` 

#  name id group 
#1 john 1  2 
#2 david 2 NA 
#3 callum 3  3 
#4 joanna 4  2 
#5 allison 5  1 
#6 slocum 6  3 
#7 lisa 7  1 
3

Una soluzione Vectorised, utilizzando rebus e stringi.

library(rebus) 
library(stringi) 

creare un'espressione regolare che cattura qualsiasi dei valori in vec.

vec <- c("lis", "^jo", "um$") 
(rx <- or1(vec, capture = TRUE)) 
## <regex> (lis|^jo|um$) 

Abbinare la regex, quindi convertire in fattore e integer.

matches <- stri_match_first_regex(df$name, rx)[, 2] 
df$group <- as.integer(factor(matches, levels = c("lis", "jo", "um"))) 

df appare come segue:

 name id group 
1 john 1  2 
2 david 2 NA 
3 callum 3  3 
4 joanna 4  2 
5 allison 5  1 
6 slocum 6  3 
7 lisa 7  1 
+1

Perché non usare semplicemente 'rx <- paste (vec, collapse =" | ")' utilizzando invece un pacchetto extra? – Jaap

+1

@ Jaap Perché stavo spudoratamente proponendo il mio pacco. Devo ammettere che è un po 'eccessivo per una regex così semplice. –

Problemi correlati