如何使用循环计算P值以增加样本量?

时间:2019-05-27 16:22:45

标签: r loops for-loop random

我在创建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")
}

2 个答案:

答案 0 :(得分:3)

尽管我知道您可能想使用for循环,但这是使用sapplylapply的好机会。我将使用iris演示替代方法。尽管我将对所有示例使用iris$Sepal.Length的简化测试“不等于5”,但是您应该为特定数据更新alternative=和其他参数。

选择1:如果您只需要p值,我们就可以捕获...或捕获整个模型并进行p值的第二阶段检索。

选择2:我们可以使用*apply函数之一,它读起来很好(一旦您更习惯于R向量代码),或者可以坚持使用for循环。第一种选择具有可读性,尽管您可能更喜欢使用for循环,在这种情况下,您应该真正地预先分配列表/向量。 (预先定义长而空的列表/向量的原因:虽然您可以轻松地将向量outout <- 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)第一个元素为空,这并不奇怪,因为我们跳过了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

通过使用我上面演示的“匿名函数”定义,人们可以从模型中获取其他任何属性,例如模型系数。

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 

答案 1 :(得分:1)

@ r2evans有一个很好的答案。我只专注于您的代码,并尝试将其绘制出来。

改进之处包括:

  1. for的语法为for (i in seq_along(n))以遍历每个i。在您的情况下,您实际上要执行for (i in 2:200),因为i==1将无法计算p.value。
  2. 您需要将数据样本分配给一个变量。照原样,什么也没有发生。或者,您可以将sample语句直接放在t.test()调用中。
  3. 您要将每个循环的结果保存到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))

理论上,您所需要做的就是将iris$Sepal.Length替换为data$column1,然后使用您喜欢的n