Str_replace A1-A9 - A01-A09 и т.д.

Привет У меня есть следующие строки в моих данных и я хотел бы заменить A1-A9 на A01-A09 и B1-B9 на B01-B09, но сохранить числа >=10.

rep_data=data.frame(Str= c("A1B10", "A2B3", "A11B1", "A5B10"))

    Str
1 A1B10
2  A2B3
3 A11B1
4 A5B10

Здесь есть аналогичный пост , но моя проблема немного отличается! и не видели подобного примера здесь str_replace.

Будет очень рад, если вы знаете решение.

ожидаемый выход

Str
1 A01B10
2 A02B03
3 A11B01
4 A05B10

Ответ 1

Я думаю, что это должно получить то, что вы хотите:

gsub("(?<![0-9])([0-9])(?![0-9])", "0\\1", rep_data$Str, perl = TRUE)
#[1] "A01B10" "A02B03" "A11B01" "A05B10"

Он использует поиск /lookbehind PCRE для соответствия 1-значному числу, а затем вставляет на него "0".

Ответ 2

Как насчет чего-то вроде этого

num_pad <- function(x) {
  x <- as.character(x)
  mm <- gregexpr("\\d+|\\D+",x)  
  parts <- regmatches(x, mm)
  pad_number <- function(x) {
    nn<-suppressWarnings(as.numeric(x))
    x[!is.na(nn)] <- sprintf("%02d", nn[!is.na(nn)])
    x
  }
  parts <- lapply(parts, pad_number)
  sapply(parts, paste0, collapse="")
}


num_pad(rep_data$Str)
# [1] "A01B10" "A02B03" "A11B01" "A05B10"

В основном мы используем регулярные выражения для разбиения строк на цифры и группы без цифр. Затем мы находим те значения, которые выглядят как числа, и используют sprintf() для нулевого заполнения их до двух символов. Затем вставляем добавленные значения в вектор и вставляем все обратно.

Ответ 3

Не проверено полностью

x = c("A1B10", "A2B3", "A11B1", "A5B10")
sapply(strsplit(x, ""), function(s){
    paste(sapply(split(s, cumsum(s %in% LETTERS)), function(a){
        if(length(a) == 2){
            a[2] = paste0(0, a[2])
        }
        paste(a, collapse = "")
    }), collapse = "")
})
#[1] "A01B10" "A02B03" "A11B01" "A05B10"

Ответ 4

Решение от tidyverse и stringr.

library(tidyverse)
library(stringr)

rep_data2 <- rep_data %>%
  extract(Str, into = c("L1", "N1", "L2", "N2"), regex = "(A)(\\d+)(B)(\\d+)") %>%
  mutate_at(vars(starts_with("N")), funs(str_pad(., width = 2, pad = "0"))) %>%
  unite(Str, everything(), sep = "")
rep_data2
     Str
1 A01B10
2 A02B03
3 A11B01
4 A05B10

Ответ 5

Это самое сжатое решение, которое я могу придумать:

library(tidyverse)
library(stringr)

rep_data %>%
  mutate(
    num_1 = str_match(Str, "A([0-9]+)")[, 2],
    num_2 = str_match(Str, "B([0-9]+)")[, 2],
    num_1 = str_pad(num_1, width = 2, side = "left", pad = "0"),
    num_2 = str_pad(num_2, width = 2, side = "left", pad = "0"),
    Str = str_c("A", num_1, "B", num_2)
  ) %>%
  select(- num_1, - num_2)

Ответ 6

Вот один из вариантов: gsubfn

library(gsubfn)
gsubfn("(\\d+)", ~sprintf("%02d", as.numeric(x)), as.character(rep_data$Str))
#[1] "A01B10" "A02B03" "A11B01" "A05B10"

Ответ 7

Немного похоже на ответ @Mike, но это решение использует один положительный результат:

gsub("(\\D)(?=\\d(\\D|\\b))", "\\10", rep_data$Str, perl = TRUE)
# [1] "A01B10" "A02B03" "A11B01" "A05B10"

С tidyverse:

library(dplyr)
library(stringr)

rep_data %>%
  mutate(Str = str_replace_all(Str, "(\\D)(?=\\d(\\D|\\b))", "\\10"))

#      Str
# 1 A01B10
# 2 A02B03
# 3 A11B01
# 4 A05B10

Это регулярное выражение соответствует всем нецифровым номерам, за которыми следует цифра, и либо другой, но не цифрой или границей слов. \\10 довольно обманчив, так как похоже, что он заменяет соответствие 10-й группе захвата. Вместо этого он заменяет совпадение первой группой захвата плюс нуль сразу после.