【问题标题】:Developing R package, testing with `foreach`, while running simulations at same time with different package version开发R包,用`foreach`测试,同时用不同的包版本运行模拟
【发布时间】:2019-08-07 06:37:25
【问题描述】:

我几乎所有的 R 代码都写在工作包中(并使用 git)。我大量使用devtools,特别是load_all 的捷径等,因为我更新了包中使用的函数。我对devtools 有一个粗略的了解,因为load_all 制作了一个包的临时副本,我非常喜欢这个用于测试包中的功能更新的工作流程。

是否有一种简单的方法/工作流程可以根据软件包运行模拟,同时开发它,而不会“破坏”这些模拟?

我怀疑有一个我忽略的简单解决方案。

现在我要做的是:

  1. 获取包“mypackage”以准备运行模拟。复制包含项目的整个文件夹。使用新的包名称“mypackage2”在复制的文件夹中运行模拟)。运行包含library(mypackage2) 但不包含library(mypackage) 的模拟脚本。这令人讨厌地意味着我需要将library(mypackage) 调用更新为library(mypackage2) 调用。如果我使用library(mypackage) 运行模拟并避免使用library(mypackage2),那么我需要确保mypackage 的当前构建版本是“旧”版本,它不反映下面2. 中的更新(但下面2.也需要重建包!)。处理这一切变得一团糟。

  2. 当模拟在复制的文件夹中运行时,我可以通过使用load_all 或重建包来更新“mypackage”中的函数。我经常需要重建包(即在测试包的更新不是可行的解决方案时使用load_all而不重建包)因为我想测试功能使用doParallelforeach 等(在Windows 上)运行小型并行模拟,并且我修改和想要测试的任何函数都需要子进程中最新构建的“mypackage”,这些子进程会生成调用“mypackage”的新R 进程。我知道,当一个包在 R 中构建时,它会存储在 ..\R\R-3.6.1\library 中,当未来的 R 会话调用 library(mypackage) 时,他们将使用该版本的包。

我最想做的是,在同一个原始文件夹中,使用 mypackage 版本运行模拟,然后在模拟停止/启动时更新包中的代码,对我的开发充满信心更改不会破坏运行特定版本包的模拟。

是否有一种简单的方法来执行上述操作,而无需重新复制文件夹(并制作诸如“mypackage2”之类的东西)?

谢谢

这里描述的问题有点类似于我面临的Specify package location in foreach

问题是,如果我使用“mypackage”运行需要几天时间的模拟,并多次调用foreach,并在测试更改时更新和重建“mypackage”,那么来自模拟的未来foreach 调用可能会选择升级软件包的新更新版本,这将是一场灾难。

【问题讨论】:

  • 您链接到的其他问题中的解决方案对您不起作用?为什么?
  • @Alexis 如果我不尝试在运行模拟的同时开发包(如果中断可能需要数天才能运行),那么另一个问题的答案将起作用。如果我正在更改包代码并重建,而其他脚本正在运行模拟(调用foreach 并生成新的 R 进程,在库文件夹中加载最新版本的包),我认为存在任何风险模拟中的子 R 进程中断,因为它们反映了包中的更改。

标签: r parallel-processing devtools parallel-foreach


【解决方案1】:

我认为另一个问题的答案确实适用, 但是你需要做一些额外的步骤。

假设您有一个要测试的软件包版本。 您仍会为该版本创建一个特定文件夹,但将其留空。 这里我以/tmp/mypkg2 为例。 在 RStudio 中打开项目时,执行:

withr::with_libpaths(c("/tmp/mypkg2", .libPaths()), devtools::install())

这会将该版本的软件包安装到提供的文件夹中。

然后你可以有一个包装脚本, 说wrapper.R, 类似于:

pkg_path <- commandArgs(trailingOnly = TRUE)[1L]

cat("Using package at", pkg_path, "\n")

.libPaths(c(pkg_path, .libPaths()))

library(doParallel)

workers <- makeCluster(detectCores())
registerDoParallel(workers)

# We need to modify the lib path in each worker too
parallel::clusterExport(workers, "pkg_path")
parallel::clusterEvalQ(workers, .libPaths(c(pkg_path, .libPaths())))

# ... Your code calling your package and doing stuff

parallel::stopCluster(workers)

之后,从命令行(在 R/RStudio 之外), 您可以输入(假设 Rscript 在您的路径中):

Rscript path/to/wrapper.R /tmp/mypkg2

这样,实际的测试代码可以保持不变 (包括致电library) 而R会自动先搜索pkg_path, 加载您的特定软件包版本, 然后在标准位置搜索您可能拥有的任何依赖项。

【讨论】:

  • @mpirie 请注意,每次使用makeCluster 创建worker 时都必须调整lib 路径,所以如果你的包的函数这样做,他们需要访问pkg_path 和一些特殊的至少在发育过程中进行相应的治疗。
【解决方案2】:

我不完全理解你的用例(关于你为什么要这样做),但我在测试一个包的两个版本时通常做的是将最新版本推送到我在 GitHub 中的 dev 分支然后使用devtools::load_all() 来测试我目前正在做的事情。然后通过使用remotes::install_github() 并指定dev 分支,您可以使用mypackage::func 运行GitHub 版本,使用func 运行devtools 版本

【讨论】:

  • 感谢您的反馈。当你想使用 foreach(多于 1 个核心),而在 foreach 中调用的代码取决于你的包代码的更新时会发生什么? foreach 中的代码仍将使用您上次构建的 R 包版本(并且不会反映您在 load_all 中所做的任何更改)。同时,您正在运行其他需要数小时/数天才能完成的 R 脚本,它们取决于您最近更新之前的“原始”包版本
  • 老实说,这是一个我不熟悉的特定用例,我无法理解为什么你使用foreach 进行测试,你不能重新运行@ 987654329@?
  • 具体来说,我正在测试“mypackage”中的函数,这些函数将在其中调用foreach……在foreach调用中,只有mypackage::func会被调用。对func 的任何更改都不会反映在那些foreach 调用中。这就是为什么我在测试包的新版本时必须构建包的原因
猜你喜欢
  • 2018-05-05
  • 1970-01-01
  • 1970-01-01
  • 2018-10-20
  • 2019-11-10
  • 1970-01-01
  • 1970-01-01
  • 2014-10-14
  • 2014-03-14
相关资源
最近更新 更多