【问题标题】:R: perform parameter sweep and collect results in long data frameR:在长数据帧中进行参数扫描并收集结果
【发布时间】:2014-12-16 03:41:43
【问题描述】:

我正在寻找正确的 R 习惯用法来对一组参数运行一个函数并从结果中创建一个长数据框。假设您有以下玩具功能:

fun <- function(sd, mean, foobar = "foobar") {
  list(random = rnorm(10) * sd + mean + 1:10, foobar = foobar)
}

现在您想在 sdmean 的不同值上运行 fun

par_sd <- rep(1:5, 3)
par_mean <- rep(0:2, each = 5)
pars <- data.frame(sd = par_sd, mean = par_mean)

我想对pars 的每一行中的参数运行fun,并将结果收集到具有sdmeanposvalue 列的数据框中。这是一个相当笨拙的解决方案:

set.seed(42)

## Run fun
res <- lapply(seq_len(nrow(pars)), function(x) {
  do.call(fun, as.list(pars[x, ]))
})

## Select the result we need
res <- lapply(res, "[[", "random")

## Make it a single data frame
res <- do.call(rbind, res)

## Together with the parameters
res <- as.data.frame(cbind(sd = par_sd, mean = par_mean, res))
colnames(res) <- c("sd", "mean", 1:10)

## Make it a long data frame
res <- reshape2::melt(res, id.vars=c("sd", "mean"), 
         variable.name = "pos", value.name="value")

## Done
res[1:5,]
#>   sd mean pos      value
#> 1  1    0   1 2.37095845
#> 2  2    0   1 3.60973931
#> 3  3    0   1 0.08008422
#> 4  4    0   1 2.82180049
#> 5  5    0   1 2.02999300

有没有更简单的方法来做到这一点?任何人都知道一个包做这样的事情?我的快速搜索没有给出任何好的结果...

【问题讨论】:

标签: r idioms


【解决方案1】:

如果您愿意修改 fun() 以返回 data.frame,我发现最优雅的解决方案是 plyr 的 mdply

fun <- function(sd, mean, foobar = "foobar") {
  data.frame(random = rnorm(10) * sd + mean + 1:10, foobar = foobar)
}

par_sd <- rep(1:5, 3)
par_mean <- rep(0:2, each = 5)
pars <- data.frame(sd = par_sd, mean = par_mean)

results = mdply(pars, fun, foobar = "stuff")
str(results)

【讨论】:

  • 我应该补充一点,我一直在使用这个精确的序列(通常在expand.grid 之前进行参数组合),每天和几年;我发现它与 ggplot2 结合使用可以挽救生命。这是我不切换到 dplyr 的唯一原因。
  • 是的,这正是我要找的东西。我知道这一定存在,它只是出现了很多次!返回数据框太糟糕了,但也许没关系。
  • 顺便说一句.2。结果与我需要的形式略有不同,但也许我可以解决这个问题,现在查看mdply....
  • 有mlply,如果你需要做中间处理,我相信它会将参数作为属性嵌入
  • 我相信mdplymelt 或多或少会起到作用。
【解决方案2】:

mapply 似乎很合适:

