Накопительное время со сбросом
У меня есть набор данных, который выглядит следующим образом:
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
объекты в качестве ввода.
Смотрите также: