在R中计算加权平均寿命

4

我希望在R中计算贷款的加权平均寿命(WAL)。计算WAL的公式在这里给出。

我已经在R中创建了以下样本数据。

样本数据

library(data.table)
DT<-data.table(date=c(rep(seq(from = 2015, to = 2016.25,by = .25),2),
seq(from = 2015, to = 2017.5,by = .5)),
           value=c(rep(100,5), 0, 100, 80, 60, 40, 20, 0, 100, 70, 40, 30, 20, 0),
           id=rep(c("a","b","c"),each=6))

DT

       date value id
 1: 2015.00   100  a
 2: 2015.25   100  a
 3: 2015.50   100  a
 4: 2015.75   100  a
 5: 2016.00   100  a
 6: 2016.25     0  a
 7: 2015.00   100  b
 8: 2015.25    80  b
 9: 2015.50    60  b
 10: 2015.75    40  b
 11: 2016.00    20  b
 12: 2016.25     0  b
 13: 2015.00   100  c
 14: 2015.50    70  c
 15: 2016.00    40  c
 16: 2016.50    30  c
 17: 2017.00    20  c
 18: 2017.50     0  c

因此,此示例中的每笔贷款的到期日为5年,在到期日时,贷款完全摊销。注意:日期不总是按半年或一季度递增,可能会有所不同(请参见示例数据)。
为了计算WAL,我创建了以下R代码。
Counter <- unique(DT$id)

# LOOP OVER ID
for (i in 1:length(Counter)) {

# SUBSET ONE ID
DTSub <- DT[id == Counter[i], ]

# LOOP OVER THE AMORTIZATIONDATES
CounterSub <- unique(DTSub$date)

for (j in 1:length(CounterSub)) {

# SUBSET RANGE OF DATES IN COUNTERSUB
DTSub_Date <- DTSub[date >= CounterSub[j], ]
DTSub_Date[, t := abs(min(date)-date)]
DT[id == Counter[i] & date == CounterSub[j], 
       wal_calc := round(sum(abs(diff(DTSub_Date$value)) 
       / max(DTSub_Date$value) * DTSub_Date$t[2:nrow(DTSub_Date)]),3)]

}
}

代码的输出
DT

       date value id wal_calc
 1: 2015.00   100  a    1.250
 2: 2015.25   100  a    1.000
 3: 2015.50   100  a    0.750
 4: 2015.75   100  a    0.500
 5: 2016.00   100  a    0.250
 6: 2016.25     0  a    0.000
 7: 2015.00   100  b    0.750
 8: 2015.25    80  b    0.625
 9: 2015.50    60  b    0.500
 10: 2015.75    40  b    0.375
 11: 2016.00    20  b    0.250
 12: 2016.25     0  b    0.000
 13: 2015.00   100  c    1.300
 14: 2015.50    70  c    1.143
 15: 2016.00    40  c    1.125
 16: 2016.50    30  c    0.833
 17: 2017.00    20  c    0.500
 18: 2017.50     0  c    0.000

代码的输出是正确的 (wal_calc),但使用了双重 for 循环,在相对较大的数据集上速度较慢(我的数据集有77k行和200列)。
第一个 for 循环对ID进行子集划分,第二个循环基于第一个子集按ID子集划分未来日期。 请求 我希望能够以更快、更有效的方式生成此示例数据上的WALS,避免使用这种双重循环。可能有一种非常简单的解决方案。
如果有任何不清楚的地方,请告诉我。
2个回答

3
这将不需要使用for循环来完成。
DT[order(date), WAL := {
  pmts <- matrix(value[-.N] - value[-1L], 
                 nrow = n2 <- .N - 1L, ncol = n2)
  ts <- matrix(date[-1L] - date[-.N], nrow = n2, ncol = n2)
  ts[upper.tri(ts)] <- 0
  ts <- apply(ts, 2, cumsum)
  c(colSums(pmts * ts) / value[-.N], 0)}, by = id]
DT
     date value id       WAL
# 1: 2015.00   100  a 1.2500000
# 2: 2015.25   100  a 1.0000000
# 3: 2015.50   100  a 0.7500000
# 4: 2015.75   100  a 0.5000000
# 5: 2016.00   100  a 0.2500000
# 6: 2016.25     0  a 0.0000000
# 7: 2015.00   100  b 0.7500000
# 8: 2015.25    80  b 0.6250000
# 9: 2015.50    60  b 0.5000000
# 10: 2015.75    40  b 0.3750000
# 11: 2016.00    20  b 0.2500000
# 12: 2016.25     0  b 0.0000000
# 13: 2015.00   100  c 1.3000000
# 14: 2015.50    70  c 1.1428571
# 15: 2016.00    40  c 1.1250000
# 16: 2016.50    30  c 0.8333333
# 17: 2017.00    20  c 0.5000000
# 18: 2017.50     0  c 0.0000000

我只是想发表同样的评论。顺便说一句,它并不是这样的。对于其他部分,你的答案似乎可行。而且,它要快得多! - Dave van Brecht
@DavevanBrecht 好的。如果你能在你的例子中提供这个,那会很有帮助。 - MichaelChirico
好的,我会更改示例数据。 - Dave van Brecht
@DavevanBrecht 已修复。我不喜欢使用 apply,但它似乎是最快的方法。同时意识到我们不需要 sweep,使用它总是让我感到困惑。 - MichaelChirico
1
非常感谢您的帮助。您的答案似乎以更高效的方式复制了我的先前代码。 - Dave van Brecht
2
线性代数是避免所有for循环的核心;-) - MichaelChirico

1
你可以使用apply来替代第一个子集,这样你只需要一个for循环。
ids <- unique(DT$id)

DTSub <- apply(DT, 1, function(x) if x$id %in% ids)

CounterSub <- unique(DTSub$date)

感谢Seekheart的建议。不过,理想情况下我希望使用快速的data.table解决方案,因为这个函数会在Shiny应用程序中使用,并且应该能够实时计算WALS(即尽可能快速和高效)。肯定还有其他方法可以实现这一点。我已经搜索了一些特定的软件包,但没有找到它们。 - Dave van Brecht

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接