For loops: How could I repeat several steps? - r

I am relatively new to R and am a little bit confused about for loops.
I tried to repeat several steps within a for loop after a specified condition is hit. I tried to put the loop one step back, but have recognized that the loop keeps counting up and ignores my command. Here is a simple example to illustrate what I meant:
a <- 1:10
b <- rep(NA, 15)
fun <- function(){
i <- 1
for(i in 1:10){
b[i] <- a[i]
i <- i - 1 # This is the line I am talking about.
}
return(b)
}
fun()
[1] 1 2 3 4 5 6 7 8 9 10 NA NA NA NA NA
I have expected that this code would run forever and assigns only 1's. Is there a way how I could move one step back within for loops or would I have to do a completely different approach?
Thanks a lot in advance!

The way a for loop works is that if you have for (i in 1:10), first i is set to 1, and the body of the loop is executed. Then we go back to the top and i is set to 2, and the body of the loop is executed again. Basically, this loop:
for (x in 1:3) {
print(x^2)
}
is equivalent to this code:
x = 1
print(x^2)
x = 2
print(x^2)
x = 3
print(x^2)
So your attempt to roll the loop back a step by changing i doesn't work because the for loop doesn't actually monitor what i is, it just runs the body of the loop once for each element of the original 1:10 sequence.
If you want to sometimes repeat a step, you should use a while loop and increment i manually:
a <- 1:10
b <- rep(NA, 15)
i <- 1
while (i <= 10) {
b[i] <- a[i]
# Flip a coin, don't move on if it comes up tails
# Replace this test with the relevant condition you
# want to check for
if (sample(c(0, 1), 1) == 1) {
i <- i + 1
} else {
print(paste("Repeating with i =", i))
}
}

Related

Return value in R function using for loop

I've made this simple code to test something that isn't working.
funcion=function(x,p){
for(i in 1:p){
return(x+i)
}
}
funcion(5,5)
This returns the value 6, and not 6,7,8,9,10 which is what I would've expected and what I'm looking for.
Can someone explain why this works this way and how can I make it so that I get what I want?
Thank you
We need to collect the output in an object and then return
f1 <- function(x, p) {
# // create an object to store the output from each iteration
out <- numeric(p)
for(i in seq_len(p)) {
out[i] <- x + i # // assign the output based on the index
}
return(out)
}
f1(5, 5)
#[1] 6 7 8 9 10
In R, this can be executed without a for loop i.e.
5 + seq_len(5)
#[1] 6 7 8 9 10
The issue is that return inside a function returns only once. So, it gets executed the first time with x + 1 and it return that output instead of the full output

Problems with forloops inside a function R

