R: l'Arresto di un Ciclo Quando viene Soddisfatta una Condizione di

0

Domanda

Sto lavorando con il linguaggio di programmazione R. Ho creato il seguente ciclo che genera 1000 numeri casuali - e poi si ripete questo processo 10 volte:

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)

Domanda: vorrei cambiare questo ciclo ad che, invece che solo la generazione 1000 numeri casuali, mantiene la generazione di numeri casuali fino a quando una certa condizione è soddisfatta, ad esempio: MANTENERE la generazione di numeri casuali FINO a d_i$a > 10 E d_i$b > 10.

Utilizzando un "WHILE()" dichiarazione", ho provato a fare questo:

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)

Problema: Tuttavia, questo restituisce le seguenti avvertenze (10 volte):

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

E produce una tabella vuota:

> results_df

data frame with 0 columns and 0 rows

Qualcuno può aiutarmi a risolvere questo problema?

Grazie!

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

Migliore risposta

3

I messaggi di errore nel post originale, sono dovuti al fatto che d_i$a e d_i$b sono vettori con 1.000 elementi e 10 è uno scalare. Pertanto, R confronta il primo elemento d_i$a e il primo elemento d_i$b con 10.

Per risolvere il messaggio di errore di cui abbiamo bisogno per confrontare un vettore di lunghezza 1 per scalare 10. Questo richiede di ristrutturazione il codice per generare i numeri casuali, uno alla volta. Dalla descrizione nel post originale, non è chiaro se questo comportamento è stato intenzionale.

Cercherò di semplificare il problema, eliminando il set di 10 repliche per illustrare come creare un frame di dati con numeri casuali fino a quando una riga è presente sia a e b con valori superiori a 10.

Primo, abbiamo impostato un seme per rendere la risposta riproducibile e quindi inizializzare alcuni oggetti. Impostando a e b a 0 possiamo garantire che il while() ciclo viene eseguito almeno una volta.

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 

Aver inizializzato a e bil while() ciclo di valuta TRUE genera due numeri casuali, assegna un valore di indice, e li scrive come un frame di dati per la results elenco. La logica per la while() ciclo indica che se a è inferiore o uguale a 10 o b è inferiore o uguale a 10, il ciclo continua a scorrere. Si ferma quando sia a e b sono più di 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
}

Il ciclo si interrompe l'esecuzione dopo la nona ripetizione, come si può vedere dalla stampa i dati risultanti fotogramma dopo combinare le singole righe con do.call() e rbind().

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

...e l'output:

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

Si noti che l'ultima riga del frame di dati ha valori maggiori di 10 per entrambi a e b.

Più repliche del ciclo while

Ripetere il processo per 10 volte come è fatto nel post originale, concludere l'operazione for() loop, e aggiungere un secondo elenco, combined_results per salvare i risultati di ogni iterazione.

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

...e l'uscita per i primi 4 iterazioni del ciclo esterno:

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

Di nuovo si nota che l'ultima riga di ogni iterazione (9, 18, 19 e 21) hanno valori maggiori di 10 per entrambi a e b.

Si noti che questo approccio non riesce ad approfittare di vettorializzare le operazioni in R, il che significa che invece di generare 1000 numeri casuali con ogni chiamata a rnorm()il codice basato su un while() genera un singolo numero casuale per ogni chiamata a rnorm(). Dal rnorm() è una risorsa per la cpu funzione, il codice che riduce al minimo il numero di volte rnorm() esegue, è auspicabile.

2021-11-24 20:45:06
2

Spero che queste osservazioni aiutano a seguire come funziona. Principalmente si fa uso di repeat che è solo un loop infinito. Esso può essere fermato con l' break parola chiave.

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

Rompere fuori di un ciclo (while o for), semplicemente a un break() dopo un if condizione.

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

Sarà l'interruzione di un ciclo. Da ?break: il controllo viene trasferito alla prima affermazione esterna più interna loop.

Tuttavia, dalla tua domanda non è del tutto chiaro il motivo per cui si sta tentando questo - tale flusso di controllo potrebbe non essere la soluzione più appropriata, come vettorializzare soluzione potrebbe esistere. Inoltre, diffidare di fare superflua cose all'interno di un ciclo - è una causa comune per rallentare l'esecuzione di codice. Qui possiamo prendere alcune cose fuori dal ciclo for, come d_i$iteration e d_i$indexe ancora finire con lo stesso risultato. Guardate il Terzo Cerchio.

2021-11-23 23:46:14

In altre lingue

Questa pagina è in altre lingue

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