How to match a value in a vector with the one before and after in r?

Viewed 797

I'm trying to identify mismatched values based on one element value before or after the focal value in a vector. Any thought about how to do it?

Let's say, I have a vector: x<-c(1,1,2,1,3,3). If element[i] matches with the element before or after item i (element[i-1] and element[i+1]). If there is a match element[i] should equal "yes", otherwise it should equal "no".

The expected output for x<-c(1,1,2,1,3,3) should be c("yes","yes","no","no","yes","yes").

4 Answers

Use rle() to identify runs of equal values. lengths == 1 means there is no equal values before or after the current one.

with(rle(x), rep(ifelse(lengths == 1, "no", "yes"), lengths))

# [1] "yes" "yes" "no"  "no"  "yes" "yes"

Edit: more concise version(thanks for @dww's comment)

with(rle(x), rep(lengths != 1, lengths))

# [1]  TRUE  TRUE FALSE FALSE  TRUE  TRUE

A one liner for this is to use diff

c(diff(x) == 0, F) | c(F, diff(x) == 0)
[1]  TRUE  TRUE FALSE FALSE  TRUE  TRUE

c(diff(x) == 0, F) will be true for each index with element[i] == element[i+1] (not applicable for last element) and c(F, diff(x) == 0) will be true for each index with element[i] == element[i-1] (not applicable for first element)

Here is one base R approach. We can generate shifted vectors, either one position to the left, or one position to the right, from your original input vector. Then, we can assert whether each position in the original vector matches either of the same position in the shifted vectors. To give a visual:

x:  [ 1, 1, 2, 1, 3, 3]
------------------------
x1: [NA, 1, 1, 2, 1, 3]
x2: [ 1, 2, 1, 3, 3, NA]

We can see the result your expect by inspection. Here is a code snippet implementing this:

x <- as.character(c(1,1,2,1,NA,NA))
x1 <- c('NA', x[1:length(x)-1])
x2 <- c(x[2:length(x)], 'NA')
result <- (x==x1 | is.na(x) & is.na(x1) | x==x2 | is.na(x) & is.na(x2))
output <- ifelse(is.na(result) | !result, "no", "yes")
output

[1] "yes" "yes" "no"  "no"  "yes" "yes"

Note that I deliberately converted your numerical vector into a character vector, so that I may use 'NA', a string literal, as a placeholder for a missing value. If we used the above logic with a numeric vector, NA could collide with actual missing values.

Here's one way to do it (using TRUE and FALSE in place of "yes" and "no"). Explanation in comments.

pre_or_post_matches <- function(vec){
  # get length of `vec`, create empty return vector `out` that we fill
  len <- length(vec)
  out <- rep(NA, len)
  # first element: just check if it equals the second 
  out[1] <- vec[1]==vec[2]
  # last element: just check if it equals the second to last 
  out[len] <- vec[len]==vec[len-1]
  # the other elements: check if equal to at least one neighbor 
  for (idx in 2:(len-1)){
    out[idx] <- (vec[idx]==vec[idx-1]) | (vec[idx]==vec[idx+1])
  }
  return(out)
}


# apply func to example data provided by OP 
x <- c(1, 1, 2, 1, 3, 3)

pre_or_post_matches(x)
## [1]  TRUE  TRUE FALSE FALSE  TRUE  TRUE
Related