我有以下数据集(实际上超过30.000行):
dt <- tibble::tibble(x=seq.Date(as.Date("2019-01-01"), as.Date("2019-01-10"), "days"), y=c(1,2,3,2,1,1,3,1,2,1))
A tibble: 10 x 2
x y
<date> <dbl>
1 2019-01-01 1
2 2019-01-02 2
3 2019-01-03 3
4 2019-01-04 2
5 2019-01-05 1
6 2019-01-06 1
7 2019-01-07 3
8 2019-01-08 1
9 2019-01-09 2
10 2019-01-10 1
我想标识一个给定阈值以上的日期范围,例如y> =2。该范围的第一个出现日期应另存为“开始”,而最后一个出现的日期应保存为“结束”。每个“开始” /“结束”组合应保存在单独的行中。范围由低于阈值(例如y <2)的“ y”值彼此分隔。结果应如下表所示:
result <- tibble::tibble(start=as.Date(c("2019-01-02", "2019-01-07", "2019-01-09")), end=as.Date(c("2019-01-04", "2019-01-07", "2019-01-09")))
A tibble: 3 x 2
start end
<date> <date>
1 2019-01-02 2019-01-04
2 2019-01-07 2019-01-07
3 2019-01-09 2019-01-09
我当前的解决方案是使用for循环。但是,这导致执行非常缓慢。
有什么想法可以改善性能并更优雅地解决此问题?
谢谢您的想法。
答案 0 :(得分:1)
这应该起作用,并且基于这样的想法,即日期的差异减去1将具有彼此相等的累积和。这就是为什么我们可以按它分组。
dt%>%
filter(y >= 2)%>%
group_by(grouping = cumsum(c(0, diff.Date(x) - 1)))%>%
summarize(start = min(x)
, end = max(x))%>%
select(-grouping)
# A tibble: 3 x 2
start end
<date> <date>
1 2019-01-02 2019-01-04
2 2019-01-07 2019-01-07
3 2019-01-09 2019-01-09
答案 1 :(得分:1)
这是一个识别状态向量的连续值组的函数
f = function(x, min) {
## 'run length encoding' of values satisfying the condition
r = rle(x > min)
## replace TRUE values with a grouping variable; FALSE values are coerced to 0
## (probably better to also replace r$values[!r$values] = NA)
r$values[r$values] = seq_len(sum(r$values))
## expand the modified run length encoding to the shape of the original vector
inverse.rle(r)
}
为您提供数据,
> mutate(dt, grp = f(y, 1))
# A tibble: 10 x 3
x y grp
<date> <dbl> <int>
1 2019-01-01 1 0
2 2019-01-02 2 1
3 2019-01-03 3 1
4 2019-01-04 2 1
5 2019-01-05 1 0
6 2019-01-06 1 0
7 2019-01-07 3 2
8 2019-01-08 1 0
9 2019-01-09 2 3
10 2019-01-10 1 0
然后我们可以使用标准的dplyr操作
mutate(dt, grp = f(y, 1)) %>%
filter(grp != 0) %>%
group_by(grp) %>%
summarize(start = min(x), end = max(x), n = n())
与输出
# A tibble: 3 x 4
grp start end n
<int> <date> <date> <int>
1 1 2019-01-02 2019-01-04 3
2 2 2019-01-07 2019-01-07 1
3 3 2019-01-09 2019-01-09 1