R: Остановка цикла При выполнении условия

0

Вопрос

Я работаю с языком программирования 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 случайных чисел он продолжал генерировать случайные числа до тех пор, пока не будет выполнено определенное условие, например: ПРОДОЛЖАЙТЕ генерировать случайные числа ДО тех ПОР, ПОКА 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

Может кто-нибудь, пожалуйста, помочь мне решить эту проблему?

Спасибо!

data-manipulation loops r while-loop
2021-11-23 23:09:34
3

Лучший ответ

3

Сообщения об ошибках в исходном сообщении связаны с тем, что d_i$a и d_i$b являются векторами с 1000 элементами, а 10-скаляр. Поэтому R сравнивает первый элемент в d_i$a и первый элемент в d_i$b с 10.

Чтобы устранить сообщение об ошибке, нам нужно сравнить вектор длиной 1 со скаляром 10. Это требует реструктуризации кода для генерации случайных чисел по одному за раз. Из описания в оригинальном посте неясно, было ли это поведение преднамеренным.

Я упрощу проблему, исключив набор из 10 повторов, чтобы проиллюстрировать, как создать фрейм данных со случайными числами до тех пор, пока в строке не появятся оба a и b со значениями, превышающими 10.

Сначала мы задаем начальное значение, чтобы сделать ответ воспроизводимым, а затем инициализируем некоторые объекты. Установив 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
>

Обратите внимание, что последняя строка во фрейме данных имеет значения, превышающие 10 для обоих a и b.

Многократные репликации цикла 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 для обоих a и b.

Обратите внимание, что этот подход не использует преимущества векторизованных операций в R, что означает, что вместо генерации 1000 случайных чисел при каждом вызове rnorm(), код, основанный на while() генерирует одно случайное число для каждого вызова rnorm(). С тех пор rnorm() это ресурсоемкая функция, код, который сводит к минимуму количество раз rnorm() желательно выполнить.

2021-11-24 20:45:06
2

Я надеюсь, что эти комментарии помогут понять, как это работает. Он в основном использует 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
  
}
2021-11-24 01:19:52
2

Чтобы вырваться из цикла (во время или для), просто в 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

Вырвется из петли. От ?break: управление передается первому оператору за пределами самого внутреннего цикла.

Однако из вашего вопроса не совсем понятно, почему вы пытаетесь это сделать - такой поток управления может оказаться неподходящим решением, поскольку может существовать векторизованное решение. Кроме того, остерегайтесь делать ненужные вещи внутри цикла - это обычная причина медленного выполнения кода. Здесь мы можем вывести некоторые вещи из цикла for, такие как d_i$iteration и d_i$index, и все равно в итоге получается тот же результат. Взгляните на Третий Круг.

2021-11-23 23:46:14

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................