R:满足条件时停止循环

R: Stopping a Loop When a Condition is Met

提问人:stats_noob 提问时间:11/24/2021 更新时间:11/25/2021 访问量:2409

问:

我正在使用 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 生成随机数,直到 d_i$a > 10 和 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)

问题:但是,这将返回以下警告(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

并生成一个空表:

> results_df

data frame with 0 columns and 0 rows

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

谢谢!

R 循环 while 循环 数据操作

评论

1赞 mnist 11/24/2021
“直到 d_i 美元 10 美元>d_i> 美元 10 美元”是什么意思?您创建 1000 秒和秒。这也是警告的原因ab
0赞 stats_noob 11/24/2021
@MNIST:谢谢你的回复!我的意思是,继续生成随机数,直到出现“d_i”中的第一行,其中“d_i$a > 10 和 d_i$b > 10”。
0赞 stats_noob 11/24/2021
即当 d_i$a > 10 和 d_i$b > 10 时停止生成随机数。
1赞 mnist 11/24/2021
我仍然不确定你说的“直到第一排......”是什么意思。您始终生成 1000 行。您的意思是“直到第一个 data.frame 出现,其中至少有一行同时具有 > 10 和 b>10”?
0赞 stats_noob 11/24/2021
@Mnist:谢谢你的回复!由于在满足条件之前我不知道如何生成随机数,因此我只是尝试生成大量随机数,希望在这些随机数中满足所需的条件。你能告诉我如何做到这一点吗?谢谢!

答:

2赞 Donald Seinen 11/24/2021 #1

要打破循环(while 或 for),只需在条件之后。break()if

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

将打破循环。From : 控制权被转移到最内层循环之外的第一个语句。?break

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

2赞 mnist 11/24/2021 #2

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

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
  
}
3赞 Len Greski 11/24/2021 #3

原始帖子中的错误消息是由于 和 是具有 1,000 个元素的向量,而 10 是标量。因此,R 将 in 中的第一个元素和 in 中的第一个元素与 10 进行比较。d_i$ad_i$bd_i$ad_i$b

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

我将通过消除 10 个复制的集合来简化问题,以说明如何创建具有随机数的数据框,直到一行同时具有两者且值大于 10。ab

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

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 

初始化 和 后,循环的计算结果为生成两个随机数,分配一个索引值,并将它们作为数据框写入列表。循环的逻辑指示,如果小于或等于 10 或小于或等于 10,则循环将继续迭代。当两者都大于 10 时,它停止。abwhile()TRUEresultswhile()abab

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
>

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

while 循环的多个复制

为了像原始帖子中那样重复该过程 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
> 

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

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