Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Match a vector inside a vector

Tags:

r

I have:

vec1 <- c(0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1)
vec2 <- c(1, 1)

I expect:

magicFUN(x = vec1, y = vec2)
[1] 4 7 8

That means that I want the position of a complete vector inside another vector. match and is.element were not useful because they return the position of each element of vec2 and I need that magicFUN matches the complete vec2 into vec1.

like image 997
Wencheng Lau-Medrano Avatar asked May 30 '18 20:05

Wencheng Lau-Medrano


People also ask

Can we use == in vector?

In case of vectors, the operator “==” is overloaded to find the result quickly.

How do you check if a vector is equal to another vector?

C++ Vector Library - operator== Function The C++ function std::vector::operator== tests whether two vectors are equal or not. Operator == first checks the size of both container, if sizes are same then it compares elements sequentially and comparison stops at first mismatch.

What does vector Pop_back do?

vector::pop_back()() pop_back() function is used to pop or remove elements from a vector from the back. The value is removed from the vector from the end, and the container size is decreased by 1.


2 Answers

A general solution:

magicFUN <- function(vec1, vec2) {
  if(length(vec2) > length(vec1)) stop("vec 2 should be shorter")
  len <- length(vec1) - length(vec2) + 1
  out <- vector(mode = "logical", length=len)     
  for(i in 1:len) {
    out[i] <- identical(vec2, vec1[i:(i+length(vec2)-1)])
  }
  return(which(out))
}

vec1 <- c(0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1)
vec2 <- c(1, 1)

magicFUN(vec1, vec2)

[1] 4 7 8

A for loop will be the fastest solution (besides using Rcpp). See benchmarks below:

magicFUN <- function(vec1, vec2) {
  if(length(vec2) > length(vec1)) stop("vec 2 should be shorter")
  len <- length(vec1) - length(vec2) + 1
  out <- vector(mode = "logical", length=len)     
  for(i in 1:len) {
    out[i] <- identical(vec2, vec1[i:(i+length(vec2)-1)])
  }
  return(which(out))
}

magicFUN2 <- function(vec1, vec2){
  l1 <- length(vec1)
  l2 <- length(vec2)
  which(colSums(sapply(1:(l1-l2), function(i) vec1[i:(i+l2-1)]) == vec2) == l2)
}

magicFUN3 <- function(vec1, vec2){
  which(c(zoo::rollapply(vec1, width=length(vec2), 
                    function(x)all(x==vec2), align = "left"),rep(FALSE,length(vec2)-1))==TRUE)
}

library(microbenchmark)
microbenchmark(magicFUN(vec1, vec2), magicFUN2(vec1, vec2), magicFUN3(vec1, vec2))

Unit: milliseconds
                  expr       min        lq      mean    median        uq       max neval cld
  magicFUN(vec1, vec2)  6.083572  6.575844  7.292443  6.878016  7.421208  13.35746   100  a 
 magicFUN2(vec1, vec2)  8.289640  8.976736 11.007967  9.338644  9.951492 139.68886   100  a 
 magicFUN3(vec1, vec2) 39.131268 42.369479 46.303722 44.203563 45.053252 172.46151   100   b
like image 70
thc Avatar answered Sep 28 '22 00:09

thc


Here is one way, but wouldn't scale well, if length of vec2 grow:

which(head(vec1, -1) == vec2[1] & tail(vec1, -1) == vec2[2])
# [1] 4 7 8

Edit: More general solution.

magicFUN <- function(vec1, vec2){
  l1 <- length(vec1)
  l2 <- length(vec2)
  which(colSums(sapply(1:(l1-l2), function(i) vec1[i:(i+l2-1)]) == vec2) == l2)
}

magicFUN(vec1, vec2)
# [1] 4 7 8
like image 36
zx8754 Avatar answered Sep 27 '22 22:09

zx8754