# Apply multiple functions to same list

I'd like to get the Min, Max, Median, Mean, etc. for the same list. For now I'm doing the following:

y = {1, 2, 3, 4, 5, 6, 7};
Map[{Max[#] , Min[#] , Median[#], Mean[#]} &, y, {0}]


It seems like there should be a better way, not that this is awful. Is there a cleaner way to do this?

• You are looking for Through: Through[{Max, Min, Median, Mean}[y]]. Feb 18, 2016 at 22:46
• A more restricted case of this question: (3127). More advanced questions possibly of interest: (3858), (11298) Feb 19, 2016 at 18:20

Also,

y = {1, 2, 3, 4, 5, 6, 7};

#[y] & /@ {Max, Min, Median, Mean}

(*  {7, 1, 4, 4}  *)


EDIT: comparing the timings:

n = 100000;

Do[Through[{Max, Min, Median, Mean}[y]], n] // AbsoluteTiming

(*  {0.548089, Null}  *)

Do[#[y] & /@ {Max, Min, Median, Mean}, n] // AbsoluteTiming

(*  {0.709574, Null}  *)


Through is more efficient, at least in this case.

• I use this more often than Through. Feb 19, 2016 at 6:42
• @Mr.Wizard any reasons, or just personal preference? Feb 19, 2016 at 9:33
• It never occurred to me that I could do that with map. I never put the # in place of a function. In retrospect I don't know why. Feb 19, 2016 at 12:32
• @LLlAMnYP I use Map for so much else that it is very familiar, whereas Through still takes a moment of thought. More importantly this works with held arguments, e.g. #[2 + 2] & /@ {Hold, HoldForm, Defer, MakeBoxes}. And as nearly everyone knows I like terse coding and this is a few keystrokes shorter. Feb 19, 2016 at 17:54
• @Mr.W, ahh, how right you are, & has the attribute HoldAll. Convenient. Sometimes, of course, the opposite behavior may be desired. Feb 19, 2016 at 17:57

You can use Through.

Through[{Max, Min, Median, Mean}[y]]

(* {7, 1, 4, 4} *)


Hope this helps.

• I've always had trouble getting the syntax of Through correct, since to me it seems more natural if it were instead Through[{Max, Min, Median, Mean}][y] Feb 19, 2016 at 1:49
• @murray You can use Prefix if it feels more natural: Through@{Max, Min, Median, Mean}[y] Feb 19, 2016 at 3:44
• Thanks! - I had tried through, but I used it wrong. I tried to map or apply it. Feb 19, 2016 at 12:30

Query offers a reasonable syntax for this case:

y = {1, 2, 3, 4, 5, 6, 7};

y // Query[{Max, Min, Median, Mean}]
(* {7, 1, 4, 4} *)


Query has the nice feature that we can apply such lists of functions at deeper levels without too much additional thought:

ys = {{1, 2, 6}, {4, 5, 9}, {10, 20, 60}};

ys // Query[All, {Max, Min, Median, Mean}]
(* {{6, 1, 2, 3}, {9, 4, 5, 6}, {60, 10, 20, 30}} *)

• I did not realize Query could be used like that! +1 of course. However it is not a general replacement for Through as the output is different, e.g. "x" // Query[{Max, Min, Median, Mean}] returns {"x", "x", Missing["Indeterminate"], Missing["Indeterminate"]}, and it is much slower than Through or Map, undoubtely related to (56609) Feb 19, 2016 at 21:05
• @Mr.Wizard Your points are absolutely valid, but as usual you and I have different pain thresholds when labelling something as "much slower" :D Using Normal @ Query[...] will eliminate both the semantic differences and the performance differences since the query will then be compiled down to a wafer-thin wrapper over Through (the canonical answer). But I find myself using Query as-is with increasing frequency these days because its one-stop-shopping syntactic convenience usually overshadows the other considerations (for me, YMMV). Feb 19, 2016 at 23:57

I prefer:

{Max[#], Min[#], Median[#], Mean[#]} & @ y


Clean, simple, and elegant.

murray wrote:

I've always had trouble getting the syntax of Through correct, since to me it seems more natural if it were instead Through[{Max, Min, Median, Mean}][y]

each[x : _[__]][arg__] := Through[ x @ arg ]

foo // each[bar[a, b, c]]

(* out:   bar[a[foo], b[foo], c[foo]]   *)

Sequence[foo, bar] // each[{a, b, c}]

(* out:   {a[foo, bar], b[foo, bar], c[foo, bar]}   *)


I rather like that idea. Thanks, murray.

Comments below Bob Hanlon's answer remind me one thing this lacks as written is the ability to work with held arguments, which #[y] & /@ {f1, f2, . . .} has by nature. If I am going to actually use this abstraction I will need to address that. One possibility:

ClearAll[each]

each[x : _[__]] := Function[, Through @ Unevaluated @ x[##], HoldAll]


Now:

2 + 2 // each[{Hold, HoldForm, Defer, MakeBoxes}]

(* out:   {Hold[2 + 2], 2 + 2, 2 + 2, RowBox[{2, +, 2}]}  *)


Update: also notably this case which is a bit harder to get with Map:

2 + 2 // each[ Hold[foo, bar, baz] ]

(* out:   Hold[foo[2 + 2], bar[2 + 2], baz[2 + 2]]   *)

• I still find the (* ...*) output convention much clearer. Feb 19, 2016 at 13:38
• @Dr.belisarius I honored your opinion in this answer but I cannot promise that I will change my habit. We'll see. Feb 19, 2016 at 18:08

And (so far), no one has suggested the right solution.

If you have a bundle of operations that you want to reuse, define a function.

stats[x_List]:= {Max[#] , Min[#] , Median[#], Mean[#]}& [x]
...

y = {1, 2, 3, 4, 5, 6, 7};
stats[y]
(* {7, 1, 4, 4} *)

• Why not stats[x_List] := #[y] & /@ {Max, Min, Median, Mean}, especially if you want to extend the list of stats? Feb 21, 2016 at 7:12
• @garej: Habit of mind: morphisms go to the left of the objects they act on. (If I were an adherent to certain European algebraist schools, I'd feel the other way about it.) Feb 21, 2016 at 19:19
• Why not just stats[x_List] := {Max[x], Min[x], Median[x], Mean[x]} ? Jul 11, 2020 at 22:01
• @PaulCommentary : Habit of mind: If expression x has side effects or if expression x takes significant resources to compute, repeatedly evaluating it is foolish. To make it very clear what is guarded against, contrast Clear[stats]; stats[x_] := {Max[#], Min[#], Median[#], Mean[#]} &[Activate[x]]; stats[y = 3; Inactive[Table[y++, {3}]]] with Clear[stats]; stats[x_] := {Max[Activate[x]], Min[Activate[x]], Median[Activate[x]], Mean[Activate[x]]}; stats[y = 3; Inactive[Table[y++, {3}]]]. Jul 12, 2020 at 16:02
• Good answer @Er Jul 12, 2020 at 19:15