R - Вставка переменного количества пробелов в строку почтового индекса

У меня есть набор почтовых индексов Великобритании, которые необходимо переформатировать. Они состоят из инкода и ауткода, где инкод имеет форму «цифра буква буква», например. 2DB, а исходящий код представляет собой комбинацию от 2 до 4 букв и цифр, например. NW1 или SW10 или EC1A

В настоящее время между incode и outcode есть один пробел, но мне нужно переформатировать их, чтобы полный почтовый индекс имел длину 7 символов, например: («-» означает пробел)

  • NW1-2DB -> NW1-2DB (1 пробел между исходящим и входным кодом)
  • SW10-9NH -> SW109NH (0 пробелов)
  • E1-6QL -> E1--6QL (2 пробела)

Данные:

df <- data.frame("postcode"=c("NW1 2DB","SW10 9NH","E1 6QL"))
df
#   postcode
# 1  NW1 2DB
# 2 SW10 9NH
# 3   E1 6QL

Я написал строку регулярного выражения для разделения исходящего кода и входящего кода, но не смог найти способ добавить переменное количество пробелов между ними (в этом примере просто создаются два пробела между исходящим кодом и входящим кодом).

require(dplyr)
df <- df %>% mutate(postcode_2sp = gsub('?(\\S+)\\s*?(\\d\\w{2})$','\\1  \\2', postcode)

Чтобы обойти это, я пытался использовать mutate(), nchar() и rep():

df<-df %>% 
  mutate(outcode=gsub('?(\\S+)\\s*\\d\\w{2}$','\\1',postcode),
         incode=gsub('\\S+\\s*?(\\d\\w{2})$','\\1',postcode)) %>%
  mutate(out_length=nchar(outcode))%>%
  mutate(postcode7=paste0(outcode,
                          paste0(rep(" ",4-out_length),collapse=""),
                          incode))

но получить эту ошибку:

Ошибка: неверный аргумент "times"

без последнего шага по созданию почтового индекса7 df выглядит следующим образом:

df
#   postcode outcode incode out_length 
# 1  NW1 2DB     NW1    2DB          3  
# 2 SW10 9NH    SW10    9NH          4 
# 3   E1 6QL      E1    6QL          2 

И если я установлю аргумент rep 'times' на константу, код будет работать, как и ожидалось (но не делает то, что мне нужно!)

df<-df %>% 
  mutate(outcode=gsub('?(\\S+)\\s*\\d\\w{2}$','\\1',postcode),
         incode=gsub('\\S+\\s*?(\\d\\w{2})$','\\1',postcode)) %>%
  mutate(out_length=nchar(outcode))%>%
  mutate(postcode7=paste0(outcode,
                          paste0(rep(" ",4),collapse=""),
                          incode))
df
#   postcode outcode incode out_length   postcode7
# 1  NW1 2DB     NW1    2DB          3  NW1    2DB
# 2 SW10 9NH    SW10    9NH          4 SW10    9NH
# 3   E1 6QL      E1    6QL          2   E1    6QL

Есть ли способ заставить rep() принимать столбец в качестве аргумента times при изменении? Или я должен смотреть на совершенно другой подход?

РЕДАКТИРОВАТЬ: я только что понял, что могу использовать оператор if для каждого случая 2 символов, 3 символов или 4 символов в внешнем коде, но это не очень элегантно.


person lapsel    schedule 29.06.2016    source источник
comment
Вы должны использовать регулярное выражение, чтобы разделить свой почтовый индекс? В чем проблема с strsplit?   -  person Psidom    schedule 29.06.2016
comment
@Psidom По умолчанию strsplit также использует регулярное выражение, но проблема в том, что strsplit нужно что-то для разделения. Если вы посмотрите на регулярное выражение OP, вы увидите, что пробел в середине не является обязательным во входных данных.   -  person Konrad Rudolph    schedule 29.06.2016
comment
Вы правы, strsplit работает до тех пор, пока между входным и выходным кодом есть пробел (как я указал), но Конрад прав в том, что почтовые индексы не всегда форматируются таким образом. Моя ошибка за то, что я слишком конкретен в вопросе.   -  person lapsel    schedule 29.06.2016
comment
@КонрадРудольф Хорошо. Я читал заявление OP как В настоящее время между входным кодом и исходящим кодом есть один пробел. Не очень внимательно смотрел на выражение регулярного выражения. Это имеет смысл.   -  person Psidom    schedule 29.06.2016


Ответы (4)


Другое решение, использующее sprintf для форматирования вывода и tidyr::extract для сопоставления. Преимущество этого заключается в радикальном упрощении как шаблона, так и кода для заполнения:

df %>%
    extract(postcode, into = c('out', 'in'), '(\\S{2,4})\\s*(\\d\\w\\w)') %>%
    mutate(postcode = sprintf('% -4s%s', out, `in`))

Мне нравится версия separate, опубликованная выше, но она требует, чтобы все почтовые индексы были разделены пробелами. По моему опыту это обычно не так.

person Konrad Rudolph    schedule 29.06.2016

Взгляните на метод str_pad из пакета stringr, который подходит для вашего случая:

library(stringr)
df<-df %>% 
    mutate(outcode=gsub('?(\\S+)\\s*\\d\\w{2}$','\\1',postcode),
           incode=gsub('\\S+\\s*?(\\d\\w{2})$','\\1',postcode)) %>%
    mutate(out_length=nchar(outcode)) %>% 
    mutate(postcode7 = paste(outcode, str_pad(incode, 7-out_length), sep = ""))

df
#   postcode outcode incode out_length postcode7
# 1  NW1 2DB     NW1    2DB          3   NW1 2DB
# 2 SW10 9NH    SW10    9NH          4   SW109NH
# 3   E1 6QL      E1    6QL          2   E1  6QL
person Psidom    schedule 29.06.2016

Используя str_pad и отдельно:

library(dplyr)
library(tidyr)
library(stringr)

df %>% 
  separate(postcode, into = c("incode", "outcode"), remove = FALSE) %>% 
  mutate(
    postcode8 = paste0(incode,
                       str_pad(outcode,
                               8 - nchar(incode), side = "left", pad = " ")))

#   postcode incode outcode postcode8
# 1  NW1 2DB    NW1     2DB  NW1  2DB
# 2 SW10 9NH   SW10     9NH  SW10 9NH
# 3   E1 6QL     E1     6QL  E1   6QL
person zx8754    schedule 29.06.2016

person    schedule
comment
Этот код вообще не работает, он требует (а) наличия хотя бы одного пробела, разделяющего группы почтовых индексов, и (б) чтобы фрейм данных содержал хотя бы один почтовый индекс, который уже правильно отформатирован; иначе оправдание не сработает. - person Konrad Rudolph; 30.06.2016