重置累计时间

时间:2018-12-20 19:21:23

标签: r dplyr data.table difftime

我有一个像这样的数据集:

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))]

我在这里尝试过各种解决方案:Calculating cumulative time in R

我不确定计算时间间隔的最佳方法(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

2 个答案:

答案 0 :(得分:1)

我无法复制@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()函数,该函数将表中的“正上方的行”移到表中(这意味着您必须事先arrange()将其填充)。

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

然后我只需重用lag()函数即可获取cumtime.land变量。

答案 1 :(得分:0)

我相信您正在追寻:

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

as.numeric(datetime)将其转换为,因此我们使用86400将其转换为

在直接利用时间/日期类的意义上,更“官方”的是使用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对象作为输入。

另请参阅: