首页 > 解决方案 > 如何在这个简单的 R 代码中使用向量函数而不是 FOR 循环?

问题描述

这是我昨天发布的同名问题的扩展。昨天我过度简化了我的示例,因为还有其他变量可以驱动这个摊销示例。我想在下面的 R 代码中使用向量函数而不是 FOR 循环。下面的效果很好(我还在下面显示了正确的输出),但我理解随着模型的增长,向量函数会变得更快。

我有 Excel/VBA 背景,是 R 的新手,我正试图了解 R 向量。

摊销 = begin_bal*((1+npr)(1-mpr)(1-co/12)) = end_bal。下面的 Fin(收益率)不计入期末余额。

下面是 FOR 循环代码:

n_periods <- 8
begin_bal <- 10000
yld <- .20
npr <- .09
mpr <- .10           
co <- .10            

period <- seq(0, n_periods, 1)
fin <- 0
pur <- 0
pmt <- 0
ch_off <- 0
end_bal <- begin_bal

for (i in 1:n_periods) {
  fin[i + 1] <- end_bal[i]*yld / 12
  pur[i + 1] <- end_bal[i]*npr
  pmt[i + 1] <- end_bal[i]*mpr
  ch_off[i + 1] <- end_bal[i]*co / 12
  end_bal[i + 1] <- end_bal[i] + pur[i + 1] - pmt[i + 1] - ch_off[i + 1]
}

amort <- data.frame(period, fin, pur, pmt, ch_off, end_bal)

这是(正确的)输出:

print(amort,row.names=FALSE)
 period      fin      pur       pmt   ch_off   end_bal
      0   0.0000   0.0000    0.0000  0.00000 10000.000
      1 166.6667 900.0000 1000.0000 83.33333  9816.667
      2 163.6111 883.5000  981.6667 81.80556  9636.694
      3 160.6116 867.3025  963.6694 80.30579  9460.022
      4 157.6670 851.4020  946.0022 78.83351  9286.588
      5 154.7765 835.7929  928.6588 77.38823  9116.334
      6 151.9389 820.4700  911.6334 75.96945  8949.201
      7 149.1534 805.4281  894.9201 74.57668  8785.132
      8 146.4189 790.6619  878.5132 73.20944  8624.072

标签: r

解决方案


没那么难,一旦你停止思考 Excel 的方式(我在 5-6 个月前开始 R 时也遇到了类似的问题,这就是为什么建议你)。您只需要将逻辑转换为数学方式。

  • 实际上,您的月度/期刊payment包含三个项目,,pur仅取决于以前的余额。pmtch_off
  • 因此,如果我们计算(mpr + co/12 - npr)每个时期,我们payment就可以计算出来。
  • period从这里开始就更容易了0,我们可以使用数学公式^来计算end_bal每个period.
  • 此后,其余值很容易计算。

BaseR 版本

n_periods <- 8
begin_bal <- 10000
yld <- .20
npr <- .09
mpr <- .10           
co <- .10

amort <- data.frame(period = seq(0, n_periods, 1))
amort$end_bal <- begin_bal * (1 - (mpr + co/12 - npr))^amort$period
amort$fin <- c(0, (amort$end_bal * yld/12)[-nrow(amort)])
amort$pur <- c(0, (amort$end_bal * npr)[-nrow(amort)])
amort$pmt <- c(0, (amort$end_bal * mpr)[-nrow(amort)])
amort$ch_off <- c(0, (amort$end_bal * co/12)[-nrow(amort)])

amort
#>   period   end_bal      fin      pur       pmt   ch_off
#> 1      0 10000.000   0.0000   0.0000    0.0000  0.00000
#> 2      1  9816.667 166.6667 900.0000 1000.0000 83.33333
#> 3      2  9636.694 163.6111 883.5000  981.6667 81.80556
#> 4      3  9460.022 160.6116 867.3025  963.6694 80.30579
#> 5      4  9286.588 157.6670 851.4020  946.0022 78.83351
#> 6      5  9116.334 154.7765 835.7929  928.6588 77.38823
#> 7      6  8949.201 151.9389 820.4700  911.6334 75.96945
#> 8      7  8785.132 149.1534 805.4281  894.9201 74.57668
#> 9      8  8624.072 146.4189 790.6619  878.5132 73.20944

dplyr 版本

n_periods <- 8
begin_bal <- 10000
yld <- .20
npr <- .09
mpr <- .10           
co <- .10
library(dplyr)

seq(0, n_periods, 1) %>% as.data.frame() %>%
  setNames('period') %>%
  mutate(end_bal = begin_bal * (1 - (mpr + co/12 - npr))^period,
         payment = -1 * c(0, diff(end_bal)),
         fin = c(0, (end_bal * yld/12)[-nrow(.)]),
         pur = c(0, (end_bal * npr)[-nrow(.)]),
         pmt = c(0, (end_bal * mpr)[-nrow(.)]),
         ch_off = c(0, (end_bal * co/12)[-nrow(.)]))

#>   period   end_bal  payment      fin      pur       pmt   ch_off
#> 1      0 10000.000   0.0000   0.0000   0.0000    0.0000  0.00000
#> 2      1  9816.667 183.3333 166.6667 900.0000 1000.0000 83.33333
#> 3      2  9636.694 179.9722 163.6111 883.5000  981.6667 81.80556
#> 4      3  9460.022 176.6727 160.6116 867.3025  963.6694 80.30579
#> 5      4  9286.588 173.4337 157.6670 851.4020  946.0022 78.83351
#> 6      5  9116.334 170.2541 154.7765 835.7929  928.6588 77.38823
#> 7      6  8949.201 167.1328 151.9389 820.4700  911.6334 75.96945
#> 8      7  8785.132 164.0687 149.1534 805.4281  894.9201 74.57668
#> 9      8  8624.072 161.0608 146.4189 790.6619  878.5132 73.20944

reprex 包于 2021-05-13 创建 (v2.0.0 )

注意创建的额外列payment也可以删除

seq(0, n_periods, 1) %>% as.data.frame() %>%
  setNames('period') %>%
  mutate(end_bal = begin_bal * (1 - (mpr + co/12 - npr))^period,
         fin = c(0, (end_bal * yld/12)[-nrow(.)]),
         pur = c(0, (end_bal * npr)[-nrow(.)]),
         pmt = c(0, (end_bal * mpr)[-nrow(.)]),
         ch_off = c(0, (end_bal * co/12)[-nrow(.)]))

  period   end_bal      fin      pur       pmt   ch_off
1      0 10000.000   0.0000   0.0000    0.0000  0.00000
2      1  9816.667 166.6667 900.0000 1000.0000 83.33333
3      2  9636.694 163.6111 883.5000  981.6667 81.80556
4      3  9460.022 160.6116 867.3025  963.6694 80.30579
5      4  9286.588 157.6670 851.4020  946.0022 78.83351
6      5  9116.334 154.7765 835.7929  928.6588 77.38823
7      6  8949.201 151.9389 820.4700  911.6334 75.96945
8      7  8785.132 149.1534 805.4281  894.9201 74.57668
9      8  8624.072 146.4189 790.6619  878.5132 73.20944

dplyr::lag也可以使用

seq(0, n_periods, 1) %>% as.data.frame() %>%
  setNames('period') %>%
  mutate(end_bal = begin_bal * (1 - (mpr + co/12 - npr))^period,
         fin = lag(end_bal, default = 0) * yld/12,
         pur = lag(end_bal, default = 0) * npr,
         pmt = lag(end_bal, default = 0) * mpr,
         ch_off = lag(end_bal, default = 0) * co/12)

推荐阅读