# Map-Thread-Through-Apply a list of functions onto a list of (lists of) values

I have a list of functions:

fns = {f, g, h}


and a list of triples:

list = {{1,2,3},{11,22,33},{111,222,333},{1111,2222,3333}};


What's the best way to apply f to the first element of every triple, g to the second elements, and h to the last elements?

{
{f[1], g[2], h[3]},
{f[11], g[22], h[33]},
{f[111], g[222], h[333]},
{f[1111], g[2222], h[3333]}
}


(I know a few methods, but I'm looking for more.)

-
Suddenly everybody is using Compose; I think I started a trend. – Mr.Wizard Sep 30 '12 at 6:48
@J.M. I know, which is why I'm amused that three answers below are using it. – Mr.Wizard Sep 30 '12 at 9:50
@Leonid three replies come to mind: (1) You don't think I actually read all that stuff do you? (2) I'm senile and I have no idea why they trust me with the keys. (3) Great minds think alike. -- Take your pick. – Mr.Wizard Sep 30 '12 at 13:42
@WReach, if my opinion counts for anything, I was bummed when they "replaced" Compose[] with Composition[]. Oh well... – J. M. Sep 30 '12 at 14:06
@Mr.Wizard My reasons are completely irrational and focus more on what I consider elegant or interesting approaches rather than absolute speed. So I liked the approaches that used Inner and ListCorrelate over faster ones that used Map. (I should also say that my final code will probably end up using Leonid's double Transpose. And for what it's worth, there was also much up-voting involved on my part prior to finally accepting an answer.) – Brett Champion Oct 4 '12 at 4:21

Inner[#2@#1 &, list, fns, List, 2]


or

MapIndexed[fns[[Last@#2]]@#1 &, list, {2}]


or

ListCorrelate[{fns}, list, {1, -1}, {}, Compose, Sequence]


or

MapThread[Compose, {Array[fns &, Length@list], list}, 2]


or

ReplacePart[list, {i_, j_} :> fns[[j]][list[[i, j]]]]


or

list // Query[All, Thread[Range@Length@fns -> fns]]


or (cheating a little)

list // Query[All, {1 -> f, 2 -> g, 3 -> h}]

-
The Inner approach is the one I'm currently using. – Brett Champion Sep 30 '12 at 1:28
I added another method to make up for it :) – WReach Sep 30 '12 at 1:48
Like J.M. says Compose has been obsolete since more than 20(!) years. – stevenvh Oct 3 '12 at 17:12
@steven, that's nothing; Mathematica 8 still has Release[], and that has been deprecated far longer... – J. M. Oct 3 '12 at 17:16
@J.M. "far longer" seems a bit difficult; according to the documentation center both are obsolete since version 2 (1991), and I can't imagine functions being deprecated starting version 1 :-). – stevenvh Oct 3 '12 at 17:19

Map[MapThread[Compose, {fns, #}] &, list]


or

Transpose@MapThread[Map, {fns, Transpose[list]}]

-
Like J.M. says Compose has been obsolete since more than 20(!) years. – stevenvh Oct 3 '12 at 17:13
@stevenvh And so :-) ? – Leonid Shifrin Oct 3 '12 at 17:15
@steven, it may not exactly be recommended, but it still works. From an old army saw: "if a dumb thing works, then it ain't dumb." – J. M. Oct 3 '12 at 17:23
@Leonid - The documentation center doesn't give any description of it any more, so how can it help OP, when he doesn't know what it does? (You don't explain it either) – stevenvh Oct 4 '12 at 15:53
@stevenvh Fair enough. I will add the description. But I guess the OP would know what it is, in this particular case, given his affiliation :-) – Leonid Shifrin Oct 4 '12 at 15:58

The OP said: "I know a few methods, but I'm looking for more." so here are my offerings for the sake of interest. The second is intentionally a bit convoluted. The third may actually be of interest as the method could be used for in-place modification.

With[{op = MapIndexed[#[Slot @@ #2] &, fns]}, op & @@@ list]

Fold[RotateLeft@MapAt[#2, #, 1] &, list\[Transpose], Function[x, x /@ # &] /@ fns]\[Transpose]

Module[{x = list\[Transpose]}, Table[x[[i]] = fns[[i]] /@ x[[i]], {i, Length@x}]; x\[Transpose]]


Or for in-place modification:

With[{x = list}, Table[x[[All, i]] = fns[[i]] /@ x[[All, i]], {i, Length@First@x}]; x]


This post is primarily to provide the service of comparative timings. I will be using Mathematica 7.

Timings using an array of 1.5 million Integers and three inert symbolic heads:

fns = {f, g, h};
list = RandomInteger[1*^6, {500000, 3}];
times = timeAvg[#[]] & /@ methods;


Using an array of Reals and three trig functions:

fns = {Sin, Cos, Csc};
list = RandomReal[1*^6, {500000, 3}];
times = timeAvg[#[]] & /@ methods;


To explore performance with different shapes here is as above but with 500 random trig functions:

fns = RandomChoice[{Sin, Cos, Sec, Csc, Tan}, 500];
list = RandomReal[1*^6, {5000, 500}];
times = timeAvg[#[]] & /@ methods;


Functions as I named and used them:

SetAttributes[timeAvg, HoldFirst]
timeAvg[func_] :=
Do[If[# > 0.3, Return[#/5^i]] & @@ Timing@Do[func, {5^i}], {i, 0, 15}]

leonid1[] := Map[MapThread[Compose, {fns, #}] &, list]
rm1[]     := Replace[list, x_List :> MapIndexed[fns[[First@#2]]@#1 &, x], {1}]
rm2[]     := MapIndexed[fns[[First@#2]]@#1 &, #] & /@ list
kguler1[] := Inner[#1@#2 &, fns, #, List] & /@ list
kguler2[] := Inner[Compose, fns, #, List] & /@ list
wreach1[] := Inner[#2@#1 &, list, fns, List, 2]
wreach2[] := MapIndexed[fns[[Last@#2]]@#1 &, list, {2}]
wreach3[] := ListCorrelate[{fns}, list, {1, -1}, {}, Compose, Sequence]
wreach4[] := MapThread[Compose, {Array[fns &, Length@list], list}, 2]
wizard1[] := With[{op = MapIndexed[#[Slot @@ #2] &, fns]}, op & @@@ list]
wizard2[] := Fold[RotateLeft@MapAt[#2, #, 1] &, list\[Transpose], Function[x, x /@ # &] /@ fns]\[Transpose]
wizard3[] := Module[{x = list\[Transpose]}, Table[x[[i]] = fns[[i]] /@ x[[i]], {i, Length@x}]; x\[Transpose]]

methods = {leonid1, leonid2, rm1, rm2, kguler1, kguler2, wreach1,
wreach2, wreach3, wreach4, wizard1, wizard2, wizard3};

-
Inner[#1@#2 &, fns, #, List] & /@ list
(*or *)
Inner[Compose, fns, #, List] & /@ list
% //TableForm


-
Like J.M. says Compose has been obsolete since more than 20(!) years. – stevenvh Oct 3 '12 at 17:14
@stevenvh And it still works just fine, thank you. :D – Mr.Wizard Jul 15 '14 at 8:42

Another solution using MapIndexed and —

1. Replace:

Replace[list, x : {_, _, _} :> MapIndexed[fns[[First@#2]]@#1 &, x], {1}]

2. Map:

MapIndexed[fns[[First@#2]]@#1 &, #] & /@ list

-