我的问题如下:
我正在寻找在较大的表中查找2D表的列和行索引的最快方法。小表的大小为n×m,并且可以在j×k的较大表中多次出现,其中j> n且k> m。我一直试图用'data.table'包来做,但失败了。我的问题非常类似于以下问题:Matlab how to find out if a matrix is in another martrix
但我希望在R中快速完成这项工作。我希望在实施for循环的暴力方法之前先了解您的观点。请注意,在表格中您可能有数字和字符串。
如果你需要一个例子。您可以考虑我需要搜索以下data.frame:
data.frame(A=c(1.7,1.5,1.7),B=c(0.3,0.3,0.2),C=c("setosa","setosa","setosa"))
您必须在'iris'data.frame中搜索。输出答案是: 第19行和第3列。
答案 0 :(得分:0)
基于this answer:
library(dplyr)
# Problems with factors when checking equality so strings instead
df1 <- data.frame(A=c(1.7,1.5,1.7),
B=c(0.3,0.3,0.2),
C=c("setosa","setosa","setosa")) %>%
dplyr::mutate_if(is.factor, as.character)
df <- iris %>%
dplyr::mutate_if(is.factor, as.character)
# find all matches of the top left corner of df1
hits <- which(df == df1[1,1], arr.ind=TRUE)
# remove those matches that can't logically fit in the data
hits <- hits[hits[,"row"] <= nrow(df)-nrow(df1)+1 &
hits[,"col"] <= ncol(df)-ncol(df1)+1, , drop=FALSE]
for (j in seq_len(ncol(df1))) {
for (i in seq_len(nrow(df1))) {
if (i == 1 && j == 1) next
hits <- hits[df[sweep(hits, 2, c(i, j) - 1, '+')] == df1[i, j], , drop = FALSE]
}
}
这应该快速而简单。
答案 1 :(得分:0)
data.table
速度很快,但基本的matrix
非常快。我的建议是将表存储为矩阵,并使用更简单的子集来比较子矩阵。
从一些示例数据开始:bigmat
是我们要在其中查找匹配项的大矩阵,smallmat_in
是bigmat
的子矩阵,smallmat_out
是一个不在bigmat
内的矩阵。
bigmat <- matrix(c(1:50, 1:50), nrow = 10)
smallmat_in <- bigmat[6:8, 2:3]
smallmat_out <- smallmat_in
smallmat_out[6] <- 0
bigmat
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
# [1,] 1 11 21 31 41 1 11 21 31 41
# [2,] 2 12 22 32 42 2 12 22 32 42
# [3,] 3 13 23 33 43 3 13 23 33 43
# [4,] 4 14 24 34 44 4 14 24 34 44
# [5,] 5 15 25 35 45 5 15 25 35 45
# [6,] 6 16 26 36 46 6 16 26 36 46
# [7,] 7 17 27 37 47 7 17 27 37 47
# [8,] 8 18 28 38 48 8 18 28 38 48
# [9,] 9 19 29 39 49 9 19 29 39 49
# [10,] 10 20 30 40 50 10 20 30 40 50
smallmat_in
# [,1] [,2]
# [1,] 16 26
# [2,] 17 27
# [3,] 18 28
smallmat_out
# [,1] [,2]
# [1,] 16 26
# [2,] 17 27
# [3,] 18 0
我们可以快速找到bigmat
的哪些元素可以作为匹配子矩阵的左上角,而不是尝试迭代bigmat
的每个可能的3x2子矩阵。
index_matching_first <- function(small, big) {
max_big_row <- nrow(big) - nrow(small) + 1
max_big_col <- ncol(big) - ncol(small) + 1
valid_rows <- seq_len(max_big_row)
valid_cols <- seq_len(max_big_col)
which(big[valid_rows, valid_cols] == small[[1]], arr.ind = TRUE)
}
index_matching_first(smallmat_in, bigmat)
# row col
# [1,] 6 2
# [2,] 6 7
index_matching_first(smallmat_out, bigmat)
# row col
# [1,] 6 2
# [2,] 6 7
smallmat_in
和smallmat_out
仅在最后一个元素上有所不同,因此它们的第一个元素具有相同的匹配。接下来,我们将定义一个函数,该函数采用小矩阵(small
),大矩阵(big
)和行 - 列对(big_first_index
)。如果行列对是与big
匹配的small
子矩阵的左上角,则返回TRUE
。否则,FALSE
。
is_matrix_match <- function(small, big, big_first_index) {
row_indices <- seq(big_first_index[1], by = 1, length.out = nrow(small))
col_indices <- seq(big_first_index[2], by = 1, length.out = ncol(small))
all(small == big[row_indices, col_indices])
}
is_matrix_match(smallmat_in, bigmat, c(6, 2))
# [1] TRUE
is_matrix_match(smallmat_out, bigmat, c(6, 2))
# [1] FALSE
因此,当我们为其提供行列对时,这是有效的。我们现在可以在index_matching_first(...)
的输出上迭代地应用此函数,并查看是否找到任何匹配项。
in_matrix <- function(small, big) {
first_matches <- index_matching_first(small, big)
is_same <- apply(
first_matches,
MARGIN = 1,
FUN = is_matrix_match,
small = small,
big = big
)
any(is_same)
}
in_matrix(smallmat_in, bigmat)
# [1] TRUE
in_matrix(smallmat_out, bigmat)
# [1] FALSE
因为这是一个概念验证,所以它没有任何检查(比如确保big
实际上大于small
)。在生产设置中,这样会很好。
我不知道你使用了多大的矩阵,但这里有一些更大矩阵的速度测量:
hugemat <- matrix(rep_len(1:7, 1e7), nrow = 10)
format(object.size(hugemat), "MB")
# [1] "38.1 Mb"
huge_submat <- hugemat[2:9, 200:300]
huge_not_submat <- huge_submat
huge_not_submat[] <- 1
system.time(in_matrix(huge_submat, hugemat))
# user system elapsed
# 10.51 0.00 10.53
system.time(in_matrix(huge_not_submat, hugemat))
# user system elapsed
# 10.62 0.00 10.69
答案 2 :(得分:0)
软件包fpos
中的函数kit
返回小矩阵在大矩阵中的位置。它也适用于向量。将您的data.frame转换为矩阵,然后使用fpos
。请注意,该函数是用C实现的,因此应该很快。