8
$\begingroup$

Suppose that I have an ordered list of numbers. The numbers in the list may be evenly spaced, but they may not be. Here are two examples, list1 and list2:

list1 = {0, 0.2, 0.4, 0.6, 0.8, 1};
list2 = {0, 0.3, 0.4, 0.7, 0.9, 1};

I would like to write a function to determine the midpoints (or "midvalues"). So, I would like to write fun such that with the following input I will obtain the following output:

fun[list1]
fun[list2]
{0.1, 0.3, 0.5, 0.7, 0.9}
{0.15, 0.35, 0.55, 0.8, 0.95}

Is there any built-in way to do this in Mathematica 8? I am looking for a simple method. I came up with the following, which seems to work fine, but I am afraid I may be overcomplicating it:

midpoint[a_, b_] := (a + b)/2
fun[list_List] := Map[Apply[midpoint, #] &, Partition[list, 2, 1]]

where

fun[list1]
fun[list2]

gives

{0.1, 0.3, 0.5, 0.7, 0.9}
{0.15, 0.35, 0.55, 0.8, 0.95}

Do you have any suggestions to make this simpler?

$\endgroup$
14
$\begingroup$

MovingAverage will do the job.

MovingAverage[list1, 2]

(* {0.1, 0.3, 0.5, 0.7, 0.9} *)

MovingAverage[list2, 2]

(* {0.15, 0.35, 0.55, 0.8, 0.95} *)

$\endgroup$
  • $\begingroup$ Beat me to it. +1 $\endgroup$ – Mr.Wizard Aug 1 '13 at 15:46
7
$\begingroup$

Since I missed posting MovingAverage, here's the manual way:

midpoints = Mean @ {Most[#], Rest[#]} &;

midpoints /@ {list1, list2}
{{0.1, 0.3, 0.5, 0.7, 0.9}, {0.15, 0.35, 0.55, 0.8, 0.95}}

One could also use ListCorrelate which I believe is equivalent to MovingAverage:

ListCorrelate[{0.5, 0.5}, #] & /@ {list1, list2}
{{0.1, 0.3, 0.5, 0.7, 0.9}, {0.15, 0.35, 0.55, 0.8, 0.95}}

In this case the "manual" way is actually faster (v7 timings):

big = Sort @ RandomReal[99, {1500000}];

time = Function[x, First @ Timing @ Do[x, {100}] / 100, HoldAll];

MovingAverage[big, 2]          // time
ListCorrelate[{0.5, 0.5}, big] // time
midpoints[big]                 // time

0.04508

0.04586

0.01529

RK recommended: {0.5, 0.5}.{Most[#], Rest[#]} & and this is faster still:

{0.5, 0.5}.{Most[#], Rest[#]} &@big // time

0.01201


Here are the timings in V9 (Michael E2's machine, different base speed).

MovingAverage[big, 2] // time
midpoints[big] // time
ListCorrelate[{0.5, 0.5}, big] // time

0.02919396

0.04955550

0.02882864

$\endgroup$
  • 1
    $\begingroup$ +1. As a matter of fact, at some point (that was probably for V8) I have implemented several financial indicators and then compared the speed to the corresponding built-in ones. I was surprised to have found that for some of them, my hand-written ones (compiled to C) were an order of magnitude faster than those built-ins. It may have changed by now, I did not check for a long time. $\endgroup$ – Leonid Shifrin Aug 1 '13 at 15:55
  • $\begingroup$ In my v9 MovingAverage is about twice as fast as your midpoints. ListCorrelate is between them. +1 anyway! $\endgroup$ – Michael E2 Aug 1 '13 at 16:02
  • $\begingroup$ It's appears to be useful to be aware of the timing. +1 $\endgroup$ – Suba Thomas Aug 1 '13 at 16:02
  • 2
    $\begingroup$ Here is the order on my computer: ListCorrelate (0.02796875), MovingAverage (0.02906250), midpoints (0.03531250). V9.0.1 Windows 8.1 x64 $\endgroup$ – RunnyKine Aug 1 '13 at 16:13
  • 1
    $\begingroup$ Similar to midpoints would be {.5,.5}.{Most@#,Rest@#}& or .5(Most@#+Rest@#)&. $\endgroup$ – Ray Koopman Aug 1 '13 at 19:59
3
$\begingroup$

Another way:

midpoints[list_] := Median /@ Partition[list, 2, 1]
$\endgroup$
  • $\begingroup$ FYI: Mean appears to be somewhat faster than Median here, at least in v7. $\endgroup$ – Mr.Wizard Aug 1 '13 at 16:11
2
$\begingroup$

First create partitions,

par = Partition[list1, 2, 1]

Than find Median for each sublist as

Table[Median[par[[i]]], {i, 1, Length[par]}] 

gives {0.1, 0.3, 0.5, 0.7, 0.9}

Similarly for second list.

$\endgroup$
  • 2
    $\begingroup$ It is simpler to use /@ as Anon did, but if you prefer Table you can use this shorter form: Table[Median[i], {i, par}]. (Case #2 here). Also I don't know why you are using DeleteDuplicates. $\endgroup$ – Mr.Wizard Aug 1 '13 at 16:44
  • $\begingroup$ @Mr.Wizard: Thanks for pointing out,actually I was trying something else and it appended the list, and I carried on with same list. I have corrected it. $\endgroup$ – Rorschach Aug 1 '13 at 17:12
2
$\begingroup$

In Mathematica 10.2 this can also be achieved with BlockMap:

list1 = {0, 0.2, 0.4, 0.6, 0.8, 1};
list2 = {0, 0.3, 0.4, 0.7, 0.9, 1};

Then:

BlockMap[Mean, list1, 2, 1] (* gives: {0.1, 0.3, 0.5, 0.7, 0.9} *)

And:

BlockMap[Mean, list2, 2, 1] (* gives: {0.15, 0.35, 0.55, 0.8, 0.95} *)
$\endgroup$
  • $\begingroup$ Finally PartitionMap[] is ready for primetime… :) $\endgroup$ – J. M. is away Jul 17 '15 at 0:17
1
$\begingroup$

MovingAverage is probably the way to go. But since Suba already beat me to it. Here is another way using pure functions:

fun[x_List] := (#1 + #2)/2 & @@@ Partition[x, 2, 1]

fun /@ {list1, list2}

{{0.1, 0.3, 0.5, 0.7, 0.9}, {0.15, 0.35, 0.55, 0.8, 0.95}}

$\endgroup$
1
$\begingroup$

out of the box..

Rest@First@ImageData@
       ImageFilter[Mean@First@# &, Image[{#}], {{0, 0}, {1, 0}}]  &@ list1

ugly as it is i suspect it performs pretty well.. <-Edit wrong its really bad..oh well..

$\endgroup$
1
$\begingroup$

Inspired by the 'out of the box' response by george2079, here is another solution that is based on a moving average filter.

ssm = StateSpaceModel[y[k] == (u[k] + u[k + 1])/2, y[k], u[k], y[k], k];

OutputResponse[ssm, list1]
(* {{0.1, 0.3, 0.5, 0.7, 0.9}} *)

OutputResponse[ssm, list2]
(* {{0.15, 0.35, 0.55, 0.8, 0.95}} *)

Although the OP was looking for a simple method, I'm thinking this would add to the variety.

$\endgroup$

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.