> str(with(pars, mapply(fun, sd=sd, mean=mean) ) )
List of 30
 $ : num [1:10] 3.16 2.28 2.84 1.49 3.43 ...
 $ : chr "foobar"
 $ : num [1:10] 3.429 0.157 0.583 1.542 6.485 ...
 $ : chr "foobar"
 $ : num [1:10] -4.56 -1.51 -1.33 7.16 3.21 ...
 $ : chr "foobar"
 $ : num [1:10] -2.275 2.225 4.196 0.962 15.739 ...
 $ : chr "foobar"
 $ : num [1:10] 6.23 10.08 2.85 6.81 4.51 ...
 $ : chr "foobar"
 $ : num [1:10] 1.65 3.15 5.62 5.91 6.14 ...
 $ : chr "foobar"
 $ : num [1:10] 4.26 1.95 7.33 2.72 6.29 ...
 $ : chr "foobar"
 $ : num [1:10] 7.53 6.74 3.6 6.43 3.08 ...
 $ : chr "foobar"
 $ : num [1:10] -0.4181 -0.0584 5.5812 1.038 8.2482 ...
 $ : chr "foobar"
 $ : num [1:10] 0.2377 4.8557 5.2177 -0.0706 2.0434 ...
 $ : chr "foobar"
 $ : num [1:10] 2.95 4.3 5.26 8.58 5.81 ...
 $ : chr "foobar"
 $ : num [1:10] -0.85 4.83 8.19 5.17 6.58 ...
 $ : chr "foobar"
 $ : num [1:10] 3.59 11.46 6.29 6.57 2.97 ...
 $ : chr "foobar"
 $ : num [1:10] 0.117 3.142 10.473 10.196 5.56 ...
 $ : chr "foobar"
 $ : num [1:10] 13.03 2.64 -1.07 5.29 1.97 ...
 $ : chr "foobar"
 - attr(*, "dim")= int [1:2] 2 15
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:2] "random" "foobar"
  ..$ : NULL

默认情况下mapply 将尝试简化,如果您想将它们作为单独的对象保留,您可以取消该默认值:

> str(with(pars, mapply(fun, sd=sd, mean=mean, SIMPLIFY=FALSE) ) )
List of 15
 $ :'data.frame':   10 obs. of  2 variables:
  ..$ random: num [1:10] 1.08 0.68 3.16 3.38 5.96 ...
  ..$ foobar: Factor w/ 1 level "foobar": 1 1 1 1 1 1 1 1 1 1
 $ :'data.frame':   10 obs. of  2 variables:
  ..$ random: num [1:10] 0.0927 5.1506 -1.0109 2.7136 2.1263 ...
  ..$ foobar: Factor w/ 1 level "foobar": 1 1 1 1 1 1 1 1 1 1
 $ :'data.frame':   10 obs. of  2 variables:
  ..$ random: num [1:10] -0.331 2.9 -1.705 5.471 4.712 ...
  ..$ foobar: Factor w/ 1 level "foobar": 1 1 1 1 1 1 1 1 1 1
snipped

如果您需要将它们放在一个堆叠的数据框中,那就是:

> str(do.call( rbind, with(pars, mapply(fun, sd=sd, mean=mean, SIMPLIFY=FALSE) ) ))
'data.frame':   150 obs. of  2 variables:
 $ random: num  1 3.34 2.5 4.72 4.25 ...
 $ foobar: Factor w/ 1 level "foobar": 1 1 1 1 1 1 1 1 1 1 ...

如果您想用 sd 和平均值“标记”这些值,只需对构造函数进行以下修改:

 fun <- function(sd, mean, foobar = "foobar") {
         data.frame(random = rnorm(10) * sd + mean + 1:10, 
                    sd=sd, mean=mean, foobar = foobar)
        }
 str(do.call( rbind, with(pars, 
                     mapply(fun, sd=sd, mean=mean, SIMPLIFY=FALSE) ) ))
 #---------------
'data.frame':   150 obs. of  4 variables:
 $ random: num  1.42 1.13 3.73 4.5 5.63 ...
 $ sd    : int  1 1 1 1 1 1 1 1 1 1 ...
 $ mean  : int  0 0 0 0 0 0 0 0 0 0 ...
 $ foobar: Factor w/ 1 level "foobar": 1 1 1 1 1 1 1 1 1 1 ...

【讨论】:

  • 嗯,这是一个步骤,但如果我理解正确的话,它只是替换了我的第一个lapply 步骤。我还需要跳剩下的舞。
  • 不是很复杂的舞蹈。那就是do.call(rbind, res)
  • 嗯,这个其实改了fun,结果也和我的不一样。
  • 是的,它改变了fun。如果我没有将随机种子重置为 42,结果可能会有所不同。关键是返回创建这些值的 mean 和 sd 的值。
  • 我所说的“不同”是指列名不同,实际上列也不同。你没有我需要的pos,而你有我不需要的foobar
猜你喜欢
  • 2021-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-20
  • 1970-01-01
  • 2019-11-29
相关资源
最近更新 更多