0

我习惯了 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"
  }
}

但这太慢了。我尝试在大约 300 万行的数据帧上运行它,但它花费的时间太长了。关于“R”方式的任何提示?

请注意,df$Payment_Datedf$First_Payment_date字段被格式化为日期。

4

4 回答 4

2

如果您初始化为“错误”,然后覆盖使用逻辑索引枚举的条件,这应该会快得多。每行的那些 if(){}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"
于 2014-04-12T07:42:02.727 回答
1

我正在基准测试data.framedata.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.tabledata.frame生成的 100 万行数据快 4 倍。

> 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.framedata.table小型数据集(比如 10000 行)更快。

于 2014-04-12T08:26:45.787 回答
0

我不确定这是否会大大加快速度,但是您应该会看到比for以前的循环有所改进。在这些条件下,else' 并不是真正必要的。

此外,R 具有充当for循环和其他类型循环的函数。见?apply

试一试,看看它是如何工作的。我无法测试它,因为我们没有您的数据。

> 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]) }

    })
于 2014-04-12T07:48:46.193 回答
0

处理这类事情的通常方法是 via 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"))))
于 2014-04-12T09:46:13.480 回答