我在创建 for 循环时遇到问题。 我想将样本数量从 1 增加到 200,并在每次新添加的观测值后计算 p 值。 因此,首先我样本 1 个观测值 - 计算第一个 p 值,然后样本 2 个观测值 - 计算第二个 p 值,然后是 3...最多 200 个观测值,以便我得到 200 个 p 值。
观测值将从数据框的一列中全部采样(带替换)。
假设数据框的列称为 data$column1。 样本数量从 1:200 开始,每"轮次"增加 1。
如何创建一个 for 循环,以便对于每个"轮次",再采样一个观测值并计算新的 p 值? 最后,我想绘制所有 p 值。
n <- 1:200
for i in length(n) {
sample(data$column1,n, replace = TRUE)
pvalue <- t.test(data$column1, alternative = "greater")
}
虽然我知道你可能想使用for
循环,但这是使用sapply
或lapply
的好机会。我将使用iris
演示替代方案。虽然我将对所有样本使用"不等于 5"的简化测试iris$Sepal.Length
,但您应该更新特定数据的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)
.
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
都是相同的,所以我不会展示它。
2.*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
.所有数字在其他方面都是相等的。
3.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)第一个元素是空的,这并不奇怪,因为我们跳过了1i
;(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]
因为我们知道第一个是空的。我们可以很容易地在上面的for
循环之后立即完成out <- out[-1]
。
通过使用我上面演示的"匿名函数"定义,可以从模型中获取任何其他属性,例如模型系数。
4.*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
@r2evans有一个很好的答案。我将只关注您的代码并尝试使其绘制。
改进包括:
for
语法for (i in seq_along(n))
遍历每个 i。在您的情况下,您实际上想要执行for (i in 2:200)
i==1
因为将无法计算 p.值。- 需要将数据样本分配给变量。照原样,什么也没发生。或者,您可以直接将
sample
语句放在t.test()
调用中。 - 您希望将每个循环的结果保存为 pvalue。如果它按原样工作,
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))
理论上,您需要做的就是用data$column1
替换iris$Sepal.Length
并使用您喜欢的任何n
。