У меня есть таблица данных, к которой я хочу добавить обратный отсчет, пока в столбце flag не появится значение 1.
dt = structure(list(date = structure(19309:19318, class = c("IDate",
"Date")), flag = c(0, 0, 0, 0, 0, 1, 0, 0, 0, 1)), class = c("data.table",
"data.frame"), row.names = c(NA, -10L), .internal.selfref = <pointer: 0x55af7de49cb0>)
> dt
date flag
1: 2022-11-13 0
2: 2022-11-14 0
3: 2022-11-15 0
4: 2022-11-16 0
5: 2022-11-17 0
6: 2022-11-18 1
7: 2022-11-19 0
8: 2022-11-20 0
9: 2022-11-21 0
10: 2022-11-22 1
Вот ожидаемый результат
date flag countdown
1: 2022-11-13 0 5
2: 2022-11-14 0 4
3: 2022-11-15 0 3
4: 2022-11-16 0 2
5: 2022-11-17 0 1
6: 2022-11-18 1 0
7: 2022-11-19 0 3
8: 2022-11-20 0 2
9: 2022-11-21 0 1
10: 2022-11-22 1 0
Решение data.table является предпочтительным.





Решение data.table не только предпочтительно, но и красиво.
library(data.table)
dt = structure(list(date = structure(19309:19318, class = c("IDate",
"Date")), flag = c(0, 0, 0, 0, 0, 1, 0, 0, 0, 1)), class = c(
"data.frame"), row.names = c(NA, -10L))
setDT(dt)
dt[, countdown := rev(1:.N), by=rleid(flag)][flag==1, countdown:=0 ]
dt
#> date flag countdown
#> 1: 2022-11-13 0 5
#> 2: 2022-11-14 0 4
#> 3: 2022-11-15 0 3
#> 4: 2022-11-16 0 2
#> 5: 2022-11-17 0 1
#> 6: 2022-11-18 1 0
#> 7: 2022-11-19 0 3
#> 8: 2022-11-20 0 2
#> 9: 2022-11-21 0 1
#> 10: 2022-11-22 1 0
Created on 2022-11-07 with reprex v2.0.2
редактировать
dt[, countdown := .N:1 * !flag, by=rleid(flag)]
для краткости.
О, вы получаете предупреждение, когда используете данные воспроизведения OP без шага setDT.
Еще data.table вариант
> cbind(dt, dt[, .(countdonwn = .N - seq(.N)), rev(cumsum(rev(flag)))][, 2])
date flag countdonwn
1: 2022-11-13 0 5
2: 2022-11-14 0 4
3: 2022-11-15 0 3
4: 2022-11-16 0 2
5: 2022-11-17 0 1
6: 2022-11-18 1 0
7: 2022-11-19 0 3
8: 2022-11-20 0 2
9: 2022-11-21 0 1
10: 2022-11-22 1 0
Использование sequence:
dt[ , countdown := {
ix = which(flag == 1)
v = ix - c(1, head(ix, -1) + 1)
sequence(v + 1, v, -1L)
}]
date flag countdown
1: 2022-11-13 0 5
2: 2022-11-14 0 4
3: 2022-11-15 0 3
4: 2022-11-16 0 2
5: 2022-11-17 0 1
6: 2022-11-18 1 0
7: 2022-11-19 0 3
8: 2022-11-20 0 2
9: 2022-11-21 0 1
10: 2022-11-22 1 0
Используя rleid и rowid:
dt[ , countdown := {rf = rev(flag); rev(rowid(rleid(rf)) * !rf)}]
Обе альтернативы значительно быстрее на больших данных:
n = 1e7
set.seed(1)
d = data.table(flag = c(rbinom(n, size = 1, prob = 0.2), 1))
d1 = copy(d)
d2 = copy(d)
d3 = copy(d)
d4 = copy(d)
microbenchmark(
ric = d1[, r := .N:1 * !flag, by=rleid(flag)],
thomas = {d2_2 = cbind(d2, d2[, .(r = .N - seq(.N)), rev(cumsum(rev(flag)))][, 2])},
h1 = d4[ , r := {
ix = which(flag == 1)
v = ix - c(1, head(ix, -1) + 1)
sequence(v + 1, v, -1L)
}],
h2 = d3[ , r := {rf = rev(flag); rev(rowid(rleid(rf)) * !rf)}],
times = 10L)
Unit: milliseconds
expr min lq mean median uq max neval
ric 3349.0538 3693.8509 3707.6691 3720.3821 3777.8165 3923.4453 10
thomas 12914.1821 12942.3472 13049.8039 12985.2630 13162.3061 13359.6016 10
h1 107.5380 109.0322 140.6891 110.0880 120.4707 261.9755 10
h2 261.1989 283.2164 310.9347 293.2629 337.0358 431.7687 10
all.equal(d1, d2_2)
# [1] TRUE
all.equal(d1, d3)
# [1] TRUE
all.equal(d1, d4)
# [1] TRUE
Спасибо, Хенрик, за быстрое решение. Не могли бы вы добавить пояснение к утверждению - v = ix - c(1, head(ix, -1) + 1) в свой ответ?
Спасибо за ваш отзыв @Saurabh. В настоящее время я занят на работе, но добавлю некоторые пояснения, как только смогу. Ваше здоровье
Нет, я не знаю. Что такое предупреждение? Я использую 4.2.1 и data.table
installed.packages()["data.table","Version"] == "1.14.2"