Get arguments supplied to a purrr::pmap call - r

I'm running a function with many arguments and I'm exploring how changes in some arguments affect the output of the function. I'm doing that through purrr::pmap. I'd like to keep track of the arguments used for each function call: I'd like the function to return its output, as well as a named list of the values of all the arguments used.
Here's a MWE:
f <- function (a, b, c) a + b + c
a_values <- 1:5
effect_of_a <- pmap(list(a = a_values), f, b = 0, c = 0)
I'd like effect_of_a to be a list of list, where each sublist contains not only the result f(a,b,c), but also the values of a, b and c used. I could code that list manually, but I have many arguments and they may change. So is there a way to capture the list of arguments and their values in a function call initiated by purrr:pmap?

You could use with :
pmap(list(a = a_values), ~with(list(...), list(f = f(...),args = list(...))),
b = 0,
c = 0)
[[1]]
[[1]]$f
[1] 1
[[1]]$args
[[1]]$args$a
[1] 1
[[1]]$args$b
[1] 0
[[1]]$args$c
[1] 0
...

Here is a general solution where the arguments are captured in an extra function f_2, which is called with pmap instead of f:
library(purrr)
f <- function (a, b, c) a + b + c
f_2 <- function(a, b, c) {
list(result = f(a, b, c),
args = c(as.list(environment())))
}
a_values <- 1:3
pmap(list(a = a_values), f_2, b = 0, c = 0)
[[1]]
[[1]]$result
[1] 1
[[1]]$args
[[1]]$args$a
[1] 1
[[1]]$args$b
[1] 0
[[1]]$args$c
[1] 0
[[2]]
[[2]]$result
[1] 2
[[2]]$args
[[2]]$args$a
[1] 2
[[2]]$args$b
[1] 0
[[2]]$args$c
[1] 0
[[3]]
[[3]]$result
[1] 3
[[3]]$args
[[3]]$args$a
[1] 3
[[3]]$args$b
[1] 0
[[3]]$args$c
[1] 0

For a list of lists, you could also try something like:
effect_of_a <- map(a_values %>% as.list, function(a, b = 0, c = 0) {
list(f_eval = f(a, b, c),
a = a,
b = b,
c = c)
})
If the values for b & c are changing too, you could try:
b_values <- 2:6
c_values <- 3:7
effect_of_a <- list(a_values, b_values, c_values) %>%
map(~.x %>% as.list) %>%
pmap(~list(f_eval = f(..1, ..2, ..3),
a = ..1,
b = ..2,
c = ..3))

Related

Creating all multi indices of given length

Given an integer K and a dimension d, how can I get all multi-indices alpha with length(alpha) = d and sum(alpha) = K in R?
Example: For K=3 and d=2 and if we organize the multi-indices in a list alphas we would get
alphas[[1]] = c(3,0)
alphas[[2]] = c(2,1)
alphas[[3]] = c(1,2)
alphas[[4]] = c(0,3)
Based on the description, we may use expand.grid on the sequence from '0' to 'k', after replicating the list 'd' times, then Filter only the rows having the sum as 'k'
f1 <- function(k, d) {
lapply(Filter(function(x) sum(x) == k,
asplit(expand.grid(rep(list(0:k), d)), 1)), unname)
}
-testing
> f1(3, 2)
[[1]]
[1] 3 0
[[2]]
[1] 2 1
[[3]]
[1] 1 2
[[4]]
[1] 0 3
Or slightly more faster would be to filter with rowSums
d1 <- expand.grid(rep(list(0:3), 2))
asplit(unname(d1)[rowSums(d1) == 3,], 1)
There is also a constraints based combinations functions in RcppAlgos
f2 <- function(k, d) {
out <- RcppAlgos::comboGeneral(0:k, d, constraintFun = "sum",
comparisonFun = "==", limitConstraints = k)
asplit(rbind(out, out[, ncol(out):1]), 1)
}
-testing
> f2(3, 2)
[[1]]
[1] 0 3
[[2]]
[1] 1 2
[[3]]
[1] 3 0
[[4]]
[1] 2 1
Or as #JosephWood mentioned permuteGeneral would be more adequate compared to comboGeneral
k <- 3
d <- 2
permuteGeneral(0:k, d, TRUE, constraintFun = "sum",
comparisonFun = "==", limitConstraints = k)
Or with compositions
library(partitions)
asplit(compositions(k, d), 2)

