Простая сумма, если выражение

Это мои данные:

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