2

I'm trying to generate several series of random numbers, each series with a different set of parameters. Then I want to replicate all of that. I want to have a function to do this, and it seems like it should be a 1-liner with something like rnorm() to generate the numbers, mapply() to generate those numbers for several parameter sets, and replicate() to repeat the whole process.

To achieve that, I wrote a function whose ... arguments I want to correspond to the ... in mapply, but it appears that ... is being ignored by mapply. I have some examples that should clarify my desired output, as well as the problem:

 # Some values to be passed to the ... of mapply
 mus <- c(-30, 0, 30)
 sds <- c(0.1, 1, 10)

 # =================================
 # = What I want, if it would work =
 # =================================
 f <- function(n=3, ...){
    replicate(2, mapply(rnorm, MoreArgs=list(n=n), ...))
 }
 f(mean=mus, sd=sds) # Doesn't work :(

>, , 1
>
>           [,1]
>[1,] -0.4901243
>[2,]  0.8268027
>[3,] -0.4829781
>
>, , 2
>
>            [,1]
>[1,] -0.02025903
>[2,] -1.57011537
>[3,]  0.49234503


# ==========================================================
# = What I can currently get to work, but not style I want =
# ==========================================================
# I can't understand why f.inside is needed!
f2 <- function(n=3, ...){
    f.inside <- function() mapply(rand.gen, MoreArgs=list(n=n), ...)
    replicate(2, f.inside())
}
f2(mean=mus, sd=sds) # Desired output

>, , 1
>
>          [,1]        [,2]      [,3]
>[1,] -29.83762 -0.06138165  9.956601
>[2,] -30.04880  1.39123405 33.036675
>[3,] -29.94070 -1.15741513 19.337497
>[4,] -29.92360  0.74300731 38.741367
>[5,] -29.81723  0.84565813 22.261605
>
>, , 2
>
>          [,1]       [,2]     [,3]
>[1,] -30.01407 -0.5198845 20.85942
>[2,] -29.77586 -0.1705062 22.06274
>[3,] -29.96901 -0.4412471 21.42849
>[4,] -30.04079  0.4230790 28.35480
>[5,] -30.04794  0.3000821 50.09012

I'm guessing I need to wrap the ... in something; I've haphazardly tried things like alist(), list(), bquote(), expression(), as.call() and several other failed approaches.

1) Why does mapply in f() appear to completely ignore the ...? EDIT: This was an ancillary question, to which the answer is "dots don't work with replicate". OK, on to the core question...

# =====================================
# = OK, Function for Refined Question =
# =====================================
f.edit <- function(n=3, ...){
    l <- list(...)
    replicate(2,mapply(rnorm, MoreArgs=list(n=n), l))
}
f.edit(mean=mus, sd=sds) # Doesn't work :(

2) How do I split the elements of an object into length(object) elements, and pass those as separate items to mapply's ...?

I need to get better at my ellipses. I'm really stumped on this one.

rbatt
  • 4,677
  • 4
  • 23
  • 41
  • 1
    Better solution is to use the fact that `rnorm` is already vectorized over its `mean` and `sd` arguments. For example, `f<-function(n,reps,mean,sd) {m<-length(mean);aperm(array(rnorm(n*m*reps,mean,sd),c(m,n,reps)),c(2,1,3))}` – A. Webb Aug 05 '15 at 20:35
  • @A.Webb Nice, good point; unfortunately, I'm doing this in the style of `arima.sim`; i.e., `rnorm` can be any number-generating function that has `n` as an argument. But you make a good point. – rbatt Aug 05 '15 at 20:38
  • 1
    Regarding the `f.edit` edit, try instead `function(n=3, ...){dots<-list(...);replicate(2, do.call(mapply,c(list(FUN=rnorm),dots,list(MoreArgs=list(n=n)))))}` – A. Webb Aug 05 '15 at 20:46
  • @A.Webb That's it! OK, I had tried something along the lines of `do.call`, but I botched it; I'm still trying to wrap my head around why that exact structure of `list()` and `c()` is needed, but asfaik, it is necessary. Nice, thank you. – rbatt Aug 05 '15 at 20:53
  • @nongkrong Your solution works, but I am still trying to understand it. I'd never used the ``` notation before, so thanks for introducing me to that, as well as `as.call`, which I'm also not familiar with. Thanks a ton! – rbatt Aug 05 '15 at 20:54
  • @rbatt it is similar to the `do.call` solution, except you construct the call yourself. The backticks are just a way to name/call things, for example try `\`one and two\` <- 3; \`one and two\`` – Rorschach Aug 05 '15 at 21:08

2 Answers2

1

In the fashion you are attempting in f.edit...

  • You can construct the argument list and perform a do.call with mapply

    function(n=3, ...){
      dots<-list(...);
      replicate(2,do.call(mapply,c(list(FUN=rnorm),dots,list(MoreArgs=list(n=n)))))
    }
    
  • Or, extract the arguments and call directly

    function(n=3, ...){
      dots<-list(...);
      replicate(2,mapply(rnorm,dots$mean,dots$sd,MoreArgs=list(n=n)))
    }
    

In either case you force evaluation of ... in the correct environment, as is also done in your f2 and MrFlick's alternative.

The problem with f.edit as written is that rnorm is being called as rnorm(n,list(means=mus,sd=sds)) rather than rnorm(n,means=mu,sd=sds).


However, in this case you might consider using the fact that rnorm is already vectorized over its mean and sd arguments

function(n,reps,mean,sd) {
  m<-length(mean);
  aperm(array(rnorm(n*m*reps,mean,sd),c(m,n,reps)),c(2,1,3))
}
Community
  • 1
  • 1
A. Webb
  • 26,227
  • 1
  • 63
  • 95
  • so in `f.edit`, there's no way of doing something to `l` to allow it to work? I'm just trying to wrap my head around this. I know I'm only passing 1 argument, instead of several ... but can I split that 1 into several? I guess that's what you're doing with do.call. – rbatt Aug 05 '15 at 21:08
  • @rbatt See if edits above clear up what's going on in `f.edit`. – A. Webb Aug 06 '15 at 13:39
1

The problem is that you are not allowed to use ... with replicate as already discussed here.

I already think you found the most readable alternative with the helper function f.inside() to grab the ... terms.

Another options is to avoid replicate and use lapply (with index values that you ultimately ignore)

f3 <- function(n=3, ...){
    lapply(1:2, function(i, ...) mapply(rnorm, MoreArgs=list(n=n), ...), ...)
}
f3(mean=mus, sd=sds)
Community
  • 1
  • 1
MrFlick
  • 195,160
  • 17
  • 277
  • 295