Эффективный способ добавить цифры в алфавитно-цифровые строки в R - PullRequest
0 голосов
/ 07 декабря 2018

У меня есть data.frame с идентификаторами, состоящими из последовательности буквенно-цифровых символов (например, id = c(A001, A002, B013)).Я искал простую функцию в stringr или stirngi, которая легко могла бы выполнить математику с этими строками (id + 1 должен вернуть c(A002, A003, B014)).

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

str_add_n <- function(df, string, n, width=3){

  string <- enquo(string)

  ## split the string using pattern
df <-  df %>%
    separate(!!string,
             into = c("text", "num"), 
             sep = "(?<=[A-Za-z])(?=[0-9])",
             remove=FALSE
    ) %>%
    mutate(num = as.numeric(num),
           num = num + n,
           num = stringr::str_pad(as.character(num),
                                  width = width,
                                  side = "left",
                                  pad = 0 
                                  )
           ) %>%
    unite(next_string, text:num, sep = "")


return(df)  
}

Давайте сделаемигрушка df

df <- data.frame(id = c("A001", "A002", "B013"))
str_add_n(df, id, 1)
    id next_string
1 A001        A002
2 A002        A003
3 B013        B014

Опять же, это работает, мне интересно, есть ли лучший способ сделать это, все твики приветствуются!

ОБНОВЛЕНИЕ

Основано наиз предложенных ответов я провел некоторое тестирование, и похоже, что оба очень близки, я был бы склонен к str_add_n_2 (я изменил имя, чтобы можно было запускать оба, и принял предложение x<-as.character(x))

microbenchmark::microbenchmark(question = str_add_n(df, id, 1),
 answer = df %>% mutate_at(vars(id), funs(str_add_n_2(., 1))),
 string_add = df %>% mutate_at(vars(id), funs(string_add(as.character(.)))))

Что дает

Unit: milliseconds
       expr      min       lq     mean   median       uq
   question 4.312094 4.448391 4.695276 4.570860 4.755748
     answer 2.932146 3.017874 3.191262 3.117627 3.240688
 string_add 3.388442 3.466466 3.699363 3.534416 3.682762
      max neval cld
 10.29253   100   c
  8.24967   100 a  
  9.05441   100  b 

Больше твиков приветствуются!

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

Я бы предположил, что проще определить функцию на основе вектора строк, а не жестко кодировать его для поиска столбцов в кадре;для последнего всегда можно использовать что-то вроде mutate_at(vars(id,...), funs(str_add_n)).

str_add_n <- function(x, n = 1L) {
  gr <- gregexpr("\\d+", x)
  reg <- regmatches(x, gr)
  widths <- nchar(reg)
  regmatches(x, gr) <- sprintf(paste0("%0", widths, "d"), as.integer(reg) + n)
  x
}

vec <- c("A001", "A002", "B013")
str_add_n(vec)
# [1] "A002" "A003" "B014"

Если в кадре:

df <- data.frame(id = c("A001", "A002", "B013"), x = 1:3,
                 stringsAsFactors = FALSE)
library(dplyr)
df %>%
  mutate_at(vars(id), funs(str_add_n(., 3)))
#     id x
# 1 A004 1
# 2 A005 2
# 3 B016 3

Предостережение: для этого в молчании требуется true character, а не factor ... возможной защитной тактикой может быть добавление x <- as.character(x) в определение функции.

0 голосов
/ 07 декабря 2018

Вот способ с gsubfn

id <- c("A001", "A002", "B013")

library(gsubfn)
gsubfn("([0-9]+)", function(x) sprintf("%03.0f", as.numeric(x) + 1), id)
#[1] "A002" "A003" "B014"

Вы можете сделать его функцией

string_add <- function(string, add = 1, width = 3) {
  gsubfn::gsubfn("([0-9]+)", function(x) sprintf(paste0("%0", width, ".0f"), as.numeric(x) + add), string)
}

string_add(id, add = 10, width = 5)
#"A00011" "A00012" "B00023"
...