在R中的矩阵中查找模式

nic*_*ico 15 r matrix

例如,我有一个8 xn矩阵

set.seed(12345)
m <- matrix(sample(1:50, 800, replace=T), ncol=8)
head(m)

     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]   37   15   30    3    4   11   35   31
[2,]   44   31   45   30   24   39    1   18
[3,]   39   49    7   36   14   43   26   24
[4,]   45   31   26   33   12   47   37   15
[5,]   23   27   34   29   30   34   17    4
[6,]    9   46   39   34    8   43   42   37
Run Code Online (Sandbox Code Playgroud)

我想在矩阵中找到一个特定的模式,例如我想知道在哪里可以找到一个37,然后在下一行中找到一个10和29以及后面的一个42

例如,这发生在上述矩阵的第57:59行

m[57:59,]
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]  *37   35    1   30   47    9   12   39
[2,]    5   22  *10  *29   13    5   17   36
[3,]   22   43    6    2   27   35  *42   50
Run Code Online (Sandbox Code Playgroud)

一个(可能是低效的)解决方案是获得包含37的所有行

sapply(1:nrow(m), function(x){37 %in% m[x,]})
Run Code Online (Sandbox Code Playgroud)

然后使用几个循环来测试其他条件.

我怎么能写一个有效的函数来做到这一点,可以推广到任何用户给定的模式(不一定超过3行,可能有"漏洞",每行中有可变数量的值等).

编辑:回答各种评论

  • 我需要找到精确的模式
  • 同一行中的顺序无关紧要(如果它使事情更容易,则可以在每一行中排序值)
  • 线条必须相邻.
  • 我想获得返回的所有模式的(起始)位置(即,如果模式在矩阵中多次出现,我想要多个返回值).
  • 用户将通过GUI输入模式,我还没有决定如何.例如,为了搜索上述模式,他可能会写出类似的东西

37;10,29;42

Where ;表示新行并,在同一行上分隔值.同样我们可能会寻找

50,51;;75;80,81
Run Code Online (Sandbox Code Playgroud)

第n行中的50和51,第n + 2行中的75和第n + 3行中的80和81

flo*_*del 5

这很容易理解,并且有望为您提供足够的概括性:

has.37 <- rowSums(m == 37) > 0
has.10 <- rowSums(m == 10) > 0
has.29 <- rowSums(m == 29) > 0
has.42 <- rowSums(m == 42) > 0

lag <- function(x, lag) c(tail(x, -lag), c(rep(FALSE, lag)))

which(has.37 & lag(has.10, 1) & lag(has.29, 1) & lag(has.42, 2))
# [1] 57
Run Code Online (Sandbox Code Playgroud)

编辑:这是一个可以使用正面和负面滞后的概括:

find.combo <- function(m, pattern.df) {

   lag <- function(v, i) {
      if (i == 0) v else
      if (i > 0)  c(tail(v, -i), c(rep(FALSE, i))) else
      c(rep(FALSE, -i), head(v, i))
   }

   find.one <- function(x, i) lag(rowSums(m == x) > 0, i)
   matches  <- mapply(find.one, pattern.df$value, pattern.df$lag)
   which(rowSums(matches) == ncol(matches))

}
Run Code Online (Sandbox Code Playgroud)

在这里测试:

pattern.df <- data.frame(value = c(40, 37, 10, 29, 42),
                         lag   = c(-1,  0,  1,  1,  2))

find.combo(m, pattern.df)
# [1] 57
Run Code Online (Sandbox Code Playgroud)

编辑2 :在OP关于GUI输入的编辑之后,这是一个将GUI输入转换为pattern.df我的find.combo函数所需的函数:

convert.gui.input <- function(string) {
   rows   <- strsplit(string, ";")[[1]]
   values <- strsplit(rows,   ",")
   data.frame(value = as.numeric(unlist(values)),
              lag = rep(seq_along(values), sapply(values, length)) - 1)
}
Run Code Online (Sandbox Code Playgroud)

在这里测试:

find.combo(m, convert.gui.input("37;10,29;42"))
# [1] 57
Run Code Online (Sandbox Code Playgroud)


Jul*_*ora 4

这是一个广义函数:

PatternMatcher <- function(data, pattern, idx = NULL) {
  p <- unlist(pattern[1])
  if(is.null(idx)){
    p <- unlist(pattern[length(pattern)])
    PatternMatcher(data, rev(pattern)[-1], 
                   idx = Filter(function(n) all(p %in% intersect(data[n, ], p)),
                                1:nrow(data)))
  } else if(length(pattern) > 1) {
    PatternMatcher(data, pattern[-1], 
                   idx = Filter(function(n) all(p %in% intersect(data[n, ], p)), 
                                idx - 1))
  } else
    Filter(function(n) all(p %in% intersect(data[n, ], p)), idx - 1)
}
Run Code Online (Sandbox Code Playgroud)

