Эффективный способ расчета скользящей агрегации по дате за последние 30 дней - PullRequest
0 голосов
/ 10 января 2019

Мои коды выполняются просто отлично, однако для их завершения требуется огромное количество времени. Хотелось бы помочь оптимизировать код, если возможно, способ выполнить скользящее агрегирование по нескольким столбцам.

Я пробовал несколько других способов, создав функцию и векторизовав мой фрейм данных с библиотекой (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

1 Ответ

0 голосов
/ 13 января 2019

Если я правильно понимаю, ОП запросил агрегировать значения за период 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

Значения R30 и R30_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 запросил способ выполнить скользящее агрегирование для нескольких столбцов , вот пример.

Сначала нам нужно создать дополнительное значение var в образце набора данных OP:

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

Обратите внимание, что новые столбцы были названы программно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...