Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace/Modify values in logical vector (Pattern Matching)

Tags:

r

The question looks simple but I didn't figure out how it can done in R. I want to modify a logical vector depending on patterns of its values. There are two modification steps:

  1. If there is a single FALSE surrounded by TRUE values, switch it to TRUE.
  2. If there are less then 3 successive TRUE values, switch them to FALSE.

Everything else should remain as it is. Here's an example:

# input  
x = c(FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE,
    FALSE, TRUE, TRUE, FALSE, FALSE, TRUE,  TRUE,  TRUE,  TRUE, FALSE)

# output
xo = c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, 
   TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE,  TRUE,  TRUE,  TRUE, FALSE)

cbind(x,xo) is

          x    xo
 [1,] FALSE FALSE
 [2,]  TRUE FALSE
 [3,] FALSE FALSE
 [4,] FALSE FALSE
 [5,]  TRUE FALSE
 [6,]  TRUE FALSE
 [7,] FALSE FALSE
 [8,] FALSE FALSE
 [9,]  TRUE  TRUE
[10,]  TRUE  TRUE
[11,]  TRUE  TRUE
[12,] FALSE  TRUE
[13,]  TRUE  TRUE
[14,]  TRUE  TRUE
[15,] FALSE FALSE
[16,] FALSE FALSE
[17,]  TRUE  TRUE
[18,]  TRUE  TRUE
[19,]  TRUE  TRUE
[20,]  TRUE  TRUE
[21,] FALSE FALSE

I dont want to use a for loop because its slow and I would have to do a lot of if statements.

Is there a better way to get this working?

like image 810
Stefan Avatar asked Jun 21 '26 05:06

Stefan


1 Answers

Here is an approach:

#sample data 
x <- c(FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE,
    FALSE, TRUE, TRUE, FALSE, FALSE, TRUE,  TRUE,  TRUE,  TRUE, FALSE)

First, find the indices where FALSE values need to be changed to TRUE values, by looking for FALSE values that follow and are followed by TRUE values

tochange <- 
  intersect(
    intersect(
     which(x == FALSE),   # not strictly necessary
     which(diff(x) == 1)  # FALSEs followed by a TRUE
     ),
    which(diff(x) == -1) + 1 # FALSEs that follow a TRUE
    )

Change the values

x[tochange] <- TRUE

Next, look for runs of TRUE (and FALSE) that are less than 3 in length, and set them to FALSE.

xrle <- rle(x)

xrle$values[xrle$lengths < 3] <-  FALSE

newx <- inverse.rle(xrle) # thanks to Frank for pointing out inverse.rle!

# [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
#[10]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE
#[19]  TRUE  TRUE FALSE
like image 151
Jota Avatar answered Jun 23 '26 04:06

Jota