R: la Detención de un Bucle Cuando se cumple una Condición

0

Pregunta

Estoy trabajando con el departamento de I lenguaje de programación. He creado el siguiente bucle que genera 1000 números aleatorios y, a continuación, repite este proceso 10 veces:

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)

Pregunta: me gustaría cambiar este bucle que, en vez de sólo la generación de 1000 números aleatorios, se mantiene la generación de números aleatorios hasta que se cumpla cierta condición, por ejemplo: MANTENER la generación de números aleatorios HASTA d_i$a > 10 Y d_i$b > 10.

El uso de un "WHILE()" declaración", traté de hacer esto:

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: sin Embargo, este devuelve las siguientes advertencias (10 veces):

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

Y produce una tabla vacía:

> results_df

data frame with 0 columns and 0 rows

Por favor alguien puede ayudarme a solucionar este problema?

Gracias!

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

Mejor respuesta

3

Los mensajes de error en el post original se debe al hecho de que d_i$a y d_i$b son vectores con 1.000 elementos y 10 es un escalar. Por lo tanto, R compara el primer elemento en d_i$a y el primer elemento en d_i$b con 10.

Para resolver el mensaje de error tenemos que comparar un vector con longitud de 1 a escalar 10. Esto requiere reestructurar el código para generar los números aleatorios de una en una. A partir de la descripción en el post original, no está claro si este comportamiento fue intencional.

Voy a simplificar el problema mediante la eliminación de la serie de 10 repeticiones para ilustrar cómo crear un marco de datos con números aleatorios hasta una fila tiene tanto a y b con valores mayores que 10.

En primer lugar, realizaremos una semilla para que la respuesta es reproducible, y luego inicializar algunos de los objetos. Mediante el establecimiento de a y b a 0 nos aseguramos de que el while() bucle se ejecutará al menos una vez.

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 

De haber inicializado a y bel while() bucle se evalúa a TRUE genera dos números aleatorios, se le asigna un valor de índice, y escribe como un marco de datos para la results lista. La lógica de la while() loop indica que si bien a es menor o igual a 10 o b es menor o igual a 10, el bucle mantiene la iteración. Se detiene cuando ambos a y b son mayores que 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
}

El bucle deja de ejecutarse después de la novena iteración como podemos ver por la impresión de los datos resultantes del marco después de combinar las filas individuales con do.call() y rbind().

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

...y el resultado:

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

Observe que la última fila en el marco de datos tiene valores mayores que 10 para ambos a y b.

Múltiples repeticiones del bucle while

Repetir el proceso 10 veces como se hace en el post original, que envuelva la operación en un for() bucle, y agregar una segunda lista, combined_results para guardar los resultados de cada iteración.

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

...y la salida de las 4 primeras iteraciones del bucle externo:

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

De nuevo observamos que la última fila en cada iteración (9, 18, 19, y 21) tienen valores mayores que 10 para ambos a y b.

Tenga en cuenta que este enfoque no toma ventaja de vectorizados operaciones en R, lo que significa que en lugar de generar 1,000 números aleatorios con cada llamada a rnorm()el código que se basa en un while() genera un único número aleatorio por llamada a rnorm(). Desde rnorm() es un uso intensivo de los recursos de la función, el código que minimiza el número de veces rnorm() ejecuta es deseable.

2021-11-24 20:45:06
2

Espero que estos comentarios ayudan a seguir de cómo funciona. Principalmente se hace uso de repeat que es sólo un bucle infinito. Puede ser dejado de usar el break la palabra clave.

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

Para salir de un bucle (while o for), simplemente en un break() después de un if condición.

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

Va a salir de un bucle. De ?break: el control se transfiere a la primera instrucción del exterior al interior de la mayoría de bucle.

Sin embargo, su pregunta no está del todo claro por qué están tratando de esta - tales de control de flujo puede no ser la solución adecuada, como un vectorizados solución podría existir. Además, tenga cuidado de hacer innecesarios cosas dentro de un bucle es una causa común para retardar la ejecución de código. Aquí podemos tomar algunas cosas de la for-loop, tales como d_i$iteration y d_i$indexy aún así terminar con el mismo resultado. Tener una mirada en el Tercer Círculo.

2021-11-23 23:46:14

En otros idiomas

Esta página está en otros idiomas

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