【问题标题】:Generating interactive partial dependence plots in R using loop extremely slow使用循环在R中生成交互式部分依赖图非常慢
【发布时间】:2019-08-12 18:30:41
【问题描述】:

我正在尝试通过循环遍历数据集中的列来生成交互式部分依赖图。

一个可重现的例子:

library(pdp)
library(xgboost)
library(Matrix)
library(ggplot2)
library(plotly)

data(mtcars)
target <- mtcars$mpg
mtcars$mpg <- NULL

mtcars.sparse <- sparse.model.matrix(target~., mtcars)

fit <- xgboost(data=mtcars.sparse, label=target, nrounds=100)

for (i in seq_along(names(mtcars))){
  p1 <- pdp::partial(fit,
                     pred.var = names(mtcars)[i],
                     pred.grid = data.frame(unique(mtcars[names(mtcars)[i]])),
                     train = mtcars.sparse,
                     type = "regression",
                     cats = c("cyl", "vs", "am", "gear", "carb"),
                     plot = FALSE)
  p2 <- ggplot(aes_string(x = names(mtcars)[i] , y = "yhat"), data = p1) +
    geom_line(color = '#E51837', size = .6) +
    labs(title = paste("Partial Dependence plot of", names(mtcars)[i] , sep = " ")) +
    theme(text = element_text(color = "#444444", family = 'Helvetica Neue'),
          plot.title = element_text(size = 13, color = '#333333'))

  print(ggplotly(p2, tooltip = c("x", "y")))

}

我的真实数据集(约 22k 行,30 列)的绘图循环大约需要 2 个小时。关于如何加快速度的任何想法?

【问题讨论】:

    标签: r ggplot2 partial ggplotly


    【解决方案1】:

    由于 R 中使用数据结构的方式,如果您不小心,for() 循环可能会非常缓慢。如果您想了解更多关于这背后的技术原因,请查看 Hadley Wickham 的 Advanced R

    实际上,有两种主要方法可以加快您要执行的操作:优化for() 循环,以及使用apply() 系列函数。虽然这两种方法都可以很好地工作,但apply() 方法往往更快,甚至比优化编写的for() 循环还要快,所以我会坚持使用那个解决方案。

    apply 方法:

    plotFunction <- 
      function(x) {
        p1 <- pdp::partial(fit,
                           pred.var = x,
                           pred.grid = data.frame(unique(mtcars[x])),
                           train = mtcars.sparse,
                           type = "regression",
                           cats = c("cyl", "vs", "am", "gear", "carb"),
                           plot = FALSE)
        p2 <- ggplot(aes_string(x = x , y = "yhat"), data = p1) +
          geom_line(color = '#E51837', size = .6) +
          labs(title = paste("Partial Dependence plot of", x , sep = " ")) +
          theme(text = element_text(color = "#444444", family = 'Helvetica Neue'),
                plot.title = element_text(size = 13, color = '#333333'))
        return(p2)
      }
    
    
    plot.list <- lapply(varNames, plotFunction)
    
    system.time(lapply(varNames, plotFunction))
       user  system elapsed 
      0.471   0.004   0.488 
    

    for() 循环上运行相同的基准测试结果:

       user  system elapsed 
      3.945   0.616   3.519 
    

    您会注意到,只需将循环代码粘贴到函数中,只需稍作修改,速度就会提高大约 10 倍。

    如果你想要更高的速度,你可以对你的函数进行一些调整,但apply() 方法最强大的方面可能是它非常适合并行化,这可以通过像 @ 这样的包来完成987654332@

    实施pbmcapply 让您的速度更快;

    library(pdp)
    library(xgboost)
    library(Matrix)
    library(ggplot2)
    library(plotly)
    library(pbmcapply)
    
    # Determines the number of cores you want to use for paralell processing
    # I like to leave two of mine available, but you can get away with 1
    nCores <-  detectCores() - 1
    
    data(mtcars)
    target <- mtcars$mpg
    mtcars$mpg <- NULL
    
    mtcars.sparse <- sparse.model.matrix(target~., mtcars)
    
    fit <- xgboost(data=mtcars.sparse, label=target, nrounds=100)
    
    varNames <- 
      names(mtcars) %>%
      as.list
    
    plotFunction <- 
      function(x) {
        p1 <- pdp::partial(fit,
                           pred.var = x,
                           pred.grid = data.frame(unique(mtcars[x])),
                           train = mtcars.sparse,
                           type = "regression",
                           cats = c("cyl", "vs", "am", "gear", "carb"),
                           plot = FALSE)
        p2 <- ggplot(aes_string(x = x , y = "yhat"), data = p1) +
          geom_line(color = '#E51837', size = .6) +
          labs(title = paste("Partial Dependence plot of", x , sep = " ")) +
          theme(text = element_text(color = "#444444", family = 'Helvetica Neue'),
                plot.title = element_text(size = 13, color = '#333333'))
        return(p2)
      }
    
    
    plot.list <- pbmclapply(varNames, plotFunction, mc.cores = nCores)
    

    让我们看看效果如何

       user  system elapsed 
      0.842   0.458   0.320 
    

    lapply() 的小改进,但这种改进应该随着您更大的数据集而扩展。希望这会有所帮助!

    【讨论】:

    • @Debbie,这回答了你的问题吗?
    • 很抱歉回复太晚了。我最终使用了第一个选项。非常感谢。
    猜你喜欢
    • 2021-08-19
    • 1970-01-01
    • 2022-12-04
    • 1970-01-01
    • 1970-01-01
    • 2011-09-18
    • 2013-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多