如何最小化创建的 R 代码的不可接受的长时间运行时间

Posted

技术标签:

【中文标题】如何最小化创建的 R 代码的不可接受的长时间运行时间【英文标题】:How to minimize the unacceptably long run time of the created R code 【发布时间】:2021-12-23 06:17:26 【问题描述】:

有一个代码包含三个 for 循环,其中包含足够缺失值的数据。主要问题是运行时间长得令人无法接受,这似乎至少需要一个多月的时间,尽管我尽量让我的电脑在一天中的大部分时间都保持打开状态。

当我用很少的数据点进行测试时,下面的结构是 100% 正确的。但是当列数和行数分别变为 2781 和 280 时,我认为这需要很长时间,尽管我 100% 确定它运行正常,即使我每次刷新时看到 R-Studio 的更新环境窗口也是如此。

我的数据也有很多缺失值,大概 40% 左右。我认为这也使得计算时间变得非常长。

数据维度为 315 * 2781。 但是,我正在尝试以 280 * 2781 矩阵形式实现输出。

能否请我帮助我最小化以下代码的运行时间? 如果可以的话将不胜感激!

options(java.parameters = "- Xmx8000m")
memory.limit(size=8e+6)

data=read.table("C:/Data/input.txt",T,sep="\t");
data=data.frame(data)[,-1]



corr<-NULL
corr2<-NULL
corr3<-NULL


for(i in 1:280) 

  corr2<-NULL
  for(j in 1:2781)
  
    data2<-data[,-j]
    corr<-NULL
    for(k in 1:2780)
    
      ifelse((is.error(grangertest(data[i:(i+35),j] ~ data2[i:(i+35),k], order = 1, na.action = na.omit)$P[2])==TRUE) || (grangertest(data[i:(i+35),j] ~ data2[i:(i+35),k], order = 1, na.action = na.omit)$P[2])>0.05|| (is.na(grangertest(data[i:(i+35),j] ~ data2[i:(i+35),k], order = 1, na.action = na.omit)$P[2])==TRUE),corr<-cbind(corr,0),corr<-cbind(corr,1))
    
    corr2<-rbind(corr2,corr)
  
  corr3<-rbind(corr3,rowSums(corr2))

我的数据的sn-p如下:

