How to modify the n following value of a selected row

Viewed 50

I want to replace the 3 values following a 1 by a 1, and replace all the remaining NAs by 0.

My Data (reproductible exemple)

datadf <-
  data.frame(
    seq_time = seq.POSIXt(
      from = as.POSIXct("2020-09-01 04:30:00"),
      to = as.POSIXct("2020-09-01 04:30:11"),
      by = 1
    ),
    typeA = c(0,0,NA,NA,NA,1,rep(NA, 4),0, NA),
    typeB = c(0,NA,NA,1,1,rep(NA,6),0)
  )
seq_time  |  typeA | typeB
-----------------------------
04:30:00     0        0
04:30:01     0        NA
04:30:02     NA       NA
04:30:03     NA       1
04:30:04     NA       1
04:30:05     1        NA
04:30:06     NA       NA
04:30:07     NA       NA
04:30:08     NA       NA
04:30:09     NA       NA
04:30:10     0        NA
04:30:11     NA       0

What I would like

seq_time  |  typeA | typeB
-----------------------------
04:30:00     0        0
04:30:01     0       *0*
04:30:02     *0*     *0*
04:30:03     *0*      1
04:30:04     *0*      1
04:30:05     1       *1*
04:30:06     *1*     *1*
04:30:07     *1*     *1*
04:30:08     *1*     *0*
04:30:09     *0*     *0*
04:30:10     0       *0*
04:30:11     *0*

Where am I so far

I'm struggling to avoid a humongous for loop in R. I know it's not the right direction, but I can't figure out what else to do.

get_all_the_ones <- function(col) { 

    for (ro in 1:nrow(datadf[col])) {
  
      if (datadf[ro,col] == 1) {
        datadf[seq(ro, ro+3), col] = 1
      }
  
    }

}

Is there a function in dplyr to select the n subsequent rows of a specificied row based on a condition?

My real dataframe is 53000 rows for time, and 26 types to check.

Best Regards,

BYF

Edit : Changed title

2 Answers

This can be easily done by writing a helper function like this

my_fun <- function(x){
  require(dplyr)
  cond <- (x == 1) | (lag(x, 1) == 1) | (lag(x, 2) == 1) | (lag(x, 3) == 1)
  new_values <- if_else(cond == TRUE, 1, 0, missing = 0)
  return(new_values)
}

If you need to apply the function to all your type variables, you can use across from the dplyr package

library(dplyr)

datadf %>% 
  mutate(across(starts_with("type"), ~my_fun(.)))
#               seq_time typeA typeB
# 1  2020-09-01 04:30:00     0     0
# 2  2020-09-01 04:30:01     0     0
# 3  2020-09-01 04:30:02     0     0
# 4  2020-09-01 04:30:03     0     1
# 5  2020-09-01 04:30:04     0     1
# 6  2020-09-01 04:30:05     1     1
# 7  2020-09-01 04:30:06     1     1
# 8  2020-09-01 04:30:07     1     1
# 9  2020-09-01 04:30:08     1     0
# 10 2020-09-01 04:30:09     0     0
# 11 2020-09-01 04:30:10     0     0
# 12 2020-09-01 04:30:11     0     0

You need lag function. Try this:

library(dplyr)
x <- datadf$typeA

a <- (is.na(x) & lag(x,1)==1) |
  (is.na(x) & is.na(lag(x)) & lag(x,2)==1) |
  (is.na(x) & is.na(lag(x)) & is.na(lag(x,2)) & lag(x,3)==1)

a[is.na(a)] <- FALSE

datadf$typeA[a] <- 1
datadf$typeA[is.na(datadf$typeA)] <- 0


x <- datadf$typeB

a <- (is.na(x) & lag(x,1)==1) |
  (is.na(x) & is.na(lag(x)) & lag(x,2)==1) |
  (is.na(x) & is.na(lag(x)) & is.na(lag(x,2)) & lag(x,3)==1)

a[is.na(a)] <- FALSE

datadf$typeB[a] <- 1
datadf$typeB[is.na(datadf$typeB)] <- 0

Output:

#               seq_time typeA typeB
# 1  2020-09-01 04:30:00     0     0
# 2  2020-09-01 04:30:01     0     0
# 3  2020-09-01 04:30:02     0     0
# 4  2020-09-01 04:30:03     0     1
# 5  2020-09-01 04:30:04     0     1
# 6  2020-09-01 04:30:05     1     1
# 7  2020-09-01 04:30:06     1     1
# 8  2020-09-01 04:30:07     1     1
# 9  2020-09-01 04:30:08     1     0
# 10 2020-09-01 04:30:09     0     0
# 11 2020-09-01 04:30:10     0     0
# 12 2020-09-01 04:30:11     0     0

This code only replace only NA values.

Related