在更大的2D表中查找2D表

时间:2017-10-30 18:34:41

标签: r performance data.table

我的问题如下:

我正在寻找在较大的表中查找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列。

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_inbigmat的子矩阵,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_insmallmat_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实现的,因此应该很快。