【问题标题】:Converting for-loop into -apply function where input is a dataframe not vector将 for 循环转换为 -apply 函数,其中输入是数据帧而不是向量
【发布时间】:2017-06-12 05:07:29
【问题描述】:

我有一个包含 3 列的数据,大致如下所示:

uid <- c(1,1,1,1,1,1,2,2,2)
sale <- c(0,1,1,0,0,0,0,1,0)
e <- as.data.frame(cbind(uid, sale))
e$uid <- as.factor(e$uid)
e$sincesale <- NA

对于每个唯一 ID,我想应用相同的程序 - 计算自上次销售以来的天数。

我可以很容易地想出可以做到这一点的 for 循环。问题是我有数百万行。因此,完成此过程需要花费太多时间。我想在e$uid 上使用tapply。但是,tapply 只接受向量作为输入。

可以使用什么替代方法(比 for 循环更快)?

我的 for 循环:

for (i in 2:length(e$uid)){
  #working within the good with the same unique id (uid)
  if (e$uid[i] == e$uid[i-1]){
    if (e$sale[i]==1){
      sincesale[i] <- sincesale[i-1]+1
    }
    if (e$sale[i]==0){
      #if sale just ended, number of days since sale is 1
      if (e$sale[i-1]==1){
        e$sincesale[i] <- 1
      }
      #if sale ended a few periods ago add 1 to previous value of "sincesale"
      if (e$sale[i-1] == 0){
        e$sincesale[i] <- e$sincesale[i-1] + 1
      }
    }
  }
}

更新:

好的,老实说,我昨晚和早上都尝试自己工作,但无法想出解决新问题的方法。我尝试使用建议的方法,但一个小问题是他们开始计算“自销售”第一行(因为即使销售不是从头开始的,第一行的 sale==0 也是如此)。以下示例输入使用 for-loop ("sincesale") 和建议的 dplyr ("sincesale4") 生成结果:

uid <- c(1,1,1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4)
sale <- c(0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0)
e <- as.data.frame(cbind(uid, sale))
e$uid <- as.factor(e$uid)

   uid sale first sincesale sincesale4
1    1    0     1        NA          0
2    1    0     1        NA          1
3    1    1     0        NA          1
4    1    0     0         1          2
5    1    0     0         2          3
6    1    0     0         3          4
7    2    0     1        NA          0
8    2    1     1        NA          0
9    2    0     0         1          1
10   2    1     0        NA          1
11   3    0     1        NA          0
12   3    0     1        NA          1
13   3    0     0        NA          2
14   3    0     0        NA          3
15   3    0     0        NA          4
16   3    0     0        NA          5
17   3    1     0        NA          5
18   3    1     0        NA          5
19   3    0     0         1          6
20   4    0     1        NA          0
21   4    0     1        NA          1
22   4    0     0        NA          2

【问题讨论】:

  • 我相信只有e &lt;- data.frame(uid, sale); e$uid &lt;- as.factor(e$uid); e$sincesale &lt;- NA 应该对其进行排序。

标签: r for-loop tapply


【解决方案1】:

使用ave 在每个uid 组内查看并获得非销售天数cumsum 的累计总和:

e$sincesale2 <- ave(!e$sale, e$uid, FUN=cumsum)-1

#  uid sale sincesale sincesale2
#1   1    0        NA          0
#2   1    1        NA          0
#3   1    1        NA          0
#4   1    0         1          1
#5   1    0         2          2
#6   1    0         3          3
#7   2    0        NA          0
#8   2    1        NA          0
#9   2    0         1          1

转换为data.table 将是:

library(data.table)
setDT(e)
e[, sincesale3 := cumsum(!sale)-1, by=uid]

dplyr 向@RonakShah 致敬:

library(dplyr)
e %>%
  group_by(uid) %>%
  mutate(sincesale4 = cumsum(!sale)-1)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-02
    • 1970-01-01
    • 2023-04-05
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多