2015-04-09 4 views
2

Я пытаюсь извлечь часть столбца в фрейме данных, используя регулярные выражения. Проблемы, с которыми я столкнулся, включают факты, которые grep возвращает целое значение, а не только совпадающую часть, и что str_extract, похоже, не работает в векторном виде.Замените столбец фрейма данных на основе regex

Вот что я пытаюсь. Я хотел бы df$match показать alpha.alpha., где шаблон существует и NA в противном случае. Как показать только совпадающую часть?

Кроме того, как я могу заменить [a-zA-Z] в R regex? Могу ли я использовать класс символов или код POSIX, например [:alpha:]?

v1 <- c(1:4) 
v2 <- c("_a.b._", NA, "_C.D._", "_ef_") 
df <- data.frame(v1, v2, stringsAsFactors = FALSE) 

df$match <- grepl("[a-zA-Z]\\.[a-zA-Z]\\.", df$v2) 
df$match 

#TRUE FALSE TRUE FALSE 

v2grep <- grep("[a-zA-Z]\\.[a-zA-Z]\\.", df$v2, value = TRUE) 

df$match[df$match == TRUE] <- v2grep 
df$match[df$match == FALSE] <- NA 

df 

#v1 v2  match 
#1 _a.b._ _a.b._ 
#2 <NA> <NA> 
#3 _C.D._ _C.D._ 
#4 _ef_ <NA> 

Что я хочу:

#v1 v2  match 
#1 _a.b._ a.b. 
#2 <NA> <NA> 
#3 _C.D._ C.D. 
#4 _ef_ <NA> 

ответ

4

4 Подходы ...

Вот 2 подхода в базе, а также с rm_default(extract=TRUE) в qdapRegex пакете я поддерживаю и stringi упаковка.

unlist(sapply(regmatches(df[["v2"]], gregexpr("[a-zA-Z]\\.[a-zA-Z]\\.", df[["v2"]])), function(x){ 
     ifelse(identical(character(0), x), NA, x) 
    }) 
) 

## [1] "a.b." NA  "C.D." NA 

pat <- "(.*?)([a-zA-Z]\\.[a-zA-Z]\\.)(.*?)$" 
df[["v2"]][!grepl(pat, df[["v2"]])] <- NA 
df[["v2"]] <- gsub(pat, "\\2", df[["v2"]]) 

## [1] "a.b." NA  "C.D." NA 

library(qdapRegex) 
unlist(rm_default(df[["v2"]], pattern = "[a-zA-Z]\\.[a-zA-Z]\\.", extract = TRUE)) 

## [1] "a.b." NA  "C.D." NA 

library(stringi) 
stri_extract_first_regex(df[["v2"]], "[a-zA-Z]\\.[a-zA-Z]\\.") 

## [1] "a.b." NA  "C.D." NA 
+0

Мне нравится 'strringi' решение. Это действительно богатый пакет, с ним можно многое сделать, когда у него есть время, чтобы изучить его! –

+0

Кажется, что 'stringi' делает то, что мне нужно. Будет ли это также позволять мне выделять части шаблона, как вы делаете с 'gsub (pat," \\ 2 ", df [[" v2 "]])' во втором решении? –

+0

Да, но вам нужен другой шаблон и, возможно, функция в зависимости от того, что вам нужно. –

3

Одно из возможных решений с использованием как grepl и sub:

# First, remove unwanted characters around pattern when detected 
df$match <- sub(pattern = ".*([a-zA-Z]\\.[a-zA-Z]\\.).*", 
       replacement = "\\1", x = df$v2) 
# Second, check if pattern is present; otherwise set to NA 
df$match <- ifelse(grepl(pattern = "[a-zA-Z]\\.[a-zA-Z]\\.", x = df$match), 
        yes = df$match, no = NA) 

Результаты

df 

# v1  v2 match 
# 1 1 _a.b._ a.b. 
# 2 2 <NA> <NA> 
# 3 3 _C.D._ C.D. 
# 4 4 _ef_ <NA> 

данных

v1 <- c(1:4) 
v2 <- c("_a.b._", NA, "_C.D._", "_ef_") 
df <- data.frame(v1, v2, stringsAsFactors = FALSE) 
+0

Можете ли вы объяснить, «\\ 1'» делает? Это относится к первой сгруппированной секции '()' в шаблоне? Это кажется прямолинейным, но когда я экспериментирую в чем-то более сложном, похоже, что это не так. Есть ли хороший ресурс для такого рода обозначений? –

+0

Да, '\\ 1' относится к первому (и только на этот раз) сгруппированному шаблону (внутри круглых скобок). Когда у вас несколько групп круглых скобок, а некоторые - в других, это становится немного более сложным, но все же довольно простым. Для источников '? Regex' - хорошее начало (и в конце есть другие ссылки). –

4

решение Base R с помощью regmatches и regexpr, который возвращает -1 если совпадения регулярных выражений не найдено:

r <- regexpr("[a-zA-Z]\\.[a-zA-Z]\\.", df$v2) 
df$match <- NA 
df$match[which(r != -1)] <- regmatches(df$v2, r) 

# v1  v2 match 
#1 1 _a.b._ a.b. 
#2 2 <NA> <NA> 
#3 3 _C.D._ C.D. 
#4 4 _ef_ <NA> 
Смежные вопросы