Tom Wenseleers - 3 months ago 23

R Question

I have a list of filtering functions

`f1,f2,f3,f4,....`

`m`

`metaf1, metaf2, metaf3,...`

`f2`

`f3`

`"metafiltering"`

`metafiltering`

EDIT: to give an example, say I have matrix

`m=replicate(10, rnorm(20))`

and filtering functions (these are just examples, obviously mine are more complicated :-) )

`f1=function(m,opt1,opt2) {`

return(m[(m[,2]>opt1)&(m[,1]>opt2),])

}

f2=function(m,opt1) {

return(m[(m[,3]>opt1),])

}

And I have defined the following

`metafiltering`

`metafilterfuncs=list(fun1=f1(opt1=0.1,opt2=0.2),fun2=f2(opt1=0.5))`

class("metafilterfuncs")="metafiltering"

The question I have then is how I could apply the filtering steps of an arbitrary

`metafiltering`

Answer

**pryr** has a function, `compose`

, like what you need, but it doesn't quite cut it. The compose function requires the functions to be given one by one, not in a list, and it cannot take arguments. It's also oddly placed in that package. A similar function can be found in **plyr**, namely *each*. But this function does not apply functions sequentially, but individually and outputs a named vector (list?).

agstudy provided a solution above, but it suffers from a problem: it can only take scalar arguments because it gives the arguments in a named vector. The solution to this is to use a named list instead. So, here's an improved function to replace the one in **pryr**.

```
compose2 = function(x, funcs, args, msg_intermediate = F) {
if (length(funcs) != length(args)) stop("length of functions and arguments must match")
for (i in seq_along(funcs)) {
x = do.call(what = funcs[[i]], args = c(x, args[[i]]))
if ((i != length(funcs)) && msg_intermediate) message(x)
}
x
}
```

`msg_intermediate`

is a nice debugging argument that messages the intermediate results, so one can easier understand what happens.

Test it:

```
adder = function(x, n) x + n
compose2(0,
funcs = list(adder, adder, adder),
args = list(list(n = 1), list(n = 2), list(n = 3)),
msg_intermediate = T
)
```

Outputs:

```
1
3
[1] 6
```

This is what you get when you take 0, then add 1 (=1), then add 2 (=3), then add 3 (=6).

The `args`

argument for compose2 takes a list of lists, so that one can supply non-scalar function arguments. Here's an example:

```
add_div = function(x, n, d) (x + n) / d
compose2(0,
funcs = list(add_div, add_div, add_div),
args = list(list(n = 1, d = 1), list(n = 2, d = 2), list(n = 3, d = 3)),
msg_intermediate = T
)
```

Output:

```
1
1.5
[1] 1.5
```

Which is what you get when you take 0, add 1, divide by 1 (=1), then take 1, add 2 then divide by 2 (=1.5), then take 1.5, add 3 and then divide by 3 (=1.5).