在学习 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”创建变量名称时,发现“分配”可以完成工作,但它也说:
请注意,如果您计划进行一些模拟, 许多专家会说你应该使用列表。
所以我认为学习列表会很好......
同时我还发现了一个很好的其他选择...... dds <- 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 和 2)以及其他一些问题(3 和 4):
我得到一个包含 10 个元素的列表,其中 6 个为空元素,然后是 4 个带有子列表的元素。我似乎不知道如何使用列表(sd)的元素 1:4 和 6:9 名称(非常 sd)。
即使我尝试过,我也无法通过“for”循环来命名列表元素。对这些问题的任何见解都会很棒。
因为在模拟的背景下。您认为哪个更好:嵌套列表(带有子列表的列表)或简单(更长)列表?
我想知道“应用”功能在这里是否有任何帮助,我尝试做一些事情,例如:
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))
但看起来我还没有接近......
问题出在你的索引中:你正在从 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,也许您不会立即将其用作列表中的索引器?
现在,如果您确实希望改进(但想坚持列表),那么您确实想使用应用样式函数:
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 的采样数据):您可以将所有内容存储为三维数组,但由于您指定了它作为学习练习(嘿,也许数组可能是你的下一个练习:-)),我就这样吧。
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 ...
然后我们可以计算观测值的数量 < 20 per 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 向量中的第四个值,或者,因为我命名了输出列表的元素 out
,我们可以使用out[["SD10"]]
来了解模拟结果。
回答有关循环等的一些具体问题,
names()
,例如names(mylist)
<- c("foo","bar"). You'd be better off in your loop calling
names()` 每次循环迭代一次,以便在单个镜头中设置名称 - 您可能不想在进行过程中填写名称,因为那样效率很低。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)))
给出与之前相同的输出。
我将使用
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