9
$\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$

8 Answers 8

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$
1
  • $\begingroup$ Beat me to it. +1 $\endgroup$
    – Mr.Wizard
    Commented Aug 1, 2013 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$
8
  • 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$ Commented Aug 1, 2013 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
    Commented Aug 1, 2013 at 16:02
  • $\begingroup$ It's appears to be useful to be aware of the timing. +1 $\endgroup$ Commented Aug 1, 2013 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
    Commented Aug 1, 2013 at 16:13
  • 1
    $\begingroup$ Similar to midpoints would be {.5,.5}.{Most@#,Rest@#}& or .5(Most@#+Rest@#)&. $\endgroup$ Commented Aug 1, 2013 at 19:59
3
$\begingroup$

Another way:

midpoints[list_] := Median /@ Partition[list, 2, 1]
$\endgroup$
1
  • $\begingroup$ FYI: Mean appears to be somewhat faster than Median here, at least in v7. $\endgroup$
    – Mr.Wizard
    Commented Aug 1, 2013 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
  • 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
    Commented Aug 1, 2013 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$ Commented Aug 1, 2013 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$
1
  • $\begingroup$ Finally PartitionMap[] is ready for primetime… :) $\endgroup$ Commented Jul 17, 2015 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 and acknowledge you have read our privacy policy.

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