Assume you have an undefined nr of lists as possible arguments for a function, for example the following 3 can be picked (this example is as simple as possible, so vectors are stored in lists):
a <- list(c(1,2,3,4,5))
b <- list(c(3,6,7,2,1))
c <- list(c(3,9,8))
If I want to calculate the intersection of all three lists, this can be done as follows:
Map(intersect,c,Map(intersect,a,b))
# or equivalent:
mapply(intersect,c,mapply(intersect,a,b,SIMPLIFY=F))
# [1] 3
But how can I change the nr of arguments to be undefined? I read about ..., but I cannot get it to work. First idea was to write a function, that can have multiple list arguments defined by ...:
intersectio <- function(...){
Map(function(...){
intersect(...)
})
}
Q: But that doesn't work of course, because intersect must be applied recursively. Is there any way to achieve this in R?
Q2: Here is an updated example with a nested list structure. How can it be done in this case, i.e. intersect every sublist of the parent list with the associated sublist (same index) of the other parent lists?
a <- list(list(c(1,2,3,4,5)),list(c(3,6,7,2,1)),list(c(3,9,8)))
b <- list(list(c(1,2)),list(c(3,6,9,11,12)),list(c(3)))
c <- list(list(c(1,9)),list(c(65,23,12)),list(c(14,15)))
As #Roland suggested, you can use Reduce to solve your problem. In the case of flat lists (as in the first version of the question), you can use the following:
Reduce(intersect, c(a, b, c))
In the case of nested lists (as in the updated question), you can just have to wrap that inside a mapply call:
mapply(function(...) Reduce(intersect, c(...)), a, b, c)
To generalize, you can define a function and then call it with as many arguments as you want.
list_intersect <- function(...){
mapply(function(...) Reduce(intersect, c(...)), ...)
}
list_intersect(a, b, c)
Related
I have a do.call use-case (as result of calling mclapply) simplified to the following:
myfunc <- function(x, y) {
str(x)
#return(x + y)
}
res <- list(list(x=1,y=1),list(x=2,y=2),list(x=3,y=3))
str(res)
do.call(what="myfunc", res)
No matter what I do in myfunc it never works. I'd expect do.call to call myfunc three times and pass the resp. x and y but instead it calls it once with all the data and complains it doesn't know what to do with the remaining arguments ... what's the correct way to implement the what function such that will apply something over each list element as it does with any other base function?
UPDATE What lead to my confusion over the use of do.call is that I always saw its use to "reduce" the results of a collection. What I didn't realize is that the functions being called by do.call do handle list types and reduce scenarios e.g. rbind
lapply handles the looping, do.call applies list elements as arguments. Try this:
lapply(res, function(l) do.call('myfunc', l))
In tidyverse, walk + reduce can use used
library(purrr)
walk(res, reduce, myfunc)
#num 1
#num 2
#num 3
My apologies if this has been answered somewhere else. I've defined two functions in R and then nested them with good results. Now I would like to evaluate these two nested functions by changing a variable in the second function. I've tried creating a list for the changing variable and then using lapply to evaluate each element, but I'm getting an error.
My code looks something like this:
# First function
FirstFun <- function(a, b, c, d) {
answer1 <- (a + b)/(1-(0.2*(c/d))-(0.8*(c/d)^2))
return(answer1)
}
# First function evaluated
FirstFun(13,387,1728,1980)
# Second function
SecondFun <- function(answer1,c,d) {
answer2 <- answer1*(1-(0.2*(c/d))-(0.8*(c/d)^2))
return(answer2)
}
# Nested function evaluated
SecondFun(FirstFun(13,387,1728,1980),1728,1980)
# Nested function evaluated with elements of a list
c <- list(0:1980)
lapply(c, SecondFun(FirstFun(13,387,1728,1980),c,1980))
if I under stand you correctly - you are looking for :
SecondFun(FirstFun(13,387,1728,1980),0:1980,1980)
or maybe this :
SecondFun(FirstFun(13,387,1728,0:1980),0:1980,1980)
both return a numeric vector of length 1981.
2 things -
1. no need for a list. a range would work.
2. calling a variable 'c' is a bad idea..... c is reserved
I have two lists of functions that I try to combine to one list of functions (one nested in the other).
funlist=list()
funlist[[1]] <- function(x1){2*x1}
funlist[[2]] <- function(x2){3*x2}
anotherlist=list()
anotherlist[[1]]=function(y1){0.5*y1}
anotherlist[[2]]=function(y2){0.7*y2}
The lists will have the same length. Desired outcome is another function list:
finalfun=list()
finalfun[[1]] which shall be the same as funlist[[1]](anotherlist[[1]])
...
Hence, if I called
finalfun[[1]](6), I would get the result "6" [2*(0.5*6)], similar to calling:
funlist[[1]](anotherlist[[1]](6))
I tried the following to create a function that nests two input functions:
nestfun <- function(infun,outfun) {lapply(1:length(infun),function(i){outfun[[i]](infun[[i]])})}
test=nestfun(infun=anotherlist, outfun=funlist)
Which produced an error:
Error in 2 * x1 : non-numeric argument to binary operator
Called from: lapply(1:length(infun), function(i) {
outfun[[i]](infun[[i]])
})
What am I overlooking?
This should work:
fnest<-function(f1,f2){
force(f1)
force(f2)
nest<-function(x) f1(f2(x))}
finalfun<-list()
finalfun<-lapply(1:length(funlist), function (i) fnest(funlist[[i]], anotherlist[[i]]) )
finalfun[[2]](10)
#21
Note that finalfun is a list of closures. The function fnest takes two functions as inputs and returns a composite function (a closure).
It's now trivial to create a function that operates on your two function lists:
nestfun<-function(funlist,anotherlist){
fnest<-function(f1,f2){
force(f1)
force(f2)
nest<-function(x) f1(f2(x))}
finalfun<-list()
finalfun<-lapply(1:length(funlist), function (i) fnest(funlist[[i]], anotherlist[[i]]) ) }
finalfun<-list()
finalfun<-nestfun(funlist,anotherlist)
EDIT: In case people are curious about the use of force(), check out this question on lazy evaluation: Explain a lazy evaluation quirk
Here is an approach that uses Compose from the functional package. Note, due to lazy evaluation you need to force the arguments
library(functional)
Composed <- Map(function(x, y) {
x <- force(x)
y <- force(y)
Compose(y, x)},
funlist,anotherlist)
# A Couple of examples
Composed[[1]](6)
# [1] 6
Composed[[2]](6)
# [1] 12.6
I have a sublist of principal component rotation vectors computed by prcomp, where each list item is an Nx2 array (i.e., two column vectors), for each class.
Using those vectors, I'd like to project some data similarly structured into a list of classes, each class item containing arrays with dimension NxMxT, where T is the number of trials.
My problem is, I can write simple vectorized functions with apply and its variants, but I'm having trouble generalizing this to apply that over each list.
Example data:
somedata <- list(array(rnorm(100),dim=c(5,4,5)),array(rnorm(100),dim=c(5,4,5)))
somevectors <- list(array(rnorm(10),dim=c(5,2)),array(rnorm(10),dim=c(5,2)))
Here is a simple example of the operation over each list element:
o.proj.1 <- apply(somedata[[1]],3,function(x){
t(somevectors[[1]]) %*% x
}) # returns an array where each projected trial is a column
I tried fitting this inside a call to lapply(), but didn't find much success:
lapply(somedata, y = somevectors, function(x,y){
apply(x,3,function(z){
t(y) %*% z
})
})
Error in t(y) %*% z : requires numeric/complex matrix/vector arguments
Basically my algorithm is to put the appropriate apply type (here lapply) around the more local function and remove the index that will be vectorized (here [[]]). What am I missing?
Of the *apply family of functions, mapply is the one to use when you want to loop simultaneously over two or more objects. Try:
o.proj <- mapply(function(x,y){
apply(x,3,function(z){
t(y) %*% z
})
}, somedata, somevectors, SIMPLIFY = FALSE)
I suppose you will want to use SIMPLIFY = FALSE to return a list, otherwise mapply will attempt to simplify your output into an array, a little like sapply does.
Also know that you can use Map as a shortcut for mapply(..., SIMPLIFY = FALSE).
As a guideline I prefer apply functions on elements of a list using lapply or *ply (from plyr) rather than explicitly iterating through them. However, this works well when I have to process one list at a time. When the function takes multiple arguments, I usually do a cycle.
I was wondering if it's possible to have a cleaner construct, still functional in nature. One possible approach could be to define a function similar to Python, zip(x,y), which takes the input lists, and returns a list, whose i-th element is list(x, y), and then apply the function to this list. But my question is whether I am using the cleanest approach or not. I am not worried about performance optimization, but rather clarity/elegance.
Below is the naive example.
A <- as.list(0:9)
B <- as.list(0:9)
f <- function(x, y) x^2+y
OUT <- list()
for (n in 1:10) OUT[[n]] <- f(A[[n]], B[[n]])
OUT
[[1]]
[1] 0
[[2]]
[1] 2
...
And here is the zipped example (which could be extended to arbitrary arguments):
zip <- function(x, y){
stopifnot(length(x)==length(y))
z <- list()
for (i in seq_along(x)){
z[[i]] <- list(x[[i]], y[[i]])
}
z
}
E <- zip(A, B)
lapply(E, function(x) f(x[[1]], x[[2]]))
[[1]]
[1] 0
[[2]]
[1] 2
...
I think you're looking for mapply:
‘mapply’ is a multivariate version of ‘sapply’. ‘mapply’ applies
‘FUN’ to the first elements of each ... argument, the second
elements, the third elements, and so on. Arguments are recycled
if necessary.
For your example, use mapply(f, A, B)
I came across a similar problem today. And after learning the usage of the func mapply, I know how to solve it now.
mapply is so cool!!
Here is an examples:
en = c("cattle", "chicken", "pig")
zh = c("牛", "鸡", "猪")
dict <- new.env(hash = TRUE)
Add <- function(key, val) dict[[key]] <- val
mapply(Add, en, zh)
## cattle chicken pig
## "牛" "鸡" "猪"
I think you could do this with what I call an 'implicit loop' (this name does not hit it fully, but whatever), taking into account that you can loop over vectors within *apply:
OUT <- lapply(1:10, function(x) (A[[x]]^2 + B[[x]]))
or
OUT <- lapply(1:10, function(x) f(A[[x]], B[[x]]))
Note that you then could also use vapply (or 'sapply`) for output managing (i.e. if you don't want a list).
(by the way, I am not getting what you want with the zip function, so I am sorry, if I missed your point.)