> dput(data[1:30, 1:10])
structure(c(0.567388170165941, 0.193093325709924, 0.965938209090382, 
0.348295788047835, 0.496113050729036, 0.0645384560339153, 0.946750836912543, 
0.642093246569857, 0.565092500532046, 0.0952424583956599, 0.444063827162609, 
0.709971546428278, 0.756330407923087, 0.601746253203601, 0.341865634545684, 
0.953319212188944, 0.0788604547269642, 0.990508111426607, 0.35519331949763, 
0.697004508692771, 0.285368352662772, 0.274287624517456, 0.575733694015071, 
0.12937490013428, 0.00476219342090189, 0.684308280004188, 0.189448777819052, 
0.615732178557664, 0.404873769031838, 0.357331350911409, 0.565436001634225, 
0.380773033713922, 0.348490287549794, 0.0473814208526164, 0.389312234241515, 
0.562123290728778, 0.30642102798447, 0.911173274740577, 0.566258994862437, 
0.837928073247895, 0.107747194357216, 0.253737836843356, 0.651503744535148, 
0.187739939894527, 0.951192815322429, 0.740037888288498, 0.0817571650259197, 
0.740519099170342, 0.601534485351294, 0.120900869136676, 0.415282893227413, 
0.591146623482928, 0.698511375114322, 0.08557975362055, 0.139396222075447, 
0.303953414550051, 0.0743798329494894, 0.0293272000271827, 0.335832208395004, 
0.665010208031163, 0.0319741254206747, 0.678886031731963, 0.154593498911709, 
0.275712370406836, 0.828485634410754, 0.921500099124387, 0.651940459152684, 
0.00574865937232971, 0.82236105017364, 0.55089360428974, 0.209424041677266, 
0.861786168068647, 0.672873278381303, 0.301034058211371, 0.180336013436317, 
0.481560358777642, 0.901354183442891, 0.986482679378241, 0.90117057505995, 
0.476308439625427, 0.638073122361675, 0.27481731469743, 0.689271076582372, 
0.324349449947476, 0.56620552809909, 0.867861548438668, 0.78374840435572, 
0.0668482843320817, 0.276675389613956, 0.990600393852219, 0.990227151894942, 
0.417612489778548, 0.391012848122045, 0.348758921027184, 0.0799746725242585, 
0.88941288786009, 0.511429069796577, 0.0338982092216611, 0.240115304477513, 
0.0268365524243563, 0.67206134647131, 0.816803207853809, 0.344421110814437, 
0.864659120794386, 0.84128700569272, 0.116056860191748, 0.303730394458398, 
0.48192183743231, 0.341675494797528, 0.0622653553728014, 0.823110743425786, 
0.483212807681412, 0.968748248415068, 0.953057422768325, 0.116025703493506, 
0.327919023809955, 0.590675016632304, 0.832283023977652, 0.342327545629814, 
0.576901035616174, 0.942689201096073, 0.59300709143281, 0.565881528891623, 
0.600007816683501, 0.133237989619374, 0.873827134957537, 0.744597729761153, 
0.755133397178724, 0.0245723063126206, 0.97799762734212, 0.636845340020955, 
0.73828601022251, 0.644093665992841, 0.57204390084371, 0.496023115236312, 
0.703613247489557, 0.149237307952717, 0.0871439634356648, 0.0632112647872418, 
0.83703236351721, 0.433215840253979, 0.430483993608505, 0.924051651498303, 
0.913056606892496, 0.914889572421089, 0.215407102368772, 0.76880722376518, 
0.269207723205909, 0.865548757137731, 0.28798541566357, 0.391722843516618, 
0.649806497385725, 0.459413924254477, 0.907465039752424, 0.48731207777746, 
0.554472463205457, 0.779784266138449, 0.566323830280453, 0.208658932242543, 
0.958056638715789, 0.61858483706601, 0.838681482244283, 0.286310768220574, 
0.895410191034898, 0.448722236789763, 0.297688684659079, 0.33291415637359, 
0.0115265529602766, 0.850776052568108, 0.764857453294098, 0.469730701530352, 
0.222089925780892, 0.0496484278701246, 0.32886885642074, 0.356443469878286, 
0.612877089297399, 0.727906176587567, 0.0292073413729668, 0.429160050582141, 
0.232313714455813, 0.678631312213838, 0.642334033036605, 0.99107678886503, 
0.542449960019439, 0.835914565017447, 0.52798323193565, 0.303808332188055, 
0.919654499506578, 0.944237019168213, 0.52141259261407, 0.794379767496139, 
0.72268659202382, 0.114752230467275, 0.175116094760597, 0.437696389388293, 
0.852590200025588, 0.511136321350932, 0.30879021063447, 0.174206420546398, 
0.14262041519396, 0.375411552377045, 0.0204910831525922, 0.852757754037157, 
0.631567053496838, 0.475924106314778, 0.508682047016919, 0.307679089019075, 
0.70284536993131, 0.851252349093556, 0.0868967010173947, 0.586291917832568, 
0.0529140203725547, 0.440692059928551, 0.207642213441432, 0.777513341512531, 
0.141496006632224, 0.548626560717821, 0.419565241318196, 0.0702310993801802, 
0.499403427587822, 0.189343606121838, 0.370725362794474, 0.888076487928629, 
0.83070912421681, 0.466137421084568, 0.177098380634561, 0.91202046489343, 
0.142300580162555, 0.823691181838512, 0.41561916610226, 0.939948018174618, 
0.806491429451853, 0.795849160756916, 0.566376683535054, 0.36814984655939, 
0.307756055146456, 0.602875682059675, 0.506007500691339, 0.538658684119582, 
0.420845189364627, 0.663071365095675, 0.958144341595471, 0.793743418296799, 
0.983086514985189, 0.266262857476249, 0.817585011478513, 0.122843299992383, 
0.989197303075343, 0.71584410732612, 0.500571243464947, 0.397394519997761, 
0.659465527161956, 0.459530522814021, 0.602246116613969, 0.250076721422374, 
0.17533828667365, 0.6599256307818, 0.184704560553655, 0.15679649473168, 
0.513444944983348, 0.205572377191857, 0.430164282443002, 0.131548407254741, 
0.914019819349051, 0.935795902274549, 0.857401241315529, 0.977940042736009, 
0.41389597626403, 0.179183913161978, 0.431347143370658, 0.477178965462372, 
0.121315707685426, 0.107695729471743, 0.634954946814105, 0.859707030234858, 
0.855825762730092, 0.708672808250412, 0.674073817208409, 0.672288877889514, 
0.622144045541063, 0.433355041313916, 0.952878215815872, 0.229569894727319, 
0.289388840552419, 0.937473804224283, 0.116283216979355, 0.659604362910613, 
0.240837284363806, 0.726138337515295, 0.68390148691833, 0.381577257299796, 
0.899390475358814, 0.26472729514353, 0.0383855854161084, 0.855232689995319, 
0.655799814499915, 0.335587574867532, 0.163842789363116, 0.0353666560258716, 
0.048316186061129), .Dim = c(30L, 10L))

