这些是我的样本数据:
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
现在我想创建一个名为"everin欠款"的额外列,如果id曾经拖欠(历史上),将被分配为"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
请注意,贷款id a
在2013年历史上没有拖欠(这发生在2014年),所以这就是为什么everin欠款在2013年也得到0。
您可以做以下操作(感谢@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]
其他人的回答略有不同:
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]
不确定这样是否更有效;在某种程度上,这当然取决于数据。
您可以使用ave
:
dt$EverinArrears = as.integer(!!ave(dt$monthsinarrears, dt$id, FUN=cumsum))
或者使用data.table:
dt[, EverinArrears := +(!!cumsum(monthsinarrears)), id][]
Using package 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