如何根据每周日期创建移动平均线,按data.table中的多列分组?

问题描述 投票:0回答:2

我正在读取一个非常大的数据集作为速度的data.table。相关列是DATE(年 - 月 - 日字符串中的每周数据,例如“2017-12-25”),V1(整数),V2(字符串),V3(数字)。我想生产V4,这是V3的移动平均值,过去3周(DATEDATE-7和DATE-14)这里是一个天真的尝试/解决方案,这是非常低效的:

dt <- fread("largefile.csv")

dt$DATE <- as.IDate(dt$DATE) //convert dates to date format

V1_list <- sort(unique(dt$V1))

V2_list <- sort(unique(dt$V2))

DATE_list <- sort(unique(dt$DATE))

for(i in 1:length(V1_list)){
for(j in 1:length(V2_list)){
for(k in 3:length(DATE_list){
dt[which(dt$V1 == V1_list[i] && dt$V2 == V2_list[j] && dt$DATE == DATE_list[k]),"V4"] 
<- mean(dt[which(dt$V1 == V1_list[i] && dt$V2 == V2_list[j] && dt$DATE %in% DATE_list[k-2:k]),"V3"])
}
}
}

我正在避免使用plyr,部分原因是由于我使用的50M行的计算限制。我用setkey()zoo /滚动函数研究了选项,但我无法弄清楚如何在日期组件中进行分层(假设我按V1V2V3平均分组)。不提供示例代码的道歉。

r data.table grouping zoo moving-average
2个回答
2
投票

OP已经要求增加一个新的列,这是V3V1在过去3周内的V2的滚动平均值,用于50 M行的data.table

如果DATE值没有间隙,即在所有组中没有错过数周,一种可能的方法是使用rollmeanr()包中的zoo函数:

DT[order(DATE), V4 := zoo::rollmeanr(V3, 3L, fill = NA), by = .(V1, V2)]
DT[order(V1, V2, DATE)]
          DATE V1 V2 V3 V4
 1: 2017-12-04  1  A  1 NA
 2: 2017-12-11  1  A  2 NA
 3: 2017-12-18  1  A  3  2
 4: 2017-12-25  1  A  4  3
 5: 2017-12-04  1  B  5 NA
 6: 2017-12-11  1  B  6 NA
 7: 2017-12-18  1  B  7  6
 8: 2017-12-25  1  B  8  7
 9: 2017-12-04  2  A  9 NA
10: 2017-12-11  2  A 10 NA
11: 2017-12-18  2  A 11 10
12: 2017-12-25  2  A 12 11
13: 2017-12-04  2  B 13 NA
14: 2017-12-11  2  B 14 NA
15: 2017-12-18  2  B 15 14
16: 2017-12-25  2  B 16 15

请注意,有意引入NAs是因为我们没有每组中前两行的DATE-7和DATE-14值。

另请注意,此方法不需要字符日期的类型转换。

Data

根据OP的描述,data.table有4列:DATE是标准明确格式%Y-%m-%d的每周字符日期,V1是整数类型,V2是类型字符,V3是double(数字)类型。 V1V2用于分组。

library(data.table)
# create data
n_week = 4L
n_V1 = 2L
# cross join
DT <- CJ(
  DATE = as.character(rev(seq(as.Date("2017-12-25"), length.out = n_week, by = "-1 week"))),
  V1 = seq_len(n_V1),
  V2 = LETTERS[1:2]
)
DT[order(V1, V2, DATE), V3 := as.numeric(seq_len(.N))][]
          DATE V1 V2 V3
 1: 2017-12-04  1  A  1
 2: 2017-12-04  1  B  5
 3: 2017-12-04  2  A  9
 4: 2017-12-04  2  B 13
 5: 2017-12-11  1  A  2
 6: 2017-12-11  1  B  6
 7: 2017-12-11  2  A 10
 8: 2017-12-11  2  B 14
 9: 2017-12-18  1  A  3
10: 2017-12-18  1  B  7
11: 2017-12-18  2  A 11
12: 2017-12-18  2  B 15
13: 2017-12-25  1  A  4
14: 2017-12-25  1  B  8
15: 2017-12-25  2  A 12
16: 2017-12-25  2  B 16

0
投票

所以我尝试使用dplyr包中的两个inner_joins来解决您的问题:

首先,我创建了一个示例data.frame(1.000.000行):

V3 <- seq(from=1, to=1000000, by =1 )
DATE <- seq(from=1, to= 7000000, by =7)
dt <- data.frame(V3, DATE)

它看起来是否正确?我删除了所有不必要的内容并忽略了日期格式(你可以用与整数相同的方式减去日期)

接下来,我在DATE列上做了两个内连接,但第二个data.frame包含DATE +7和DATE +14,所以你加入了正确的日期。最后,我选择3个有趣的列并计算rowMean。我在糟糕的MacBook上花了5秒钟。

inner_join(
    inner_join(x= dt, y=mutate(dt, DATE=DATE+7), by= 'DATE'),
    y = mutate(dt, DATE= DATE+14), by= 'DATE')  %>% 
    select(V3 , V3.y, V3.x) %>% 
    rowMeans()

如果你想将它添加到你的dt中,请记住前2个日期没有平均值因为没有DATE-14和DATE-7存在。

dt$V4 <-   c(NA, NA, inner_join(
        inner_join(x= dt, y=mutate(dt, DATE=DATE+7), by= 'DATE'),
        y = mutate(dt, DATE= DATE+14), by= 'DATE')  %>% 
        select(V3 , V3.y, V3.x) %>% 
        rowMeans())
© www.soinside.com 2019 - 2024. All rights reserved.