使用标准评估和do_在没有do.call的参数网格上运行模拟

jai*_*ash 15 simulation design-patterns r tidyverse

目标

我想使用dplyr在参数网格上运行模拟.具体来说,我想要一个我可以在另一个程序中使用的函数

  • 传递一个data.frame
  • 对于每一行,使用每列作为参数计算一些模拟
  • 也传递了一些额外的数据(例如,初始条件)

这是我的方法

require(dplyr)
run <- function(data, fun, fixed_parameters, ...) {
   ## ....
   ## argument checking
   ##

   fixed_parameters <- as.environment(fixed_parameters)
   grouped_out <- do_(rowwise(data), ~ do.call(fun, c(., fixed_parameters, ...)))
   ungroup(grouped_out)
 }
Run Code Online (Sandbox Code Playgroud)

这有效.例如,对于

growth <- function(n, r, K, b) {
  # some dynamical simulation
  # this is an obviously-inefficient way to do this ;)
  n  + r - exp(n) / K - b - rnorm(1, 0, 0.1)
}
growth_runner <- function(r, K, b, ic, ...) {
  # a wrapper to run the simulation with some fixed values
  n0 = ic$N0
  T = ic$T
  reps = ic$reps
  data.frame(n_final = replicate(reps, {for(t in 1:T) {
                                          n0 <- growth(n0, r, K, b)
                                        };
                                        n0})
  )
}
Run Code Online (Sandbox Code Playgroud)

我可以定义和运行,

   data <- expand.grid(b = seq(0.01, 0.5, length.out=10),
                       K = exp(seq(0.1, 5, length.out=10)),
                       r = seq(0.5, 3.5, length.out=10))
   initial_data = list(N0=0.9, T=5, reps=20)
   output <- run(data, growth_runner, initial_data)
Run Code Online (Sandbox Code Playgroud)

即使这似乎有效,我想知道是否有办法没有do.call.(部分原因是因为do.call存在问题.)

我真的很感兴趣的是grouped_out <- do_(rowwise(data), ~ do.call(fun, c(., fixed_parameters, ...)))用一种能做同样事情但却没有的东西替换线路do.call.编辑:以某种方式避免do.call上述链接中概述的使用性能损失的方法也可以.

注释和参考

had*_*ley 5

我发现遵循你的代码有点棘手,但我认为这是等价的.

首先,我定义了一个执行您感兴趣的计算的函数:

growth_t <- function(n0, r, K, b, T) {
  n <- n0

  for (t in 1:T) {
    n <- n + r - exp(n) / K - b - rnorm(1, 0, 0.1)
  }
  n
}
Run Code Online (Sandbox Code Playgroud)

然后我定义了你想要改变的数据,包括代表的"虚拟"变量:

data <- expand.grid(
  b = seq(0.01, 0.5, length.out = 5),
  K = exp(seq(0.1, 5, length.out = 5)),
  r = seq(0.5, 3.5, length.out = 5),
  rep = 1:20
)
Run Code Online (Sandbox Code Playgroud)

然后我可以把它喂进去purrr::pmap_d().pmap_d()做一个"并行"映射 - 即它将一个列表(或数据帧)作为输入,并调用该函数改变每次迭代的所有命名参数.固定参数在函数名后提供.

library(purrr)
data$output <- pmap_dbl(data[1:3], growth_t, n0 = 0.9, T = 5)
Run Code Online (Sandbox Code Playgroud)

这对我来说真的不是一个dplyr问题,因为它并不是真正的数据操作.


Tch*_*hke 1

下面避免使用do.call并以与OP相同的方式呈现输出。

首先,将函数的参数替换为您将传入的向量 - 这是您将使用 apply 传递的内容。

growth_runner <- function(data.in, ic, ...) {
  # a wrapper to run the simulation with some fixed values
  n0 = ic$N0
  T = ic$T
  reps = ic$reps
  data.frame(n_final = replicate(reps, {for(t in 1:T) {
    n0 <- growth(n0, data.in[3], data.in[2], data.in[1])
  };
    n0})
  )
}
Run Code Online (Sandbox Code Playgroud)

设置您要搜索的网格,就像以前一样。

data <- expand.grid(b = seq(0.01, 0.5, length.out=10),
                    K = exp(seq(0.1, 5, length.out=10)),
                    r = seq(0.5, 3.5, length.out=10))
initial_data = list(N0=0.9, T=5, reps=20)
Run Code Online (Sandbox Code Playgroud)

使用 apply 遍历网格,然后附加结果

output.mid = apply(data, 1, ic=initial_data, FUN=growth_runner)
output <- data.frame('n_final'=unlist(output.mid))
Run Code Online (Sandbox Code Playgroud)

并且您无需任何调用do.call或任何外部库即可获得输出。

> dim(output)
[1] 20000     1
> head(output)
     n_final
1 -0.6375070
2 -0.7617193
3 -0.3266347
4 -0.7921655
5 -0.5874983
6 -0.4083613
Run Code Online (Sandbox Code Playgroud)