Generalize R %in% operator to match tuples

match, operators, r, tuples

Solution

I'm wondering if you have made this a bit more complicated than it is. For example,

set.seed(1618)
vec <- c(1,3)
mat <- matrix(rpois(1000,3), ncol = 2)
rownames(mat) <- 1:nrow(mat)


mat[sapply(1:nrow(mat), function(x) all(vec %in% mat[x, ])), ]

# gives me this
#     [,1] [,2]
# 6      3    1
# 38     3    1
# 39     3    1
# 85     1    3
# 88     1    3
# 89     1    3
# 95     3    1
# 113    1    3
# ...

you could subset this further if you care about the order or you could modify the function slightly:

mat[sapply(1:nrow(mat), function(x) 
  all(paste(vec, collapse = '') %in% paste(mat[x, ], collapse = ''))), ]

#      [,1] [,2]
# 85     1    3
# 88     1    3
# 89     1    3
# 113    1    3
# 133    1    3
# 139    1    3
# 187    1    3
# ...

another example with a longer vector

set.seed(1618)
vec <- c(1,4,5,2)
mat <- matrix(rpois(10000, 3), ncol = 4)
rownames(mat) <- 1:nrow(mat)

mat[sapply(1:nrow(mat), function(x) all(vec %in% mat[x, ])), ]

#      [,1] [,2] [,3] [,4]
# 57      2    5    1    4
# 147     1    5    2    4
# 279     1    2    5    4
# 303     1    5    2    4
# 437     1    5    4    2
# 443     1    4    5    2
# 580     5    4    2    1
# ...

I see a couple that match:

mat[sapply(1:nrow(mat), function(x) 
  all(paste(vec, collapse = '') %in% paste(mat[x, ], collapse = ''))), ]

#      [,1] [,2] [,3] [,4]
# 443     1    4    5    2
# 901     1    4    5    2
# 1047    1    4    5    2

but only three

for your single row case:

vec <- c(1,4,5,2)
mat <- matrix(c(1,4,5,2), ncol = 4)
rownames(mat) <- 1:nrow(mat)

mat[sapply(1:nrow(mat), function(x) 
  all(paste(vec, collapse = '') %in% paste(mat[x, ], collapse = ''))), ]

# [1] 1 4 5 2

here is a simple function with the above code

is.tuplein <- function(vec, mat, exact = TRUE) {  
  rownames(mat) <- 1:nrow(mat)
  if (exact) 
    tmp <- mat[sapply(1:nrow(mat), function(x) 
      all(paste(vec, collapse = '') %in% paste(mat[x, ], collapse = ''))), ]
  else tmp <- mat[sapply(1:nrow(mat), function(x) all(vec %in% mat[x, ])), ]
  return(tmp)
}

is.tuplein(vec = vec, mat = mat)
# [1] 1 4 5 2

seems to work, so let's make our own `%in%` operator:

`%tuple%` <- function(x, y) is.tuplein(vec = x, mat = y, exact = TRUE)
`%tuple1%` <- function(x, y) is.tuplein(vec = x, mat = y, exact = FALSE)

and try her out

set.seed(1618)
c(1,2,3) %tuple% matrix(rpois(1002,3), ncol = 3)

#     [,1] [,2] [,3]
# 133    1    2    3
# 190    1    2    3
# 321    1    2    3

set.seed(1618)
c(1,2,3) %tuple1% matrix(rpois(1002,3), ncol = 3)

#     [,1] [,2] [,3]
# 48     2    3    1
# 64     2    3    1
# 71     1    3    2
# 73     3    1    2
# 108    3    1    2
# 112    1    3    2
# 133    1    2    3
# 166    2    1    3

Problem

I spent a while the other day looking for a way to check if a row vector is contained in some set of row vectors in R. Basically, I want to generalize the `%in%` operator to match a tuple instead of each entry in a vector. For example, I want: ``` row.vec = c("A", 3) row.vec # [1] "A" "3" data.set = rbind(c("A",1),c("B",3),c("C",2)) data.set # [,1] [,2] # [1,] "A" "1" # [2,] "B" "3" # [3,] "C" "2" row.vec %tuple.in% data.set # [1] FALSE ``` for my made-up operator `%tuple.in%` because the row vector `c("A",3)` is not a row vector in data.set. Using the `%in%` operator gives: ``` row.vec %in% data.set # [1] TRUE TRUE ``` because "A" and 3 are in `data.set`, which is not what I want. I have two questions. First, are there any good existing solutions to this? Second, since I couldn't find them (even if they exist), I tried to write my own function to do it. It works for an input matrix of row vectors, but I'm wondering if any experts have proposed improvements: ``` is.tuple.in <- function(matrix1, matrix2){ # Apply rbind() so that matrix1 has columns even if it is a row vector. matrix1 = rbind(matrix1) if(ncol(matrix1) != ncol(matrix2)){ stop("Matrices must have the same number of columns.") } # Now check for the first row and handle other rows recursively row.vec = matrix1[1,] tuple.found = FALSE for(i in 1:nrow(matrix2)){ # If we find a match, then this row exists in matrix 2 and we can break the loop if(all(row.vec == matrix2[i,])){ tuple.found = TRUE break } } # If there are more rows to be checked, use a recursive call if(nrow(matrix1) > 1){ return(c(tuple.found, is.tuple.in(matrix1[2:nrow(matrix1),],matrix2))) } else { return(tuple.found) } } ``` I see a couple problems with that that I'm not sure how to fix. First, I'd like the base case to be clear at the start of the function. I didn't manage to do this because I pass `matrix1[2:nrow(matrix1),]` in the recursive call, which produces an error if `matrix1` has one row. So instead of getting to a case where `matrix1` is empty, I have an if condition at the end deciding if more iterations are necessary. Second, I think the use of `rbind()` at the start is sloppy, but I needed it for when `matrix1` had been reduced to a single row. Without using `rbind()`, `ncol(matrix1)` produced an error in the 1-row case. I figure my trouble here has to do with a lack of knowledge about R data types. Any help would be appreciated.

Original source

Related problems