【问题标题】:using lists for simulation使用列表进行模拟
【发布时间】:2011-09-12 06:44:16
【问题描述】:

我在学习 R 的道路上给自己设置了一点挑战。问题是,给定一个包含 500 个正态分布数字的样本,平均值为 20,对于从 6 到 10 的标准偏差,我会得到多少个 20 以下的数字。只是为了了解更多信息,我决定为每个 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”可以完成工作,但它也表示:

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

所以我认为学习列表会很好......

与此同时,我还发现了一个很棒的其他选择... ddss

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 和 2)和其他一些问题(3 和 4):

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

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

  3. 因为在这种模拟环境中。您认为哪个更好:嵌套列表(带有子列表的列表)还是简单(更长的)列表?

  4. 我想知道“应用”功能在这里是否有帮助,我尝试做一些事情,例如:

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

但看起来我还没有接近....

感谢您抽出宝贵时间阅读本文!

你不妨多花点时间回复一下;-)。

【问题讨论】:

  • 试试lapply。它通过一个列表。您可以将lapply (ssdd) 嵌套在lapply 中,并将sam 作为参数传递(另请参阅dotsMethods)。

标签: r random simulation nested-lists


【解决方案1】:

我将使用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

【讨论】:

    【解决方案2】:

    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 ...
    

    然后我们可以计算每个 SD

    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 个值,或者因为我将输出列表的元素命名为 @ 987654332@,我们可以使用out[["SD10"]]作为模拟结果。

    回答有关您的循环等的一些具体问题,

    • 要将名称添加到列表中,请使用names(),例如names(mylist) . You'd be better off in your loop callingnames()` 每次循环迭代一次以一次性设置名称 - 您可能不想将名称填写为你继续,因为那会效率低下。
    • 我不认为根据我的示例使用嵌套列表或包含矩阵的列表没有太大区别。要更改 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)))
    

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

    【讨论】:

    • 打败了我。你可能会证明 out[["SD10"]] 给出了他想要的样本的结果。
    • 感谢乔里斯的建议。已添加。
    【解决方案3】:

    问题出在您的索引中:您正在从 ddss 运行索引器 i,该索引器从 6 到 10 运行。因此,在您的外部循环的第一次任务中,您的第一条语句确实说:liss[[6]]&lt;-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 样式功能:

    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 保留采样数据):您可以将所有内容存储为三维数组,但是由于您把它指定为一个学习练习(嘿,也许数组可以是你的下一个练习:-)),我就这样吧。

    【讨论】:

      猜你喜欢
      • 2020-07-09
      • 1970-01-01
      • 2011-11-08
      • 2011-03-18
      • 2020-08-20
      • 2017-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多