【问题讨论】:

在此过程中对代码进行注释以确保我们理解它应该做什么会很棒。作为 R 中的一般建议:-crbindcbind 重写向量的计算量非常大,最好的办法是创建一个具有预期元素数量的输出向量,然后在每个位置覆盖它与 for 循环的输出。 - 因为 R 中的循环非常慢,我建议您利用 apply/sapply/lapply 语法 - 一次删除一行 (data[,-j]) 也可能非常繁重 - 一次删除一列是也 如果您的机器有多个内核,那么您可以并行化您的代码,@Eric。见:Parallelized loops with R 一旦i 到达246data[i:(i+35),j] 不会出错? 您可以发布示例数据吗?请使用dput(data[1:30, 1:10]) 的输出编辑问题 @RuiBarradas:请检查我上面的输入。 【参考方案1】:

我只将内部循环转换为mapply 并进行了快速速度测试:

library(lmtest)

data <- matrix(runif(315*2781), nrow = 315)

get01 <- function(x, y) 
  try(gt <- grangertest(x ~ y, order = 1, na.action = na.omit)$P[2])
  if (exists("gt")) 
    if (gt > 0.05 || is.na(gt)) 
      return(0)
     else 
      return(1)
    
   else 
    return(0)
  


i <- 1; j <- 1
system.time(corr <- mapply(function(k) get01(data[i:(i+35),j], data[i:(i+35),k]), (1:2781)[-j]))
#>    user  system elapsed 
#>  21.505   0.014  21.520

它需要执行mapply 778680 次,因此大约需要 200 天。您要么需要不同的方法来进行 Granger 测试,要么需要几个核心。这是替换完整循环的命令:

corr3 <- t(mapply(function(i) colSums(mapply(function(j) mapply(function(k) get01(data[i:(i+35),j], data[i:(i+35),k]), (1:2781)[-j]), 1:2781)), 1:280))

