我有一个像这样的数据集:
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.table
,rleid
甚至是嵌套for
循环的各种方法,但均未成功。使用以下代码时出现错误:
DT[, total :=land*diff(as.numeric(datetime)), .(id, rleid(land))]
我在这里尝试过各种解决方案:Calculating cumulative time in 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
答案 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
(并且我们不在第一行) id
或lag(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将其转换为天。
在直接利用时间/日期类的意义上,更“官方”的是使用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
对象作为输入。
另请参阅: