23
$\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$
  • 8
    $\begingroup$ You are looking for Through: Through[{Max, Min, Median, Mean}[y]]. $\endgroup$ – Leonid Shifrin Feb 18 '16 at 22:46
  • 2
    $\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
34
$\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.

| improve this answer | |
$\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
30
$\begingroup$

You can use Through.

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

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

Hope this helps.

| improve this answer | |
$\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
  • 2
    $\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}} *)
| improve this answer | |
$\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.

| improve this answer | |
$\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]]   *)
| improve this answer | |
$\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} *)
| improve this answer | |
$\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
  • $\begingroup$ Why not just stats[x_List] := {Max[x], Min[x], Median[x], Mean[x]} ? $\endgroup$ – PaulCommentary Jul 11 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$ – Eric Towers Jul 12 at 16:02
  • $\begingroup$ Good answer @Er $\endgroup$ – PaulCommentary Jul 12 at 19:15

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.