An R function to return TRUE if the length of 3 objects is the same

I was wondering how I could test to see if the length of 3 R objects are the same or not?
Here is a simple example and what I have tried with no success:
a = c(2, 3) ; b = 2 ; c = "hi"
is.df = function(x, y, z) length(x) != length(y) != length(z) ## gives error
foo = function(...){
length(unique(lengths(list(...)))) == 1
}
foo(a, b, c)
#[1] FALSE

Using list elements in its' definition

I am having a relatively simple problem with R, which I hope we could find a solution to.
My aim is to define a following list, in which the c element should be the sum of a and b elements defined previously:
ex.list = list(
a = 1,
b = 2,
c = a+b
)
Code throws an error (Error: object 'a' not found), indicating that we cannot use the a and b elements defined just above.
Of course we can simply count the sum out of list definition
ex.list = list(
a = 1,
b = 2
)
ex.list$c = ex.list$a + ex.list$b
Or use another elements in creating the list
a.ex = 1
b.ex = 2
ex.list = list(
a = a.ex,
b = b.ex,
c = a.ex+b.ex
)
Unfortunately, I am not interested in the above solutions. Is there any way to do the sum in the list definition?
You can write your own list function that does lazy evaluation:
lazyList <- function(...) {
tmp <- match.call(expand.dots = FALSE)$`...`
lapply(tmp, eval, envir = tmp)
}
lazyList(
a = 1,
b = 2,
c = a+b
)
#$a
#[1] 1
#
#$b
#[1] 2
#
#$c
#[1] 3
However, obviously, the following is not possible with lazy evaluation:
lazyList(
a = 1,
b = 2,
d = c * a,
c = a+b
)
No, you can't do that. But you can do mad things like this:
> (function(a,b,c=a+b){list(a=a,b=b,c=c)})(11,22)
$a
[1] 11
$b
[1] 22
$c
[1] 33
But really, if you have a list you wish to construct in a particular way, write a function to do it. Its not difficult.

difference between plyr::mutate and dplyr::mutate

dplyr::mutate() works the same way as plyr::mutate() and similarly to base::transform(). The key difference between mutate() and transform() is that mutate allows you to refer to columns that you just created. - Introduction to dplyr
There are some differences between the mutate function in dplyr and plyr. The main difference is of course that plyr::mutate can be applied to lists and dplyr::mutate is faster.
Moreover, when referring to the just created columns, plyr cannot reassign them again, but dplyr does.
# creating a temporary variable and removing it later
plyr::mutate(data.frame(a = 2), tmp = a, c = a*tmp, tmp = NULL)
## a tmp c
## 1 2 2 4
dplyr::mutate(data.frame(a = 2), tmp = a, c = a*tmp, tmp = NULL)
## a c
## 1 2 4
# creating a temporery variable and changing it later
plyr::mutate(data.frame(a = 2), b = a, c = a*b, b = 1)
## a b c
## 1 2 2 4
dplyr::mutate(data.frame(a = 2), b = a, c = a*b, b = 1)
## a b c
## 1 2 1 4
Now I am looking for the functionality of the dplyr mutate function for list objects. So I am looking for a function that mutates a list and can reassign just created variables.
plyr::mutate(list(a = 2), b = a, c = a*b, b = 1)
## $a
## [1] 2
##
## $b
## [1] 2
##
## $c
## [1] 4
dplyr::mutate(list(a = 2), b = a, c = a*b, b = 1)
## Error in UseMethod("mutate_") :
## no applicable method for 'mutate_' applied to an object of class "list"
desired_mutate(list(a = 2), b = a, c = a*b, b = 1)
## $a
## [1] 2
##
## $b
## [1] 1
##
## $c
## [1] 4
I realize that in this simple case, I can just use
plyr::mutate(list(a = 2), c = {b = a; a*b})
But in my actual use case, I assign random numbers to a temporary variable and would like to remove it afterwards. Something like the following:
desired_mutate(list(a = c(1, 2, 5, 2)),
tmp = runif(length(a)),
b = tmp * a,
c = tmp + a,
tmp = NULL)
Corrected original for loop in mutate function (using cols position instead of names):
desired_mutate <- function (.data, ...)
{
stopifnot(is.data.frame(.data) || is.list(.data) || is.environment(.data))
cols <- as.list(substitute(list(...))[-1])
cols <- cols[names(cols) != ""]
col_names <- names(cols)
for (i in seq_along(col_names) ) {
if(!is.null(cols[[i]])) {
.data[[col_names[i]]] <- eval(cols[[i]], .data, parent.frame())
} else {
.data[[col_names[i]]] <- NULL
}
}
.data
}
Test:
> str( desired_mutate(list(a = c(1, 2, 5, 2)),
+ tmp = runif(length(a)),
+ b = tmp * a,
+ c = tmp + a,
+ tmp = NULL) )
List of 3
$ a: num [1:4] 1 2 5 2
$ b: num [1:4] 0.351 1.399 3.096 1.4
$ c: num [1:4] 1.35 2.7 5.62 2.7

