【问题标题】:Efficient way to calculate date-by-date rolling aggregation of the last 30 days计算过去 30 天的逐日滚动聚合的有效方法
【发布时间】:2019-01-24 00:58:46
【问题描述】:

我的代码执行得很好,但是需要大量时间才能完成。如果可能的话,需要一些帮助来优化代码,一种在多列上执行滚动聚合的方法。

我通过创建一个函数并使用 library(data.table) 向量化我的数据框来尝试其他一些方法,但这样做没有成功,我实际上得到了我应该得到的一半,我只能用一次一列。

#   Creating functions
fun <- function(x, date, thresh) {
    D <- as.matrix(dist(date)) #distance matrix between dates
    D <- D <= thresh
    D[lower.tri(D)] <- FALSE #don't sum to future
    R <- D * x #FALSE is treated as 0
    colMeans(R, na.rm = TRUE)
}

setDT(df_2)
df_2[, invoiceDate := as.Date(invoiceDate, format = "%m/%d/%Y")]
setkey(df_2, cod_unb, cod_pdv, invoiceDate)

df_2[, volume_total_diario_RT30 := fun(volume_total_diario, invoiceDate, 30), by = list(cod_unb, cod_pdv)]

这是我当前运行良好的代码,但需要太多时间(超过 8 小时才能处理 30 天)

years <- c(2017:2019)
months <- c(1:12)
days <- c(1:31)

df_final <- df_n[1,c('cod_unb','cod_pdv','cpf_cnpj','idade_pdv_meses','status_telefone','col1','col2','col3','year','month','day')] #eliminating first line

for (i in years) {
    for (j in months) {
        for (k in days) {
            if (j == 1){
                df_temp <- df_n[(df_n$years == i & df_n$months == j & df_n$days <= k) | (df_n$years == (i-1) & df_n$months == 12 & df_n$days >= k),]    
            }                                    
            if (j != 1){                                   
                df_temp <- df_n[(df_n$years == i & df_n$months == j & df_n$days <= k) | (df_n$years == i & df_n$months == (j - 1) & df_n$days >= k),] 
            }

            #Agreggate.
            if(nrow(df_temp) >= 1){
df_temp <- aggregate(df_temp[, c('col1','col2','col3')], by = list(df_temp$cod_unb,df_temp$cod_pdv,df_temp$cpf_cnpj,df_temp$idade_pdv_meses,df_temp$status_telefone), FUN = mean)

names(df_temp)[names(df_temp) == "Group.1"] <- "cod_unb"
names(df_temp)[names(df_temp) == "Group.2"] <- "cod_pdv"
names(df_temp)[names(df_temp) == "Group.3"] <- "cpf_cnpj"
names(df_temp)[names(df_temp) == "Group.4"] <- "idade_pdv_meses"
names(df_temp)[names(df_temp) == "Group.5"] <- "status_telefone"

        df_temp$years <- i         
        df_temp$months <- j
        df_temp$days <- k        
        df_final <- rbind(df_final,df_temp)
            }                                
        }                       
    }           
}

df_final <- df_final[-1,]

输出应该是列R30

cod_unb;cod_pdv;Years;Months;Days;date;volume_total_diario;R30
111;1005;2018;11;3;03/11/2018;0.48;
111;1005;2018;11;9;09/11/2018;0.79035;
111;1005;2018;11;16;16/11/2018;1.32105;
111;1005;2018;11;24;24/11/2018;0.6414;
111;1005;2018;11;30;30/11/2018;0.6;
111;1005;2018;12;7;07/12/2018;1.79175;1.02891
111;1005;2018;12;15;15/12/2018;1.4421;1.15926
111;1005;2018;12;21;21/12/2018;0.48;0.99105
111;1005;2018;12;28;28/12/2018;0.5535;0.97347
111;1005;2019;1;4;04/01/2019;0.36;0.92547

【问题讨论】:

  • 您每天要汇总 30 次。您可以汇总到一天,然后计算这些每日汇总中的滚动平均值。
  • 我没有每天为同一个键注册一个注册表。我的客户通常每周购买一次或两次。我正在尝试汇总客户在 d-30 日期范围内每天的所有购买。
  • 那么根据每次购买将所有 d 到 d+30 的总和相加可能是最有意义的。对聚合的调用很慢,您需要用一些代码替换它,以实现您所需要的。

