【问题标题】:R: Stopping a Loop When a Condition is MetR:满足条件时停止循环
【发布时间】:2022-01-02 10:05:30
【问题描述】:

我正在使用 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 generate random numbers 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)

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

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

谢谢!

【问题讨论】:

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

标签: r loops while-loop data-manipulation


【解决方案1】:

要跳出循环(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

【讨论】:

    【解决方案2】:

    我希望这些 cmets 有助于了解其工作原理。它主要利用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
      
    }
    

    【讨论】:

      【解决方案3】:

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

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

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

      首先,我们设置种子以使答案可重现,然后初始化一些对象。通过将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 循环的多次复制

      要像原始帖子中那样重复该过程 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)对于 ab 的值都大于 10。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-12
        • 1970-01-01
        • 2019-01-29
        • 2013-11-22
        • 1970-01-01
        • 1970-01-01
        • 2019-05-16
        • 2021-12-13
        相关资源
        最近更新 更多