这是一个递归函数,它在每次迭代中都会减少pattern,并且仅检查紧接在上一次迭代中识别的行之后的行。列表结构允许以方便的方式传递模式:

PatternMatcher(m, list(37, list(10, 29), 42))
# [1] 57
PatternMatcher(m, list(list(45, 24, 1), 7, list(45, 31), 4))
# [1] 2
PatternMatcher(m, list(1,3))
# [1] 47 48 93
Run Code Online (Sandbox Code Playgroud)

编辑:上面函数的想法似乎很好:检查向量的所有行pattern[[1]]并获取索引r1,然后检查行r1+1pattern[[2]]获取r2等。但是在遍历所有行时,第一步需要花费很多时间。当然,每个步骤都会花费很多时间m <- matrix(sample(1:10, 800, replace=T), ncol=8),例如,即当索引没有太大变化时r1r2...所以这里是另一种方法,这里PatternMatcher看起来非常相似,但是还有另一个函数matchRow用于查找具有所有内容的行要点vector

matchRow <- function(data, vector, idx = NULL){
  if(is.null(idx)){
    matchRow(data, vector[-1], 
             as.numeric(unique(rownames(which(data == vector[1], arr.ind = TRUE)))))
  } else if(length(vector) > 0) {
    matchRow(data, vector[-1], 
             as.numeric(unique(rownames(which(data[idx, , drop = FALSE] == vector[1], arr.ind = TRUE)))))
  } else idx
}
PatternMatcher <- function(data, pattern, idx = NULL) {
  p <- pattern[[1]]
  if(is.null(idx)){
    rownames(data) <- 1:nrow(data)
    p <- pattern[[length(pattern)]]
    PatternMatcher(data, rev(pattern)[-1], idx = matchRow(data, p))
  } else if(length(pattern) > 1) {
    PatternMatcher(data, pattern[-1], idx = matchRow(data, p, idx - 1))
  } else
    matchRow(data, p, idx - 1)
}
Run Code Online (Sandbox Code Playgroud)

与之前的功能对比:

library(rbenchmark)
bigM <- matrix(sample(1:50, 800000, replace=T), ncol=8)
benchmark(PatternMatcher(bigM, list(37, c(10, 29), 42)), 
          PatternMatcher(bigM, list(1, 3)), 
          OldPatternMatcher(bigM, list(37, list(10, 29), 42)), 
          OldPatternMatcher(bigM, list(1, 3)), 
          replications = 10,
          columns = c("test", "elapsed"))
#                                                  test elapsed
# 4                 OldPatternMatcher(bigM, list(1, 3))   61.14
# 3 OldPatternMatcher(bigM, list(37, list(10, 29), 42))   63.28
# 2                    PatternMatcher(bigM, list(1, 3))    1.58
# 1       PatternMatcher(bigM, list(37, c(10, 29), 42))    2.02

verybigM1 <- matrix(sample(1:40, 8000000, replace=T), ncol=20)
verybigM2 <- matrix(sample(1:140, 8000000, replace=T), ncol=20)
benchmark(PatternMatcher(verybigM1, list(37, c(10, 29), 42)), 
          PatternMatcher(verybigM2, list(37, c(10, 29), 42)), 
          find.combo(verybigM1, convert.gui.input("37;10,29;42")),
          find.combo(verybigM2, convert.gui.input("37;10,29;42")),          
          replications = 20,
          columns = c("test", "elapsed"))
#                                                      test elapsed
# 3 find.combo(verybigM1, convert.gui.input("37;10,29;42"))   17.55
# 4 find.combo(verybigM2, convert.gui.input("37;10,29;42"))   18.72
# 1      PatternMatcher(verybigM1, list(37, c(10, 29), 42))   15.84
# 2      PatternMatcher(verybigM2, list(37, c(10, 29), 42))   19.62
Run Code Online (Sandbox Code Playgroud)

而且现在的pattern论点应该是像list(37, c(10, 29), 42)而不是list(37, list(10, 29), 42)。最后:

fastPattern <- function(data, pattern)
  PatternMatcher(data, lapply(strsplit(pattern, ";")[[1]], 
                    function(i) as.numeric(unlist(strsplit(i, split = ",")))))
fastPattern(m, "37;10,29;42")
# [1] 57
fastPattern(m, "37;;42")
# [1] 57  4
fastPattern(m, "37;;;42")
# [1] 33 56 77
Run Code Online (Sandbox Code Playgroud)