9

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?

Andrew
  • 10,569
  • 5
  • 51
  • 104

8 Answers8

14

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} *)

Suba Thomas
  • 8,716
  • 1
  • 17
  • 32
7

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

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 1
    +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. – Leonid Shifrin Aug 01 '13 at 15:55
  • In my v9 MovingAverage is about twice as fast as your midpoints. ListCorrelate is between them. +1 anyway! – Michael E2 Aug 01 '13 at 16:02
  • It's appears to be useful to be aware of the timing. +1 – Suba Thomas Aug 01 '13 at 16:02
  • @Michael Good to know MovingAverage has been improved. Would you please append v9 timings for the three methods to this answer? – Mr.Wizard Aug 01 '13 at 16:04
  • 2
    Here is the order on my computer: ListCorrelate (0.02796875), MovingAverage (0.02906250), midpoints (0.03531250). V9.0.1 Windows 8.1 x64 – RunnyKine Aug 01 '13 at 16:13
  • @Mr.Wizard Done. Actually every time I run ListCorrelate it gets faster! The above done with a fresh kernel, though. (It started tied with yours -- hmm.) – Michael E2 Aug 01 '13 at 16:14
  • 1
    Similar to midpoints would be {.5,.5}.{Most@#,Rest@#}& or .5(Most@#+Rest@#)&. – Ray Koopman Aug 01 '13 at 19:59
  • @Ray The 0.5(a+b) method was obvious, but I didn't consider Dot, and it's quite fast. I'm adding it to my timings. Thanks! – Mr.Wizard Aug 02 '13 at 02:02
3

Another way:

midpoints[list_] := Median /@ Partition[list, 2, 1]
C. E.
  • 70,533
  • 6
  • 140
  • 264
2

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.

Pankaj Sejwal
  • 2,063
  • 14
  • 23
  • 2
    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. – Mr.Wizard Aug 01 '13 at 16:44
  • @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. – Pankaj Sejwal Aug 01 '13 at 17:12
2

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} *)
Arnoud Buzing
  • 9,801
  • 2
  • 49
  • 58
1

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}}

RunnyKine
  • 33,088
  • 3
  • 109
  • 176
1

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..

george2079
  • 38,913
  • 1
  • 43
  • 110
1

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.

Suba Thomas
  • 8,716
  • 1
  • 17
  • 32