Build up a value in a list of dataframes in a cumulative manner

Viewed 33

A list of data frames:

mylist <- diamonds %>% 
  mutate(somenum = rnorm(nrow(.))) %>% 
  group_by(cut, color) %>% 
  group_split %>% 
  map(~ list(dta = ., initial_val = rnorm(1)))

E.g. the first item in mylist:

mylist[1]
[[1]]
[[1]]$dta
# A tibble: 163 x 11
   carat cut   color clarity depth table price     x     y     z somenum
   <dbl> <ord> <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>   <dbl>
 1  0.75 Fair  D     SI2      64.6    57  2848  5.74  5.72  3.7    0.302
 2  0.71 Fair  D     VS2      56.9    65  2858  5.89  5.84  3.34   0.500
 3  0.9  Fair  D     SI2      66.9    57  2885  6.02  5.9   3.99   0.179
 4  1    Fair  D     SI2      69.3    58  2974  5.96  5.87  4.1    1.25 
 5  1.01 Fair  D     SI2      64.6    56  3003  6.31  6.24  4.05  -0.731
 6  0.73 Fair  D     VS1      66      54  3047  5.56  5.66  3.7    0.758
 7  0.71 Fair  D     VS2      64.7    58  3077  5.61  5.58  3.62  -1.43 
 8  0.91 Fair  D     SI2      62.5    66  3079  6.08  6.01  3.78   0.820
 9  0.9  Fair  D     SI2      65.9    59  3205  6     5.95  3.94  -1.81 
10  0.9  Fair  D     SI2      66      58  3205  6     5.97  3.95  -0.179
# … with 153 more rows

[[1]]$initial_val
[1] 1.788348

The list item contains a data frame as well as a number 'initial_val'.

For each data frame in the list, I would like to mutate onto it a new field 'cumsum_someval' that starts with the initial_val for the list item and then 'builds it up' by adding the lagged cumsum of initial_val with the rows entry for somenum. E.g. the first row, the value of cumsum_someval will just be the initial_val 1.788348. But the second row of cumsum_someval should be 1.788348 + 0.302 = 2.090348. Then, the 3rd row would be 2.090348 + 0.500 = 2.590348. And so on.

Perhaps something like purrr::map with a custom func along the lines...?

myfun <- function(dta, initial_val) {
  cum_val = initial_val + dta$somenum[<rownumber here>]
}

Open to suggestions.

For each data frame in mylist, how can I build up this new mutated field that starts with initial_val and then progresses to sum the lag of each instance of somenum?

2 Answers

We loop over the list with map, extract the 'dta' dataset, created the new 'cum_val', by taking the lag of 'somenum', specify the default as 'initial_val', do the cumsum, assign the output back to 'dta' and return the whole list element i.e. .x

library(purrr)
library(dplyr)
map(mylist, ~ {
       .x$dta <- .x$dta %>%
         mutate(cum_val = cumsum(lag(somenum, default = .x$initial_val)))
     .x

      })

Although this is not as elegant as Arun's solution, I will dedicate it to him and also to Doug for presenting us with very good challenges every once in a while:

library(purrr)

mylist %>%
  map_dbl("initial_val") %>%
  map2(mylist, function(a, b) {
    b %>% imap(~ if(.y == "dta") {
      .x %>% mutate(cumsum_someval = accumulate(c(a, .x$somenum[-nrow(.x)]), `+`))
    } else {
      .x
    })
  }) %>% `[`(1)

[[1]]
[[1]]$dta
# A tibble: 163 x 12
   carat cut   color clarity depth table price     x     y     z somenum cumsum_someval
   <dbl> <ord> <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>   <dbl>          <dbl>
 1  0.75 Fair  D     SI2      64.6    57  2848  5.74  5.72  3.7   0.0684         0.0391
 2  0.71 Fair  D     VS2      56.9    65  2858  5.89  5.84  3.34  0.436          0.108 
 3  0.9  Fair  D     SI2      66.9    57  2885  6.02  5.9   3.99 -0.0591         0.543 
 4  1    Fair  D     SI2      69.3    58  2974  5.96  5.87  4.1   1.08           0.484 
 5  1.01 Fair  D     SI2      64.6    56  3003  6.31  6.24  4.05 -0.478          1.57  
 6  0.73 Fair  D     VS1      66      54  3047  5.56  5.66  3.7  -0.600          1.09  
 7  0.71 Fair  D     VS2      64.7    58  3077  5.61  5.58  3.62 -0.825          0.487 
 8  0.91 Fair  D     SI2      62.5    66  3079  6.08  6.01  3.78 -1.09          -0.338 
 9  0.9  Fair  D     SI2      65.9    59  3205  6     5.95  3.94 -0.672         -1.42  
10  0.9  Fair  D     SI2      66      58  3205  6     5.97  3.95 -0.273         -2.10  
# ... with 153 more rows

[[1]]$initial_val
[1] 0.03913573
Related