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$iteration
和 d_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$a
和d_i$b
是具有1,000个元素的向量,而10是标量。因此,R 将 d_i$a
中的第一个元素和 d_i$b
中的第一个元素与 10.
进行比较
要解决错误消息,我们需要将长度为 1 的向量与标量 10 进行比较。这需要重构代码以一次生成一个随机数。从原文post中的描述来看,不清楚这种行为是否是故意的。
我将通过消除 10 个重复的集合来简化问题,以说明如何创建具有随机数的数据框,直到一行中的值都大于 10 的 a
和 b
.
首先,我们设置一个种子使答案可重现,然后初始化一些对象。通过将 a
和 b
设置为 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
初始化a
和b
后,while()
循环计算为TRUE
生成两个随机数,分配一个索引值,并将它们写入数据框到 results
列表。 while()
循环的逻辑表明,如果 a
小于或等于 10 或 b
小于或等于 10,则循环将继续迭代。当 a
和 b
都大于 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
>
请注意,对于 a
和 b
,数据框中最后一行的值均大于 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
>
我们再次注意到,对于 a
和 b
,每次迭代(9、18、19 和 21)的最后一行的值均大于 10。
请注意,此方法未能利用 R 中的向量化运算,这意味着每次调用 rnorm()
时,代码不会生成 1,000 个随机数,而是基于 while()
生成单个每次调用 rnorm()
的随机数。由于 rnorm()
是一个资源密集型函数,因此需要尽量减少 rnorm()
执行次数的代码。
我正在使用 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$iteration
和 d_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$a
和d_i$b
是具有1,000个元素的向量,而10是标量。因此,R 将 d_i$a
中的第一个元素和 d_i$b
中的第一个元素与 10.
要解决错误消息,我们需要将长度为 1 的向量与标量 10 进行比较。这需要重构代码以一次生成一个随机数。从原文post中的描述来看,不清楚这种行为是否是故意的。
我将通过消除 10 个重复的集合来简化问题,以说明如何创建具有随机数的数据框,直到一行中的值都大于 10 的 a
和 b
.
首先,我们设置一个种子使答案可重现,然后初始化一些对象。通过将 a
和 b
设置为 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
初始化a
和b
后,while()
循环计算为TRUE
生成两个随机数,分配一个索引值,并将它们写入数据框到 results
列表。 while()
循环的逻辑表明,如果 a
小于或等于 10 或 b
小于或等于 10,则循环将继续迭代。当 a
和 b
都大于 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
>
请注意,对于 a
和 b
,数据框中最后一行的值均大于 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
>
我们再次注意到,对于 a
和 b
,每次迭代(9、18、19 和 21)的最后一行的值均大于 10。
请注意,此方法未能利用 R 中的向量化运算,这意味着每次调用 rnorm()
时,代码不会生成 1,000 个随机数,而是基于 while()
生成单个每次调用 rnorm()
的随机数。由于 rnorm()
是一个资源密集型函数,因此需要尽量减少 rnorm()
执行次数的代码。