28
$\begingroup$

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?

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

7 Answers 7

41
$\begingroup$

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.

$\endgroup$
9
  • 4
    $\begingroup$ I use this more often than Through. $\endgroup$
    – Mr.Wizard
    Feb 19, 2016 at 6:42
  • 1
    $\begingroup$ @Mr.Wizard any reasons, or just personal preference? $\endgroup$
    – LLlAMnYP
    Feb 19, 2016 at 9:33
  • $\begingroup$ 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. $\endgroup$ Feb 19, 2016 at 12:32
  • $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Feb 19, 2016 at 17:54
  • $\begingroup$ @Mr.W, ahh, how right you are, & has the attribute HoldAll. Convenient. Sometimes, of course, the opposite behavior may be desired. $\endgroup$
    – LLlAMnYP
    Feb 19, 2016 at 17:57
34
$\begingroup$

You can use Through.

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

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

Hope this helps.

$\endgroup$
3
  • 4
    $\begingroup$ 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] $\endgroup$
    – murray
    Feb 19, 2016 at 1:49
  • 2
    $\begingroup$ @murray You can use Prefix if it feels more natural: Through@{Max, Min, Median, Mean}[y] $\endgroup$
    – Edmund
    Feb 19, 2016 at 3:44
  • $\begingroup$ Thanks! - I had tried through, but I used it wrong. I tried to map or apply it. $\endgroup$ Feb 19, 2016 at 12:30
20
$\begingroup$

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}} *)
$\endgroup$
2
  • 1
    $\begingroup$ 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) $\endgroup$
    – Mr.Wizard
    Feb 19, 2016 at 21:05
  • 2
    $\begingroup$ @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). $\endgroup$
    – WReach
    Feb 19, 2016 at 23:57
15
$\begingroup$

I prefer:

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

Clean, simple, and elegant.

$\endgroup$
13
$\begingroup$

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]]   *)
$\endgroup$
2
  • 1
    $\begingroup$ I still find the (* ...*) output convention much clearer. $\endgroup$ Feb 19, 2016 at 13:38
  • 1
    $\begingroup$ @Dr.belisarius I honored your opinion in this answer but I cannot promise that I will change my habit. We'll see. $\endgroup$
    – Mr.Wizard
    Feb 19, 2016 at 18:08
4
$\begingroup$

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} *)
$\endgroup$
5
  • $\begingroup$ Why not stats[x_List] := #[y] & /@ {Max, Min, Median, Mean}, especially if you want to extend the list of stats? $\endgroup$
    – garej
    Feb 21, 2016 at 7:12
  • $\begingroup$ @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.) $\endgroup$ Feb 21, 2016 at 19:19
  • $\begingroup$ Why not just stats[x_List] := {Max[x], Min[x], Median[x], Mean[x]} ? $\endgroup$ Jul 11, 2020 at 22:01
  • $\begingroup$ @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}]]]. $\endgroup$ Jul 12, 2020 at 16:02
  • $\begingroup$ Good answer @Er $\endgroup$ Jul 12, 2020 at 19:15
3
$\begingroup$

As of 2022 we have at our disposal the resource function called ThroughOperator that can do that. This is a development thanks to @Sjoerd Smit.

It was first suggested here. In the comment section under the answer @Sjoerd Smit gives motivation for its development and subsequent use for those interested. It was further used in this thread.

The way it works for the example at hand is the following:

ResourceFunction["ThroughOperator"][{Max, Min, Median, Mean}]@{1, 2, 
  3, 4, 5, 6, 7}

l

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.