在每个循环的R-in中自动创建和使用自定义函数 - 将结果存储在一个DF-3D阵列中

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在每个循环的R-in中自动创建和使用自定义函数 - 将结果存储在一个DF-3D阵列中相关的知识,希望对你有一定的参考价值。

几天前,我问这个主题是关于在一个循环中调用一个自定义函数,该循环可以通过组合很好地解决

 eval(parse(text = Function text))

这是链接:Automatic creation and use of custom made function in R。这允许我使用for loop并从存储要创建的函数体的Data框架中自动调用我需要的函数。

现在我想将问题提升到一个新的水平。我的问题是计算时间。我需要评估来自超声类图像的52个指数。这意味着在R中我的高光谱图像被加载为512x512x204波段的3d数组。

我想要做的是并行运行索引的评估以减少计算时间。这里是我想要模仿的虚拟示例,但不是并行计算。

# create a fake  matrix rappresenting my Hyperpectral image
HYPR_IMG=array(NA,dim=c(5,3,4))
HYPR_IMG[,,1]=1
HYPR_IMG[,,2]=2
HYPR_IMG[,,3]=3
HYPR_IMG[,,4]=4

image.plot(HYPR_IMG[,,1], zlim=c(0,20))
image.plot(HYPR_IMG[,,2], zlim=c(0,20))
image.plot(HYPR_IMG[,,3], zlim=c(0,20))
image.plot(HYPR_IMG[,,4], zlim=c(0,20))




#create a fake DF for simulating my indices stored in the dataframe
IDXname=c("IDX1","IDX2","IDX3","IDX4")
IDXFunc=c("HYPR_IMG[,,1] + 3*HYPR_IMG[,,2]",
          "HYPR_IMG[,,3] + HYPR_IMG[,,2]",
          "HYPR_IMG[,,4] + HYPR_IMG[,,2] - HYPR_IMG[,,3]",
          "HYPR_IMG[,,1] + HYPR_IMG[,,4] + 4*HYPR_IMG[,,2] + HYPR_IMG[,,3]")
IDX_DF=as.data.frame(cbind(IDXname,IDXFunc))


# that was what I did before
Store_DF=data.frame(NA)
for (i in 1: length(IDX_DF$IDXname)) 
  IDX_ID=IDX_DF$IDXname[i]
  IDX_Fun_tmp=IDX_DF$IDXFunc[which(IDX_DF$IDXname==IDX_ID)] #use for extra care to select the right fuction
  IDXFunc_call=paste("IDXfun_tmp=function(HYPR_IMG)",IDX_Fun_tmp,"",sep="")
  eval(parse(text = IDXFunc_call))
  IDX_VAL=IDXfun_tmp (HYPR_IMG)
  image.plot(IDX_VAL,zlim=c(0,20)); title(main=IDX_ID) 
  temp_DF=as.vector(IDX_VAL)
  Store_DF=cbind(Store_DF,temp_DF)
  names(Store_DF)[i+1] <- as.vector(IDX_ID)

我的最终目标是拥有相同的Store_DF,存储所有指数值。在这里我有一个for循环,但使用foreach循环的东西应该加快。如果需要,我正在使用Windows 8或更多操作系统。

真的有可能吗?我能否在最后,减少具有相同Store_DF数据帧的整体计算时间或类似于具有列名称的矩阵的类似物?

非常感谢!!!

答案

对于使用像data.table或并行应用这样的包的并行化构建的具体示例可能更有益。下面是如何使用parApply包中的parallel实现结果的最小示例。请注意,输出是一个矩阵,实际上在基本R中产生稍好的性能(不是必须在tidyverse或data.table中)。如果data.frame结构至关重要,您将不得不使用data.frame进行转换。

cl <- parallel::makeCluster( parallel::detectCores() )
result <- parallel::parApply(cl = cl, X = IDX_DF, MARGIN = 1, FUN = function(x, IMAGES)
  IDX_ID <- x[["IDXname"]]
  eval(parse(text = paste0("IDXfun_tmp <- function(HYPR_IMG)", x[["IDXFunc"]], "")))
  IDX_VAL <- as.vector(IDXfun_tmp(IMAGES))
  names(IDX_VAL) <- IDX_ID
  IDX_VAL
, IMAGES = HYPR_IMG)
colnames(result) = IDXname
IDXname
parallel::stopCluster(cl)

请注意stopCluster(cl),这对于关闭任何松散的R会话非常重要。基准测试结果(4个小核心):

Unit: milliseconds
     expr      min       lq      mean   median       uq      max neval
     Loop 8.420432 9.027583 10.426565 9.272444 9.943783 26.58623   100
 Parallel 1.382324 1.491634  2.038024 1.554690 1.907728 18.23942   100

对于基准的复制,代码已在下面提供:

