【问题标题】:Calculate adstock using data.table使用 data.table 计算广告库存
【发布时间】:2020-01-07 07:47:55
【问题描述】:

我有一个如图所示的数据框:

structure(list(ID = c(1, 1, 1, 1, 2, 2, 2, 2), ColA = c(2, 3, 
4, 5, 2, 3, 4, 5), ColB = c(1, 2, 3, 4, 1, 2, 3, 4), ColA_0.2 = c(2, 
3.4, 4.68, 5.936, 2, 3.4, 4.68, 5.936), ColB_0.2 = c(1, 2.2, 
3.44, 4.688, 1, 2.2, 3.44, 4.688)), class = "data.frame", row.names = c(NA, 
-8L))

我需要什么? - 对于每个 ID,我想计算 ColA_adColB_ad。用户将传递一个参数“广告”。

例如 - 如果 'ad' 是 0.2,那么这些值将被计算为:

  • 第一行 - 与 ColA 相同(即 2)
  • 第二行 - 将ColA 的第二行添加到 0.2*ColA_ad 的第一行(即Sum(3,0.2*2)=3.4
  • 第三行 - 将第三行 ColA 添加到 0.2*第二行 ColA_ad(即Sum(4,0.2*3.4)=4.68) 等等。

所有其他列(此处为 ColB)的计算方式相同,可以在单独的向量中提及。

总结 - 我会取上一个计算行的 0.2 倍结转效果并添加到新行。

结果显示在ColA_adColB_ad 列中。

由于我的数据集非常大,我正在寻找 data.table 解决方案。

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    这是一个基本的 R 解决方案,其中应用了线性代数属性来加速您的迭代计算。

    • 基本思路(以id = 1为例)

      • 您首先构造一个低三角矩阵,用于从col 映射到col_ad,即,
    l <- 0.2**abs(outer(seq(4),seq(4),"-"))
    l[upper.tri(l)] <- 0
    

    给了

    > l
          [,1] [,2] [,3] [,4]
    [1,] 1.000 0.00  0.0    0
    [2,] 0.200 1.00  0.0    0
    [3,] 0.040 0.20  1.0    0
    [4,] 0.008 0.04  0.2    1
    
    • 然后你使用lover 列col,即,
    > l %*% as.matrix(subset(df,ID == 1)[-1])
          ColA  ColB
    [1,] 2.000 1.000
    [2,] 3.400 2.200
    [3,] 4.680 3.440
    [4,] 5.936 4.688
    
    • 代码
    ad <- 0.2
    col_ad <- do.call(rbind,
                      c(make.row.names = F,
                        lapply(split(df,df$ID), 
                               function(x) {
                                 l <- ad**abs(outer(seq(nrow(x)),seq(nrow(x)),"-"))
                                 l[upper.tri(l)]<- 0
                                 `colnames<-`(data.frame(l%*% as.matrix(x[-1])),paste0(names(x[-1]),"_",ad))
                               }
                        )
                      )
    )
    
    dfout <- cbind(df,col_ad)
    

    这样

    > dfout
      ID ColA ColB ColA_0.2 ColB_0.2
    1  1    2    1    2.000    1.000
    2  1    3    2    3.400    2.200
    3  1    4    3    4.680    3.440
    4  1    5    4    5.936    4.688
    5  2    2    1    2.000    1.000
    6  2    3    2    3.400    2.200
    7  2    4    3    4.680    3.440
    8  2    5    4    5.936    4.688
    
    • 数据
    df <- structure(list(ID = c(1, 1, 1, 1, 2, 2, 2, 2), ColA = c(2, 3, 
                                                                  4, 5, 2, 3, 4, 5), ColB = c(1, 2, 3, 4, 1, 2, 3, 4)), class = "data.frame", row.names = c(NA, 
                                                                                                                                                            -8L))
    
    

    【讨论】:

      【解决方案2】:

      非递归选项:

      setDT(DT)[, paste0(cols,"_",ad) := { 
          m <- matrix(unlist(shift(ad^(seq_len(.N)-1L), 0L:(.N-1L), fill = 0)), nrow=.N)
          lapply(.SD, function(x) c(m%*%x))
      }, by = ID, .SDcols = cols]
      

      另一个递归选项:

      library(data.table)
      setDT(DT)[, paste0(cols,"_",ad) := {
              a <- 0
              b <- 0
              .SD[, {
                  a <- ColA + ad*a        
                  b <- ColB + ad*b
                  .(a, b)
      
              }, seq_len(.N)][, (1) := NULL]
          }, 
          by = ID]
      

      输出:

         ID ColA ColB ColA_0.2 ColB_0.2
      1:  1    2    1    2.000    1.000
      2:  1    3    2    3.400    2.200
      3:  1    4    3    4.680    3.440
      4:  1    5    4    5.936    4.688
      5:  2    2    1    2.000    1.000
      6:  2    3    2    3.400    2.200
      7:  2    4    3    4.680    3.440
      8:  2    5    4    5.936    4.688
      

      数据:

      DT <- structure(list(ID = c(1, 1, 1, 1, 2, 2, 2, 2), ColA = c(2, 3, 
          4, 5, 2, 3, 4, 5), ColB = c(1, 2, 3, 4, 1, 2, 3, 4), ColA_0.2 = c(2, 
              3.4, 4.68, 5.936, 2, 3.4, 4.68, 5.936), ColB_0.2 = c(1, 2.2, 
                  3.44, 4.688, 1, 2.2, 3.44, 4.688)), class = "data.frame", row.names = c(NA, 
                      -8L))
      ad <- 0.2
      cols <- c("ColA", "ColB")
      

      【讨论】:

        【解决方案3】:

        这是data.table 使用Reduce 的一种方式:

        #Columns to apply function to
        cols <- names(df)[2:3]
        
        #Create a function to apply 
        apply_fun <- function(col, ad) {
           Reduce(function(x, y) sum(y, x * ad), col, accumulate = TRUE)
        }
        
        library(data.table)
        #Convert dataframe to data.table
        setDT(df)
        #set ad value
        ad <- 0.2
        #Apply funnction to each columns of cols
        df[, (paste(cols, ad, sep =  "_")) := lapply(.SD, apply_fun, ad), .SDcols = cols, by = ID]
        
        df
        #   ID ColA ColB ColA_0.2 ColB_0.2
        #1:  1    2    1    2.000    1.000
        #2:  1    3    2    3.400    2.200
        #3:  1    4    3    4.680    3.440
        #4:  1    5    4    5.936    4.688
        #5:  2    2    1    2.000    1.000
        #6:  2    3    2    3.400    2.200
        #7:  2    4    3    4.680    3.440
        #8:  2    5    4    5.936    4.688
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-06-11
          • 1970-01-01
          • 1970-01-01
          • 2015-10-06
          • 1970-01-01
          • 2018-07-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多