例如,我有一个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行,可能有"漏洞",每行中有可变数量的值等).
编辑:回答各种评论
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
这很容易理解,并且有望为您提供足够的概括性:
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)
这是一个广义函数:
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+1
并pattern[[2]]
获取r2
等。但是在遍历所有行时,第一步需要花费很多时间。当然,每个步骤都会花费很多时间m <- matrix(sample(1:10, 800, replace=T), ncol=8)
,例如,即当索引没有太大变化时r1
,r2
...所以这里是另一种方法,这里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)