Find the indices of an element in a nested list?

I have a list like:
mylist <- list(a = 1, b = list(A = 1, B = 2), c = list(C = 1, D = 3))
is there an (loop-free) way to identify the positions of the elements, e.g. if I want to replace a values of "C" with 5, and it does not matter where the element "C" is found, can I do something like:
Aindex <- find_index("A", mylist)
mylist[Aindex] <- 5
I have tried grepl, and in the current example, the following will work:
mylist[grepl("C", mylist)][[1]][["C"]]
but this requires an assumption of the nesting level.
The reason that I ask is that I have a deep list of parameter values, and a named vector of replacement values, and I want to do something like
replacements <- c(a = 1, C = 5)
for(i in names(replacements)){
indx <- find_index(i, mylist)
mylist[indx] <- replacements[i]
}
this is an adaptation to my previous question, update a node (of unknown depth) using xpath in R?, using R lists instead of XML
One method is to use unlist and relist.
mylist <- list(a = 1, b = list(A = 1, B = 2), c = list(C = 1, D = 3))
tmp <- as.relistable(mylist)
tmp <- unlist(tmp)
tmp[grep("(^|.)C$",names(tmp))] <- 5
tmp <- relist(tmp)
Because list names from unlist are concatenated with a ., you'll need to be careful with grep and how your parameters are named. If there is not a . in any of your list names, this should be fine. Otherwise, names like list(.C = 1) will fall into the pattern and be replaced.
Based on this question, you could try it recursively like this:
find_and_replace <- function(x, find, replace){
if(is.list(x)){
n <- names(x) == find
x[n] <- replace
lapply(x, find_and_replace, find=find, replace=replace)
}else{
x
}
}
Testing in a deeper mylist:
mylist <- list(a = 1, b = list(A = 1, B = 2), c = list(C = 1, D = 3, d = list(C=10, D=55)))
find_and_replace(mylist, "C", 5)
$a
[1] 1
$b
$b$A
[1] 1
$b$B
[1] 2
$c
$c$C ### it worked
[1] 5
$c$D
[1] 3
$c$d
$c$d$C ### it worked
[1] 5
$c$d$D
[1] 55
This can now also be done using rrapply in the rrapply-package (an extended version of base rapply). To return the position of an element in the nested list based on its name, we can use the special arguments .xpos and .xname. For instance, to look up the position of the element with name "C":
library(rrapply)
mylist <- list(a = 1, b = list(A = 1, B = 2), c = list(C = 1, D = 3))
## get position C-node
(Cindex <- rrapply(mylist, condition = function(x, .xname) .xname == "C", f = function(x, .xpos) .xpos, how = "unlist"))
#> c.C1 c.C2
#> 3 1
We could then update its value in the nested list with:
## update value C-node
mylist[[Cindex]] <- 5
The two steps can also be combined directly in the call to rrapply:
rrapply(mylist, condition = function(x, .xname) .xname == "C", f = function(x) 5, how = "replace")
#> $a
#> [1] 1
#>
#> $b
#> $b$A
#> [1] 1
#>
#> $b$B
#> [1] 2
#>
#>
#> $c
#> $c$C
#> [1] 5
#>
#> $c$D
#> [1] 3

Resources