尝试在R中复制SQL窗口函数时遇到麻烦,尤其是与创建指定我要求和的前几个月总数的求和有关。
尽管R中的sqldf包允许进行数据操作,但它似乎不支持窗口功能。
我在R中有一些模拟数据
?
对于每行,我想为前两个月(不包括当月)创建一个与客户有关的值的累积总和(Cust_ID)。
这意味着对于每个客户,第1个月和第2个月的行应为空(假设前两个月没有2个行),第3个月应包含该客户的第1个月和第2个月的SalesValue总和,而第4个月应包含包含第2个月和第3个月的总销售价值。
在SQL中,我将使用类似于以下的语法:SUM(SalesValue)OVER(PUSTITION按Cust_ID排序按月DESC行在2个PRECEDING和1个PRECEDING之间)作为PAST_3Y_SALES
在R中是否可以实现这一点-理想情况下使用data.table(为了提高效率)?任何指导将不胜感激。
PS注意:这是模拟数据,在我的“真实”数据中,客户拥有不同的数据量-即某些客户拥有5个月的数据,其他客户具有> 36个月的数据,等等。
答案 0 :(得分:4)
由于OP已使用data.table
,因此使用RcppRoll::roll_sumr
范围为data.table
的解决方案可以是:
library(data.table)
library(RcppRoll)
# Order on 'Cust_ID' and 'Month'
setkeyv(data_1,c("Cust_ID","Month"))
data_1[, Sum_prev:=shift(roll_sumr(SalesValue, n=2)), by=Cust_ID]
data_1
# Cust_ID Month StatusCode SalesValue Sum_prev
# 1: 1 1 D 1055 NA
# 2: 1 2 F 669 NA
# 3: 1 3 E 495 1724
# 4: 1 4 D 786 1164
# 5: 2 1 E 445 NA
# 6: 2 2 D 448 NA
# 7: 2 3 F 377 893
# 8: 2 4 E 173 825
# 9: 3 1 F 873 NA
# 10: 3 2 E 995 NA
# 11: 3 3 D 673 1868
# 12: 3 4 F 943 1668
方法是首先计算宽度为2
的总和,然后使用data.table::shift
和lag
来获取具有前两行总和的当前行的先前值。
答案 1 :(得分:2)
这是使用dplyr
library(dplyr)
library(zoo)
as.data.frame(data_1) %>% group_by(Cust_ID) %>% arrange(Cust_ID, Month) %>%
mutate(Sum_prev =rollapplyr(SalesValue, list(-(1:2)), sum, fill = NA))
# A tibble: 12 x 5
# Groups: Cust_ID [3]
Cust_ID Month StatusCode SalesValue Sum_prev
<dbl> <dbl> <chr> <dbl> <dbl>
1 1 1 D 1055 NA
2 1 2 F 669 NA
3 1 3 E 495 1724
4 1 4 D 786 1164
5 2 1 E 445 NA
6 2 2 D 448 NA
7 2 3 F 377 893
8 2 4 E 173 825
9 3 1 F 873 NA
10 3 2 E 995 NA
11 3 3 D 673 1868
12 3 4 F 943 1668
使用data.table:
library(data.table)
library(zoo)
#dt <- data_1[order(Cust_ID,Month)]
#dt[, Sum_prev:= rollapplyr(SalesValue, list(-(1:2)), sum, fill = NA), by=Cust_ID][]
#OR Without chaining
data_1[, Sum_prev := rollapplyr(SalesValue, list((1:2)), sum, fill = NA), by = Cust_ID][order(Cust_ID,Month)]
Cust_ID Month StatusCode SalesValue Sum_prev
1: 1 1 D 1055 NA
2: 1 2 F 669 NA
3: 1 3 E 495 1724
4: 1 4 D 786 1164
5: 2 1 E 445 NA
6: 2 2 D 448 NA
7: 2 3 F 377 893
8: 2 4 E 173 825
9: 3 1 F 873 NA
10: 3 2 E 995 NA
11: 3 3 D 673 1868
12: 3 4 F 943 1668
答案 2 :(得分:2)
data.table解决方案:
# sort the data first if the Month column is not ordered for any Cust_ID
data_1 <- data_1[order(Cust_ID, Month)]
# sum up the value of two previous Month for each Cust_ID
data_1[, rsum := shift(SalesValue, 1) + shift(SalesValue, 2), by = Cust_ID]
# Cust_ID Month StatusCode SalesValue rsum
# 1: 1 1 D 1055 NA
# 2: 1 2 F 669 NA
# 3: 1 3 E 495 1724
# 4: 1 4 D 786 1164
# 5: 2 1 E 445 NA
# 6: 2 2 D 448 NA
# 7: 2 3 F 377 893
# 8: 2 4 E 173 825
# 9: 3 1 F 873 NA
# 10: 3 2 E 995 NA
# 11: 3 3 D 673 1868
# 12: 3 4 F 943 1668
答案 3 :(得分:1)
1)sqldf / RpostgreSQL 您可以将窗口函数与PostgreSQL后端一起使用,并且您的代码(经过稍微修改即可工作)在R内是这样的(其中data_1
是您的数据帧工作区)。
library(RPostgreSQL)
library(sqldf)
sql <- 'select *, SUM("SalesValue") OVER (PARTITION BY "Cust_ID"
ORDER BY "Month" DESC
ROWS BETWEEN 2 PRECEDING AND 1 PRECEDING ) as PAST_3Y_SALES
from "data_1"'
sqldf(sql)
给予:
Cust_ID Month StatusCode SalesValue past_3y_sales
1 1 4 D 786 NA
2 1 3 E 495 786
3 1 2 F 669 1281
4 1 1 D 1055 1164
5 2 4 E 173 NA
6 2 3 F 377 173
7 2 2 D 448 550
8 2 1 E 445 825
9 3 4 F 943 NA
10 3 3 D 673 943
11 3 2 E 995 1616
12 3 1 F 873 1668
2)data.table / rollapply
或者使用data.table并通过rollapply使用list(-2:-1)
将宽度指定为偏移量。
下面的代码已编写为与问题中的SQL代码相对应,但如果您希望每个Cust_ID具有两个NA,而不是一个,则对前几个月的月份求和,其中月份按升序排列(不按指定的降序排列)在问题的SQL中),然后在-Month
语句中将Month
更改为setorder
,并在partial=TRUE
中删除rollapply
自变量。
library(data.table)
library(zoo)
setorder(data_1, Cust_ID, -Month)
roll <- function(x) rollapply(x, list(-2:-1), sum, partial = TRUE, fill = NA)
data_1[, past_3y_sales := roll(SalesValue), by = Cust_ID]
给予:
> data_1
Cust_ID Month StatusCode SalesValue past_3y_sales
1: 1 4 D 786 NA
2: 1 3 E 495 786
3: 1 2 F 669 1281
4: 1 1 D 1055 1164
5: 2 4 E 173 NA
6: 2 3 F 377 173
7: 2 2 D 448 550
8: 2 1 E 445 825
9: 3 4 F 943 NA
10: 3 3 D 673 943
11: 3 2 E 995 1616
12: 3 1 F 873 1668
答案 4 :(得分:0)
我遇到了类似的问题,但是上述解决方案并没有帮助我。我的数据是data_1
:
CIF_ID LEAD_RESULT
10000009 1
10000009 0
10000025 0
10000025 0
10000055 0
我需要用CIF_ID
对LEAD_RESULT求和。
我在library(data.table)
内进行了以下操作:
dt <- data.table::as.data.table(data_1)
dt<-dt[, group_sum := sum(LEAD_RESULT), by = "CIF_ID"][]
dt
结果:
CIF_ID LEAD_RESULT group_sum
10000009 1 1
10000009 0 1
10000025 0 0
10000025 0 0
10000055 0 0