У меня есть список строк, например

myvar
[1] "VT" "AK" "AL2" "CA24" "NY12" 
[6] "AZ6" "WY4"

Я хотел бы вставить символ «0» после второго символа во всех строках, состоящих из трех символов, и «01» в конце строки во всех строках, содержащих два символа, чтобы получить результат

myvar
[1] "VT01" "AK01" "AL02" "CA24" "NY12" 
[6] "AZ06" "WY04"

Я думал, что могу сделать это в одной строке, используя регулярное выражение вперед и назад, но я не могу продвинуться дальше этого:

sub('(?<=.{2})(?=.{1})', '0', myvar, perl=T)

myvar
[1] "VT" "AK" "AL002" "CA024" "NY012" 
[6] "AZ06" "WY04"

Любая помощь приветствуется,

Симона

3
simone 16 Окт 2015 в 13:56

4 ответа

Лучший ответ

О статической идее вырезать и вставить:

paste0(substr(myvar, 0, 2), sub("00", "01", gsub(" ", "0", sprintf("% 2s", substr(myvar, 3, 4)))))

# [1] "VT01" "AK01" "AL02" "CA24" "NY12" "AZ06" "WY04"

Получите 2 последних символа с помощью substr, дополните их до 2 символов, замените пробелы на 0, а затем 00 на 01, вставьте 2 первых символа, и вы получите результат.


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

myvar[nchar(myvar)<4] <- paste0(myvar[nchar(myvar)<4],sprintf(paste0("%0",4-nchar(myvar[nchar(myvar)<4]),"i"),1))

Цель состоит в том, чтобы получить вектор из 4-х символьных записей, поэтому для всех записей под 4-мя символами (myvar[nchar(myvar)<4]) выведите их вдоль 0 слева заполненной "1" длины 4 минус фактическая длина записи.

Вероятно, есть способ с помощью with избежать избыточного вызова myvar[nchar(myvar)<4], но поскольку я к этому не привык, я копаюсь.

5
Cath 16 Окт 2015 в 13:07

Вы можете поместить вывод команды sub или gsub в качестве ввода для другой команды sub или gsub.

myvar <- c("VT", "AK", "AL2", "CA24", "NY12",
           "AZ6", "WY4")
sub("^(.{2})$", "\\101", sub("^(.{2})(.)$", "\\10\\2", myvar))
# [1] "VT01" "AK01" "AL02" "CA24" "NY12" "AZ06" "WY04"
2
Avinash Raj 16 Окт 2015 в 16:54

Другой вариант (может быть одноразовый (длинный) лайнер ...):

mapply(function(x, dc_x){
           if(nchar(x)<4) paste0(dc_x[1], "0", ifelse(length(dc_x)-1, dc_x[2], "1")) else x
        }, 
       x=myvar, dc_x=strsplit(myvar,  "(?<=^.{2})", perl=T))
#    VT     AK    AL2   CA24   NY12    AZ6    WY4 
# "VT01" "AK01" "AL02" "CA24" "NY12" "AZ06" "WY04" 

объяснение:
dc_x - это список векторов, по одному для каждого элемента myvar, причем первый элемент - это первые 2 символа соответствующего элемента в myvar. Таким образом, для элементов менее 4 символов вы вставляете 2 первых символа с «01», если есть только 2 символа, или с «0» и остальной частью строки, если их более 2-х символов.

3
Cath 16 Окт 2015 в 13:11

Мы можем извлечь числовую часть с помощью sub, преобразовать строку в класс numeric, изменить значения NA (с приведения) на 1 и использовать sprintf для вставки нечисловых ({ {X3}}) и форматированной числовой части.

 v1 <- as.numeric(sub('\\D+', '', myvar))
 v1[is.na(v1)] <- 1
 sprintf('%s%02d', sub('\\d+', '', myvar),v1)
 #[1] "VT01" "AK01" "AL02" "CA24" "NY12" "AZ06" "WY04"

Или используйте gsubfn. Мы создаем условие ifelse для тех элементов, которые не содержат числовых элементов, и вставляем их с 1. Сопоставляем числовую часть в gsubfn (\\d+), заменяем ее форматированием на { {X4}}.

 library(gsubfn)
 gsubfn('\\d+', ~sprintf('%02d', as.numeric(x)),
     ifelse(!grepl('\\d+', myvar), paste0(myvar, 1), myvar))
 #[1] "VT01" "AK01" "AL02" "CA24" "NY12" "AZ06" "WY04"

Или в более компактной версии используется sub для добавления 1 к тем элементам, которые не имеют числовой части

 gsubfn('\\d+', ~sprintf('%02d', as.numeric(x)) ,sub('(?<=[A-Z])$', '1', myvar, perl=TRUE))
 #[1] "VT01" "AK01" "AL02" "CA24" "NY12" "AZ06" "WY04"

Или сделать его более компактным без обходных путей,

gsubfn('\\d+', ~sprintf('%02d', as.numeric(x)), sub('(\\D+)$', '\\11', myvar))
#[1] "VT01" "AK01" "AL02" "CA24" "NY12" "AZ06" "WY04"
6
akrun 16 Окт 2015 в 12:42