logical based on rows below and above in other vector

70 views Asked by At

A sample of data I have.
hour <- c(rep(0,5), rep(1,5),rep(0,5), rep(1,5)) rain <- c(rep(0.1,10),rep(0.0,10)) df1<-data.frame(rain,hour) df1$csum <- with(df1, ave(df1$hour, cumsum(df1$hour == 0), FUN = cumsum))

Steps:
1. Find values of 1 in 'csum' (6th/16th).
2. Look at values 3 rows up and 5 down in 'rain' (1:9/11:19 in this case), around position determined by 1st step in var 'csum',
3. if their sum is >= 0.1; than first three values of 1 in var 'hour' (6:8) are converted to 0, in second case they are staying the same because sum is 0.

Expected output: First three values of 1 in df1$hour are switched to 0

hour = c(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1)
1

There are 1 answers

5
joel.wilson On BEST ANSWER

there could be much improvements to this sol:

x = which(df1$csum == 1)

func <- function(i) {
  lower_lim = i-5 # out-of-bound errors can happen, prefer max(1, (i-5))
  upper_lim = i+3 #  prefer min(1, (i+3))
  if (sum(df1$rain[lower_lim:upper_lim]) >= 0.1){
    y <- df1$hour[lower_lim:upper_lim]
    y[which(y == 1)[1:3]] = 0
    df1$hour[lower_lim:upper_lim] <- y
  }
  assign("df1", df1, envir = .GlobalEnv)
}

sapply(x, func)

# df1
#   rain hour csum
#1   0.1    0    0
#2   0.1    0    0
#3   0.1    0    0
#4   0.1    0    0
#5   0.1    0    0
#6   0.1    0    1
#7   0.1    0    2
#8   0.1    0    3
#9   0.1    1    4
#10  0.1    1    5
#11  0.0    0    0
#12  0.0    0    0
#13  0.0    0    0
#14  0.0    0    0
#15  0.0    0    0
#16  0.0    1    1
#17  0.0    1    2
#18  0.0    1    3
#19  0.0    1    4
#20  0.0    1    5