Как извлечь 1 или 2 слова перед n цифрами?

#r #regex

#r #регулярное выражение

Вопрос:

У меня есть этот пример фрейма данных:

 address <- c("11537 W LARKSPUR RD EL MIRAGE 85335", "6702 E CPT DREYFUS SCOTTSDALE 85254", "114 S PUEBLO ST GILBERT 85233", "16981 W YOUNG ST SURPRISE 85388")
person <- c("Maria", "Jose", "Adan", "Eva")

my_address <- tibble(person, address)
  

Мне нужно извлечь city из address столбца. Город может состоять из 1 слова или 2, но они всегда находятся перед почтовым индексом, который состоит из 5 цифр.

Из фрейма данных я хотел бы получить: «ЭЛЬ МИРАЖ», «СКОТТСДЕЙЛ» и «ГИЛБЕРТ» в новом столбце: city

Важно:

Города всегда после слова из 2 или 3 букв, например: ST, AVE, RD.

Например, из: «16981 W YOUNG ST SURPRISE 85388». Я хотел бы получить СЮРПРИЗ, который находится после «ST».

Итак, я пробовал это регулярное выражение:

 my_address$city <-gsub("(.*)([a-zA-Z])([0-9]{5})(.*)", "\2", my_address$address)
  

Но он возвращает весь текст в столбце, а не нужные города. Кроме того, я заметил, что я не указывал ему проверять наличие 1 или 2 слов перед 5 цифрами, поэтому он будет извлекать только 1 слово?

ОБНОВЛЕНИЕ 1:

 string1 <- "114 S PUEBLO ST GILBERT 85233"
sapply(stringr::str_extract_all(string1,"\w{4,}"),"[",3)
  

возвращает: 85233 , когда GILBERT ожидалось.

Комментарии:

1. Почему не ДРЕЙФУС СКОТТДЕЙЛ?

2. @NelsonGon Дрейфус — АВЕНЮ, а СКОТТДЕЙЛ — Город.

3. @NelsonGon Также слова перед городом всегда имеют длину 2 или 3 («RD», «ST», «AVE». Я думаю, это могло бы помочь в регулярном выражении. Ваш ответ возвращает «MIRAGE», когда требуется «EL MIRAGE».

4. Ваши текущие требования приводят к bp{L}{2,3}s ((?:p{L} s )?p{L} )s d{5}b регулярному выражению, см. regex101.com/r/zHTjX6/1 . CPT перед DREYFUS имеет длину 3 символа.

5. @OmarGonzales Что gsub ? Это должно использоваться как str_match(my_address$address,"\b\p{L}{2,3}\s ((?:\p{L} \s )?\p{L} )\s \d{5}\b")[,2] . Но дело не в этом. Дело в том, что ваши требования означают, что вы ожидаете DREYFUS SCOTTSDALE от 6702 E CPT DREYFUS SCOTTSDALE 85254 . Если у вас здесь нет правила 100%, это означает, что вы не можете использовать обычный язык и решения нет. «Я хочу это» не является требованием программирования.

Ответ №1:

Это решение dplyr stringr / tidyverse основано на том факте, что вы знаете, какие слова из 2-3 букв предшествуют городу…

 # vector with  2-3 letter words before a city?
v.before <- c("ST", "RD", "AVE")
#with this vector, we can build an 'or'-pattern for a regex    

library( dplyr )
library( stringr )
data.frame( person, address) %>% 
  mutate( place = stringr::str_extract( address, paste0("(?<=", paste0(v.before, collapse = " |" ), " ).*(?= [0-9]{5})") ) ) %>%
  #no match found?, then the city is the second last word from address
  mutate( place = ifelse( is.na( place ), stringr::word(address, -2), place))

#   person                             address      place
# 1  Maria 11537 W LARKSPUR RD EL MIRAGE 85335  EL MIRAGE
# 2   Jose 6702 E CPT DREYFUS SCOTTSDALE 85254 SCOTTSDALE
# 3   Adan       114 S PUEBLO ST GILBERT 85233    GILBERT
# 4    Eva     16981 W YOUNG ST SURPRISE 85388   SURPRISE
  

Ответ №2:

Обычно предпочитают однострочные строки, хотя это кажется чрезмерно сложным и потребует еще одного шага для удаления «ST» перед «СЮРПРИЗОМ». Это было сделано здесь, предполагая, что все начинается с «ST».

  library(stringr)
 new_s<-unlist(str_extract_all(my_address$address,"\w{2,} \w{3,}"))
 newer_s<-str_remove_all(new_s,"^\w{3}.*\D$")
 newer_s<-str_remove_all(newer_s,"\s.*\d")
 res<-str_remove_all(newer_s,"^ST ")
 res[res==""]<-NA 
 my_address$city<-res[complete.cases(res)]
  

Результат:

  my_address
# A tibble: 4 x 3
#  person address                             city      
#  <chr>  <chr>                               <chr>     
#1 Maria  11537 W LARKSPUR RD EL MIRAGE 85335 EL MIRAGE 
#2 Jose   6702 E CPT DREYFUS SCOTTSDALE 85254 SCOTTSDALE
#3 Peter  16981 W YOUNG ST SURPRISE 85388     SURPRISE  
#4 Paul   114 S PUEBLO ST GILBERT 85233       GILBERT 
  

Данные:

 address <- c("11537 W LARKSPUR RD EL MIRAGE 85335", "6702 E CPT DREYFUS SCOTTSDALE 85254",
             "16981 W YOUNG ST SURPRISE 85388","114 S PUEBLO ST GILBERT 85233")
person <- c("Maria", "Jose","Peter","Paul")

my_address <- tibble::tibble(person, address)
  

Комментарии:

1. Я добавил другой, менее эффективный ответ. Возможно, у кого-то другого есть более элегантное решение.

2. Отредактировано @OmarGonzales. Для будущих пользователей. Возможно, потребуется удалить несколько путей, как в принятом ответе. Я только удалил ST здесь.