Вопрос

Я привык к Python и JS и довольно новичок в R, но мне он нравится для анализа данных.Я хотел создать новое поле в своем фрейме данных на основе некоторой логики if/else и попытался сделать это стандартным/процедурным способом:

for (i in 1:nrow(df)) {
  if (is.na(df$First_Payment_date[i]) == TRUE) {
    df$User_status[i] = "User never paid"
  } else if (df$Payment_Date[i] >= df$First_Payment_date[i]) {
    df$User_status[i] = "Paying user"
  } else if (df$Payment_Date[i] < df$First_Payment_date[i]) {
    df$User_status[i] = "Attempt before first payment"
  } else {
    df$User_status[i] = "Error"
  }
}

Но это было БЕЗУМНО медленно.Я попробовал запустить это на фрейме данных из примерно 3 миллионов строк, и это заняло слишком много времени.Есть какие-нибудь советы о том, как это сделать с помощью буквы «R»?

Обратите внимание, что df$Payment_Date и df$First_Payment_date поля форматируются как даты.

Это было полезно?

Решение

Я сравниваю data.frame и data.table для относительно большого набора данных.

Сначала мы генерируем некоторые данные.

set.seed(1234)
library(data.table)
df = data.frame(First_Payment_date=c(sample(c(NA,1:100),1000000, replace=1)),
                 Payment_Date=c(sample(1:100,1000000, replace=1)))
dt = data.table(df)

Затем установите эталон.Я тестирую ответ @BondedDust и его data.table эквивалентность.Я немного модифицировал (отладил) его код.

library(microbenchmark)

test_df = function(){
    df$User_status <- "Error"
    df$User_status[ is.na(df$First_Payment_date) ] <- "User never paid"
    df$User_status[ df$Payment_Date >= df$First_Payment_date ] <- "Paying user"
    df$User_status[ df$Payment_Date < df$First_Payment_date ] <- "Attempt before first payment"
}

test_dt = function(){
    dt[, User_status := "Error"]
    dt[is.na(First_Payment_date), User_status := "User never paid"]
    dt[Payment_Date >= First_Payment_date, User_status := "Paying user"]
    dt[Payment_Date < First_Payment_date, User_status := "Attempt before first payment"]
}

microbenchmark(test_df(), test_dt(), times=10)

Результат: data.table в 4 раза быстрее, чем data.frame для сгенерированных данных из 1 миллиона строк.

> microbenchmark(test_df(), test_dt(), times=10)
Unit: milliseconds
      expr       min        lq    median        uq       max neval
 test_df() 247.29182 256.69067 287.89768 319.34873 330.33915    10
 test_dt()  66.74265  69.42574  70.27826  72.93969  80.89847    10

Примечание

data.frame быстрее, чем data.table для небольшого набора данных (скажем, 10 000 строк).

Другие советы

Если вы инициализируете на «Ошибка», а затем перезаписать для условий, перечисленных с использованием логической индексации, это должно быть намного быстрее.Те, если () {} else {} утверждения для каждой строки убивают вас.

df$User_status <- "Error"
df$User_status[ is.na(df$First_Payment_date) ] <- "User never paid"
df$User_status[ df$Payment_Date >= df$First_Payment_date ] <- "Paying user"
df$User_status[ df$Payment_Date < df$First_Payment_date ] <- "Attempt before first payment"
.

Я не уверен, что это много ускорит, но вы должны увидеть некоторое улучшение по поводу петлей for, который вы имели раньше.Генеракодицетагкод на самом деле не нужен в этих условиях.

также, R имеет функции, которые действуют как генеракодицетагкодные петли, а также другие типы петель.Смотрите else.

Дайте это выстрел, посмотрите, как это работает.Я не могу проверить это, так как у нас нет ваших данных.

> df$User_status[i] <- rep("Error", nrow(df)) 
      ## allocate a vector, fill it with "Error"

> sapply(seq(nrow(df)), function(i){

    if(is.na(df$First_Payment_date[i])){ 
      gsub("Error", "User never paid", df$User_status[i]) }

    if(df$Payment_Date[i] >= df$First_Payment_date[i]){
      gsub("Error", "Paying user", df$User_status[i]) }

    if (df$Payment_Date[i] < df$First_Payment_date[i]) {
      gsub("Error", "Attempt before first payment", df$User_status[i]) }

    })
.

Обычный способ обрабатывать такого рода вещей - через ifelse.

df$User_status <- with(df,
    ifelse(is.na(First_Payment_date), "User never paid",
    ifelse(Payment_Date >= First_Payment_date, "Paying user",
    ifelse(Payment_Date < First_Payment_date, "Attempt before first payment",
    "Error"))))
.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top