使用列表进行模拟

时间:2011-06-10 07:01:48

标签: r random simulation nested-lists

我在学习R的路上给自己设置了一点挑战。问题是,如果正常分布的500个数字的样本平均为20,那么20个标准偏差的数量将从6到10得到多少。要了解更多,我决定为每个sd获得4个样本。所以到最后我应该:

  

sd6samp1:...

     

sd6samp2:...

     

...

     

sd10samp4:...

我的第一个方法是:

 ddss<-c(6:10) # sd's
 sam<-c(1:4) # 4 samples for each
 k=0  # counter in 0
 for (i in ddss) {   # for each sd
   for (j in sam) {  # for each sample
     nam <- paste("sam",i,".",j, sep="") # building a name
     n <- assign(nam,rnorm(500, 20, i))  # the great assign function
     k <- k+sum(n<=0)
   }
   print(assign(paste("ds",i,sep=""), k)) # ohh assign you're great
   k=0 # reset counter
 }

在寻找如何使用循环'i'创建变量名时,建立'assign'来完成工作,但它也说:

  

请注意,如果您计划进行一些模拟,   很多guRus会说你应该使用一个列表。

所以我觉得学习名单会很好......

同时我也发现了另一个很好的选择...... ddss&lt; - c(6:10)

for (i in ddss) {
   print(paste('prob. x<=0), with sd=',i))
   print(pnorm(0,mean=20,sd=i)*500)
}

这有助于回答这个问题,但这些清单仍有待完成......而且很多R还有待学习。主要的想法是不知道负面的概率或数量......但要学习R,特别是一些循环。

所以,我一直在尝试使用上面提到的列表

我最接近的方法是:

ddss<-c(6:10) # sd's to be calculated.
sam<-c(1:4) # 4 samples for each sd
liss<-list()  # initializing the list
for (i in ddss) {   # for each sd
   liss[[i]] <- list()
   for (j in sam) {  # for each sample
      liss[[i]][[j]] <- rnorm(500, 20, i)
      print(paste('ds',i,'samp',j,'=',sum(liss[[i]][[j]]<0)))
   }
}

有了这个,我得到的信息,但我想知道两个问题(1&amp; 2)和一些其他问题(3&amp; 4):

  1. 我得到了10个元素的列表,6个空元素,然后是4个子列表。我似乎无法找到如何使用6:9名称(非常sd)的列表(sd)的元素1:4。

  2. 即使我尝试过,也无法通过'for'循环来命名列表元素。对这些问题的任何见解都会很棒。

  3. 因为在此模拟环境中。您认为更好的是什么:嵌套列表(带有子列表的列表)或简单(更长)列表?

  4. 我想知道'apply'功能在这里是否有任何帮助,我试着做点什么,比如:

  5. vbv<-matrix(c(6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9))
    lsl<-apply(vbv, 2, function(x) rnorm(500,20,x))
    

    但它看起来我甚至没有变得更近......

    感谢您的时间,如果您已经阅读过这篇文章了!

    您还可以采取更多回复; - )。

3 个答案:

答案 0 :(得分:4)

问题在于你的索引:你正在从ddss运行索引器i,从6到10运行。所以在你的外循环的第一个任务中,你的第一个语句真的说:liss[[6]]<-list(),暗示前5个是NULL。

因此,如果您坚持使用循环,那么您应该这样做(检查?seq_along):

ddss<-c(6:10) # sd's to be calculated.
sam<-c(1:4) # 4 samples for each sd
liss<-list()  # initializing the list
for (i in seq_along(ddss)) {   # now, i runs from 1 to 5
   liss[[i]] <- list()
   for (j in sam) {  # for each sample
      liss[[i]][[j]] <- rnorm(500, 20, i)
      print(paste('ds',ddss[i],'samp',j,'=',sum(liss[[i]][[j]]<0)))
   }
   names(liss[[i]])<-as.character(sam)#this should solve your naming issue (1/2)
}
names(liss)<-as.character(ddss)#this should solve your naming issue (2/2)

请注意,与往常一样,将变量命名为比i或j更有用的东西是个好主意:如果你将它命名为curds,也许你不会立即将它用作列表中的索引器?

现在,如果你真的想要改进(但想要坚持列表),你确实想要使用apply style函数:

