将 for-loop 转换为 -apply 函数,其中输入是数据帧而不是向量
Converting for-loop into -apply function where input is a dataframe not vector
我有一个包含 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
}
}
}
}
更新:
好吧,老实说,我昨晚和早上都尝试自己工作,但无法想出新的解决方案 problem.I 尝试使用建议的方法,但一个小问题是他们开始计算"sincesale" 从第一行开始(因为 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
使用 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)
我有一个包含 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
}
}
}
}
更新:
好吧,老实说,我昨晚和早上都尝试自己工作,但无法想出新的解决方案 problem.I 尝试使用建议的方法,但一个小问题是他们开始计算"sincesale" 从第一行开始(因为 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
使用 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)