I would like to skip iterations in a for loop based on conditions. Intuitively I thought this would work:
for(i in 1:10){
if(i %in% c(1,2,3,4,5)){
print(i)
i <- i+2}
}
I would want it to return
1
4
but instead it returns
1
2
3
4
5
I am aware why this is happening.
Is there another way to skip (multiple) iterations based on a condition in a for loop in R?
It's not just bad practice to increment the counter inside the loop in R. It simply will not work. That's not the way the language is built. If you want to get 1 and 4 printed then try:
for(i in seq(1,10,by=3) ){
if(i %in% c(1,2,3,4,5)){
print(i)
}
}
Do also note that for-loops actually return NULL. There would be a side-effect of printing to the console, but no values of variables would change. If you want values to change you need to do assignment inside the loop.
The is a next control statement:
for(i in seq(1,10) ){
if( !(i %in% c(1,4)) ){ next }
print(i)
}
Related
I'm trying to write a function that identifies if a number within a numerical vector is odd or even. The numerical vector has a length of 1000.
I know that the for loop works fine, and I just wanted to generalize it in the form of a function that takes a vector of any length
out<-vector()
f3<- function(arg){
for(i in 1:length(arg)){
if((arg[i]%%2==0)==TRUE){
out[i]<-1
}else{out[i]<-0
}
}
}
When run within a function, however, it just returns a NULL. Why is that, or what do I need to do to generalize the function work with any numerical vector?
As already mentioned by PKumar in the comments: Your function doesn't return anything, which means, the vector out exists only in the environment of your function.
To change this you can add return(out) to the end of your function. And you should also start your function with creating out before the loop. So your function would look like outlined below.
Note, that I assume you want to pass a vector of a certain length to your function, and get as a result a vector of the same length which contains 1 for even numbers and 0 for odd numbers. f3(c(1,1,2)) would return 0 0 1.
f3 <- function(arg){
out <- vector(length = length(arg), mode = "integer")
for(i in 1:length(arg)){
if((arg[i]%%2==0)==TRUE){ # note that arg[i]%%2==0 will suffice
out[i]<-1
} else {out[i]<-0
}
}
return(out) # calling out without return is enough and more inline with the tidyverse style guide
}
However, as also pointed out by sebastiann in the comments, some_vector %% 2 yields almost the same result. The difference is, that odd numbers yield 1 and even numbers 0. You can also put this into a function and subtract 1 from arg to reverse 0 and 1 :
f3 <- function(arg){
(arg-1) %% 2
}
A few thing to note about your code:
A function must return something
The logical if((arg[i]%%2==0)==TRUE) is redundant. if(arg[i]%%2==0) is enough, but wrong, because arg[i] does not exist.
the length(arg) is the length(1000) which, if ran, returns 1
You should change arg[i] with i and assign to i all the values from 1:1000, as follows:
R
out <-vector()
f3 <- function(arg){
for(i in 1:arg){
if(arg[i] %% 2 == 0){
out[i] <- 1
}
else{
out[i] <- 0
}
}
return(out)
}
f3(1000)
I'm trying to save each iteration of this for loop in a vector.
for (i in 1:177) {
a <- geomean(er1$CW[1:i])
}
Basically, I have a list of 177 values and I'd like the script to find the cumulative geometric mean of the list going one by one. Right now it will only give me the final value, it won't save each loop iteration as a separate value in a list or vector.
The reason your code does not work is that the object ais overwritten in each iteration. The following code for instance does what precisely what you desire:
a <- c()
for(i in 1:177){
a[i] <- geomean(er1$CW[1:i])
}
Alternatively, this would work as well:
for(i in 1:177){
if(i != 1){
a <- rbind(a, geomean(er1$CW[1:i]))
}
if(i == 1){
a <- geomean(er1$CW[1:i])
}
}
I started down a similar path with rbind as #nate_edwinton did, but couldn't figure it out. I did however come up with something effective. Hmmmm, geo_mean. Cool. Coerce back to a list.
MyNums <- data.frame(x=(1:177))
a <- data.frame(x=integer())
for(i in 1:177){
a[i,1] <- geomean(MyNums$x[1:i])
}
a<-as.list(a)
you can try to define the variable that can save the result first
b <- c()
for (i in 1:177) {
a <- geomean(er1$CW[1:i])
b <- c(b,a)
}
I'm trying to manually increment the i variable when a condition is met.
for(i in 1:x){
if(condition){
i <- i + 2
}
}
When debugging, the (i<-i+2) line is definitely being run, but i still only increments by 1, instead of 3. (+2 from the line and an additional +1 from the auto increment)
How can I increment while I'm within the loop?
So essentially you want to skip a few loop iterations based on a condition. It's a design choice that's rightfully frowned upon, but if you must, you need next. The following code skips the third, fifth and seventh iteration:
for(i in 1:10){
if(i %in% c(3,5,7)){
next
}
print(i)
}
Say you need to increment with 3 based on a certain condition, then you can use a temporary variable that helps you skip a number of steps. Note that this does go through every iteration, it just breaks out of the iteration in time:
skip <- 0 # the temporary variable helping us keeping track of the iterations
for(i in 1:10){
if(i == 5){ # the condition that causes the skip
skip <- 3
}
if(skip > 0){ # the control that skips as long as necessary
skip <- skip - 1
next
}
print(i)
}
When you run the loop, the value of the variable i is stored in tmp*. This means that whenever we reach the top the loop, i resets. For example
for(i in 1:2){
message(i)
i <- 10
message(i)
}
#1
#10
#2
#10
To get what you want, you could have something like:
k =1
for(i in 1:10){
if(condition) k <- k + 2
}
Once the sequence is created, you pretty much lose a lot of control over looping. In cases like this, I change it into a while loop and then do the conditional incrementing/decrementing at the end of the loop.
I'm agree with joris-meys, it's "frowned upon". But... A more simple approach is:
for(i in (0:3)*2+1){
cat(i," ")
}
or
for(i in (1:4)){
cat(i," ")
}
for(i in seq(0, 10, 2) ){
print(i)
}
you can do this..
I would like to have 2 sets of Sys.sleep. One long one and very short at each iteration. The current example does the first "long sleep" however doesn't proceed with the next one.
Here is example:
out <- as.numeric()
for(i in 1:20){
out[i] <- i*5
if(i==seq(5,20,5)){
Sys.sleep(5); print("Long sleep")
} else {
for(j in 1:5){
Sys.sleep(0.15); cat(j)
}}}
out
The loop should have a "Long sleep" with 5sec at each 5th iteration and 0.15sec at each iteration.
What I'm doing wrong here? Thanks.
The error the condition has length > 1 and only the first element will be used should have been an indicator that your if test was incorrect. Let's try it on the console:
i <- 5
i == seq(5,20,5)
## [1] TRUE FALSE FALSE FALSE
When you do that in an if statement, it is expecting one and only one logical out of the comparison, so it is rightfully confused. (Which would you use?)
Perhaps you meant i %in% seq(5,20,5)? Even better, I suggest you pre-assign the sequence and compare against it, otherwise you are creating a static vector every time.
myseq <- seq(5,20,5)
for(i in 1:20) {
out[i] <- i*5
if(i %in% myseq) {
Sys.sleep(5); print("Long sleep")
} else {
for(j in 1:5) {
Sys.sleep(0.15); cat(j)
}
}
}
As an alternative, you could also check (i %% 5 == 0), which can be faster, depending on the size of your test sequence.
I have a command that generates a variable every 10 loops in R (index1, index2, index3... and so on). The command I have is functional, but I am thinking of a smarter way to write this command. Here's what my command looks like:
for (counter in 1:10){
for (i in 1:100){
if (counter == 1){
index1 <- data1 ## some really long command here, I just changed it to this simple command to illustrate the idea
}
if (counter == 2){
index2 <- data2
}
.
.
.
# until I reach index10
} indexing closure
} ## counter closure
Is there a way to write this without having to write the conditional if commands? I would like to generate index1, index2.... I am sure there is some easy way to do this but I just cannot think of it.
Thanks.
What you need is the modulo operator %%. inside the inner loop. Ex: 100%%10 returns 0 101%%10 returns 1 92%%10 returns 2 - in other words if it is multiple of 10 then you get 0. And the assign function.
Note: You no longer need the outer loop used in your example.
So to create a variable at every 10 iteration do something like this
for(i in 1:100){
#check if i is multiple of 10
if(i%%10==0){
myVar<-log(i)
assign(paste("index",i/10,sep=""), myVar)
}
}
ls() #shows that index1, index2, ...index10 objects have been created.
index1 #returns 2.302585
update:
Alternatively, you can store results in a vector
index<-vector(length=10)
for(i in 1:100){
#check if i is multiple of 10
if(i%%10==0){
index[i/10]<-log(i)
}
}
index #returns a vector with 10 elements, each a result at end of an iteration that is a multiple of 10.