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.
Related
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 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)
}
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..
UPDATE
Thanks to the help and suggestions of #CarlWitthoft my code was simplified to this:
model <- unlist(sapply(1:length(model.list),
function(i) ifelse(length(model.list[[i]][model.lookup[[i]]] == "") == 0,
NA, model.list[[i]][model.lookup[[i]]])))
ORIGINAL POST
Recently I read an article on how vectorizing operations in R instead of using for loops are a good practice, I have a piece of code where I used a big for loop and I'm trying to make it a vector operation but I cannot find the answer, could someone help me? Is it possible or do I need to change my approach? My code works fine with the for loop but I want to try the other way.
model <- c(0)
price <- c(0)
size <- c(0)
reviews <- c(0)
for(i in 1:length(model.list)) {
if(length(model.list[[i]][model.lookup[[i]]] == "") == 0) {
model[i] <- NA
} else {
model[i] <- model.list[[i]][model.lookup[[i]]]
}
if(length(model.list[[i]][price.lookup[[i]]] == "") == 0) {
price[i] <- NA
} else {
price[i] <- model.list[[i]][price.lookup[[i]]]
}
if(length(model.list[[i]][reviews.lookup[[i]]] == "") == 0) {
reviews[i] <- NA
} else {
reviews[i] <- model.list[[i]][reviews.lookup[[i]]]
}
size[i] <- product.link[[i]][size.lookup[[i]]]
}
Basically the model.list variable is a list from which I want to extract a particular vector, the location from that vector is given by the variables model.lookup, price.lookup and reviews.lookup which contain logical vectors with just one TRUE value which is used to return the desired vector from model.list. Then every cycle of the for loop the extracted vectors are stored on variables model, price, size and reviews.
Could this be changed to a vector operation?
In general, try to avoid if when not needed. I think your desired output can be built as follows.
model <- unlist(sapply(1:length(model.list), function(i) model.list[[i]][model.lookup[[i]]]))
model[model=='']<-NA
And the same for your other variables. This assumes that all model.lookup[[i]] are of length one. If they aren't, you won't be able to write the output to a single element of model in the first place.
I would also note that you are grossly overcoding, e.g. x<-0 is better than x<-c(0), and don't bother with length evaluation on a single item.
Would it be possible to return the data from the current iteration when a user would abort R?
Something like:
if (user.aborts == TRUE) { return(data) }
This would be really useful since the input for this procedure is only data. It could then be broken up, and continued afterwards.
It is possible, if what you mean is manually stopping the process, e.g.
myfun <- function(x){
on.exit(return(x))
for(i in 1:5){
x <- x + 1
Sys.sleep(1)
}
}
x <- myfun(1) # Stopping before it finishes
x
[1] 4