Reputation: 508
(I know for loops aren't the preferred choice in R but this was the best I could come up with)
I'm trying to loop through a vector and return the vector value once a condition is met.
Once the next condition is met I would like to drop the variable.
So far I've gotten to the following:
df = c(1:10)
sig = function (df) {
pos = integer(10)
for (i in 1:10) {
if (df[i] > 3 ) { # Once df[i] is bigger than 3 store the value of df[i]
pos[i] = df[i]
}
else if(df[i] < 7 ){ # Keep value of df[i] until next condition is met
pos[i] = pos[i - 1]
}
else{pos[i] = 0} # set the value back to 0
}
reclass(pos,df)
}
sig(df)
I'm getting the following error Error in pos[i] <- pos[i - 1] : replacement has length zero
The answer should look like the following:
df sig
1 0
2 0
3 0
4 4
5 4
6 4
7 0
8 0
9 0
10 0
Any ideas?
Upvotes: 3
Views: 680
Reputation: 4866
Here is a possible solution without using for
loops. Instead, you can use rle
:
a <- c(1:10)
r <- rle(a > 3 & a < 7)
r$values <- ifelse(r$values, a[head(cumsum(c(1, r$lengths)), -1)], 0)
inverse.rle(r)
[1] 0 0 0 4 4 4 0 0 0 0
Notice, though, that this will only work if the vector is ordered.
Another example:
> a <- c(4, 7, 9, 6, 5, 8, 10, 2, 3, 1)
> r <- rle(a %% 2 == 0)
> r$values <- ifelse(r$values, a[head(cumsum(c(1, r$lengths)), -1)], 0)
> inverse.rle(r)
[1] 4 0 0 6 0 8 8 8 0 0
Upvotes: 1
Reputation: 26343
You could also use ifelse
df <- c(1:10)
ifelse(df > 3 & df < 7, df[which(df > 3)][1], 0)
# [1] 0 0 0 4 4 4 0 0 0 0
Upvotes: 1
Reputation: 909
You can do it with data.table, here is the method
#Create the data.table
dt <- data.table(c(1:10))
#Create a keep column which is set to 1 for those which respect condition and 0 for the others
dt[,keep:=ifelse(V1>3&V1<7,min(V1),0)][]
#Then create sig column which contains only the value you want to keep
dt[,sig:=ifelse(keep==0,0,V1*keep)][]
#And finally, you want to store only the first value which respect the condition, so if your data frame is order by number, you can take the min value by V1 column.
dt[,sig:=min(sig),by=keep][]
Here is the output
dt[,c(1,3)]
V1 sig
1: 1 0
2: 2 0
3: 3 0
4: 4 4
5: 5 4
6: 6 4
7: 7 0
8: 8 0
9: 9 0
10: 10 0
Upvotes: 1
Reputation: 5017
Another way to achieve your output:
pos = integer(10)
pos[df>3 & df<7]<-df[which.max(df>3 & df<7)]
cbind(df,pos)
df pos
[1,] 1 0
[2,] 2 0
[3,] 3 0
[4,] 4 4
[5,] 5 4
[6,] 6 4
[7,] 7 0
[8,] 8 0
[9,] 9 0
[10,] 10 0
About your problem
i
start from 1, in the for loop you have pos[i-1]
, so pos[0]
but the list start from 1.
Try this:
sig = function (df) {
pos = integer(10)
for (i in 1:10) {
if (df[i] > 3 ) { # Once df[i] is bigger than 3 store the value of df[i]
pos[i] = df[i]
}
else if(df[i] < 7 ){ # Keep value of df[i] until next condition is met
if(i>1) {
pos[i] = pos[i - 1]
} else
{
pos[i]=0
}
}
else{pos[i] = 0} # set the value back to 0
}
return(cbind(df,pos))
}
return
instruction added
Your output:
sig(df)
df pos
[1,] 1 0
[2,] 2 0
[3,] 3 0
[4,] 4 4
[5,] 5 5
[6,] 6 6
[7,] 7 7
[8,] 8 8
[9,] 9 9
[10,] 10 10
The output is different from the one aspected, so you have to find other errors in your logic inside the for loop.
Upvotes: 1