我在创建for循环时遇到麻烦。
我想将样本大小从1增加到200,并在每次新添加观察值之后计算p值。
这样首先我对1个观测值进行采样-计算第一个p值,然后对2个观测值进行采样-计算第二个p值,然后对3个...进行多达200个观测值,这样我就得到200个p值。
所有观察结果都将从数据帧的一列中采样(带有替换)。
可以说数据帧的列称为data $ column1。 样本大小从“ 1:200”开始每增加一圈。
我如何创建一个for循环,以便对于每个“回合”,又进行一次观察,并计算一个新的p值? 最后,我想绘制所有p值。
n <- 1:200
for i in length(n) {
sample(data$column1,n, replace = TRUE)
pvalue <- t.test(data$column1, alternative = "greater")
}
答案 0 :(得分:3)
尽管我知道您可能想使用for
循环,但这是使用sapply
或lapply
的好机会。我将使用iris
演示替代方法。尽管我将对所有示例使用iris$Sepal.Length
的简化测试“不等于5”,但是您应该为特定数据更新alternative=
和其他参数。
选择1:如果您只需要p值,我们就可以捕获...或捕获整个模型并进行p值的第二阶段检索。
选择2:我们可以使用*apply
函数之一,它读起来很好(一旦您更习惯于R向量代码),或者可以坚持使用for
循环。第一种选择具有可读性,尽管您可能更喜欢使用for
循环,在这种情况下,您应该真正地预先分配列表/向量。 (预先定义长而空的列表/向量的原因:虽然您可以轻松地将向量out
与out <- c(out, newstuff)
串联在一起,但是从长远来看,重复执行此操作效率很低 。我强烈不建议以“大规模”方式这样做。)
在前面,有一些注意事项:
set.seed(2)
,以使结果相同。除非/除非您需要严格的可复制性,否则您不应该使用它。通常不需要生产/学术报告。seq_len
而不是2:length(...)
:以编程方式执行操作时,最好让它优雅地失败。如果将来由于某种原因您使用1:length(nrow(x))
并且x
的行数为0,那么1:0
会产生一个长度为2的向量,这是违反直觉的(并且几乎可以肯定破坏后续代码)。相反,seq_len(0)
产生一个长度为2的向量,这是一件好事。同样,这里不那么重要,但要养成良好的习惯。 (顺便说一句,seq_along(0)
仍然输出长度为1的向量,因此也容易出现此问题。)seq_len(...)[-1]
来丢弃“ 1”,因为无法完成具有单个基准的t检验。还可以做1 + seq_len(nrow(x)-1)
。for
循环,仅p值set.seed(2)
out <- rep(NA, nrow(iris))
for (i in seq_len(nrow(iris))[-1]) {
thisdat <- sample(iris$Sepal.Length, size = i)
out[i] <- t.test(thisdat, mu = 5)$p.value
}
summary(out)
# Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
# 0.0000000 0.0000000 0.0000000 0.0080013 0.0000001 0.4156151 1
(您可以假设out
对于所有后续示例都是相同的,因此我不会显示。)
*apply
,仅p值set.seed(2)
out <- sapply(seq_len(nrow(iris))[-1], function(i) {
thisdat <- sample(iris$Sepal.Length, size = i)
t.test(thisdat, mu = 5)$p.value
})
sapply
采用一个向量,通常返回以下值之一:
vector
,如果所有返回值的长度都完美地为1; matrix
,如果所有返回值都是长度完全相同的向量;或list
任何其他时间。因此,某些程序员更喜欢lapply
(总是返回list
)或vapply
(必须声明哪种返回值)如您所愿...,并且在弹出其他内容时失败)。一个人可以做:
set.seed(2)
out <- vapply(seq_len(nrow(iris))[-1], function(i) {
thisdat <- sample(iris$Sepal.Length, size = i)
t.test(thisdat, mu = 5)$p.value
}, numeric(1))
(尝试将numeric(1)
更改为numeric(2)
,您会看到values must be length 2, but FUN(X[[1]]) result is length 1
的错误。)
对于lapply
选项,它与下面的第四种方法非常相似。
请注意,这里的length(out)
将是nrow(iris)-1
,因为我们在seq_len(nrow(iris))[-1]
的输入向量上跳过了它。这意味着从技术上讲summary(out)
会有所不同:不会NA
。所有数字都相等。
for
循环,完整模型在这里,我们需要存储的不仅仅是一个数字,所以我们需要将其存储在list
中。
set.seed(2)
out <- vector("list", nrow(iris))
for (i in seq_len(nrow(iris))[-1]) {
thisdat <- sample(iris$Sepal.Length, size = i)
out[[i]] <- t.test(thisdat, mu = 5)
}
str(out[1:3])
# List of 3
# $ : NULL
# $ :List of 9
# ..$ statistic : Named num 1.31
# .. ..- attr(*, "names")= chr "t"
# ..$ parameter : Named num 1
# .. ..- attr(*, "names")= chr "df"
# ..$ p.value : num 0.416
# ..$ conf.int : num [1:2] -2.41 14.11
# .. ..- attr(*, "conf.level")= num 0.95
# ..$ estimate : Named num 5.85
# .. ..- attr(*, "names")= chr "mean of x"
# ..$ null.value : Named num 5
# .. ..- attr(*, "names")= chr "mean"
# ..$ alternative: chr "two.sided"
# ..$ method : chr "One Sample t-test"
# ..$ data.name : chr "thisdat"
# ..- attr(*, "class")= chr "htest"
# $ :List of 9
# ..$ statistic : Named num 1.76
# .. ..- attr(*, "names")= chr "t"
# ..$ parameter : Named num 2
# .. ..- attr(*, "names")= chr "df"
# ..$ p.value : num 0.22
# ..$ conf.int : num [1:2] 3.61 8.33
# .. ..- attr(*, "conf.level")= num 0.95
# ..$ estimate : Named num 5.97
# .. ..- attr(*, "names")= chr "mean of x"
# ..$ null.value : Named num 5
# .. ..- attr(*, "names")= chr "mean"
# ..$ alternative: chr "two.sided"
# ..$ method : chr "One Sample t-test"
# ..$ data.name : chr "thisdat"
# ..- attr(*, "class")= chr "htest"
列表很长,但是您可以看到(1)第一个元素为空,这并不奇怪,因为我们跳过了i
的1; (2)之后的每个元素都包含您希望模型拥有的所有内容。
好吧,让我们来看一下。我们首先分配完整列表,然后像以前一样运行for
循环。循环中的唯一区别是我们存储了整个模型(需要使用out[[i]]
而不是out[i]
)而不是仅存储$p.value
。现在,为了获得p值,我们可以使用for
循环或sapply
,我将演示后者:
head(sapply(out[-1], `[[`, "p.value"))
# [1] 0.41561507 0.22019340 0.05766889 0.08544124 0.03243253 0.09059092
# more verbose, same thing though, showing the "anonymous-function" definition
head(sapply(out[-1], function(m) m$p.value))
我使用out[-1]
是因为我们知道第一个是空的。在上面的out <- out[-1]
循环之后,我们可以很容易地完成for
。
通过使用我上面演示的“匿名函数”定义,人们可以从模型中获取其他任何属性,例如模型系数。
*sapply
,完整版这可能并不会让您感到惊讶。
set.seed(2)
out <- lapply(seq_len(nrow(iris))[-1], function(i) {
thisdat <- sample(iris$Sepal.Length, size = i)
out[[i]] <- t.test(thisdat, mu = 5)
})
如果您查看这些内容,则第一个元素也不为空(类似于上面的sapply
示例),因为我们甚至没有为其运行或预先分配。
然后,您可以对单个列表元素执行任何操作:
out[[1]]$p.value
# [1] 0.4156151
str(out[[17]])
# List of 9
# $ statistic : Named num 3.98
# ..- attr(*, "names")= chr "t"
# $ parameter : Named num 17
# ..- attr(*, "names")= chr "df"
# $ p.value : num 0.000974
# $ conf.int : num [1:2] 5.48 6.57
# ..- attr(*, "conf.level")= num 0.95
# $ estimate : Named num 6.03
# ..- attr(*, "names")= chr "mean of x"
# $ null.value : Named num 5
# ..- attr(*, "names")= chr "mean"
# $ alternative: chr "two.sided"
# $ method : chr "One Sample t-test"
# $ data.name : chr "thisdat"
# - attr(*, "class")= chr "htest"
out[[19]]$statistic
# t
# 3.420489
如果要检索所有测试统计信息(类似于获取p值),则可以执行以下操作:
head(sapply(out, `[[`, "statistic"))
# t t t t t t
# 1.307692 1.761625 3.000000 2.273030 2.935307 2.014477
答案 1 :(得分:1)
@ r2evans有一个很好的答案。我只专注于您的代码,并尝试将其绘制出来。
改进之处包括:
for
的语法为for (i in seq_along(n))
以遍历每个i。在您的情况下,您实际上要执行for (i in 2:200)
,因为i==1
将无法计算p.value。sample
语句直接放在t.test()
调用中。pvalue
将以循环的最后一个值结束。 我喜欢apply
系列,因为您不必显式地预先分配任何内容。
set.seed(1)
n <- 50
results <- sapply(seq(2, n)
, function(n) {
t.test(sample(iris$Sepal.Length, n, replace = T), mu = 5.5, alternative = 'greater')$p.value
})
plot(y = results, x = seq(2, n))
理论上,您所需要做的就是将iris$Sepal.Length
替换为data$column1
,然后使用您喜欢的n
。