2015-04-21 13 views
15

La mia domanda riguarda come creare una nuova variabile su un frame di dati in R in base al risultato di un'espressione regolare. Ecco un esempio minima dei dati:crea una nuova variabile basata su un'espressione regolare

df <- data.frame(model=c("Legacy 2.0 BG5 B4 AUTO","Legacy 2.0 BH5 AT","Legacy 2.0i CVT Non Leather","Legacy 2.0i CVT","Legacy 2.0 BL5 AUTO B4", 
       "Legacy 2.0 BP5 AUTO","Legacy 2.0 BM5 AUTO CVT"), CRSP=c(3450000,3365000,4950000,5250000,4787526,3550000,5235000)) 

df 
         model CRSP 
1  Legacy 2.0 BG5 B4 AUTO 3450000 
2   Legacy 2.0 BH5 AT 3365000 
3 Legacy 2.0i CVT Non Leather 4950000 
4    Legacy 2.0i CVT 5250000 
5  Legacy 2.0 BL5 AUTO B4 4787526 
6   Legacy 2.0 BP5 AUTO 3550000 
7  Legacy 2.0 BM5 AUTO CVT 5235000 

desidero creare un nuovo 'chassis' variabile il cui valore è il terzo elemento del 'modello' corrispondente stringa variabile, finendo così con:

df 
         model CRSP chassis 
1  Legacy 2.0 BG5 B4 AUTO 3450000  BG5 
2   Legacy 2.0 BH5 AT 3365000  BH5 
3 Legacy 2.0i CVT Non Leather 4950000  CVT 
4    Legacy 2.0i CVT 5250000  CVT 
5  Legacy 2.0 BL5 AUTO B4 4787526  BL5 
6   Legacy 2.0 BP5 AUTO 3550000  BP5 
7  Legacy 2.0 BM5 AUTO CVT 5235000  BM5 

Ho bisogno di trovare un modo per estrarre gli elementi appropriati in ogni riga e inserirli nella nuova variabile. Qualsiasi assistenza sarebbe molto apprezzata.

risposta

5

Sono un grande fan di tidyr per questo sorta di compito e l'estrazione di tutti i pezzi in colonne separate:

if (!require("pacman")) install.packages("pacman") 
pacman::p_load(dplyr, tidyr) 

regx <- "(^[A-Za-z]+\\s+[0-9.a-z]+)\\s+([A-Z0-9]+)\\s*(.*)" 

df %>% 
    extract(model, c("a", "chassis", "b"), regx, remove=FALSE) 

##       model   a chassis   b CRSP 
## 1  Legacy 2.0 BG5 B4 AUTO Legacy 2.0  BG5  B4 AUTO 3450000 
## 2   Legacy 2.0 BH5 AT Legacy 2.0  BH5   AT 3365000 
## 3 Legacy 2.0i CVT Non Leather Legacy 2.0i  CVT Non Leather 4950000 
## 4    Legacy 2.0i CVT Legacy 2.0i  CVT    5250000 
## 5  Legacy 2.0 BL5 AUTO B4 Legacy 2.0  BL5  AUTO B4 4787526 
## 6   Legacy 2.0 BP5 AUTO Legacy 2.0  BP5  AUTO 3550000 
## 7  Legacy 2.0 BM5 AUTO CVT Legacy 2.0  BM5 AUTO CVT 5235000 

si potrebbe ottenere un po 'più generico con questa espressione regolare:

regx <- "(^[^ ]+\\s+[^ ]+)\\s+([^ ]+)\\s*(.*)" 

notare Inoltre è possibile utilizzare extract per ottenere solo la colonna che stai dopo da far cadere la parentesi di raggruppamento sul primo e ultimi gruppi come segue:

regx <- "^[A-Za-z]+\\s+[0-9.a-z]+\\s+([A-Z0-9]+)\\s*.*" 

df %>% 
    extract(model, "chassis", regx, remove=FALSE) 
+2

Ho altri passaggi di manipolazione dei dati precedenti e successivi sullo stesso frame di dati all'interno di un tubo ** dplyr ** '%>%' e quindi questa soluzione è più comoda in quanto posso inserirla direttamente nel ciclo. Grazie a @TylerRinker – amo

4

da poter abbinare il carattere fino alla parte numerica tra cui il i e lo spazio, sostituirlo con '' usando sub, e quindi estrarre la prima parola con word.

library(stringr) 
word(sub('^\\D*[0-9.i ]*', '', df$model),1) 
#[1] "BG5" "BH5" "CVT" "CVT" "BL5" "BP5" "BM5" 

o della partita gli spazi, sostituirli con un unico spazio e utilizzare word

word(gsub(' +', ' ', df$model),3) 
#[1] "BG5" "BH5" "CVT" "CVT" "BL5" "BP5" "BM5" 

NOTA: Non sono sicuro lo spazio extra nel primo elemento di 'modello' è un errore di battitura. Se il set di dati originale non ha più di uno spazio tra le parole, allora word(df$model, 3) funzionerebbe.

10

Ecco una possibile soluzione usando stringi

library(stringi) 
df$chassis <- stri_extract_all_words(df$model, simplify = TRUE)[, 3] 
df 
#       model CRSP chassis 
# 1  Legacy 2.0 BG5 B4 AUTO 3450000  BG5 
# 2   Legacy 2.0 BH5 AT 3365000  BH5 
# 3 Legacy 2.0i CVT Non Leather 4950000  CVT 
# 4    Legacy 2.0i CVT 5250000  CVT 
# 5  Legacy 2.0 BL5 AUTO B4 4787526  BL5 
# 6   Legacy 2.0 BP5 AUTO 3550000  BP5 
# 7  Legacy 2.0 BM5 AUTO CVT 5235000  BM5 

O simile

df$chassis <- sapply(stri_extract_all_words(df$model), `[`, 3) 
5

Una soluzione alternativa utilizzando strsplit

# Split each of the models using space (the + accounts for multiple spaces) 
# Note that model is a factor in your data frame, so it must be cast to char 
model.split <- strsplit(as.character(df$model), " +") 
# Now go through each element of the splitted list and get the 3rd word 
df$chassis <- sapply(model.split, function(x){x[3]}) 
+2

O un approccio simile con 'data.table'. 'setDT (df) [, chassis: = tstrsplit (model, '+') [[3]]] []' – akrun

2

È possibile dividere il carattere di spazio utilizzando cSplit dal splitstackshape pacchetto:

library(splitstackshape) 
df$chassis <- cSplit(df, "model", sep = " ", "wide")$model_3 

Ciò evita la necessità di una regex o una funzione apply.

+0

'cSplit' ha un'opzione' drop = FALSE', e restituisce un data.table. Quindi, potrebbe anche funzionare 'cSplit (df," model ", sep =" "," wide ", drop = FALSE) [, c (1,5), con = FALSE]' – akrun

3

Questo può essere fatto facilmente nella base di R:

transform(df, chassis=sub("^(\\S+\\s+){2}(\\S+).*", "\\2", model)) 

produce:

     model CRSP chassis 
1  Legacy 2.0 BG5 B4 AUTO 3450000  BG5 
2   Legacy 2.0 BH5 AT 3365000  BH5 
3 Legacy 2.0i CVT Non Leather 4950000  CVT 
4    Legacy 2.0i CVT 5250000  CVT 
5  Legacy 2.0 BL5 AUTO B4 4787526  BL5 
6   Legacy 2.0 BP5 AUTO 3550000  BP5 
7  Legacy 2.0 BM5 AUTO CVT 5235000  BM5  
Problemi correlati