通过 data.table 非相等连接的相对窗口 运行 求和
relative windowed running sum through data.table non-equi join
我将数据集 customerId、transactionDate、productId、purchaseQty 加载到 data.table。对于每一行,我想计算前 45 天的总和和 purchaseQty 的平均值
productId customerID transactionDate purchaseQty
1: 870826 1186951 2016-03-28 162000
2: 870826 1244216 2016-03-31 5000
3: 870826 1244216 2016-04-08 6500
4: 870826 1308671 2016-03-28 221367
5: 870826 1308671 2016-03-29 83633
6: 870826 1308671 2016-11-29 60500
我正在寻找这样的输出:
productId customerID transactionDate purchaseQty sumWindowPurchases
1: 870826 1186951 2016-03-28 162000 162000
2: 870826 1244216 2016-03-31 5000 5000
3: 870826 1244216 2016-04-08 6500 11500
4: 870826 1308671 2016-03-28 221367 221367
5: 870826 1308671 2016-03-29 83633 305000
6: 870826 1308671 2016-11-29 60500 60500
因此,sumWindowPurchases 包含从当前交易日期起 45 天 window customer/product 的购买数量总和。一旦我开始工作,抛出平均值,我需要的其他计算应该是微不足道的
我回到我的 SQL 根源并想到了自我加入:
select DT.customerId, DT.transactionDate, DT.productId, sum(DT1.purchaseQty)
from DT
inner join DT as DT1 on
DT.customerId = DT1.customerId
and DT.productId = DT1.productId
and DT1.transactionDate between DT.transactionDate and dateadd(day, -45, DT.transactionDate)
尝试使用 data.dable 语法将其翻译成 R,我希望做这样的事情:
DT1 <- DT #alias. have confirmed this is just a pointer
DT[DT1[DT1$transactionDate >= DT$transactionDate - 45],
.(sum(DT1$purchaseQty)),
by = .(DT$customerId , DT$transactionDate ),
on = .(customerId , DT1$transactionDate <= DT$TransactionDate),
allow.cartesian = TRUE]
我想我有一个两部分的问题。什么是"R way"来做到这一点。 data.table self join 是正确的方法,还是尝试使用 Reduce 函数会更好?
我怀疑自我加入是获得滚动 45 天 window 的唯一方法。所以第 2 部分是我需要一些关于 data.table 语法的帮助来显式引用 table 列来自哪个源,因为它是自连接并且它们具有相同的列名。
我一直在研究 Frank 链接到的答案并想出了这个表达式
DT[.(p = productId, c = customerID, t = transactionDate, start = transactionDate - 45),
on = .(productId==p, customerID==c, transactionDate<=t, transactionDate>=start),
allow.cartesian = TRUE, nomatch = 0]
产生此输出:
productId customerID transactionDate purchaseQty transactionDate.1
1: 870826 1186951 2016-03-28 162000 2016-02-12
2: 870826 1244216 2016-03-31 5000 2016-02-15
3: 870826 1244216 2016-04-08 5000 2016-02-23
4: 870826 1244216 2016-04-08 6500 2016-02-23
5: 870826 1308671 2016-03-28 221367 2016-02-12
6: 870826 1308671 2016-03-29 221367 2016-02-13
7: 870826 1308671 2016-03-29 83633 2016-02-13
8: 870826 1308671 2016-11-29 60500 2016-10-15
这非常接近我需要完成的最后一步。如果我可以将此输出的购买数量相加,按 customer/product/transactionDate.1 分组,我会有一些有用的东西。但是,我无法理解语法,我不明白 transactionDate.1 名称的来源
首先,我们找出在当前日期(包括当前日期)之前window天window有多少个交易日期
setDT(df)
df[, n:= 1:.N - findInterval(transactionDate - 45, transactionDate), by=.(customerID)]
df
# productId customerID transactionDate purchaseQty n
#1: 870826 1186951 2016-03-28 162000 1
#2: 870826 1244216 2016-03-31 5000 1
#3: 870826 1244216 2016-04-08 6500 2
#4: 870826 1308671 2016-03-28 221367 1
#5: 870826 1308671 2016-03-29 83633 2
#6: 870826 1308671 2016-11-29 60500 1
接下来我们找到 purchaseQty
的滚动总和 window 大小 n
。采用一个很好的答案
g <- function(x, window){
b_pos <- seq_along(x) - window + 1 # begin positions
cum <- cumsum(x)
cum - cum[b_pos] + x[b_pos]
}
df[, sumWindowPurchases := g(purchaseQty, n),][,n:=NULL,]
df
# productId customerID transactionDate purchaseQty sumWindowPurchases
#1: 870826 1186951 2016-03-28 162000 162000
#2: 870826 1244216 2016-03-31 5000 5000
#3: 870826 1244216 2016-04-08 6500 11500
#4: 870826 1308671 2016-03-28 221367 221367
#5: 870826 1308671 2016-03-29 83633 305000
#6: 870826 1308671 2016-11-29 60500 60500
数据
structure(list(productId = c(870826L, 870826L, 870826L, 870826L,
870826L, 870826L), customerID = c(1186951L, 1244216L, 1244216L,
1308671L, 1308671L, 1308671L), transactionDate = structure(c(16888,
16891, 16899, 16888, 16889, 17134), class = "Date"), purchaseQty = c(162000L,
5000L, 6500L, 221367L, 83633L, 60500L)), .Names = c("productId",
"customerID", "transactionDate", "purchaseQty"), row.names = c("1:",
"2:", "3:", "4:", "5:", "6:"), class = "data.frame")
这个也可以,可以考虑简单一些。它的优点是不需要排序的输入集,并且依赖性更少。
我仍然不明白为什么它会在输出中产生 2 个 transactionDate 列。这似乎是 "on" 子句的副产品。事实上,输出的列和顺序似乎在 on 子句的所有元素之后附加了总和,没有它们的别名
DT[.(p=productId, c=customerID, tmin=transactionDate - 45, tmax=transactionDate),
on = .(productId==p, customerID==c, transactionDate<=tmax, transactionDate>=tmin),
.(windowSum = sum(purchaseQty)), by = .EACHI, nomatch = 0]
我将数据集 customerId、transactionDate、productId、purchaseQty 加载到 data.table。对于每一行,我想计算前 45 天的总和和 purchaseQty 的平均值
productId customerID transactionDate purchaseQty
1: 870826 1186951 2016-03-28 162000
2: 870826 1244216 2016-03-31 5000
3: 870826 1244216 2016-04-08 6500
4: 870826 1308671 2016-03-28 221367
5: 870826 1308671 2016-03-29 83633
6: 870826 1308671 2016-11-29 60500
我正在寻找这样的输出:
productId customerID transactionDate purchaseQty sumWindowPurchases
1: 870826 1186951 2016-03-28 162000 162000
2: 870826 1244216 2016-03-31 5000 5000
3: 870826 1244216 2016-04-08 6500 11500
4: 870826 1308671 2016-03-28 221367 221367
5: 870826 1308671 2016-03-29 83633 305000
6: 870826 1308671 2016-11-29 60500 60500
因此,sumWindowPurchases 包含从当前交易日期起 45 天 window customer/product 的购买数量总和。一旦我开始工作,抛出平均值,我需要的其他计算应该是微不足道的
我回到我的 SQL 根源并想到了自我加入:
select DT.customerId, DT.transactionDate, DT.productId, sum(DT1.purchaseQty)
from DT
inner join DT as DT1 on
DT.customerId = DT1.customerId
and DT.productId = DT1.productId
and DT1.transactionDate between DT.transactionDate and dateadd(day, -45, DT.transactionDate)
尝试使用 data.dable 语法将其翻译成 R,我希望做这样的事情:
DT1 <- DT #alias. have confirmed this is just a pointer
DT[DT1[DT1$transactionDate >= DT$transactionDate - 45],
.(sum(DT1$purchaseQty)),
by = .(DT$customerId , DT$transactionDate ),
on = .(customerId , DT1$transactionDate <= DT$TransactionDate),
allow.cartesian = TRUE]
我想我有一个两部分的问题。什么是"R way"来做到这一点。 data.table self join 是正确的方法,还是尝试使用 Reduce 函数会更好?
我怀疑自我加入是获得滚动 45 天 window 的唯一方法。所以第 2 部分是我需要一些关于 data.table 语法的帮助来显式引用 table 列来自哪个源,因为它是自连接并且它们具有相同的列名。
我一直在研究 Frank 链接到的答案并想出了这个表达式
DT[.(p = productId, c = customerID, t = transactionDate, start = transactionDate - 45),
on = .(productId==p, customerID==c, transactionDate<=t, transactionDate>=start),
allow.cartesian = TRUE, nomatch = 0]
产生此输出:
productId customerID transactionDate purchaseQty transactionDate.1
1: 870826 1186951 2016-03-28 162000 2016-02-12
2: 870826 1244216 2016-03-31 5000 2016-02-15
3: 870826 1244216 2016-04-08 5000 2016-02-23
4: 870826 1244216 2016-04-08 6500 2016-02-23
5: 870826 1308671 2016-03-28 221367 2016-02-12
6: 870826 1308671 2016-03-29 221367 2016-02-13
7: 870826 1308671 2016-03-29 83633 2016-02-13
8: 870826 1308671 2016-11-29 60500 2016-10-15
这非常接近我需要完成的最后一步。如果我可以将此输出的购买数量相加,按 customer/product/transactionDate.1 分组,我会有一些有用的东西。但是,我无法理解语法,我不明白 transactionDate.1 名称的来源
首先,我们找出在当前日期(包括当前日期)之前window天window有多少个交易日期
setDT(df)
df[, n:= 1:.N - findInterval(transactionDate - 45, transactionDate), by=.(customerID)]
df
# productId customerID transactionDate purchaseQty n
#1: 870826 1186951 2016-03-28 162000 1
#2: 870826 1244216 2016-03-31 5000 1
#3: 870826 1244216 2016-04-08 6500 2
#4: 870826 1308671 2016-03-28 221367 1
#5: 870826 1308671 2016-03-29 83633 2
#6: 870826 1308671 2016-11-29 60500 1
接下来我们找到 purchaseQty
的滚动总和 window 大小 n
。采用一个很好的答案
g <- function(x, window){
b_pos <- seq_along(x) - window + 1 # begin positions
cum <- cumsum(x)
cum - cum[b_pos] + x[b_pos]
}
df[, sumWindowPurchases := g(purchaseQty, n),][,n:=NULL,]
df
# productId customerID transactionDate purchaseQty sumWindowPurchases
#1: 870826 1186951 2016-03-28 162000 162000
#2: 870826 1244216 2016-03-31 5000 5000
#3: 870826 1244216 2016-04-08 6500 11500
#4: 870826 1308671 2016-03-28 221367 221367
#5: 870826 1308671 2016-03-29 83633 305000
#6: 870826 1308671 2016-11-29 60500 60500
数据
structure(list(productId = c(870826L, 870826L, 870826L, 870826L,
870826L, 870826L), customerID = c(1186951L, 1244216L, 1244216L,
1308671L, 1308671L, 1308671L), transactionDate = structure(c(16888,
16891, 16899, 16888, 16889, 17134), class = "Date"), purchaseQty = c(162000L,
5000L, 6500L, 221367L, 83633L, 60500L)), .Names = c("productId",
"customerID", "transactionDate", "purchaseQty"), row.names = c("1:",
"2:", "3:", "4:", "5:", "6:"), class = "data.frame")
这个也可以,可以考虑简单一些。它的优点是不需要排序的输入集,并且依赖性更少。
我仍然不明白为什么它会在输出中产生 2 个 transactionDate 列。这似乎是 "on" 子句的副产品。事实上,输出的列和顺序似乎在 on 子句的所有元素之后附加了总和,没有它们的别名
DT[.(p=productId, c=customerID, tmin=transactionDate - 45, tmax=transactionDate),
on = .(productId==p, customerID==c, transactionDate<=tmax, transactionDate>=tmin),
.(windowSum = sum(purchaseQty)), by = .EACHI, nomatch = 0]