Application of a recursive function within a dplyr context in R
I think you can probably get what you need here with a mix of tidyr::fill
to fill NA
values from above, combined with cumprod
to get the cumulative effect of multiplying by the coefficient, and ifelse
to choose when to use it. There's also a "working" column named V which is created and destroyed in the process.
library(dplyr)
df %>%
mutate(V = tidyr::fill(df, VALUE)$VALUE) %>%
group_by(ID) %>%
mutate(VALUE = ifelse(is.na(VALUE),
V * cumprod(ifelse(is.na(VALUE), COEFF, 1)),
VALUE)) %>% select(-V)
#> # A tibble: 10 x 3
#> # Groups: ID [2]
#> ID VALUE COEFF
#> <fct> <dbl> <dbl>
#> 1 a 1 1
#> 2 a 3 2
#> 3 a 3 1
#> 4 a 1.5 0.5
#> 5 a 150 100
#> 6 b 2 1
#> 7 b 2 1
#> 8 b 3 1
#> 9 b 3 1
#> 10 b 3 1
Created on 2020-06-30 by the reprex package (v0.3.0)
A fully recursive way :
calc <- function(val,coef){
for(i in 2:length(val))
{
if(is.na(val[i])){
val[i] <- val[i-1]*coef[i]
}
}
return(val)
}
library(dplyr)
df %>%
group_by(ID) %>%
mutate(newval = calc(VALUE, COEFF))
ID VALUE COEFF newval
<chr> <dbl> <dbl> <dbl>
1 a 1 1 1
2 a 3 2 3
3 a NA 1 3
4 a NA 0.5 1.5
5 a NA 100 150
6 b 2 1 2
7 b 2 1 2
8 b 3 1 3
9 b NA 1 3
10 b NA 1 3
group_by
provides to mutate
a subset of the original data fields for each ID.
You can then process these vectors in a standard recursive loop and give back a result vector of equal length to the mutate
statement to put the results together.
If you need speed, the for-loop can easily be accelerated with Rcpp
:
library(Rcpp)
Rcpp::cppFunction('
NumericVector calc(NumericVector val, NumericVector coef) {
int n = val.size();
int i;
for (i = 1;i<n;i++){
if(R_IsNA(val[i])){
val[i] = val[i-1]*coef[i];
}
}
return val;
}')