【问题标题】:Check whether elements of vectors are inside intervals given by matrix检查向量的元素是否在矩阵给定的区间内
【发布时间】:2018-06-09 13:21:37
【问题描述】:

实际上是一个非常好的问题,我想出了一个解决方案(见下文),但它并不漂亮:

假设您有一个 向量 x 和一个 矩阵 A,其中包含第一列中区间的开始和第二列中区间的结束。
我怎样才能得到A的元素,它们属于A给定的区间

x <- c(4, 7, 15)

A <- cbind(c(3, 9, 14), c(5, 11, 16))

预期输出:

[1] 4 15

您可以提供以下信息,如果这有助于提高性能:
矩阵的向量和行都是有序的,并且间隔不重叠。所有间隔都具有相同的长度。所有数字都是整数,但也可以是巨大

现在我不想偷懒,想出了以下解决方案,这对于长向量和矩阵来说太慢了:

x <- c(4, 7, 15)  # Define input vector

A <- cbind(c(3, 9, 14), c(5, 11, 16))  # Define matrix with intervals

b <- vector()

for (i in 1:nrow(A)) {
  b <- c(b, A[i, 1]:A[i, 2])
}

x[x %in% b]

我知道 R 中的循环可能会很慢,但我不知道如何编写没有循环的操作(也许apply 有办法)。

【问题讨论】:

    标签: r performance loops apply intervals


    【解决方案1】:

    我们可以使用sapply 循环遍历x 的每个元素,并查找它是否在这些矩阵值的any 范围内。

    x[sapply(x, function(i) any(i > A[, 1] & i < A[,2]))]
    #[1]  4 15
    

    如果length(x)nrow(A) 相同,那么我们甚至不需要sapply 循环,我们可以直接使用这个比较。

    x[x > A[, 1] & x < A[,2]]
    #[1]  4 15
    

    【讨论】:

    • 非常感谢您的回答。 x 的长度不适合 A 中的行数。但是,第一个解决方案有效。
    【解决方案2】:

    这是一个不使用显式循环或应用函数的方法。 outer 有时更快。

    x[rowSums(outer(x, A[,1], `>=`) & outer(x, A[,2], `<=`)) > 0]
    
    [1]  4 15
    

    【讨论】:

    • 非常感谢您的快速回答。该解决方案有效,了解outer 在性能方面的可能优势非常有趣。不幸的是,优化代码的问题是:它对初学者来说可读性较差。
    • @NicolasBourbaki 值得一提的是,与循环相比,这种方法可能会使用大量内存,因为每次调用 upper 都会创建一个逻辑矩阵,所以如果你有一个非常大的数据集可能会导致问题。
    【解决方案3】:

    这个答案迟了,但今天我有同样的问题要解决,我的回答可能对未来的读者有所帮助。我的解决方案如下:

    f3 <- function(x,A) {
      Reduce(f = "|",
             x = lapply(1:NROW(A),function(k) x>A[k,1] & x<A[k,2]), 
             init = logical(length(x)))
    }
    

    该函数返回一个长度(x)的逻辑向量,指示是否可以在区间中找到x中的对应值。如果我想获得元素,我只需要编写

    x[f3(x,A)]
    

    我做了一些基准测试,我的功能似乎运行良好,同时使用更大的数据进行测试。 让我们定义这篇文章中建议的其他解决方案:

    f1 <- function(x,A) {
      sapply(x, function(i) any(i > A[, 1] & i < A[,2]))
    }
    
    f2 <- function(x,A) {
      rowSums(outer(x, A[,1], `>`) & outer(x, A[,2], `<`)) > 0
    }
    

    现在他们也返回了一个逻辑向量。 我机器上的基准测试如下:

    x <- c(4, 7, 15)
    A <- cbind(c(3, 9, 14), c(5, 11, 16))
    microbenchmark::microbenchmark(f1(x,A), f2(x,A), f3(x,A))
    
    #Unit: microseconds
    #     expr  min    lq   mean median    uq  max neval
    #f1(x, A) 21.5 23.20 25.023  24.30 25.40 61.8   100
    #f2(x, A) 18.8 21.20 23.606  22.75 23.70 75.4   100
    #f3(x, A) 13.9 15.85 18.682  18.30 19.15 52.2   100
    

    看起来没有太大区别,但下面的例子会更明显:

    x <- seq(1,100,length.out = 1e6)
    A <- cbind(20:70,(20:70)+0.5)
        
    microbenchmark::microbenchmark(f1(x,A), f2(x,A), f3(x,A), times=10)
    
    #Unit: milliseconds
    #     expr      min        lq      mean    median        uq       max neval
    #f1(x, A) 4176.172 4227.6709 4419.6010 4484.2946 4539.9668 4569.7412    10
    #f2(x, A) 1418.498 1511.5647 1633.4659 1571.0249 1703.6651 1987.8895    10
    #f3(x, A)  614.556  643.4138  704.3383  672.5385  770.7751  873.1291    10
    

    可以检查所有函数都返回相同的结果,例如通过:

    all(f1(x,A)==f3(x,A))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-09
      • 2021-04-18
      • 2015-04-15
      • 1970-01-01
      • 2011-05-16
      • 2015-02-09
      • 1970-01-01
      相关资源
      最近更新 更多