Изменить первое число в столбце в зависимости от условия - PullRequest
0 голосов
/ 05 февраля 2019

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

  id subscriberid  intName
1  1   1234567890 asdfsadf
2  2   3243245324  dfsafdf
3  3   4532453245  dasdfsd

Воспроизводимый пример выглядит следующим образом:

structure(list(id = 1:3, subscriberid = c(1234567890, 3243245324, 4532453245),
    intName = c("asdfsadf", "dfsafdf", "dasdfsd")),
    row.names = c(NA, 3L), class = "data.frame")

У меня есть массив subid и везде, где subid совпадает с df $subscriberid, мне нужно изменить первую цифру идентификатора подписчика на 9.

subid = c(1234567890,2345345234)

Я пробовал следующее:

for (i in df$subscriberid) {
    df$subscriberid == sub(substr(df$subscriberid,0,1),9,df$subscriberid)
}

Я также пробовал с ifelse с substr и gsub,и разные другие комбинации.Но не смог пройти.Желаемый вывод

  id subscriberid  intName
1  1   9234567890 asdfsadf   <--- only the first digit is changed.
2  2   3243245324  dfsafdf
3  3   4532453245  dasdfsd

Ответы [ 5 ]

0 голосов
/ 05 февраля 2019

Проще использовать метод присваивания substring

# create a logical vector
i1 <- df1$subscriberid %in% subid

# convert the column to character class
df1$subscriberid <- as.character(df1$subscriberid)

#assign with substring<-
substring(df1$subscriberid[i1], 1, 1) <- '9'
df1
#   id subscriberid  intName
#1  1   9234567890 asdfsadf
#2  2   3243245324  dfsafdf
#3  3   4532453245  dasdfsd

Тесты

Включая этот метод вместе с данными @ SymbolixAU (другие функции взяты из его поста)

akrun <- function(df, subid) {
 i1 <- df$subscriberid %in% subid
 df$subscriberid <- as.character(df$subscriberid)
 substring(df$subscriberid[i1], 1, 1) <- '9'
  }


set.seed(1234)
df <- data.frame(
   subscriberid = sample(1:100000000, size = 1e5)
  )
subid <- sample( df$subscriberid, size = 10 )
library(microbenchmark)
microbenchmark(
   ronak = { ronak( df, subid ) },
   tim = { tim( df, subid ) },
   tmfmnk = { tmfmnk( df, subid ) },
   symbolix = { symbolix( df, subid ) }, akrun = {akrun(df, subid)}, times = 5)
#Unit: milliseconds
#     expr        min         lq       mean     median        uq       max neval cld
#    ronak 105.073716 128.279151 140.993520 138.241632 154.89092 178.48218     5  b 
#      tim 224.610660 246.959505 263.138679 264.685503 284.93632 294.50141     5   c
#   tmfmnk 119.734979 134.949406 138.735054 135.888113 142.91750 160.18527     5  b 
# symbolix   2.487283   3.238862   8.429718   3.540119  10.80669  22.07564     5 a  
#    akrun  29.530330  33.431953  41.649046  34.772512  36.91314  73.59730     5 a  

данные

df1 <- structure(list(id = 1:3, subscriberid = c(1234567890, 3243245324, 4532453245),
intName = c("asdfsadf", "dfsafdf", "dasdfsd")),
row.names = c(NA, 3L), class = "data.frame")
0 голосов
/ 05 февраля 2019

Подход с использованием математики, преимущество в том, что результаты возвращаются в виде чисел, и он быстрее.

Данные

df <- structure(list(id = 1:3, subscriberid = c(1234567890, 3243245324, 4532453245),
                     intName = c("asdfsadf", "dfsafdf", "dasdfsd")),
                row.names = c(NA, 3L), class = "data.frame")

subid <- c(1234567890,2345345234)

Метод

idx <- df$subscriberid %in% subid
vals <- df[ idx, "subscriberid" ]
digits <- floor( log10( vals ) )

## number of digits given by `floor( log10( vals) ) + 1`, but we want the first digit

( ( vals / 10^digits ) + 9 - floor( vals / 10^digits ) ) * (10^digits)

# [1] 9234567890

Что этоделает поиск индексов data.frame, которые соответствуют subid

Затем

  • вычисляет, сколько цифр в этих числах, используя log10
  • деление на 10 на степень этих цифр и сложение для получения первого целого числа
  • вычитание этого целого числа из 9 (ваша цель)
  • добавление его обратно в log10'dзначение
  • умножение обратно на 10 до степени этих цифр, чтобы получить исходное количество цифр обратно

