understanding purrr::accumulate in R and the paste function

53 Views Asked by At

I'm trying to understand the accumulate function with the two below examples.

I'm wondering if anyone can help me understand why the accumulate function returns different outputs even though the functions main arguments are same, only difference is how the function is set up in accumulate function

I believe I understand the first example, however I am confused why the second example doesn't produce either "Z" "ZZ" "ZZZ" "ZZZZ" "ZZZZZ" "ZZZZZZ" or return the same output as the first example?

library(tidyverse)


## this function starts with the .init argument
## and passes it through to paste. Then output of that ## initial paste is passed again through paste with the 
## first letter from the letters set. This is repeated 
## until we get to the end of the variables in .x args

accumulate(
  .x = letters[1:5]
  ,.f=paste
  ,.init = "Z"
  
)


## this time the .f set up to take an input .x 
## which starts the same (eg. the .init arg is 
## passed though to paste and returns .init) however
## this time the no other characters are passed through
## the .init arg is just repeated with no additional
## concatenation (eg. zzzz) and this repeat for the length of the vector in .x
## why doesn't this concatenate additional characters like the first example?

accumulate(
  .x = letters[1:5]
  ,.f=~paste(.x)
  ,.init = "Z"
  
)


1

There are 1 best solutions below

3
On BEST ANSWER

.f in accumulate() is a 2-argument function, but in your 2nd example you only operate with the first arg. That 2-arg formula, ~paste(.x), can be re-written as:

accu_fun <- function (.x, .y) {
  paste(.x)
}
# test:
accu_fun("foo", "bar")
#> [1] "foo"

# with accumulate:
purrr::accumulate(
  .x = letters[1:5]
  ,.f = accu_fun
  ,.init = "Z"
  
)
#> [1] "Z" "Z" "Z" "Z" "Z" "Z"

To match the 1st example, you'd need to pass both arguments to paste():

accumulate(letters[1:5], ~paste(.x, .y), .init = "Z")
#> [1] "Z"           "Z a"         "Z a b"       "Z a b c"     "Z a b c d"  
#> [6] "Z a b c d e"

Note that purrr documentation recommends using anonymous function (shorthand) , i.e. \(x, y) paste(x, y), over formula notation, ~ paste(.x, .y), unless backward compatibility with older R versions is required. It's not explicitly stated in ?purrr::accumulate, but take a look at ?purrr::map:

A function, specified in one of the following ways:

  • A named function, e.g. mean.

  • An anonymous function, e.g. ⁠\(x) x + 1⁠ or function(x) x + 1.

  • A formula, e.g. ~ .x + 1. You must use .x to refer to the first argument. Only recommended if you require backward compatibility with older versions of R.

And for multi-arg functions, I personally find it to be easier to read and interpret too:

accumulate(letters[1:5], \(x, y) paste(x, y), .init = "Z")
#> [1] "Z"           "Z a"         "Z a b"       "Z a b c"     "Z a b c d"  
#> [6] "Z a b c d e"