R:满足条件时停止循环

R: Stopping a Loop When a Condition is Met

我正在使用 R 编程语言。我创建了以下生成 1000 个随机数的循环 - 然后重复此过程 10 次:

results <- list()

for (i in 1:10){

a = rnorm(1000,10,1)
b = rnorm(1000,10,1)


d_i = data.frame(a,b)
d_i$index = 1:nrow(d_i)
d_i$iteration = as.factor(i)

 results[[i]] <- d_i

}



results_df <- do.call(rbind.data.frame, results)

问题:我想改变这个循环,让它不再只生成 1000 个随机数,而是不断生成随机数,直到满足某个条件,例如:KEEP生成随机数 UNTIL d_i$a > 10 AND d_i$b > 10.

使用“WHILE()”语句,我尝试这样做:

results <- list()

for (i in 1:10){

 while (d_i$a > 10 & d_i$b >10) {

a = rnorm(1000,10,1)
b = rnorm(1000,10,1)


d_i = data.frame(a,b)
d_i$index = 1:nrow(d_i)
d_i$iteration = as.factor(i)

 results[[i]] <- d_i

}

}


results_df <- do.call(rbind.data.frame, results)

问题: 然而,这个returns以下警告(10次):

Warning messages:
1: In while (d_i$a > 10 & d_i$b > 10) { :
  the condition has length > 1 and only the first element will be used

并生成一个空 table:

> results_df

data frame with 0 columns and 0 rows

有人可以帮我解决这个问题吗?

谢谢!

要跳出循环(while 或 for),只需在 if 条件之后的 break()

out <- vector("integer", 26)
for (i in seq_along(letters)) {
  if(letters[i] == "t") break()
  out[i] <- i+1
}
out
#> [1]  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20  0  0  0  0  0  0  0

将跳出循环。从 ?break:控制转移到最内层循环外的第一条语句。

但是,从你的问题来看,你为什么要尝试这个并不完全清楚 - 这种控制流可能不是合适的解决方案,因为可能存在矢量化解决方案。此外,请注意不要在循环内做不必要的事情——这是 运行 代码缓慢的常见原因。在这里,我们可以从 for 循环中取出一些东西,例如 d_i$iterationd_i$index,但仍然会得到相同的结果。看看 the Third Circle.

我希望这些评论有助于了解它的工作原理。它主要利用了 repeat 这只是一个无限循环。它可以使用 break 关键字停止。

results <- list()


for (i in 1:10){
  
  # do until break
  repeat {
    
    # repeat many random numbers
    a = rnorm(1000,10,1)
    b = rnorm(1000,10,1)
    
    # does any pair meet the requirement
    if (any(a > 10 & b > 10)) {
      
      # put it in a data.frame
      d_i = data.frame(a,b)
      
      # end repeat
      break
    }
  }
  
  # select all rows until the first time the requirement is met
  # it must be met, otherwise the loop would not have ended
  d_i <- d_i[1:which(d_i$a > 10 & d_i$b > 10)[1], ]
  
  # prep other variables
  d_i$index = seq_len(nrow(d_i))
  d_i$iteration = as.factor(i)
  
  results[[i]] <- d_i
  
}

原始post中的错误消息是由于d_i$ad_i$b是具有1,000个元素的向量,而10是标量。因此,R 将 d_i$a 中的第一个元素和 d_i$b 中的第一个元素与 10.

进行比较

要解决错误消息,我们需要将长度为 1 的向量与标量 10 进行比较。这需要重构代码以一次生成一个随机数。从原文post中的描述来看,不清楚这种行为是否是故意的。

我将通过消除 10 个重复的集合来简化问题,以说明如何创建具有随机数的数据框,直到一行中的值都大于 10 的 ab .

首先,我们设置一个种子使答案可重现,然后初始化一些对象。通过将 ab 设置为 0,我们确保 while() 循环将至少执行一次。

set.seed(950141238) # for reproducibility 
results <- list()
a <- 0 # initialize a to a number < 10
b <- 0 # initialize b to a number < 10 
i <- 1 # set a counter 

初始化ab后,while()循环计算为TRUE生成两个随机数,分配一个索引值,并将它们写入数据框到 results 列表。 while() 循环的逻辑表明,如果 a 小于或等于 10 或 b 小于或等于 10,则循环将继续迭代。当 ab 都大于 10 时停止。

while(a <= 10 | b <= 10){
     a <- rnorm(1,10,1) # generate 1 random number with mean of 10 and sd of 1
     b <- rnorm(1,10,1) # ditto
     results[[i]] <- data.frame(index = i,a,b)
     i <- i + 1 # increment i
}

循环在第九次迭代后停止执行,正如我们在将各个行与 do.call()rbind().

组合后打印结果数据框所见
df <- do.call(rbind,results)
df

...输出:

> df
  index         a         b
1     1  8.682442  8.846653
2     2  9.204682  8.501692
3     3  8.886819 10.488972
4     4 11.264142  8.952981
5     5  9.900112 10.918042
6     6  9.185120 10.625667
7     7  9.620793 10.316724
8     8 11.718397  9.256835
9     9 10.034793 11.634023
>

请注意,对于 ab,数据框中最后一行的值均大于 10。

while 循环的多次复制

为了像原来 post 中那样重复该过程 10 次,我们将操作包装在 for() 循环中,并添加第二个列表 combined_results 以保存每次迭代的结果。

set.seed(950141238) # for reproducibility 
combined_results <- list()
for(iteration in 1:10){
     results <- list()
     a <- 0 # initialize a to a number < 10
     b <- 0 # initialize b to a number < 10 
     i <- 1 # set a counter 
     while((a < 10) | (b < 10)){
          a <- rnorm(1,10,1) # generate 1 random number with mean of 10 and sd of 1
          b <- rnorm(1,10,1) # ditto
          results[[i]] <- data.frame(iteration,index = i,a,b)
          i <- i + 1 # increment i
     }
     combined_results[[iteration]] <- do.call(rbind,results)
}
df <- do.call(rbind,combined_results)
df[df$iteration < 5,] 

...以及外循环前 4 次迭代的输出:

> df[df$iteration < 5,]
   iteration index         a         b
1          1     1  8.682442  8.846653
2          1     2  9.204682  8.501692
3          1     3  8.886819 10.488972
4          1     4 11.264142  8.952981
5          1     5  9.900112 10.918042
6          1     6  9.185120 10.625667
7          1     7  9.620793 10.316724
8          1     8 11.718397  9.256835
9          1     9 10.034793 11.634023
10         2     1 11.634331  9.746453
11         2     2  9.195410  7.665265
12         2     3 11.323344  8.279968
13         2     4  9.617224 11.792142
14         2     5  9.360307 11.166162
15         2     6  7.963320 11.325801
16         2     7  8.022093  8.568503
17         2     8 10.440788  9.026129
18         2     9 10.841408 10.033346
19         3     1 11.618665 10.179793
20         4     1 10.975061  9.503309
21         4     2 10.209288 12.409656
> 

我们再次注意到,对于 ab,每次迭代(9、18、19 和 21)的最后一行的值均大于 10。

请注意,此方法未能利用 R 中的向量化运算,这意味着每次调用 rnorm() 时,代码不会生成 1,000 个随机数,而是基于 while() 生成单个每次调用 rnorm() 的随机数。由于 rnorm() 是一个资源密集型函数,因此需要尽量减少 rnorm() 执行次数的代码。