计算事件发生后的观察次数

Calculating the Number of Observations Since an Event Occurred

我有以下温度矢量(以 ˚C 为单位):

Temperature <- c(-3:3, 3:-3, rep(-3, 2), -2:-1, 1:3, 2:1, -1:-4)

我需要计算自上次冻结事件以来经过的时间(观察次数),我还需要计算自上次解冻事件以来经过的观察次数。冻结事件的标志是温度从正值到负值的转变,解冻事件的标志是温度从负值到正值的转变。输出应该类似于这些向量:

Time_Since_Last_Freeze <- c(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3)
Time_Since_Last_Thaw <- c(NA, NA, NA, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6, 7, 8)

我在 Stack Overflow 上看到过一些类似的问题,但其中 none 正是我所需要的。生成这两个输出向量的有效方法是什么?

你可以使用这个函数,它基本上检查原始向量中冻结和解冻的索引,然后计算在每个冻结或解冻时刻之间应用一系列长度为 dif 的连续数:

f <- function(temp, freeze){
  if(freeze)
    idx <- which(temp <= 0 & dplyr::lag(temp) > 0)
  else
    idx <- which(temp >= 0 & dplyr::lag(temp) < 0)
  
  diff <- diff(c(idx, length(temp) + 1))
  vec <- rep(NA, length(temp))
  vec[min(idx):length(temp)] <- unlist(sapply(diff, \(x) seq_len(x) - 1))
  vec
}

输出

f(Temperature, freeze = TRUE)
[1] NA NA NA NA NA NA NA NA NA NA  0  1  2  3  4  5  6  7  8  9 10 11 12  0  1  2  3

f(Temperature, freeze = FALSE)
[1] NA NA NA  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14  0  1  2  3  4  5  6  7  8

不是您想要的输出,尽管您可能会在 cgwtools:

中发现有用的内容
temp <- c(-3:3, 3:-3, rep(-3, 2), -2:3, 2:-4)
which(temp > 0)
 [1]  5  6  7  8  9 10 20 21 22 23 24
cgwtools::seqle(which(temp > 0))
Run Length Encoding
  lengths: int [1:2] 6 5
  values : int [1:2] 5 20
> cgwtools::seqle(which(temp <= 0))
Run Length Encoding
  lengths: int [1:3] 4 9 5
  values : int [1:3] 1 11 25

通过总结的方式

另一种可能的解决方案是在将 Temperature 转换为数据帧后使用 tidyverse

library(tidyverse)
library(rlang)

reduce(list(data.frame(Temperature), "thaw", "freeze"), \(y, x) y %>%
          mutate(!!x := if (x == "thaw")
            ifelse(lag(Temperature < 0) & Temperature >= 0, row_number(), NA ) else
              ifelse(lag(Temperature > 0) & Temperature <= 0, row_number(), NA )) %>%
          fill(all_of(x)) %>% 
          group_by(!!parse_expr(x)) %>% 
          mutate(!!x := ifelse(is.na(!!parse_expr(x)), NA, row_number()-1)) %>% 
          ungroup)

#> # A tibble: 27 x 3
#>    Temperature  thaw freeze
#>          <dbl> <dbl>  <dbl>
#>  1          -3    NA     NA
#>  2          -2    NA     NA
#>  3          -1    NA     NA
#>  4           0     0     NA
#>  5           1     1     NA
#>  6           2     2     NA
#>  7           3     3     NA
#>  8           3     4     NA
#>  9           2     5     NA
#> 10           1     6     NA
#> # ... with 17 more rows