Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pattern matching in a numeric matrix using R

I have a matrix containing questionnaire responses and I want to do some basic pattern checking to rule out, for example, respondents who just filled in a zig-zag pattern down their Scantron sheet. I have a 1400-by-50 (person-by-item) matrix called datonly that looks like this:

> head(datonly[,1:10])
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1    2    4    4    3    4    4    4    4     4
[2,]    1    1    4   NA    5    5    4    4    4     4
[3,]    2    2    2    3    3    3    3    3    3     1
[4,]    3    1    3    2    5    2    3    4    3     4
[5,]    4    2    3    1    5    3    5    5    3     4
[6,]    4    2    4    4    5    1    5    4    4     5

And as you can see there are NAs in there. Also, the possible valid responses are 1 to 5 for all questions.

I don't know the most efficient way to do this, but this matrix isn't large so efficiency isn't a big deal -- I just want to get it done and not linger on this, but I can't figure out a working method for checking, within each row, whether I find the pattern 1 2 3 4 5 4 3 2 1. I want the output of my function to look like this:

> which(ind==1)
[1]   24   55   66   67   74   79   83   90  127  131  147
[12]  154  162  172  221  222  248  260  263  316  339  390
[23]  402  408  436  440  456  457  460  492  497  504  526
[34]  544  550  568  583  597  602  623  628  632  639  682
[45]  684  689  705  727  747  750  751  763  764  769  784

where ind is a numeric vector containing 0 for each row (person) who does not display this pattern, and 1 for each row (person) who does. In this example, I would mark respondents #24, 55, 66, etc., as possibly bad respondents. Order does matter -- otherwise it wouldn't look like a zig-zag on the Scantron sheet -- but the pattern doesn't necessarily have to begin at 1 (however, I am ok with the function only checking for the one pattern given above). Any help is much appreciated!

like image 510
Jon Avatar asked Aug 24 '26 18:08

Jon


2 Answers

Here's a complete answer based on my comment which will catch all sequences that are 'off by one':

#random answers
set.seed(1234)
x <- matrix(sample(c(1:5, NA), 100, TRUE, prob=c(.19,.19,.19,.19,.19, .05)), ncol = 10)
#Here's the person you want to flag
x <- rbind(x, c(1:5,4:1,2))



which(
  apply(
    apply(x, 1, function(z) abs(diff(z))),2,
    function(zz) ifelse(sd(zz, na.rm = TRUE)==0,1,0)
  )== 1)
#---
[1] 11
like image 198
Chase Avatar answered Aug 26 '26 12:08

Chase


As I understand it, you probably want,

as.numeric(grepl("123454321",apply(datonly,1,paste0,collapse="")))
like image 35
James Avatar answered Aug 26 '26 13:08

James



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!