Простая сумма, если выражение
Это мои данные:
dt <- data.table(id=c("a","a","a","a","b","b"), monthsinarrears=c(0,1,0,0,1,0), date=c(2013,2014,2015,2016,2014,2015))
Таблица выглядит так:
> dt
id monthsinarrears date
1: a 0 2013
2: a 1 2014
3: a 0 2015
4: a 0 2016
5: b 1 2014
6: b 0 2015
Теперь я хочу создать дополнительный столбец под названием "EverinArrears", который будет присваиваться "1", если идентификатор всегда имел задолженность (исторически) и "0", если это не так. Таким образом, я хочу получить результат:
id monthsinarrears date EverinArrears
1: a 0 2013 0
2: a 1 2014 1
3: a 0 2015 1
4: a 0 2016 1
5: b 1 2014 1
6: b 0 2015 1
Обратите внимание, что идентификатор кредита a
не был исторически в Задолженности в 2013 году (это произошло в 2014 году), так что почему EverinArrears также получает нуль в 2013 году.
Ответы
Ответ 1
Вы можете сделать следующее (спасибо @Roland за подсказку, чтобы избежать чисел > 1):
dt[, EverinArrears := as.integer(as.logical(cumsum(monthsinarrears))), by=id]
Вывод:
# id monthsinarrears date EA
#1: a 0 2013 0
#2: a 1 2014 1
#3: a 0 2015 1
#4: a 0 2016 1
#5: b 1 2014 1
#6: b 0 2015 1
Примечание:, если вы предпочитаете более короткий код, вы также можете сделать
dt[, EverinArrears := +(!!(cumsum(monthsinarrears))), by=id]
хотя и не является "хорошей практикой" как as.integer(as.logical(...))
Как уже упоминалось @Jaap, вы также можете:
dt[, EverinArrears := +(cumsum(monthsinarrears) > 0), by = id]
или, для лучшей практики:
dt[, EverinArrears := as.integer(cumsum(monthsinarrears) > 0), by = id]
Как было предложено @Arun в комментарии, еще один, более простой способ:
dt[, EverinArrears := cummax(monthsinarrears), by = id]
Ответ 2
Здесь небольшая вариация ответов других:
dt[, newcol := cummax(monthsinarrears > 0), by=id]
Используя cummax
вместо cumsum
, мы можем сэкономить на некоторых вычислениях.
И вот способ сравнения с позицией первой записи с положительными месяцами с задолженностью:
dt[, newcol := {
z = which(monthsinarrears > 0)
if (!length(z)) rep(0L,.N)
else replace(rep(1L,.N), 1:.N < z[1], 0L)
}, by=id]
Не уверен, что это может быть более эффективным; в определенной степени это зависит от данных.
Ответ 3
Вы можете использовать ave
:
dt$EverinArrears = as.integer(!!ave(dt$monthsinarrears, dt$id, FUN=cumsum))
Или хороший подход с data.table:
dt[, EverinArrears := +(!!cumsum(monthsinarrears)), id][]
Ответ 4
Использование пакета dplyr
:
library(dplyr)
dt %>%
group_by(id) %>%
arrange(date) %>%
mutate(EverinArrears = +as.logical(cumsum(monthsinarrears))) %>%
data.table
id monthsinarrears date EverinArrears
1: a 0 2013 0
2: a 1 2014 1
3: a 0 2015 1
4: a 0 2016 1
5: b 1 2014 1
6: b 0 2015 1