cl <- parallel::makeCluster( parallel::detectCores() )
microbenchmark::microbenchmark(
  Loop = 
    Store_DF=data.frame(NA)
    for (i in 1: length(IDX_DF$IDXname)) 
      IDX_ID = IDX_DF$IDXname[i]
      IDX_Fun_tmp = IDX_DF$IDXFunc[which(IDX_DF$IDXname == IDX_ID)] #use for extra care to select the right function
      eval(parse(text = paste0("IDXfun_tmp = function(HYPR_IMG)", IDX_Fun_tmp, "")))
      IDX_VAL = IDXfun_tmp(HYPR_IMG)
      #Plotting in parallel is not a good idea. It will most often not work but might make the R session crash or slow down significantly (at best the latter at worst the prior)
      #image.plot(IDX_VAL, zlim = c(0,20)); title(main = IDX_ID) 
      temp_DF = as.vector(IDX_VAL)
      Store_DF = cbind(Store_DF,temp_DF)
      names(Store_DF)[i+1] <- as.vector(IDX_ID)
    
    rm(Store_DF)
  ,
  Parallel = 
    result <- parallel::parApply(cl = cl, X = IDX_DF, MARGIN = 1, FUN = function(x, IMAGES)
      IDX_ID <- x[["IDXname"]]
      eval(parse(text = paste0("IDXfun_tmp <- function(HYPR_IMG)", x[["IDXFunc"]], "")))
      IDX_VAL <- as.vector(IDXfun_tmp(IMAGES))
      names(IDX_VAL) <- IDX_ID
      IDX_VAL
    , IMAGES = HYPR_IMG)
    colnames(result) = IDXname
    rm(result)
  
)
parallel::stopCluster(cl)

编辑:使用foreach包

在对性能问题(可能是由于内存)做了一些评论之后,我决定说明如何使用foreach包获得相同的结果。几点说明:

  1. foreach包使用迭代器。作为标准,它可以像for循环一样使用,它将迭代data.frame中的每一列
  2. 与R中的其他并行实现一样,如果您在Windows上,通常必须导出用于计算的数据。有时可以通过一些摆弄来避免它,而foreach有时会让你不能导出数据。如果是这样,从文档中不清楚。
  3. foreach的输出将作为列表或.combine参数定义,可以是rbind,cbind或任何其他函数。
  4. 有很多评论,使代码看起来比它实际上更长。删除注释和空行,它会延长9行。

下面是代码,它将产生与上面相同的输出。注意我已经使用了data.table包。有关此软件包的更多信息,我建议their wikipedia on github.

cl <- parallel::makeCluster( parallel::detectCores() )
#Foeach uses doParallel for the parallization
doParallel::registerDoParallel(cl)
#To iterate over the rows, we need to use iterators 
# if foreach is given a matrix it will be converted to a column iterators
rowIterator <- iterators::iter(IDX_DF, by = "row") 
library(foreach)
result <- 
  foreach(
        #Supply the iterator
        row = rowIterator, 

        #Specify if the calculations needs to be in order. If not then we can get better performance not doing so
        .inorder = FALSE, 

        #In most foreach loops you will have to export the data you need for the calculations
        # it worked without doing so for me, in which case it is faster if the exported stuff is large
        #.export = c("HYPR_IMG"), 

        #We need to say how the output should be merged. If nothing is given it will be output as a list
        #data.table rbindlist is faster than rbind (returns a data.table)

        .combine = function(...)data.table::rbindlist(list(...)) ,
        #otherwise we could've used:
        #.combine = rbind 

        #if we dont use rbind or cbind (i used data.table::rbindlist for speed)
        # we will have to tell if it can take more than 1 argument 
        .multicombine = TRUE

        ) %dopar% #Specify how to do the calculations. %do% loop. %dopar% parallel loop. %:% nested loops (next foreach tells how we do the loop)
 #to do stuff in parallel we use the %dopar%. Alternative %do%. For multiple foreach we split each of them by %:%

  IDX_ID <- row[["IDXname"]]
  eval(parse(text = paste0("IDXfun_tmp <- function(HYPR_IMG)", row[["IDXFunc"]], "")))
  IDX_VAL <- as.vector(IDXfun_tmp(HYPR_IMG))
  data.frame(ID = IDX_ID, IDX_VAL)

#output is saved in result
result
result_reformatted <- dcast(result[,indx := 1:.N, by = ID], 
                            indx~ID, 
                            value.var = "IDX_VAL")
#if we dont want to use data.table we could use unstack instead
unstack(test, IDX_VAL ~ ID)

以上是关于在每个循环的R-in中自动创建和使用自定义函数 - 将结果存储在一个DF-3D阵列中的主要内容,如果未能解决你的问题,请参考以下文章

创建自定义外壳

在自定义帖子循环中显示自定义类别

如何在 Nightwatch 测试的自定义命令中添加嵌套函数 javascript - forEach - 循环遍历元素

matlab 怎么循环plot自定义颜色?

SwiftUI-自定义容器

如何将自定义函数加载到 R 中的 foreach 循环中?