由于accumulate(和accumulate2)的结构,我们不能只迭代b,我们需要为每个点包含c(b, growth)之类的东西。为此,我们将传递向量的list,而不是传递b,它可以通过以下方式生成:
with(x, pmap(list(b, growth), c))
# [[1]]
# [1] 100.0000000 0.0265944
# [[2]]
# [1] 110.00000000 0.07115916
# [[3]]
# [1] 120.00000000 0.03739895
# [[4]]
# [1] 130.00000000 0.07958855
# [[5]]
# [1] 140.00000000 0.08470159
# [[6]]
# [1] NA 0.005054528
# [[7]]
# [1] NA 0.04800139
# [[8]]
# [1] NA 0.08042529
# [[9]]
# [1] NA 0.05007772
# [[10]]
# [1] NA 0.04163871
有了这个,我们现在可以积累:
fill_in2 <- function(prev, new) if (is.na(new[1])) prev[1]*(1+new[2]) else new[1]
options(pillar.sigfig = 5)
x %>%
mutate(b = accumulate(pmap(list(b, growth), c)[-1], .init = b[1], fill_in2))
# # A tibble: 10 x 3
# a b growth
# <int> <dbl> <dbl>
# 1 1 100 0.026594
# 2 2 110 0.071159
# 3 3 120 0.037399
# 4 4 130 0.079589
# 5 5 140 0.084702
# 6 6 140.71 0.0050545
# 7 7 147.46 0.048001
# 8 8 159.32 0.080425
# 9 9 167.30 0.050078
# 10 10 174.27 0.041639
我同时使用pmap(..)[-1] 和.init=b[1] 的原因是,在accumulate 的默认行为下,.x 的第一个元素按原样传递;在这种情况下,这会将c(100, 0.0266) 作为第一个返回值传递,这不是我们想要的。为了解决这个问题,我们将其从 pmap'd 列表中删除,并将 b[1] 添加为 accumulate 的初始化值 (.init=)。
顺便说一句:这是将growth 的当前 值应用于b 的上一个 值。
另一个顺便说一句:您对fill_in 的使用使用了if_else。虽然它有效,但它是不必要和不合适的。如果要查找if 一个长度始终为1 的对象,则使用if(以及可选的else);如果希望以逻辑的向量为条件,则使用ifelse/if_else。虽然可以使用 if_else 当您知道它总是长度为 1 时,但存在开销和可能发生的其他完全不必要的事情(在基础 R 中,ifelse 具有非- 微不足道的类数据,因此应管理其使用)。
由于accumulate 一次调用您的函数时只使用一行的数据,因此使用if 更合适。
数据
set.seed(123)
x <- tibble(a = c(1:10),
b = c(seq(100, 140, 10), rep(NA_real_, 5)),
growth = runif(10, 0.001, 0.09))