【问题标题】:Possible to avoid a FOR loop in this very simple R code?在这个非常简单的 R 代码中可以避免 FOR 循环吗?
【发布时间】:2021-05-12 05:52:33
【问题描述】:

下面的答案非常有帮助。但我过度简化了我原来的问题。我想如果我过度简化然后适应我的实际需要,我会学到更多,但现在我被困住了。还有其他因素推动摊销。在此处查看更完整的代码。我喜欢使用“amort$end_bal npr 增加期末余额,ch_off 减少期末余额。这是更完整的代码:

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 的新手,我了解它的功能之一是矩阵/向量操作。在下面的示例中,我将资产摊销超过 8 个月,其中每次付款(“pmt”)是前期余额(“end_bal”)的 10%(“mpr”)。下面的工作正常。我使用了 FOR 循环。我知道 FOR 循环在大型模型中可能会很慢,更好的解决方案是使用 R 丰富的向量/矩阵函数。但在我的示例中,我不知道如何执行此操作,因为每个月的付款都是通过参考上一期期末余额来计算的。

所以我的问题是:

  1. 是否有更有效的方法来执行以下操作?
  2. 如何将周期 0 中 pmt 的 0 替换为空格?

R 代码:

n_periods <- 8                
begin_bal <- 100              
mpr <- .10                  

# Example loan amortization
pmt =     0
end_bal = begin_bal
for(i in 1:n_periods){
  {pmt[i+1] = end_bal[i]*mpr}
  end_bal[i+1] = end_bal[i]-pmt[i+1]}
amort <- data.frame(period = 0:n_periods,pmt,end_bal)
amort

正确的结果:

> amort
  period       pmt   end_bal
1      0  0.000000 100.00000
2      1 10.000000  90.00000
3      2  9.000000  81.00000
4      3  8.100000  72.90000
5      4  7.290000  65.61000
6      5  6.561000  59.04900
7      6  5.904900  53.14410
8      7  5.314410  47.82969
9      8  4.782969  43.04672

【问题讨论】:

  • 这两个答案中的任何一个都没有达到您的目的吗?您应该接受最接近您期望的答案是正确的,以便问题和答案可供将来参考。 :)

标签: r


【解决方案1】:

使用 R 的矢量化计算

n_periods <- 8                
begin_bal <- 100              
mpr <- .10

amort <- data.frame(period = seq(0, n_periods, 1))

amort$end_bal <- begin_bal * (1 - mpr)^amort$period
amort$pmt <- c(0, diff(amort$end_bal))* -1
amort
#>   period   end_bal       pmt
#> 1      0 100.00000  0.000000
#> 2      1  90.00000 10.000000
#> 3      2  81.00000  9.000000
#> 4      3  72.90000  8.100000
#> 5      4  65.61000  7.290000
#> 6      5  59.04900  6.561000
#> 7      6  53.14410  5.904900
#> 8      7  47.82969  5.314410
#> 9      8  43.04672  4.782969

reprex package (v2.0.0) 于 2021-05-12 创建

dplyr 不同情况的方式(比如)

n_periods <- 15                
begin_bal <- 1000              
mpr <- .07

library(dplyr)


seq(0, n_periods, 1) %>% as.data.frame() %>%
  setNames('period') %>%
  mutate(end_bal = begin_bal * (1 - mpr)^period,
         pmt = -1 * c(0, diff(end_bal)))
#>    period   end_bal      pmt
#> 1       0 1000.0000  0.00000
#> 2       1  930.0000 70.00000
#> 3       2  864.9000 65.10000
#> 4       3  804.3570 60.54300
#> 5       4  748.0520 56.30499
#> 6       5  695.6884 52.36364
#> 7       6  646.9902 48.69819
#> 8       7  601.7009 45.28931
#> 9       8  559.5818 42.11906
#> 10      9  520.4111 39.17073
#> 11     10  483.9823 36.42878
#> 12     11  450.1035 33.87876
#> 13     12  418.5963 31.50725
#> 14     13  389.2946 29.30174
#> 15     14  362.0439 27.25062
#> 16     15  336.7009 25.34308

reprex package (v2.0.0) 于 2021 年 5 月 12 日创建


虽然 OP 在已编辑的场景中提出了另一个问题,但这是建议的方法(供将来参考)

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,
         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

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

【讨论】:

  • 我喜欢这个直截了当的解决方案,非常数学和高效!点赞!
【解决方案2】:

如果你是“懒人”(不想制定pmtend_bal的一般表达式),可以定义一个递归函数f就像吹

f <- function(k) {
  if (k == 1) {
    return(data.frame(pmt = 100 * mpr, end_bal = 100))
  }
  u <- f(k - 1)
  end_bal <- with(tail(u, 1), end_bal - pmt)
  pmt <- mpr * end_bal
  rbind(u, data.frame(pmt, end_bal))
}

n_periods <- 8
res <- transform(
  cbind(period = 0:n_periods, f(n_periods + 1)),
  pmt = c(0, head(pmt, -1))
)

你会看到

> res
  period       pmt   end_bal
1      0  0.000000 100.00000
2      1 10.000000  90.00000
3      2  9.000000  81.00000
4      3  8.100000  72.90000
5      4  7.290000  65.61000
6      5  6.561000  59.04900
7      6  5.904900  53.14410
8      7  5.314410  47.82969
9      8  4.782969  43.04672

【讨论】:

    猜你喜欢
    • 2011-05-05
    • 2014-01-12
    • 2020-06-29
    • 2011-06-21
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    相关资源
    最近更新 更多