Контрольный показатель

library(microbenchmark)

microbenchmark(
  ronak = { ronak( df, subid ) },
  tim = { tim( df, subid ) },
  tmfmnk = { tmfmnk( df, subid ) },
  symbolix = { symbolix( df, subid ) },
  times = 5
)

# Unit: milliseconds
# expr            min         lq       mean     median         uq      max neval
# ronak    186.143804 188.618750 214.151592 191.154106 196.399341 308.4420     5
# tim      442.385985 463.510154 526.814255 506.268620 541.829769 680.0767     5
# tmfmnk   236.423472 255.418334 295.652617 295.624544 329.901976 360.8948     5
# symbolix   5.510366   5.828804   8.166222   5.850937   5.942607  17.6984     5

И показать результатыравны

res_ronak <- ronak( df, subid )
res_tim <- tim( df, subid )
res_tmfmnk <- tmfmnk( df, subid )
res_symbolix <- symbolix( df, subid )

all.equal(res_ronak, res_tim)
# [1] TRUE
all.equal(res_tim, res_tmfmnk)
# [1] TRUE
res_symbolix$subscriberid <- as.character(res_symbolix$subscriberid)
all.equal(res_tmfmnk, res_symbolix)
# [1] TRUE

Данные бенчмаркинга

set.seed(1234)
df <- data.frame(
  subscriberid = sample(1:100000000, size = 1e5)
)
subid <- sample( df$subscriberid, size = 10 )

Функции бенчмаркинга

ronak <- function(df, subid) {
  df$subscriberid <- with(df, ifelse(subscriberid %in% subid,
                  paste0("9",substring(subscriberid,2)), subscriberid))
  return(df)
}

tim <- function(df, subid) {
  regex <- paste0("\\b(", paste(subid, collapse="|"), ")\\b")
  df$subscriberid <- ifelse(grepl(regex, df$subscriberid),
                            paste0("9", substr(df$subscriberid, 2, nchar(df$subscriberid))),
                            df$subscriberid)
  return(df)
}

tmfmnk <- function(df, subid) {
  df$subscriberid <- ifelse(df$subscriberid %in% subid, 
         sub(".", "9", df$subscriberid), df$subscriberid)
  return(df)
}

symbolix <- function(df, subid) {
  idx <- df$subscriberid %in% subid
  vals <- df[ idx, "subscriberid" ]
  digits <- floor( log10( vals ) )
  df[ idx, "subscriberid" ] <- ( ( vals / 10^digits ) + 9 - floor( vals / 10^digits ) ) * (10^digits)
  return(df)
}

0 голосов
/ 05 февраля 2019

Мы можем попытаться построить шаблон регулярных выражений совпадающих идентификаторов, а затем использовать grepl, чтобы найти совпадающие строки в вашем фрейме данных:

regex <- paste0("\\b(", paste(subid, collapse="|"), ")\\b")
df$subscriberid <- ifelse(grepl(regex, df$subscriberid),
    paste0("9", substr(df$subscriberid, 2, nchar(df$subscriberid))),
    df$subscriberid)

df
  id subscriberid  intName
1  1   9234567890 asdfsadf
2  2   3243245324  dfsafdf
3  3   4532453245  dasdfsd
0 голосов
/ 05 февраля 2019

Несколько иная возможность - использовать sub():

df$subscriberid <- ifelse(df$subscriberid %in% subid, 
                   sub(".", "9", df$subscriberid), df$subscriberid)

  id subscriberid  intName
1  1   9234567890 asdfsadf
2  2   3243245324  dfsafdf
3  3   4532453245  dasdfsd

Здесь, если «subscriberid» соответствует «subid», первый символ в «subscriberid» заменяется на 9, в противном случае он остаетсято же самое.

0 голосов
/ 05 февраля 2019

Один из вариантов - использовать ifelse, и если subscriberid присутствует в subid, тогда мы paste 9 с оставшейся строкой, начиная со второго индекса.

df$subscriberid <- with(df, ifelse(subscriberid %in% subid,
                    paste0("9",substring(subscriberid,2)), subscriberid))

df
#  id subscriberid  intName
#1  1   9234567890 asdfsadf
#2  2   3243245324  dfsafdf
#3  3   4532453245  dasdfsd

Преимущество использования substring - вам нужно упомянуть только начальный индекс (здесь 2), значение по умолчанию для остановки - 1000000, которое охватывает большинство строк.

...