代码之家  ›  专栏  ›  技术社区  ›  Brent Pease

查找条件语句的最小值,其中条件也适用于之后的所有值

  •  2
  • Brent Pease  · 技术社区  · 6 年前

    我想确定第一个小于1的值,并且该元素后面的所有值也都小于1且小于或等于该值。

    我有一个DT:

    stack <- data.table(a = as.numeric(seq(1,10,1)),
                    b = as.numeric(c(1.54, 1.17, 0.75, 1.65, 0.61, 0.31, 0.90, 0.07, 0.04, 0.01)),
                   ID = as.numeric(rep(seq(1,2,1),5)))
    
    stack
         a    b ID
     1:  1 1.54  1
     2:  2 1.17  2
     3:  3 0.75  1
     4:  4 1.65  2
     5:  5 0.61  1
     6:  6 0.31  2
     7:  7 0.90  1
     8:  8 0.07  2
     9:  9 0.04  1
    10: 10 0.01  2
    

    在此示例中,我要查找的值是第7行:

       a    b ID
    7: 7 0.90  1
    

    这是第一个小于1的值,其中后面的所有值都小于1,并且也小于或等于该值。我特别感兴趣的是从列返回值 a

    我试过了 stack[,min(which(b < 1))] 但这显然缺少额外的条件要求

    2 回复  |  直到 6 年前
        1
  •  2
  •   chinsoon12    6 年前

    另一种方法:

    library(data.table)
    
    set.seed(0L)
    M <- 1e4
    DT <- data.table(a=1:M, b=10*runif(M))
    
    mtd1 <- function() {
        DT[which(b < 1 &
                sapply(seq_len(.N), 
                    function(i) all(b[min(.N, i + 1):nrow(DT)] <= b[i]))
        )[1]]   
    }
    
    mtd2 <- function() {
        DT[order(-b), .SD[b < 1][1L]]
    }
    
    identical(mtd1(), mtd2())
    #[1] TRUE
    
    library(microbenchmark)
    microbenchmark(mtd1(), mtd2(), times=3L)
    

    时间安排:

    Unit: milliseconds
       expr      min       lq       mean   median        uq      max neval
     mtd1() 737.5113 754.3420 766.047900 771.1728 780.31620 789.4596     3
     mtd2()   1.6830   1.7687   3.118033   1.8544   3.83555   5.8167     3
    
        2
  •  0
  •   IceCreamToucan    6 年前
    stack[which(b < 1 &
                sapply(seq_len(.N), 
                       function(i) all(b[min(.N, i + 1):nrow(stack)] <= b[i]))
                )[1]]
    

    如果 b[i] < 1 b[i + x] <= b[i] 我们不需要检查 b[i + x] < 1

    或由 ID

    fun <- function(b){
      N <- length(b)
      which(b < 1 &
            sapply(seq_len(N), 
                   function(i) all(b[min(N, i + 1):N] <= b[i]))
            )[1] == seq_len(N)
    }
    
    setorder(stack, ID)
    stack[stack[, fun(b), by = ID]$V1]
    

    编辑:

    我不能删除这篇帖子,因为它已经被接受,但我意识到这在很多情况下给出了错误的答案,例如下面的一篇。另一个答案是正确的(无论如何要快得多)。

    set.seed(0)
    DT <- data.table(a=1:10, b=1.1*runif(10))