Накопительное время со сбросом

У меня есть набор данных, который выглядит следующим образом:

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

Я хотел бы сделать столбец, который кумулятивно добавляет время (в днях), но ТОЛЬКО если есть 1 в land колонка. Я также хотел бы сбросить счетчик, когда id изменения.

Я пробовал различные методы, используя data.table, rleidи даже вложенный for цикл без успеха. Я получил ошибки, используя код, подобный этому:

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

Я попробовал варианты решения здесь: Расчет кумулятивного времени в R

Я не уверен, что лучший способ рассчитать временной интервал (без успеха difftime или же lubridate).

Я хочу, чтобы конечный результат выглядел так:

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 ответа

Решение

Я не мог повторить комментарий @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 (и что мы не в первой строке id, или же lag(anything) будет отсутствовать).

Я тогда просто повторно использую lag() функция, чтобы получить cumtime.land переменная.

Я полагаю, что вы после:

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

as.numeric(datetime) преобразует его в секунды, поэтому мы используем 86400 для преобразования в дни.

Несколько более "официальным" в смысле непосредственного использования классов времени / даты является использование difftime а также shift:

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 объекты в качестве ввода.

Смотрите также:

Другие вопросы по тегам