R: a interrupção de um Ciclo Quando uma Condição for Atendida

0

Pergunta

Eu estou trabalhando com a linguagem de programação R. Eu criei o seguinte loop que gera 1000 números aleatórios e, em seguida, repete-se este processo 10 vezes:

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)

Pergunta: eu gostaria de mudar este ciclo tal que, em vez de apenas gerar 1000 números aleatórios, que mantém a geração de números aleatórios até que uma certa condição seja satisfeita, por exemplo: MANTER a geração de números aleatórios ATÉ d_i$a > 10 E d_i$b > 10.

Usando um "WHILE()" declaração", eu tentei fazer isso:

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: no Entanto, este retorna os seguintes avisos (10 vezes):

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 produz uma tabela vazia:

> results_df

data frame with 0 columns and 0 rows

Alguém por favor pode me ajudar a corrigir esse problema?

Obrigado!

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

Melhor resposta

3

As mensagens de erro no post original, devido ao fato de que d_i$a e d_i$b são vetores com 1000 elementos e 10 é um escalar. Portanto, R, compara-se o primeiro elemento d_i$a e o primeiro elemento na d_i$b com 10.

Para resolver a mensagem de erro precisamos comparar um vetor com comprimento de 1 a escalar 10. Isso requer a reestruturação do código para gerar os números aleatórios uma de cada vez. A partir da descrição no post original, não está claro se este comportamento foi intencional.

Vou simplificar o problema, eliminando o conjunto de 10 repetições para ilustrar como criar um quadro de dados com números aleatórios até que uma linha tem tanto a e b com valores maiores que 10.

Primeiro, vamos definir uma semente para fazer a resposta pode ser reproduzido e, em seguida, inicializar alguns objetos. Por definição a e b para 0, podemos garantir que os while() loop será executado pelo menos uma 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 

Ter inicializado a e bo while() loop avalia a TRUE gera dois números aleatórios, atribui um valor de índice, e escreve-los como um quadro de dados para o results a lista. A lógica para a while() loop indica que se a é menor ou igual a 10 ou b é menor ou igual a 10, o loop continua a iteração. Ele pára quando ambos a e b são maiores do 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
}

O ciclo pára a execução, após a nona iteração como podemos ver através da impressão resultante quadro de dados depois de combinar as linhas individuais com do.call() e rbind().

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

...e a saída:

> 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 a última linha do quadro de dados tem valores maiores que 10 para os dois a e b.

Várias repetições do loop while

Para repetir o processo 10 vezes como é feito na postagem original, nós enrole a operação em um for() loop, e adicionar uma segunda lista, combined_results para salvar os resultados de cada iteração.

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 a saída para as 4 primeiras iterações do loop 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
> 

Novamente nota-se que a última linha em cada iteração (9, 18, 19 e 21) têm valores maiores que 10 para os dois a e b.

Nota-se que esta abordagem não tira vantagem de vetorizados operações em R, o que significa que, em vez de gerar mais de 1.000 números aleatórios com cada chamada para rnorm()o código com base em uma while() gera um único número aleatório por chamada rnorm(). Desde rnorm() é um recurso intensivo de função, o código que minimiza o número de vezes rnorm() executa é desejável.

2021-11-24 20:45:06
2

Espero que estes comentários de ajuda para seguir como funciona. É, principalmente, faz uso de repeat que é apenas um loop infinito. Ele pode ser parado usando o break palavra-chave.

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 sair de um loop (while ou for), simplesmente em um break() depois de uma if condição.

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

Vai sair de um loop. A partir de ?break: o controle é transferido para a primeira instrução fora do interior-mais de loop.

No entanto, a sua pergunta não é totalmente claro qual você está tentando esta - tais fluxo de controle pode não ser a solução mais adequada, como um vetorizados solução pode existir. Ainda, o cuidado de fazer coisas desnecessárias dentro de um ciclo - que é uma causa comum de lenta execução de código. Aqui podemos tirar algumas coisas fora do ciclo, tais como d_i$iteration e d_i$indexe ainda acabar com o mesmo resultado. Ter um olhar para o Terceiro Círculo.

2021-11-23 23:46:14

Em outros idiomas

Esta página está em outros idiomas

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