如何根据 R 中的预定义阈值比较所有行的值后进行标记?

How to flag after comparing values over all rows based on a pre-defined threshold in R?

df <- cbind(c(1,1,1,1,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,5,5,5,5), c(6,12,18,24,30,3,9,21,6,12,18,24,30,36,6,12,18,24,30,36,12,24,36,48), c(0.4,1.5,2.7,1.6,0.4,1.3,3.1,3.6,0.5,2.6,3.7,1.8,0.9,0.3,0.7,1.6,1.3,2.8,1.9,1.8,2.0,1.0,3.0,0.8))
colnames(df) <- c("ID","time","value")

我有上面代码给出的数据集。我想知道对于每个ID,从lowest/starting时间开始,该值是否与上涨前的最低值相比至少反弹了2,然后跌破或等于反弹前的最低值价值。我想将至少增加2的时间标记为反弹时间。

因此,例如,在上面的数据集中,对于 ID 1,在开始上升之前的时间 6 的最低值为 0.4。在时间 18,它达到了预定义的阈值 2,然后在时间 30,它下降到等于预反弹最低值的值。所以我想标记ID 1有反弹时间18作为反弹时间。

另一方面,对于 ID 2,虽然它至少上升了 2 个值 (1.3-->3.6),但从未回到低于或等于 1.3 的值

对于ID 3,它再次符合跳出标准(0.5-->2.6-->3.7-->1.8-->0.9-->0.3)。所以我想将 ID 2 标记为有反弹,并将第 18 个月标记为反弹时间。

对于 ID 4,虽然从 0.7-->1.6-->1.3-->2.8(在时间 24)至少上升了 2,但是,后来它从未下降到 0.7 以下反弹前的最低值。所以它不能被标记为有退回。

对于 ID 5,值为 2-->1-->3-->0.8,因此反弹至少 2 (1-->3),然后跌至以下值最低预反弹值 (0.8 <1.0)。所以这个ID应该被标记为有跳出并且跳出时间应该是时间36.

请帮助我进行动态计算,并尽可能解释代码,以便我理解这个概念。提前谢谢你。

考虑一下:

func <- function(tm, val, threshold = 2) {
  mtx <- outer(val, val, `-`)
  mtx[upper.tri(mtx)] <- NA
  if (all(mtx < threshold, na.rm = TRUE)) return(tm[NA][1])
  ij <- which.max(mtx) # counts through the matrix, along columns
  i <- (ij-1) %/% length(val) + 1
  j <- (ij-1) %% length(val) + 1
  if (i < length(val) && any(val[-seq_len(i)] <= val[i])) {
    return(tm[j])
  } else {
    return(tm[NA][i])
  }
}

df <- data.frame(
  ID = c(1,1,1,1,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,5,5,5,5),
  time = c(6,12,18,24,30,3,9,21,6,12,18,24,30,36,6,12,18,24,30,36,12,24,36,48),
  value = c(0.4,1.5,2.7,1.6,0.4,1.3,3.1,3.6,0.5,2.6,3.7,1.8,0.9,0.3,0.7,1.6,1.3,2.8,1.9,1.8,2.0,1.0,3.0,0.8)
)

我使用 which.max 以及 %/%%% 运算符,因为通常我不喜欢 which(val == max(val), arr.ind = TRUE);虽然后者有效,但它也依赖于浮点数的相等性测试,这对于极值可能会有问题。参见 Why are these numbers not equal?, Is floating point math broken?, and https://en.wikipedia.org/wiki/IEEE_754。如果您不喜欢这种安全防护,请随意调整函数以使用 which(.) 代替。

我经历 tm[NA][1] 麻烦的原因是 return 值与您输入的 time 变量完全相同 class。例如,如果您在矢量中更改的值与 class 不同,dplyr 在许多情况下会发出警告或出错。 这个警告或错误很好,因为 R 的本机(和静默)值强制转换可能有问题。例如,Sys.time() 是 class POSIXtNA 不是。但是Sys.time()[NA]classPOSIXt。同样,integernumeric 都有不同类型的 NA。也许这有点过度防御,但使用 tm[NA][1] 可确保输出始终与输入 time.

相同 class

dplyr

library(dplyr)
# # A tibble: 5 x 2
#      ID  time
# * <dbl> <dbl>
# 1     1    18
# 2     2    NA
# 3     3    18
# 4     4    NA
# 5     5    36

data.table

library(data.table)
DF <- as.data.table(df)
DF[, .(time = func(time, value)), by = .(ID)]
#       ID  time
#    <num> <num>
# 1:     1    18
# 2:     2    NA
# 3:     3    18
# 4:     4    NA
# 5:     5    36