22
$\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$
  • 7
    $\begingroup$ You are looking for Through: Through[{Max, Min, Median, Mean}[y]]. $\endgroup$ – Leonid Shifrin Feb 18 '16 at 22:46
  • 1
    $\begingroup$ A more restricted case of this question: (3127). More advanced questions possibly of interest: (3858), (11298) $\endgroup$ – Mr.Wizard Feb 19 '16 at 18:20
33
$\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$
  • 4
    $\begingroup$ I use this more often than Through. $\endgroup$ – Mr.Wizard Feb 19 '16 at 6:42
  • 1
    $\begingroup$ @Mr.Wizard any reasons, or just personal preference? $\endgroup$ – LLlAMnYP Feb 19 '16 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$ – Mitchell Kaplan Feb 19 '16 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 '16 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 '16 at 17:57
29
$\begingroup$

You can use Through.

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

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

Hope this helps.

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

I prefer:

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

Clean, simple, and elegant.

$\endgroup$
11
$\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$
  • 1
    $\begingroup$ I still find the (* ...*) output convention much clearer. $\endgroup$ – Dr. belisarius Feb 19 '16 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 '16 at 18:08
2
$\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$
  • $\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 '16 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$ – Eric Towers Feb 21 '16 at 19:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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