I have a problem with a function of the following kind:
fun.name <- function(x,y) {
a<-x
b<-y
for (i in c(a, b)){
i<-i+1
print (i)
}
print(a)
print(b)
}
fun.name(1, 2)
The result is
[1] 2
[1] 3
[1] 1
[1] 2
The same result is obtained if I do not create any a and b and I simply keep x and y ( fun.name <- function(x,y) { for (i in c(a, b))...).
I cannot understand this behavior.
What I wanted was a function which adds one to every arguments and prints the results. Why does not the loop modify the variables a and b when it is defined within the function? I guess it is a problem of environments, and that I have not understood the nature of a function arguments.
Thank you for any suggestions.
I actually expect to see your current output. Here is your code, formatted, with explanations as comments:
fun.name <- function(x,y) {
a <- x
b <- y
for (i in c(a, b)) { # i in (1, 2)
# first iteration: i = 2, print 2
# second iteration: i = 3, print 3
i <- i+1
print(i)
}
print(a) # prints 1 (a was only assigned once)
print(b) # prints 2 (same reason as above)
}
fun.name(1, 2)
There are no changes to a and b after their initial assignments inside the function. But, even if there were changes, the variables a and b would not even be visible outside the scope of the function.

How to make R skip for 2:n cycle in case n = 1?

I have the next query.
I have a cycle
for (i in 2:n) { ... }
and it executes twice in case n = 1 for i equals 2 and then for 1, but I want this cycle isn't been executed at all in such case.
I've also tried
seq(from = 2, to = 1, by = 1)
but it generates exception.
I guess than common question, but I am new in that language and wasn't able to bypass it or find workaround in internet.
Thank you in advance.
Use seq_len which is safer to use
n <- 1
for (i in seq_len(n)[-1]) {
cat("Check", i, "\n")
}
#Does not print anything
n <- 4
for (i in seq_len(n)[-1]) {
cat("Check", i, "\n")
}
#Check 2
#Check 3
#Check 4
When you use 2:n and when n = 1 it gives
2:1
#[1] 2 1
hence, it runs the loop twice.

Monte Carlo Simulation with Replacement Based On Sum of A Column

I am trying to simulate an unlikely situation in a videogame using a Monte Carlo simulation. I'm extremely new at coding and thought this would be a fun situation to simulate.
There are 3 targets and they are being attacked 8 times independently. My problem comes with how to deal with the fact that one of the columns cannot be attacked more than 6 times, when there are 8 attacks.
I would like to take any attack aimed at column 2 select one of the other 2 columns at random to attack instead, but only if column 2 has been attacked 6 times already.
Here is my attempt to simulate with 5000 repeats, for example.
#determine number of repeats
trial <- 5000
#create matrix with a row for each trial
m <- matrix(0, nrow = trial, ncol = 3)
#The first for loop is for each row
#The second for loop runs each attack independently, sampling 1:3 at random, then adding one to that position of the row.
#The function that is called by ifelse() when m[trial, 2] > 6 = TRUE is the issue.
for (trial in 1:trial){
for (attack in 1:8) {
target <- sample(1:3, 1)
m[trial, target] <- m[trial, target] + 1
ifelse(m[trial, 2] > 6, #determines if the value of column 2 is greater than 6 after each attack
function(m){
m[trial, 2] <- m[trial, 2] - 1 #subtract the value from the second column to return it to 6
newtarget <- sample(c(1,3), 1) #select either column 1 or 3 as a new target at random
m[trial, newtarget] <- m[trial, newtarget] + 1 #add 1 to indicate the new target has been selected
m}, #return the matrix after modification
m) #do nothing if the value of the second column is <= 6
}
}
For example, if I have the matrix below:
> matrix(c(2,1,5,7,1,0), nrow = 2, ncol = 3)
[,1] [,2] [,3]
[1,] 2 5 1
[2,] 1 7 0
I would like the function to look at the 2nd line of the matrix, subtract 1 from 7, and then add 1 to either column 1 or 3 to create c(2,6,0) or c(1,6,1). I would like to learn how to do this within the loop, but it could be done afterwards as well.
I think I am making serious, fundamental error with how to use function(x) or ifelse.
Thank you.
Here's an improved version of your code:
set.seed(1)
trial <- 5000
#create matrix with a row for each trial
m <- matrix(0, nrow = trial, ncol = 3)
#The first for loop is for each row
#The second for loop runs each attack independently, sampling 1:3 at random, then adding one to that position of the row.
#The function that is called by ifelse() when m[trial, 2] > 6 = TRUE is the issue.
for (i in 1:trial){
for (attack in 1:8) {
target <- sample(1:3, 1)
m[i, target] <- m[i, target] + 1
#determines if the value of column 2 is greater than 6 after each attack
if(m[i, 2] > 6){
#subtract the value from the second column to return it to 6
m[i, 2] <- m[i, 2] - 1
#select either column 1 or 3 as a new target at random
newtarget <- sample(c(1,3), 1)
#add 1 to indicate the new target has been selected
m[i, newtarget] <- m[i, newtarget] + 1
}
}
}
# Notice the largest value in column 2 is no greater than 6.
apply(m, 2, max)
set.seed is used to make the results reproducible (usually just used for testing). The ifelse function has a different purpose than the normal if-else control flow. Here's an example:
x = runif(100)
ifelse(x < 0.5, 0, x)
You'll notice any element in x that is less than 0.5 is now zero. I changed your code to have an if block. Notice that m[i, 2] > 6 returns a single TRUE or FALSE whereas in the small example above, x < 0.5 a vector of logicals is returned. So ifelse can take a vector of logicals, but the if block requires there be only a single logical.
You were on the right track with using function, but it just isn't necessary in this case. Often, but not always, you'll define a function like this:
f = function(x)
x^2
But just returning the value doesn't mean what you want is changed:
x = 5
f(5) # 25
x # still 5
For more on this, look up function scope in R.
Lastly, I changed the loop to be i in 1:trial instead of trial in 1:trial. You probably wouldn't notice any issues in your case, but it is better practice to use a separate variable than that which makes up the range of the loop.
Hope this helps.
P.S. R isn't really known for it's speed when looping. If you want to make things goes faster, you'll typically need to vectorize your code.

Looping through selected values in R

I want to iterate a loop only for some values so I am using this:
present <- c(3,5,7,8)
for(i in present)
{
print(i)
}
which gives me
[1] 3
[1] 5
[1] 7
[1] 8
however I need to jump to the next value within the loop, say I dont want 5 to be printed in above example.
I cannot use next since I want it in nested for like this
present <- c(3,5,7,8)
for(i in present)
{
k <- i
"Jump to next value of present"
while(k < "The next value for i should come here")
{
k <- k + 1
print(k)
}
}
The output would be 3 4 5 6 7 8 but the condition must check value of k if it exceeds next value of i.
Is there anyway to accomplish this?
I'll take help of C to explain further,
for(i=0; i < 10; i++)
{
for(k=i;k <= i+1;k++)
{
printf("%d", k);
}
}
The link contains output of above code
http://codepad.org/relkenY3
It is easy in C since next value is in sequence, but here next value is not known, hence the problem.
What you should do is loop through two vectors:
x <- head(present, -1)
# [1] 3 5 7
y <- tail(present, -1)
# [1] 5 7 8
and the function to do that is mapply (have a look at ?mapply). A close translation of your pseudo-code would be:
invisible(mapply(function(x, y) while(x < y) {x <- x + 1; print(x)}, x, y))
but maybe you'll find this more interesting:
mapply(seq, x + 1, y)
I suspect the answer is to use seq_along and use it as an index into "present", but as others have pointed out your code does not promise to deliver what you expect, even with that simple modification. The K <- K=1 assignment jumps ahead too far to deliver a value of 3 at any point and the termination condition is likewise not clear. It turns into an infinite loop in the form you construct. Work with this;
present <- c(3,5,7,8)
for(i in seq_along(present))
{
k <- i
while(k < length(present) )
{
k <- k + 1
print(present[k])
}
}

Resources