将第一个 mapply 替换为 simplify2array(parLapply 以进行并行化:

library(parallel)

cl <- makeCluster(detectCores())
clusterExport(cl, list("data", "get01"))
parLapply(cl, cl, function(x) require(lmtest))
corr3 <- t(simplify2array(parLapply(cl, 1:280, function(i) colSums(mapply(function(j) mapply(function(k) get01(data[i:(i+35),j], data[i:(i+35),k]), (1:2781)[-j]), 1:2781)))))
stopCluster(cl)

【讨论】:

我将转换到 Windows 机器并计算 parLappply。不过,可能需要一段时间才能完成。感谢您对行大小的澄清。 parLapply 使用起来并不难。我已经启动了我的 Windows 机器,并且正在测试代码。 只是好奇——你有多少个内核(来自detectCores())? 我已经用parLapply 更新了帖子。如果 Rui 的 granger_test 适合您,那么在没有对 lmtest::grangertestlmtest::waldtest 进行彻底检查的情况下,将其与我上面的内容一起使用可能会接近最佳解决方案,这将允许您对矩阵进行矢量化。 刚刚更新,应该是parLapply(cl, cl, function(x) require(lmtest))【参考方案2】:

这是一个未并行化的版本,可将问题中的代码速度提高 4 倍以上。

问题代码中的一些瓶颈很容易检测到:

矩阵corr? 在循环内扩展。解决办法是提前预留内存; 测试grangertest 在每次内部迭代中调用 3 次,此时只需要一次; 到cbind01 实际上是创建一个向量,而不是一个矩阵。

下面是问题代码和下面函数的对比测试。

library(lmtest)

# avoids loading an extra package
is.error <- function(x)
  inherits(x, c("error", "try-error"))


Lag <- 5L
nr <- nrow(data)
nc <- ncol(data)

t0 <- system.time(
  corr<-NULL
  corr2<-NULL
  corr3<-NULL
  for(i in 1:(nr - Lag))
  
    corr2<-NULL
    data3 <- data[i:(i + Lag), ]
    for(j in 1:nc)
    
      data2<-data[,-j]
      corr<-NULL
      for(k in 1:(nc - 1L))
      
        ifelse((is.error(grangertest(data[i:(i+Lag),j] ~ data2[i:(i+Lag),k], order = 1, na.action = na.omit)$P[2])==TRUE) ||
                 (grangertest(data[i:(i+Lag),j] ~ data2[i:(i+Lag),k], order = 1, na.action = na.omit)$P[2])>0.05 ||
                 (is.na(grangertest(data[i:(i+Lag),j] ~ data2[i:(i+Lag),k], order = 1, na.action = na.omit)$P[2])==TRUE),
               corr<-cbind(corr,0),
               corr<-cbind(corr,1)
        )
      
      corr2 <- rbind(corr2, corr)
    
    corr3<-rbind(corr3, rowSums(corr2))
  
  corr3
)

我将使用lmtest::grangertest 的简化版本。

granger_test <- function (x, y, order = 1, na.action = na.omit, ...) 
  xnam <- deparse(substitute(x))
  ynam <- deparse(substitute(y))
  n <- length(x)
  all <- cbind(x = x[-1], y = y[-1], x_1 = x[-n], y_1 = y[-n])
  y <- as.vector(all[, 2])
  lagX <- as.matrix(all[, (1:order + 2)])
  lagY <- as.matrix(all[, (1:order + 2 + order)])
  fm <- lm(y ~ lagY + lagX)
  rval <- lmtest::waldtest(fm, 2, ...)
  attr(rval, "heading") <- c("Granger causality test\n", paste("Model 1: ", 
                                                               ynam, " ~ ", "Lags(", ynam, ", 1:", order, ") + Lags(", 
                                                               xnam, ", 1:", order, ")\nModel 2: ", ynam, " ~ ", "Lags(", 
                                                               ynam, ", 1:", order, ")", sep = ""))
  rval

现在是运行测试的函数。

f_Rui <- function(data, Lag)
  nr <- nrow(data)
  nc <- ncol(data)
  corr3 <- matrix(0, nrow = nr - Lag, ncol = nc)
  data3 <- matrix(0, nrow = Lag + 1L, ncol = nc)
  data2 <- matrix(0, nrow = Lag + 1L, ncol = nc - 1L)
  for(i in 1:(nr - Lag)) 
    corr2 <- matrix(0, nrow = nc, ncol = nc - 1L)
    data3[] <- data[i:(i + Lag), ]
    for(j in 1:nc) 
      corr <- integer(nc - 1L)
      data2[] <- data3[, -j]
      for(k in 1:(nc - 1L))
        res <- tryCatch(
          grangertest(x = data2[, k], y = data3[, j], order = 1, na.action = na.omit),
          error = function(e) e
        )
        if(!inherits(res, "error") && !is.na(res[['Pr(>F)']][2]) && res[['Pr(>F)']][2] <= 0.05) 
          corr[k] <- 1L
        
      
      corr2[j, ] <- corr
    
    corr3[i, ] <- rowSums(corr2)
  
  corr3

结果是identical,而且时间要好得多。

t1 <- system.time(
  res <- f_Rui(data, 5L)
)

identical(corr3, res)
#[1] TRUE

times <- rbind(t0, t1)
t(t(times)/t1)
#   user.self sys.self  elapsed user.child sys.child
#t0  4.682908 1.736111 4.707783        NaN       NaN
#t1  1.000000 1.000000 1.000000        NaN       NaN

【讨论】:

Barrads:谢谢,但我认为最后一步中的“f_Rui(data, 5L)”对于我的情况是不可执行的。它说“f_Rui(data, 5L) 中的错误:未使用的参数 (5)”另外,您能否解释一下 5L 是什么以及为什么给出它?非常感谢。 对不起,我看到延迟是我设置为滚动窗口的东西,就像我喜欢 35 一样。 谢谢!这是一个学习的好机会!

以上是关于如何最小化创建的 R 代码的不可接受的长时间运行时间的主要内容,如果未能解决你的问题,请参考以下文章

Spring + Hibernate+ HikariCP:如何在进行长时间运行的 REST 调用时处理数据库连接?

SQL 调优,长时间运行的查询 + rownum

芹菜中持久的长时间运行的任务

PyQt5:调用长时间运行的函数时 QMainWindow 冻结

长时间运行的 py.test 在第一次失败时停止

Javascript Promises库在浏览器中制作“长时间运行代码 - 非阻塞UI”?