累计复位时间

Cumulative time with reset

我有一个如下所示的数据集:

id      land    datetime
pb1     0       2004-04-05 01:44:00
pb1     1       2004-04-05 02:00:00
pb1     1       2004-04-05 16:00:00 
pb2     1       2004-04-05 18:01:00 
pb2     1       2004-04-05 20:00:00   

library(data.table) 
DT = data.table(
  id = c("pb1", "pb1", "pb1", "pb2", "pb2"), 
  land = c(0L, 1L, 1L, 1L, 1L), 
  datetime = sprintf("2004-04-05 %02d:%02d:00", 
                     c(1, 2, 16, 18, 20), 
                     c(44, 0, 0, 1, 0))
)

我想制作一个累积增加时间(以天为单位)的列,但前提是 land 列中有“1”。我还希望在 id 更改时重置计数。

我尝试了多种使用 data.tablerleid 甚至嵌套 for 循环的方法,但都没有成功。我在使用这样的代码时遇到错误:

DT[, total :=land*diff(as.numeric(datetime)), .(id, rleid(land))]

我在这里尝试了多种解决方案:

我不确定计算时间间隔的最佳方法(difftimelubridate 均未成功)。

我希望最终结果如下所示:

id      land           datetime         cumtime.land
pb1     0       2004-04-05 01:44:00     0
pb1     1       2004-04-05 02:00:00     0
pb1     1       2004-04-06 16:00:00     1.58333
pb2     1       2004-04-05 18:00:00     0
pb2     1       2004-04-05 20:00:00     0.08333

我无法复制@Japp 的评论,但您可以使用 dplyr 轻松做到这一点。

根据您的确切预期输出,您可以在 summarize 调用之前停止:

library(dplyr)
df=read.table(text=
    "id      land    datetime
    pb1     0       '2004-04-05 01:44:00'
    pb1     1       '2004-04-05 02:00:00'
    pb1     1       '2004-04-06 16:00:00'
    pb1     1       '2004-04-07 16:00:00'
    pb2     1       '2004-04-05 18:00:00' 
    pb2     1       '2004-04-05 20:00:00'", header=T) %>% 
  mutate(datetime=as.POSIXct(datetime,format='%Y-%m-%d %H:%M:%S'))

x = df %>% 
  group_by(id) %>% 
  arrange(id, datetime) %>% 
  mutate(time.land=ifelse(land==0 | is.na(lag(land)) | lag(land)==0, 
                             0,
                             difftime(datetime, lag(datetime), units="days"))) %>% 
  mutate(cumtime.land=time.land + ifelse(is.na(lag(time.land)), 0, lag(time.land)))

  id     land datetime            time.land cumtime.land
  <fct> <int> <dttm>                  <dbl>        <dbl>
1 pb1       0 2004-04-05 01:44:00    0            0     
2 pb1       1 2004-04-05 02:00:00    0            0     
3 pb1       1 2004-04-06 16:00:00    1.58         1.58  
4 pb1       1 2004-04-07 16:00:00    1            2.58  
5 pb2       1 2004-04-05 18:00:00    0            0     
6 pb2       1 2004-04-05 20:00:00    0.0833       0.0833

关键是使用 dplyr::lag() 函数,它在 table 中获取 "line just above" (这意味着您必须事先 arrange() 它)。

通过将其包装在 ifelse 内,我正在检查 land 和之前的 land 不是 0(并且我们不在第一行的 idlag(anything) 将丢失)。

然后我只是重新使用 lag() 函数来获取 cumtime.land 变量。

我相信你在追求:

DT[land == 1, cumtime.land = 
     cumsum(c(0, diff(as.numeric(datetime))))/86400, by = id]

as.numeric(datetime) 将其转换为 所以我们使用 86400 转换为 .

在利用time/date的意义上更多"official" 类直接使用difftimeshift:

DT[land == 1, by = id,
   cumtime.land := 
     cumsum(as.double(difftime(
       datetime, shift(datetime, fill = datetime[1L]), units = 'days'
     )))]

我调换了 by 参数的顺序只是为了帮助格式化。

我们使用datetime[1L]进行填充,使得初始差为0;我们需要 as.double 因为 cumsum 错误,因为它不确定如何处理 difftime 对象作为输入。

另请参阅:

  • Calculate cumsum() while ignoring NA values