1

I want to check a matrix to see if there exist a fixed pattern "xxxx" or "yyyy", (my matrix can have sequence of either 4 x's or 4 y's, not both at the same time. unless it is less than 4). Then if for example a sequence of 4 x exists, match <- "x", otherwise match <- "y". I want to check it row-wise, column-wise and (anti)diagonal-wise.

The main problem is with the last part, to assign "x" or "y" to the variable "match".

An example of my matrix is:

m <- matrix(NA, 6, 7)
m[6,2:5] <- "x"

I tried as below for x and y:

r <- apply(m, 1, paste, collapse="")
c <- apply(m, 2, paste, collapse="") 

if (grepl("xxxx", r, fixed = TRUE) |
      grepl("xxxx", c, fixed = TRUE)){
    match <- "x"}
  else if(grepl("yyyy", r, fixed = TRUE)|
          grepl("yyyy", c, fixed = TRUE)){
    match <- "y"}

However, it does not work since "grepl" returns a logical vector and it only checks if the first element is true. I've been struggling to find a way for 4 days already, could not even think of a way to try to find this pattern diagonal wise.

I am new to programming with R, would very much appreciate any help.

Nel
  • 13
  • 4

1 Answers1

0

Not sure about how your output finally should look like, but in principle you could use "x{4}" as regex and use apply/apply. Example:

M
#    [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] "y"  NA   NA   "x"  NA   NA   NA  
# [2,] NA   "y"  NA   NA   "x"  "x"  NA  
# [3,] "y"  "y"  "y"  "y"  NA   "x"  NA  
# [4,] NA   "y"  "x"  "y"  "y"  "x"  "x" 
# [5,] NA   NA   NA   NA   NA   "x"  NA  
# [6,] NA   "x"  "x"  "x"  "x"  NA   NA 

## rows
apply(M, 1, function(x) grepl("x{4}", Reduce(paste0, x)))
# [1] FALSE FALSE FALSE FALSE FALSE  TRUE
apply(M, 1, function(x) grepl("y{4}", Reduce(paste0, x)))
# [1] FALSE FALSE  TRUE FALSE FALSE FALSE

## columns
apply(M, 2, function(x) grepl("x{4}", Reduce(paste0, x)))
# [1] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
apply(M, 2, function(x) grepl("y{4}", Reduce(paste0, x)))
# [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE

## diagonals
sapply(split(M, row(M) - col(M)), function(x) grepl("x{4}", Reduce(paste0, x)))
#    -6    -5    -4    -3    -2    -1     0     1     2     3     4     5 
# FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 
sapply(split(M, row(M) - col(M)), function(x) grepl("y{4}", Reduce(paste0, x)))
#    -6    -5    -4    -3    -2    -1     0     1     2     3     4     5 
# FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE 

Note: Credits for the diagonals also to @user20650

Note 2:

stopifnot(identical(apply(m, 1, paste, collapse=""), 
                    apply(m, 1, function(x) Reduce(paste0, x))))

Edit

You could wrap the logic into a function, based on any, that executes the lines above, unlists the result, and checks if there's any TRUE.

checkSequence <- function(M, rx) {
  any(unlist(
    c(sapply(1:2, function(margin) apply(M, margin, function(x) grepl(rx, Reduce(paste0, x)))),
      list(sapply(split(M, row(M) - col(M)), function(x) grepl(rx, Reduce(paste0, x)))))))
}

checkSequence(M, "x{4}")
# [1] TRUE
checkSequence(M, "y{4}")
# [1] TRUE
checkSequence(M, "y{3}")
# [1] TRUE
checkSequence(M, "y{5}")
# [1] FALSE

Data:

M <- unname(as.matrix(read.table(header=T, text='
     [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] "y"  NA   NA   "x"  NA   NA   NA  
[2,] NA   "y"  NA   NA   "x"  "x"  NA  
[3,] "y"  "y"  "y"  "y"  NA   "x"  NA  
[4,] NA   "y"  "x"  "y"  "y"  "x"  "x" 
[5,] NA   NA   NA   NA   NA   "x"  NA  
[6,] NA   "x"  "x"  "x"  "x"  NA   NA    ')))
jay.sf
  • 60,139
  • 8
  • 53
  • 110
  • This answer helped me with the diagonal part, but still my main problem is with the logical vector output , I only want one logical answer "true" if it exist, "false" otherwise. So I can use it in my if else statement. – Nel Jun 06 '20 at 11:31
  • So should you statement yield `TRUE` if any rows/columns/diagonals include `xxxx`OR `yyyy`? – jay.sf Jun 06 '20 at 11:35
  • Yes, that is exactly what I want. So if true, based on x or y pattern, I can assign "x" or "y" to the variable match. I tried to show that in my example code. – Nel Jun 06 '20 at 11:42
  • @Nel I understand, see update please. Note, that this also includes sequences such as `yyy` because it is a subset of `yyyy`. – jay.sf Jun 06 '20 at 11:57
  • 1
    Your answers are very helpful for me to learn more about R programming, but still for this issue I only want xxxx, more than 4 is OK but not less. – Nel Jun 06 '20 at 12:09
  • @Nel I actually twisted the logic of my last comment, I meant that `yyyyy` will also be included since they contain `yyyy` as a subset. – jay.sf Jun 06 '20 at 12:17