15

たとえば、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

マトリックス内の特定のパターンを見つけたいです。たとえば、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

(おそらく非効率的な) 解決策は、37 を含むすべての行を取得することです。

sapply(1:nrow(m), function(x){37 %in% m[x,]})

次に、いくつかのループを使用して他の条件をテストします。

これを行うための効率的な関数を作成するにはどうすればよいですか。これは、ユーザーが指定したパターンに一般化できます (必ずしも 3 行を超えるとは限りません。「穴」の可能性があり、各行に可変数の値があるなど)。

編集:さまざまなコメントに答える

  • 正確なパターンを見つける必要があります
  • 同じ行の順序は重要ではありません (物事を簡単にする場合は、各行で値を並べ替えることができます)
  • 線は隣接している必要があります。
  • 返されたすべてのパターンの (開始) 位置を取得したい (つまり、パターンがマトリックスに複数回存在する場合は、複数の戻り値が必要です)。
  • ユーザーは GUI を介してパターンを入力しますが、その方法はまだ決めていません。たとえば、上記のパターンを検索するには、次のように記述します。

37;10,29;42

Where;は改行を表し,、同じ行の値を区切ります。同様に、

50,51;;75;80,81

n 行目の 50 と 51、n+2 行目の 75、n+3 行目の 80 と 81 を意味します。

4

7 に答える 7

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

編集:正と負のラグを使用できる一般化は次のとおりです。

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))

}

ここでテスト:

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

Edit2: GUI入力に関するOPの編集に続いて、GUI入力をpattern.dfmyfind.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)
}

ここでテスト:

find.combo(m, convert.gui.input("37;10,29;42"))
# [1] 57
于 2013-01-04T12:09:00.373 に答える
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)
}

これは再帰関数であり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

編集:上記の関数のアイデアは問題ないようです。ベクトルのすべての行をチェックしpattern[[1]]てインデックスを取得しr1、次に行r1+1をチェックしpattern[[2]]て取得r2するなどです。しかし、すべての行を調べるときの最初のステップでは非常に時間がかかります。もちろん、すべてのステップは、たとえば、インデックスにm <- matrix(sample(1:10, 800, replace=T), ncol=8)あまり変化がない場合、、、...で多くの時間がかかります。したがって、ここに別のアプローチがあり、ここは非常に似ていますが、すべてを含む行を見つけるための別の関数がありますの要素。r1r2PatternMatchermatchRowvector

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)
}

前の機能との比較:

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

また、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
于 2013-01-04T12:32:59.473 に答える
3

整数があるので、行列を文字列に変換して正規表現を使用できます

ss <- paste(apply(m,1,function(x) paste(x,collapse='-')),collapse=' ')
## some funny regular expression
pattern <- '[^ \t]+[ \t]{1}[^ \t]+10[^ \t]+29[^ \t]+[ \t]{1}[^ \t]+42'
regmatches(ss,regexpr(pattern ,text=ss))
[1] "37-35-1-30-47-9-12-39 5-22-10-29-13-5-17-36 22-43-6-2-27-35-42"

 regexpr(pattern ,text=ss)
[1] 1279
attr(,"match.length")
[1] 62
attr(,"useBytes")
[1] TRUE

実際の動作を確認するには、これを見てください。

編集 パターンを動的に構築する

searchep <- '37;10,29;42'       #string given by the user
str1 <- '[^ \t]+[ \t]{1}[^ \t]+' 
str2 <- '[^ \t]'
hh <- gsub(';',str1,searchep)
pattern <- gsub(',',str2,hh)
pattern
[1] "37[^ \t]+[ \t]{1}[^ \t]+10[^ \t]29[^ \t]+[ \t]{1}[^ \t]+42"

test for searchep <- '37;10,29;;40'  ## we skip a line here 

pattern
[1] "37[^ \t]+[ \t]{1}[^ \t]+10[^ \t]29[^ \t]+[ \t]{1}[^ \t]+[^ \t]+[ \t]{1}[^ \t]+40"
regmatches(ss,regexpr(pattern ,text=ss))
"37-35-1-30-47-9-12-39 5-22-10-29-13-5-17-36 22-43-6-2-27-35-42-50 12-31-24-40"

Edit2 テスト性能

matrix.pattern <- function(searchep='37;10,29;42' ){
 str1 <- '[^ \t]+[ \t]{1}[^ \t]+' 
 str2 <- '[^ \t]+'
 hh <- gsub(';',str1,searchep)
 pattern <- gsub(',',str2,hh)
 res <- regmatches(ss,regexpr(pattern ,text=ss))
}

system.time({ss <- paste(apply(bigM,1,function(x) paste(x,collapse='-')),collapse=' ')
             matrix.pattern('37;10,29;42')})
   user  system elapsed 
   2.36    0.01    2.40 

大きな行列が変化しない場合、文字列 ID への変換のステップは 1 回だけで、パフォーマンスは非常に良好です。

system.time(matrix.pattern('37;10,29;42'))
   user  system elapsed 
   0.71    0.02    0.72 
于 2013-01-04T12:44:58.420 に答える
2

多分それは誰かを助けるでしょうが、入力に関しては、私は次のことを考えていました:

PatternMatcher <- function(data, ...) {
  Selecting procedure here.
}

PatternMatcher(m, c(1, 37, 2, 10, 2, 29, 4, 42))

関数に渡される 2 番目の部分は、開始する行、値、2 行目、2 番目の値の順で構成されます。たとえば、値が 50 の最初の行の後の 8 行目と言うことができます。

これを拡張して、値ごとに特定の X、Y 座標を要求することもできます (したがって、値ごとに 3 つの項目が関数に渡されます)。

于 2013-01-04T10:34:07.543 に答える
1

を使用する 1 つの方法を次に示しsapplyます。

which(sapply(seq(nrow(m)-2),
             function(x)
               isTRUE(37 %in% m[x,] & 
                      which(10 == m[x+1,]) < which(29 == m[x+1,]) & 
                      42 %in% m[x+2,])))

結果には、シーケンスが開始するすべての行番号が含まれます。

[1] 57
于 2013-01-04T10:28:37.140 に答える