【问题标题】:Calculate weighted average life in R计算 R 中的加权平均寿命
【发布时间】:2016-06-05 12:11:51
【问题描述】:

我想在 R 中计算一段时间内贷款的加权平均寿命 (WAL)。计算 WAL 的公式为 here

我在 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,并避免这种双重 for 循环。这个问题可能有一个非常简单的解决方案。

如果有什么不清楚的地方请告诉我。

【问题讨论】:

    标签: r data.table weighted-average


    【解决方案1】:

    这将在没有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
    

    【讨论】:

    • 我只是想发表同样的评论。不,它不是顺便说一句。对于其余部分,您的答案似乎有效。此外,它的速度更快!
    • @DavevanBrecht 好的。在您的示例中使用它会有所帮助。
    • 好的,我改一下示例数据
    • 非常感谢您的帮助。您的回答似乎重现了我以前的代码,但以更有效的方式。
    • 线性代数是所有 for 循环避免的核心 ;-)
    【解决方案2】:

    您可以使用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(即尽可能快速和高效)。必须有其他方法可以做到这一点。我已经搜索了一些特定的包,但找不到它们。
    猜你喜欢
    • 2012-06-14
    • 1970-01-01
    • 1970-01-01
    • 2011-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    相关资源
    最近更新 更多