【问题标题】:How to build efficient loops for element wise operations in R using map,sapply如何使用 map,sapply 在 R 中为元素明智的操作构建有效的循环
【发布时间】:2018-03-10 23:13:17
【问题描述】:

我有一个包含约 200k 行的数据集,我想计算多个变量的百分位数。对于单个变量,我使用的方法大约需要 10 分钟。有什么有效的方法可以做到这一点。以下是我的代码的假数据集。

library(dplyr)
library(purrr)

id <- c(1:200000)
X <- rnorm(200000,mean = 5,sd=100)
DATA <- data.frame(ID =id,Var = X)

percentileCalc <- function(value){
  per_rank <- ((sum(DATA$Var < value)+(0.5*sum(DATA$Var == value)))/length(DATA$Var))
  return(per_rank)
}

第一种方法:

res <- numeric(length = length(DATA$Var))
sta <- Sys.time()
for (i in seq_along(DATA$Var)) {
  res[i]<-percentileCalc(DATA$Var[i])
}
sto <- Sys.time()
sto - sta

输出:

Time difference of 10.51337 mins

第二种方法:

sta <- Sys.time()
res <- map(DATA$Var,percentileCalc)
sto <- Sys.time()
sto - sta

输出:

Time difference of 6.86872 mins

第三种方法:

sta <- Sys.time()
res <- sapply(DATA$Var,percentileCalc)
sto <- Sys.time()
sto - sta

输出:

Time difference of 11.1495 mins

接下来我尝试了一个简单的元素操作,但仍然需要时间

simpleOperation <- function(value){
  per_rank <- sum(DATA$Var < value)
  return(per_rank)
}

res <- numeric(length = length(DATA$Var))
sta <- Sys.time()
for (i in seq_along(DATA$Var)) {
  res[i]<-simpleOperation(DATA$Var[i])
}
sto <- Sys.time()
sto - sta

Time difference of 3.369287 mins

sta <- Sys.time()
res <- map(DATA$Var,simpleOperation)
sto <- Sys.time()
sto - sta

Time difference of 3.979965 mins

sta <- Sys.time()
res <- sapply(DATA$Var,simpleOperation)
sto <- Sys.time()
sto - sta

Time difference of 6.535737 mins

dplyr 中有 percent_rank() 可用,它做同样的事情,但我在这里担心的是,即使是一个简单的操作在迭代变量的每个元素时也会花费时间。可能是我做错了什么。

以下是我的会话信息:

> sessionInfo()
R version 3.4.0 (2017-04-21)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] purrr_0.2.2 dplyr_0.5.0

loaded via a namespace (and not attached):
[1] compiler_3.4.0 lazyeval_0.2.0 magrittr_1.5   R6_2.2.0       assertthat_0.1 DBI_0.5-1      tools_3.4.0   
[8] tibble_1.2     Rcpp_0.12.10  

【问题讨论】:

    标签: r performance loops dataframe dplyr


    【解决方案1】:

    在我看来,您正在实施(rank(DATA$Var) - 0.5) / length(DATA$Var)

    使用您的数据和一些不仅具有唯一值的数据进行验证:

    N <- 1e4
    DATA <- data.frame(
      ID   = 1:N, 
      Var  = rnorm(N, mean = 5, sd = 100),
      Var2 = sample(0:10, size = N, replace = TRUE)
    )
    
    percentileCalc <- function(value) {
      (sum(DATA$Var < value) + 0.5 * sum(DATA$Var == value)) / length(DATA$Var)
    }
    percentileCalc2 <- function(value) {
      (sum(DATA$Var2 < value) + 0.5 * sum(DATA$Var2 == value)) / length(DATA$Var2)
    }
    
    all.equal((rank(DATA$Var) - 0.5) / length(DATA$Var),
              sapply(DATA$Var, percentileCalc))
    all.equal((rank(DATA$Var2) - 0.5) / length(DATA$Var2),
              sapply(DATA$Var2, percentileCalc2))
    

    simpleOperation <- function(value) {
      sum(DATA$Var < value)
    }
    simpleOperation2 <- function(value) {
      sum(DATA$Var2 < value)
    }
    
    all.equal(rank(DATA$Var, ties.method = "min") - 1,
              sapply(DATA$Var, simpleOperation))
    all.equal(rank(DATA$Var2, ties.method = "min") - 1,
              sapply(DATA$Var2, simpleOperation2))
    

    【讨论】:

    • 也许你应该添加一些例子,因为我花了一些时间才明白为什么这是正确的。此外,您可能希望指定要获得完全相同的结果,需要除以向量的长度:(rank(DATA$Var) - 0.5) / length(DATA$Var)
    • @F. Privé rank() 适用于百分位数,但如何有效地为其他操作运行元素操作。例如我发布的第二个函数计算小于感兴趣值的值的计数,这是一个非常简单的操作,但如果将其应用于 200,000 行,仍然需要时间。是否有任何其他有效的循环方法或提出函数的矢量化实现是解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多