【问题标题】:Find 2D table inside a larger 2D table在较大的 2D 表中查找 2D 表
【发布时间】:2017-10-30 18:34:40
【问题描述】:

我的问题如下:

我正在寻找在较大表中查找 2D 表的列和行索引的最快方法。小表的大小为 n x m,并且可以在 j x 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 列。

【问题讨论】:

  • 看看stackoverflow.com/questions/31330196/…,如果这是您要查找的内容,我会将其标记为 dup,但您不应删除它,因为它以不同的方式询问,可能会帮助其他人找到它.
  • @hrbrmstr,感谢您的回复。这个问题有点相似,但不确定是否相同。在您提供的链接中,2 个答案并没有真正起作用或不完整,并且 Rcpp 答案返回给我错误。我尝试调试它但没有成功。

标签: r performance data.table


【解决方案1】:

基于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]
  }
}

这应该又快又简单。

【讨论】:

  • 感谢您的回复。我发现它在大桌子上相对较慢,但我会接受你的回答。我会根据您提供的内容尝试更快地实现一些东西。
【解决方案2】:

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)。如果行-列对是与small 匹配的big 子矩阵的左上角,则返回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

【讨论】:

  • 继续使用矩阵可能是个好主意,但您如何处理 OP 的要求:请注意,表格中可能有数字和字符串。
  • @Uwe 我的解决方案不依赖于数字比较,因此它也适用于字符矩阵。任何数字数据都可以转换为字符。
  • @NathanWerth。非常感谢您的贡献。我个人认为您的功能很有趣,但对我来说仍然太慢。我相信至少可以将计算时间除以 10。我还没做...
【解决方案3】:

kit 包中的函数fpos 会返回一个小矩阵在一个大矩阵中的位置。它也适用于向量。将您的 data.frame 转换为矩阵,然后使用 fpos。请注意,该函数是用 C 实现的,所以它应该很快。

【讨论】:

    猜你喜欢
    • 2017-10-08
    • 2021-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多