liss<-lapply(ddss, function(curds){ #apply the inline function to each ds and store results in a list
  return(lapply(sam, function(cursam){ #apply inline function to each sam and store results in a list
    rv<-rnorm(500, 20, curds)
    cat('ds',curds,'samp',cursam,'=',sum(rv<0), "\n") #maybe better for your purposes.
    return(rv)
  }))
}) 

最后,对于您的情况,实际使用列表没有太多理由(您甚至不需要保留每个ds / sam的采样数据):您可以将所有内容存储为三维数组,但是因为您将它指定为一个学习练习(嘿,也许阵列的东西可以是你的下一个练习:-)),我会留下它。

答案 1 :(得分:3)

lapply()在这里很有帮助,我们只需应用SD的值集。它有助于在rnorm()函数周围编写自定义包装器,因此我们可以为rnorm()的各种参数传递不同的值,并处理 k 复制( k = 4在你的例子中)也很漂亮。该包装器位于foo()下方:

foo <- function(sd, n, mean, reps = 1) {
    rands <- rnorm(n * reps, mean = mean, sd = sd)
    if(reps > 1)
        rands <- matrix(rands, ncol = reps)
    rands
}

我们在lapply()这样的电话中使用它:

sims <- lapply(6:10, FUN = foo, mean = 20, n = 500, reps = 4)

给出了:

R> str(sims)
List of 5
 $ : num [1:500, 1:4] 30.3 22 15.6 20 19.4 ...
 $ : num [1:500, 1:4] 20.9 21.7 17.7 35 30 ...
 $ : num [1:500, 1:4] 17.88 26.48 5.19 19.25 15.59 ...
 $ : num [1:500, 1:4] 27.41 12.72 9.38 35.09 11.08 ...
 $ : num [1:500, 1:4] 16.2 11.6 20.5 35.4 27.3 ...

然后我们可以计算观察数量&lt;每SD 20个

names(sims) <- paste("SD", 6:10, sep = "")
out <- lapply(sims, function(x) colSums(x < 20))

给出了:

R> out
$SD6
[1] 218 251 253 227

$SD7
[1] 250 242 233 232

$SD8
[1] 258 241 246 274

$SD9
[1] 252 245 249 258

$SD10
[1] 253 259 241 242

@Joris建议我展示如何访问列表的元素。例如,如果你想要SD = 20的模拟结果,我们可以做out[[4]],因为20是我们应用的SD矢量中的第4个值,或者,因为我命名了输出的元素列出out,我们可以使用out[["SD10"]]进行模拟的结果。

回答关于你的循环等的一些具体要点,

  • 使用names()向列表添加名称,例如names(mylist)&lt; - c(“foo”,“bar”). You'd be better off in your loop calling name()`每次循环迭代一次,以便在一次设置中设置名称 - 您可能不希望随着时间的推移填写名称,因为效率低下。
  • 根据我的例子,我不认为使用嵌套列表或包含矩阵的列表会有太大区别。要更改foo()以返回列表以便lapply()的输出是列表列表,我们可以这样做:

代码:

bar <- function(sd, n, mean, reps = 1) {
    rands <- rnorm(n * reps, mean = mean, sd = sd)
    if(reps > 1)
        rands <- split(rands, rep(seq_len(reps), each = n))
    rands
}
sims2 <- lapply(6:10, FUN = bar, mean = 20, n = 500, reps = 4)
names(sims2) <- paste("SD", 6:10, sep = "")
out2 <- lapply(sims2, function(x) sapply(x, function(y) sum(y < 20)))

,它提供与以前相同的输出。

答案 2 :(得分:3)

我将使用plyr包引入另一个解决方案,我认为这是为这些练习量身定制的。

library(plyr)

# generate a data frame of parameters, repeating some as required
parameters  = data.frame(mean = 20, sd = rep(6:10, each = 4))

# generate sample data for each combination of parameters
sample_data = mdply(df, rnorm, n = 500)

# generate answer by counting number of observations less than 20
answer = data.frame(
    parameters, 
    obs_less_20 = rowSums(sample_data[,-c(1, 2),] < 20)
)

head(answer)

mean sd obs_less_20
1   20  6         247
2   20  6         250
3   20  6         242
4   20  6         259
5   20  7         240
6   20  7         237