7
$\begingroup$

I have found myself recently typing over and over again similar code, namely something that looks like {f[#],g[#]}&@list. Is there a 'better' way to achieve the same result?

I have come up with just this alternative Through[{f,g}[list]].

Can you think of a better way to do something like {First[#],Rest[#]}&@list or {Mean[#],StandardDeviation[#]}&@list?

Also, is the way that Through is used, appropriate?


update: I would like to refrain from using Map, or Apply if that is possible. I just want a 'native' robust way to manipulate lists or parts of lists in 'one pass' so to speak.

update2: I am searching for a better, robust way to apply a series of functions on a list, quickly, reliably and with as little as possible typing involved. I don't have a problem to settle for an answer in the negative, or explore more specialized solutions, see comment on TakeDrop[].

$\endgroup$
6
  • 5
    $\begingroup$ Personally, when I'm in a slot-free mood, I use Through[] a lot. $\endgroup$ Commented Aug 19, 2017 at 19:29
  • 4
    $\begingroup$ Single slot alternative: #@list & /@ {f, g} $\endgroup$
    – Kuba
    Commented Aug 19, 2017 at 19:30
  • 5
    $\begingroup$ With a bit more short hand: Through@{f, g}@list. That is what I mostly use. $\endgroup$
    – Edmund
    Commented Aug 19, 2017 at 19:32
  • 1
    $\begingroup$ It escapes me why Map and Apply are not native methods for manipulating lists. $\endgroup$
    – m_goldberg
    Commented Aug 19, 2017 at 23:32
  • 2
    $\begingroup$ @m_goldberg: I never claimed that Map and Apply are not native methods. What I wrote is that I am looking for ways other than using them to achieve the desired result on the use cases I described. Just trying to improve my coding skills by finding new solutions to old problems. I'm sorry if I was not clear about it. $\endgroup$
    – user42582
    Commented Aug 20, 2017 at 6:44

3 Answers 3

5
$\begingroup$

Take a look at functions such as MinMax and ReIm - both introduced very recently in V10.1. It looks like historical need pushed for their creation. You mentioned constructs:

{First[#],Rest[#]}&@list

and

{Mean[#],StandardDeviation[#]}&@list

which are robust pairs that probably go always together for you. And you implied repetitive usage. In this case why not do define a function or even better a personal package of a set of them as, for instance:

myMeanDev[list_List] := {Mean[list], StandardDeviation[list]}

which reduces your task now, obviously, to just myMeanDev[list]. So as a final bright example, your {First[#],Rest[#]}&@list case is solved as a builtin function

In[1]:= TakeDrop[{a, b, c, d, e}, 1]
Out[1]= {{a}, {b, c, d, e}}
$\endgroup$
1
  • $\begingroup$ thanks for the suggestion; setting up a package might be a good idea-I'm still in the process of figuring out what works and what doesn't;I had no idea about the new functionality you hinted at so thanks for that too; I was hopping there would be a better, robust way to apply a series of functions on a list, quickly, reliably and with as little as possible typing involved; TakeDrop[] is indeed an instance of what I was looking for, albeit too specialized but is indeed one better alternative to what I used, so thanks for that too; $\endgroup$
    – user42582
    Commented Aug 21, 2017 at 6:38
8
$\begingroup$

In version 14.0, Comap was introduced exactly for this purpose:

list = Range[10];

Comap[{Mean, StandardDeviation}, list]
(* {11/2, Sqrt[55/6]} *)

Comap[{Mean, StandardDeviation}][list]
(* {11/2, Sqrt[55/6]} *)

Regarding the performance, it seems that for smaller list sizes, Comap is indeed the slowest, while the operator form of Comap is comparable to Query. Both Through and {Mean[#],StandardDeviation[#]}&@list seem to have the best performance. Note that for larger lists, the discrepancies in timings become negligible.

enter image description here

timings[n_] := With[{list = RandomReal[1, n]}, {
    {n, First@RepeatedTiming@Comap[{Mean, StandardDeviation}, list]},
    {n, First@RepeatedTiming@Comap[{Mean, StandardDeviation}][list]},
    {n, First@RepeatedTiming@Query[{Mean, StandardDeviation}][list]},
    {n, First@RepeatedTiming@Through[{Mean, StandardDeviation}[list]]},
    {n, First@RepeatedTiming@({Mean[#], StandardDeviation[#]} &@list)}
    }];
results = Table[With[{n = Floor[10^i]}, timings[n]], {i, 1, 6, 0.5}];
ListLogLogPlot[Transpose[results], 
 PlotLegends -> {"Comap[{Mean,StandardDeviation},list]", 
   "Comap[{Mean,StandardDeviation}][list]", 
   "Query[{Mean,StandardDeviation}][list]", 
   "Through[{Mean,StandardDeviation}[list]]", 
   "{Mean[#],StandardDeviation[#]}&@list"}, Joined -> True, 
 AxesLabel -> {"List size", "Time [s]"}]
$\endgroup$
4
  • $\begingroup$ This fucntion is superslow comparing with Through $\endgroup$
    – Lacia
    Commented Jun 16 at 13:58
  • $\begingroup$ @Lacia, since Comap is a high-level Mathematica function, it has some noticable overhead coming from the argument checking etc. (observe with Trace) However, the performance strongly depends on the size of the list, and the differences are so drastic for larger lists. $\endgroup$
    – Domen
    Commented Jun 16 at 18:57
  • $\begingroup$ When the function list gets longer like: funList=Array[f,10000]; then comparing Comap[funList][x];//RepeatedTiming with Through[funList[x]];//RepeatedTiming, it seems that Comap is 3 times slower than Through. $\endgroup$
    – Lacia
    Commented Jun 17 at 3:48
  • $\begingroup$ Agreed. I hope Comap can be improved and integrated into the kernel functions of MMA, since it is much more convenient and readable than Through. Map and Comap are just duals - they should be put on the equal footing! $\endgroup$
    – Lacia
    Commented Jun 17 at 3:50
3
$\begingroup$
list = Range[10];

With Query we don't need the hash symbols

Query[{Mean, StandardDeviation}] @ list

{11/2, Sqrt[55/6]}

Query[{First, Rest}] @ list

{1, {2, 3, 4, 5, 6, 7, 8, 9, 10}}

$\endgroup$
2
  • $\begingroup$ This only works on functions that operate on lists. It doesn't work for general functions, e.g., {f, g, h} $\endgroup$
    – Bob Hanlon
    Commented Jun 16 at 19:08
  • $\begingroup$ Thanks - in this case we could use Query[All, {f, g}]@list $\endgroup$
    – eldo
    Commented Jun 16 at 21:59

Your Answer

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

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