R: Zatrzymanie cyklu Po spełnieniu warunków

0

Pytanie

Pracuję z językiem programowania R. założyłem następny cykl, który generuje 1000 liczb losowych, a następnie powtarza ten proces 10 razy:

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)

Pytanie: chciałbym zmienić ten cykl w taki sposób, aby zamiast generowania tylko 1000 liczb losowych kontynuował generować liczby losowe, dopóki nie zostanie spełniony określony warunek, np.: w dalszym ciągu generować liczby losowe, AŻ d_i$a > 10 I d_i$b >> 10.

Za pomocą operatora "WHILE ()", próbowałem zrobić to:

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)

Problem: to Jednak zwraca następujące ostrzeżenia (10 razy):

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

I tworzy pustą tabelę:

> results_df

data frame with 0 columns and 0 rows

Może ktoś proszę mi pomóc rozwiązać ten problem?

Dziękuję!

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

Najlepsza odpowiedź

3

Komunikaty o błędach w oryginalnej wiadomości związane z tym, że d_i$a i d_i$b są wektorami z 1000 elementów, a 10-skalar. Dlatego R porównuje pierwszy element w d_i$a i pierwszy element w d_i$b 10.

Aby usunąć komunikat o błędzie, musimy porównać wektor o długości 1 z скаляром 10. To wymaga restrukturyzacji kodu do generowania losowych liczb po jednym na raz. Z opisu w oryginalnym poście nie jest jasne, czy było to zachowanie celowe.

Ułatwię problem, z wyłączeniem zestaw 10 powtórzeń, aby zilustrować, jak stworzyć ramkę danych z przypadkowymi liczbami, aż w wierszu nie pojawią się oba a i b z wartościami, niż 10.

Najpierw zadajemy wartość początkową, aby odpowiedź powtarzalne, a następnie инициализируем niektóre obiekty. Ustawienie a i b do 0 gwarantujemy, że while() pętla będzie wykonywana co najmniej raz.

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 i bw while() cykl szacuje się na TRUE generuje dwa losowe liczby, przypisuje wartość indeksu i zapisuje je w postaci ramki danych w results Lista. Logika dla while() cykl wskazuje, że jeśli kiedykolwiek a jest mniejsza lub równa 10 lub b jest mniejsza lub równa 10, cykl ciągle się powtarzać. To jest zakończone, kiedy oba a i 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
}

Cykl przerywa wykonanie po dziewiątej iteracji, jak możemy zobaczyć, drukując wyników ramka danych po połączeniu poszczególnych wierszy z do.call() i rbind().

df <- do.call(rbind,results)
df

...i wniosek:

> 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
>

Należy zwrócić uwagę, że ostatnia linia w ramce danych ma znaczenia, przekraczające 10 dla obu a i b.

Wielokrotne replikacji pętli while

Aby powtórzyć 10 razy, jak to jest zrobione w oryginalnej wiadomości, możemy stwierdzić operację w for() wykonaj cykl i dodaj druga lista, combined_results aby zapisać wyniki każdej iteracji.

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,] 

...i dane wyjściowe dla pierwszych 4 iteracji zewnętrznej pętli:

> 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
> 

Jeszcze raz należy pamiętać, że ostatni wiersz w każdej iteracji (9, 18, 19 i 21) ma wartości powyżej 10 dla obu a i b.

Należy zwrócić uwagę, że podejście to nie korzysta z wektoryzacji operacji w R, co oznacza, że zamiast generowania 1000 liczb losowych przy każdym wywołaniu rnorm(), kod oparty na while() generuje jedną losową liczbę dla każdego połączenia rnorm(). Od tego czasu rnorm() to ресурсоемкая funkcja kod, który minimalizuje ilość razy rnorm() najlepiej wykonać.

2021-11-24 20:45:06
2

Mam nadzieję, że te uwagi pomogą zrozumieć, jak to działa. W zasadzie używa repeat to jest po prostu nieskończona pętla. Można go zatrzymać za pomocą break słowo kluczowe.

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

Aby wyrwać się z pętli (w czasie lub do), po prostu w break() po tym, jak if stan.

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

Wyrwać się z pętli. Od ?break: sterowanie jest przekazywane do pierwszego operatora poza samego wewnętrznego cyklu.

Jednak z twojego pytania nie jest do końca jasne, dlaczego chcesz to zrobić - taka przepływ sterowania może być niespójny rozwiązaniem, ponieważ może istnieć векторизованное rozwiązanie. Poza tym, uważaj robić niepotrzebnych rzeczy w środku cyklu - to częsta przyczyna powolnego wykonywania kodu. Tutaj możemy wywnioskować kilka rzeczy z pętli for, takie jak d_i$iteration i d_i$indexi tak w końcu wychodzi ten sam wynik. Spójrz na Trzeci Krąg.

2021-11-23 23:46:14

W innych językach

Ta strona jest w innych językach

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