标签: r loops optimization aggregate


【解决方案1】:

如果我理解正确,OP 已要求在 30 天的滚动期内汇总值并将这些汇总附加到原始数据中。

这可以通过在非等值连接中聚合来有效解决

这是一个使用 OP 提供的样本数据的变量示例:

library(data.table)
# coerce to data.table, coerce character date to class IDate
setDT(df_n)[, date := as.IDate(date, "%d/%m/%Y")]
# intermediate result for demonstration:
df_n[.(upper = date, lower = date - 30), on = .(date <= upper, date >= lower), 
     mean(volume_total_diario), by = .EACHI]
          date       date       V1
 1: 2018-11-03 2018-10-04 0.480000
 2: 2018-11-09 2018-10-10 0.635175
 3: 2018-11-16 2018-10-17 0.863800
 4: 2018-11-24 2018-10-25 0.808200
 5: 2018-11-30 2018-10-31 0.766560
 6: 2018-12-07 2018-11-07 1.028910
 7: 2018-12-15 2018-11-15 1.159260
 8: 2018-12-21 2018-11-21 0.991050
 9: 2018-12-28 2018-11-28 0.973470
10: 2019-01-04 2018-12-05 0.925470

中间结果显示聚合中包含的日期范围的上限和下限以及各个期间的聚合值。这可用于向df_n 添加新列:

# update df_n by appending new column
setDT(df_n)[, R30_new := df_n[.(upper = date, lower = date - 30), on = .(date <= upper, date >= lower), 
                       mean(volume_total_diario), by = .EACHI]$V1]
df_n
    cod_unb cod_pdv Years Months Days       date volume_total_diario     R30  R30_new
 1:     111    1005  2018     11    3 2018-11-03             0.48000      NA 0.480000
 2:     111    1005  2018     11    9 2018-11-09             0.79035      NA 0.635175
 3:     111    1005  2018     11   16 2018-11-16             1.32105      NA 0.863800
 4:     111    1005  2018     11   24 2018-11-24             0.64140      NA 0.808200
 5:     111    1005  2018     11   30 2018-11-30             0.60000      NA 0.766560
 6:     111    1005  2018     12    7 2018-12-07             1.79175 1.02891 1.028910
 7:     111    1005  2018     12   15 2018-12-15             1.44210 1.15926 1.159260
 8:     111    1005  2018     12   21 2018-12-21             0.48000 0.99105 0.991050
 9:     111    1005  2018     12   28 2018-12-28             0.55350 0.97347 0.973470
10:     111    1005  2019      1    4 2019-01-04             0.36000 0.92547 0.925470

R30R30_new 的值相同; R30_new 还包含前 5 行的结果。

警告

为了清楚起见,已忽略其他分组变量,但可以轻松包含在内。此外,该解决方案可以扩展为聚合多个值列。

数据

