我想计算R中每日数据的时间序列中从不满足条件的时间到再次满足条件的天数。
玩具数据:
day <- data.frame(
date = seq.POSIXt(
from = ISOdatetime(2017,07,01,0,0,0),
to = ISOdatetime(2017,08,26,0,0,0),
by = "1 day" ))
var <- c(5,6,5,5,0,0,0,0,0,1,1,2,3,3,4,3,4,5,4,5,5,4,5,4,0,1,1,2,3,4,5,5,5,4,4,4,4,5,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,1,1,0,0)
ts = cbind(day, var)
条件是var> 3。
我想将每个“恢复”时间段标识为var> 0但<= 3的时间,但仅在var变为零之后。然后,我想要每个时期恢复的天数。
因此,对于此处给出的示例数据,我希望得到以下输出:
period 1 6
period 2 5
由于var永远不会在数据集的末尾“恢复”,因此我要么不希望将其标识为恢复期,要么将恢复时间设为0天。
我尝试过:
ifelse(ts$var >3, 0 ,(ifelse(ts$var>0 & ts$var<4, 1, 0)))
,我想我可以将此if else语句与只计数连续1的东西配对,并且大多数情况下会这样做。唯一的问题是,它将缓慢下降的结束时间段确定为“恢复期”,而不应这样。它仅应将零之后的时间段标识为恢复时间段。
此示例数据如下所示:plot of var over time。我认为这是我可以提供的最少数据,显示了在恢复期以外进行数据计数时遇到的现实问题。
我需要在一个较长且动态得多的时间序列中执行此操作,因此非常感谢您高效地执行此操作。
答案 0 :(得分:0)
修改 -我认为,如果var做类似的事情
,这不会像您期望的那样表现[... 0,1,2,1,0,2,4,...]
但可能适合处理这种情况。
原始答案
我还没有做过太多的测试,我建议检查一下它是否适用于怪异的情况(例如var为全零,在周期边界处开始或结束,其他极端情况...)
# ignore zeroes if they precede another zero
s <- which(var == 0 & c(tail(var, -1), NA) != 0)
e <- which(var > 3)
sapply(s, function(x) head(e[e > x], 1) - x)
此处的方法是确定期间的所有可能的起点和终点,然后找到在每个起点之后出现的第一个终点并求出差值。一个简单的循环甚至是一个聪明的正则表达式可能是一个很好的选择。
答案 1 :(得分:0)
这是一种替代方法,它使用data.table包中的rleid()
函数通过连续的零值和非零值条纹将分组。然后,它会在值> 3的第一次出现的位置找到每个组中的位置:
library(data.table)
setDT(ts)[, if (.GRP > 1) first(which(var > 3)), rleid(var == 0)]
rleid V1 1: 3 6 2: 5 5
第一个组被跳过,因为它是零条纹或没有前面的零值。
即使在Callum Webb has described in the edit of his answer的情况下,这种方法也有效:
# append data
var <- c(var, 0,1,2,1,0,2,4)
date = seq.POSIXt(
from = ISOdatetime(2017,07,01,0,0,0),
along.with = var,
by = "1 day" )
ts = data.frame(date, var)
setDT(ts)[, if (.GRP > 1) first(which(var > 3)), rleid(var == 0)]
rleid V1 1: 3 6 2: 5 5 3: 9 2
因此,它已经认识到,最终的零值之后还有2天的恢复期。
为了完整起见,如果序列0、1、2、1、0被认为也包括3天长度的恢复期,尽管 not 的值未达到3 :
setDT(ts)[, if (.GRP > 1) if (all(var %between% c(1, 3))) .N else first(which(var > 3)),
rleid(var == 0)]
rleid V1 1: 3 6 2: 5 5 3: 7 3 4: 9 2
如果所有值都在1到3之间,则这里将计算两个零之间的所有天。