【问题标题】:Using standard evaluation and do_ to run simulations on a grid of parameters without do.call使用标准评估和 do_ 在没有 do.call 的参数网格上运行模拟
【发布时间】:2016-07-20 14:59:14
【问题描述】:

目标

我想使用 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)
 }

这行得通。例如,对于

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})
  )
}

我可以定义和运行,

   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)

问题

尽管这似乎可行,但我想知道是否有办法在没有do.call 的情况下做到这一点。 (部分是因为issues with do.call。)

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

注释和参考文献

【问题讨论】:

  • FWIW 听起来和plyr::mdply 完全一样。不幸的是,这两个包相当不兼容。
  • 该死,我从来没有找到plyr 的那部分!谢谢指点
  • 我想你可能想要purrr::invoke_rows,它相当于mdply的现代版。 rpackages.ianhowson.com/cran/purrr/man/by_row.html
  • 感谢@shorpy,看起来超级有前途

标签: r design-patterns simulation tidyverse


【解决方案1】:

我发现遵循您的代码有点棘手,但我认为这是等效的。

首先我定义一个函数来执行你感兴趣的计算:

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
}

然后我定义您想要改变的数据,包括代表的“虚拟”变量:

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
)

然后我可以将它输入purrr::pmap_d()pmap_d() 执行“并行”映射 - 即,它将列表(或数据框)作为输入,并调用函数来改变每次迭代的所有命名参数。固定参数在函数名之后提供。

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

对我来说,这真的不像是 dplyr 问题,因为它与数据操作无关。

【讨论】:

  • 谢谢! fair point re dplyr,它以dplyr::do 开头。但考虑到整理数据的扩展工具,尤其是您使用purrr (例如stackoverflow.com/q/35505187/4598520)的方向,我同意它可能更好地描述为整理数据问题
【解决方案2】:

下面避免使用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})
  )
}

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

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)

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

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

而且您无需调用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

【讨论】:

  • 抱歉,您缺少问题的关键上下文:使用 dplyr。 (问题的第一行)。 5/19 的编辑清楚地说明了这一点。这是有用的代码,但可以以不太通用的方式完成相同的整体任务。谢谢!
  • 另请注意,只要您有非数字参数,apply() 就会失败
【解决方案3】:

您可以将do.call 的行替换为以下内容(感谢@shorpy 指出purrr:invoke_rows()):

  grouped_out <- purrr::invoke_rows(fun, dplyr::rowwise(data), fixed_parameters)

没有任何其他更改,这将给出一个带有data.frames列的数据框,例如

Source: local data frame [1,000 x 4]
            b        K     r                .out
        (dbl)    (dbl) (dbl)               (chr)
1  0.01000000 1.105171   0.5 <data.frame [20,1]>
2  0.06444444 1.105171   0.5 <data.frame [20,1]>
3  0.11888889 1.105171   0.5 <data.frame [20,1]>

要恢复更接近原始行为的内容,请将run 的最后一行替换为

dplyr::ungroup(tidyr::unnest(grouped_out, .out))

这给了

Source: local data frame [20,000 x 4]

       b        K     r    n_final
   (dbl)    (dbl) (dbl)      (dbl)
1   0.01 1.105171   0.5 -0.6745470
2   0.01 1.105171   0.5 -0.7500365
3   0.01 1.105171   0.5 -0.6568312

无需对代码进行其他更改:)

【讨论】:

  • 我不会依赖它,因为invoke_rows() 对这个世界来说可能不会长久
  • 感谢您的提醒!我会调查pmap(如您的回答)
猜你喜欢
  • 1970-01-01
  • 2016-02-16
  • 1970-01-01
  • 2019-07-02
  • 2019-11-29
  • 2018-06-10
  • 2018-08-01
  • 1970-01-01
  • 2019-06-10
相关资源
最近更新 更多