library(data.table)
df_n <- fread("
cod_unb;cod_pdv;Years;Months;Days;date;volume_total_diario;R30
111;1005;2018;11;3;03/11/2018;0.48;
111;1005;2018;11;9;09/11/2018;0.79035;
111;1005;2018;11;16;16/11/2018;1.32105;
111;1005;2018;11;24;24/11/2018;0.6414;
111;1005;2018;11;30;30/11/2018;0.6;
111;1005;2018;12;7;07/12/2018;1.79175;1.02891
111;1005;2018;12;15;15/12/2018;1.4421;1.15926
111;1005;2018;12;21;21/12/2018;0.48;0.99105
111;1005;2018;12;28;28/12/2018;0.5535;0.97347
111;1005;2019;1;4;04/01/2019;0.36;0.92547
")

编辑:聚合多个变量

由于 OP 要求 一种在多列上执行滚动聚合的方法,这是一个示例。

首先,我们需要在 OP 的示例数据集中创建一个附加值 var:

df_n <- fread("
cod_unb;cod_pdv;Years;Months;Days;date;volume_total_diario;R30
111;1005;2018;11;3;03/11/2018;0.48;
111;1005;2018;11;9;09/11/2018;0.79035;
111;1005;2018;11;16;16/11/2018;1.32105;
111;1005;2018;11;24;24/11/2018;0.6414;
111;1005;2018;11;30;30/11/2018;0.6;
111;1005;2018;12;7;07/12/2018;1.79175;1.02891
111;1005;2018;12;15;15/12/2018;1.4421;1.15926
111;1005;2018;12;21;21/12/2018;0.48;0.99105
111;1005;2018;12;28;28/12/2018;0.5535;0.97347
111;1005;2019;1;4;04/01/2019;0.36;0.92547
")[
  , date := as.IDate(date, "%d/%m/%Y")][, var2 := .I][]
df_n
   cod_unb cod_pdv Years Months Days       date volume_total_diario     R30 var2
 1:     111    1005  2018     11    3 2018-11-03             0.48000      NA    1
 2:     111    1005  2018     11    9 2018-11-09             0.79035      NA    2
 3:     111    1005  2018     11   16 2018-11-16             1.32105      NA    3
 4:     111    1005  2018     11   24 2018-11-24             0.64140      NA    4
 5:     111    1005  2018     11   30 2018-11-30             0.60000      NA    5
 6:     111    1005  2018     12    7 2018-12-07             1.79175 1.02891    6
 7:     111    1005  2018     12   15 2018-12-15             1.44210 1.15926    7
 8:     111    1005  2018     12   21 2018-12-21             0.48000 0.99105    8
 9:     111    1005  2018     12   28 2018-12-28             0.55350 0.97347    9
10:     111    1005  2019      1    4 2019-01-04             0.36000 0.92547   10

因此,添加了一列var2(仅包含行号)。

这是使用相同聚合函数聚合多个列的代码:

cols <- c("volume_total_diario", "var2")
setDT(df_n)[, paste0("mean_", cols) := 
       df_n[.(upper = date, lower = date - 30), 
            on = .(date <= upper, date >= lower), 
            lapply(.SD, mean), 
            .SDcols = cols, by = .EACHI][
              , .SD, .SDcols = cols]][]
df_n
    cod_unb cod_pdv Years Months Days       date volume_total_diario     R30 var2 mean_volume_total_diario mean_var2
 1:     111    1005  2018     11    3 2018-11-03             0.48000      NA    1                 0.480000       1.0
 2:     111    1005  2018     11    9 2018-11-09             0.79035      NA    2                 0.635175       1.5
 3:     111    1005  2018     11   16 2018-11-16             1.32105      NA    3                 0.863800       2.0
 4:     111    1005  2018     11   24 2018-11-24             0.64140      NA    4                 0.808200       2.5
 5:     111    1005  2018     11   30 2018-11-30             0.60000      NA    5                 0.766560       3.0
 6:     111    1005  2018     12    7 2018-12-07             1.79175 1.02891    6                 1.028910       4.0
 7:     111    1005  2018     12   15 2018-12-15             1.44210 1.15926    7                 1.159260       5.0
 8:     111    1005  2018     12   21 2018-12-21             0.48000 0.99105    8                 0.991050       6.0
 9:     111    1005  2018     12   28 2018-12-28             0.55350 0.97347    9                 0.973470       7.0
10:     111    1005  2019      1    4 2019-01-04             0.36000 0.92547   10                 0.925470       8.0

请注意,新列已以编程方式命名。

【讨论】:

  • 我收到一个错误:unused argument (on = .(date &lt;= upper, date &gt;= lower))
  • 一个可能的原因:df_n 不属于 data.table 类。请与class()str() 联系。
  • 当我运行setDT(df_n) 时,它应该设置为class data.table,不是吗?
  • 我运行了完全相同的代码并进行了正确的调整,但仍然出现错误
  • 是的setDT(df_n) 应该将df_n 的类设置为data.table。是否涉及其他数据框?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
  • 1970-01-01
  • 2022-12-07
  • 2022-01-18
相关资源
最近更新 更多