【问题标题】:Parallelization with data.table与 data.table 并行化
【发布时间】:2018-10-07 13:18:06
【问题描述】:

我有以下问题。我有一个由 (xPoints, yPoints) 描述的分段线性函数,并且想要快速计算——我必须一遍又一遍地做——一长串 x 的隐含 y 值,其中 x 可能落在外面xPoints 的范围。我编写了一个函数 f_pwl 来计算隐含的 y 值,但它很慢,所以我试图并行化它的调用。但它实际上比使用 data.table := 语法要慢。我将不胜感激通过改进我的 f_pwl 函数或实施有效的并行化来加快速度的建议,因为我可以使用 20 个内核来加快速度。

这是一个示例代码。

    # libraries
    require(data.table) # for fread, work with large data
    require(abind)      # for abind()
    require(foreach) # for parallel processing, used with doParallel
    require(doParallel) # for parallel processing, used with foreach

    f_pwl <- function(x)  {
      temp <- as.vector( rep(NA, length = length(x)), mode = "double" )
      for (i in seq(from = 1, to = length(x), by = 1))  {
        if (x[i] > max(xPoints) | x[i] < min(xPoints)) {
          # nothing to do, temp[i] <- NA
        } else if (x[i] == max(xPoints)) {
          # value equal max(yPoints)
          temp[i] <- max(yPoints)
        } else {
          # value is f_pwl(x)
          xIndexVector = as.logical( x[i] >= xPoints  &  abind(xPoints[2:length(xPoints)], max(xPoints)) > x[i] )
          xIndexVector_plus1 = shift( xIndexVector, n = 1, fill = FALSE, type = "lag" )
          alpha_j = (xPoints[xIndexVector_plus1] - x[i])/(xPoints[xIndexVector_plus1] - xPoints[xIndexVector])
          temp[i] <- alpha_j %*% yPoints[xIndexVector] + (1-alpha_j) %*% yPoints[xIndexVector_plus1]
        }
      } # end for i
      as.vector( temp, mode = "double" )
    }


    ## Main program
    xPoints <- c(4, 9, 12, 15, 18, 21)
    yPoints <- c(1, 2, 3, 4, 5, 6)

    x <- rnorm(1e4, mean = 12, sd = 5)

    dt <- as.data.table( x )
    dt[ , c("y1", "y2", "y3") := as.vector( mode = "double", NA ) ]

    # data.table := command
    system.time({
      dt[, y2 := f_pwl( x ) ]
    })

    # mapply
    system.time({
      dt[ , y1 := mapply( f_pwl, x ), by=.I  ]
    })

    # parallel
    system.time({
      #setup parallel backend to use many processors
      cores=detectCores()
      cl <- makeCluster(cores[1]-1, type="FORK") #not to overload your computer
      registerDoParallel(cl)
      dt$y3 <- foreach(i=1:nrow(dt), .combine=cbind) %dopar% {
        tempY <- f_pwl( dt$x[i] )
        tempY
      }
      #stop cluster
      stopCluster(cl)
    })

    summary( dt[ , .(y1-y2, y1-y3, y2-y3)] )   

【问题讨论】:

    标签: r parallel-processing data.table parallel.foreach doparallel


    【解决方案1】:

    首先,计算并存储alpha_j's。

    然后,首先按 x 对 DT 进行排序,并将其切割成相关的区间,然后再执行线性插值

    alpha <- c(NA, diff(yPoints) / diff(xPoints))
    
    DT[order(x), 
        y := alpha[.GRP] * (x - xPoints[.GRP-1L]) + yPoints[.GRP-1L], 
        by=cut(x, xPoints)]
    

    请告诉我它的表现。

    数据:

    library(data.table)
    
    ## Main program
    set.seed(27L)
    xPoints <- c(4, 9, 12, 15, 18, 21)
    yPoints <- c(1, 2, 3, 4, 5, 6)
    DT <- data.table(x=rnorm(1e4, mean=12, sd=5))
    

    检查:

    f_pwl <- function(x)  {
        temp <- as.vector( rep(NA, length = length(x)), mode = "double" )
        for (i in seq(from = 1, to = length(x), by = 1))  {
            if (x[i] > max(xPoints) | x[i] < min(xPoints)) {
                # nothing to do, temp[i] <- NA
            } else if (x[i] == max(xPoints)) {
                # value equal max(yPoints)
                temp[i] <- max(yPoints)
            } else {
                # value is f_pwl(x)
                xIndexVector = as.logical( x[i] >= xPoints  &  abind(xPoints[2:length(xPoints)], max(xPoints)) > x[i] )
                xIndexVector_plus1 = shift( xIndexVector, n = 1, fill = FALSE, type = "lag" )
                alpha_j = (xPoints[xIndexVector_plus1] - x[i])/(xPoints[xIndexVector_plus1] - xPoints[xIndexVector])
                temp[i] <- alpha_j %*% yPoints[xIndexVector] + (1-alpha_j) %*% yPoints[xIndexVector_plus1]
            }
        } # end for i
        as.vector( temp, mode = "double" )
    }
    system.time({
        DT[, yOP := f_pwl( x ) ]
    })
    
    DT[abs(y-yOP) > 1e-6]
    #Empty data.table (0 rows) of 3 cols: x,y,yOP
    

    【讨论】:

    • 感谢 @chinsoon12 提供使用矢量化的优雅解决方案,我不知道使用 data.table 的 .GRP 选项。性能惊人,0.005 秒,而在我的示例中使用 data.table := 命令或 mapply 选项只需 3 秒多一点。
    猜你喜欢
    • 2021-11-27
    • 2021-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    • 2016-07-11
    • 2015-12-13
    相关资源
    最近更新 更多