21
$\begingroup$

How can I construct a moving maximum function?

For example, if I have a list of 12 values: { 5, 6, 9, 3, 2, 6, 7, 8, 1, 1, 4, 7 } and I want to maximize over 3 values then the expected result would be: { 6, 9, 9, 9, 6, 7, 8, 8, 8, 4, 7, 7 }.

$\endgroup$
4
  • 2
    $\begingroup$ I'm not clear how you got 6 as the first value of your expected result. Shouldn't it be the maximum of {5, 6, 9} which is 9? $\endgroup$ Jan 16, 2014 at 6:26
  • $\begingroup$ I was centering the range on the value, but you could just as well right or left shift it. In other words, in my example, the first value is the maximum of { null, 5, 6 } $\endgroup$ Jan 16, 2014 at 6:28
  • $\begingroup$ @TylerDurden, why not {Null, Null, 6}? $\endgroup$ Sep 28, 2014 at 19:46
  • $\begingroup$ Related: (4061) $\endgroup$
    – Mr.Wizard
    Jan 23, 2015 at 22:41

5 Answers 5

31
$\begingroup$
test = {5, 6, 9, 3, 2, 6, 7, 8, 1, 1, 4, 7}

MaxFilter[test, 1]

(* {6, 9, 9, 9, 6, 7, 8, 8, 8, 4, 7, 7} *)

You can also use

Max /@ Transpose[{Rest[Append[#, 0]], #, Most[Prepend[#, 0]]}] &[yourList]

which is competitive with the MM MaxFilter, but will allow you to change the 'slide' (e.g.pad with zeroes, or other arbitrary 'start').

$\endgroup$
5
  • 1
    $\begingroup$ We've got a winner $\endgroup$
    – Rojo
    Jan 16, 2014 at 7:40
  • 2
    $\begingroup$ +1 This function needs to be in more "See also: " sections in the documentation! $\endgroup$
    – Aky
    Jan 16, 2014 at 7:47
  • $\begingroup$ I was looking for this but there was no link around MovingAverage :( $\endgroup$
    – Kuba
    Jan 16, 2014 at 7:48
  • 4
    $\begingroup$ Yes, WRI could do a better job there. Most of the stuff they call 'image processing' has a myriad of other uses. $\endgroup$
    – ciao
    Jan 16, 2014 at 7:49
  • 2
    $\begingroup$ This MaxFilter method can only handle "windows" of odd-numbered elements, since it's based on a radius, e.g. MaxFilter[test, 1] finds the maximum of 3 elements at a time, MaxFilter[test, 2] finds the maximum of 5 elements at a time, etc. For those wishing to use arbitrary window sizes, see mathematica.stackexchange.com/a/78269/6944, where I've come up with a trick for handling the even cases. $\endgroup$ Mar 26, 2015 at 10:02
15
$\begingroup$

Using the fourth and fifth arguments of Partition gives you exactly what you want

lis = {5, 6, 9, 3, 2, 6, 7, 8, 1, 1, 4, 7}
Max @@@ Partition[lis, 3, 1, {2, 2}, {}]

Gives:

{6, 9, 9, 9, 6, 7, 8, 8, 8, 4, 7, 7}

Update

As Simon Wood suggested in the comment below (I also know this but on my system the difference isn't that much), Maping Max instead of Applying it makes a difference. Also interesting, I don't notice that much difference between Developer`PartitionMapand pure Partition with Map as my timing shows with an even bigger data size, this may be a difference in version (I'm on v. 9.0.1).

Timings

lis2 = RandomInteger[10, 10^7];

(* My Solution updated with Map *)
AbsoluteTiming[Max /@ Partition[lis2, 3, 1, {2, 2}, {}]][[1]]

(* 6.203406 *)

(* My Original Solution using Apply *)

AbsoluteTiming[Max @@@ Partition[lis2, 3, 1, {2, 2}, {}]][[1]]

(* 7.750364 *)

(* Anon's Solution using PartitionMap *)

AbsoluteTiming[Developer`PartitionMap[Max, lis2, 3, 1, {2, 2}, {}]][[1]]

(* 5.675949 *)

(* Kuba's ListConvolve (You can also use ListCorrelate) *)

AbsoluteTiming[ListConvolve[{1, 1, 1}, lis2, {2, -2}, {}, Times, Max]][[1]]

(* 12.078693 *)

(* rasher's winner using MaxFilter *)

AbsoluteTiming[MaxFilter[lis2, 1]][[1]]

(* 0.640655 *)

(* rasher's second equally fast solution using Transpose and co. *)

AbsoluteTiming[Max /@ Transpose[{Rest[Append[#, 0]], #, Most[Prepend[#, 0]]}] &[lis2]][[1]]

(* 0.765662 *)

Clearly, rasher's methods are winners.

$\endgroup$
13
  • $\begingroup$ You're a better partitioner than I sir! $\endgroup$ Jan 16, 2014 at 6:43
  • 1
    $\begingroup$ @CodieCodeMonkey. Thank you sir! Partition is a very powerful too. I spent quite a while exploring how to use it. Sal Mangano also does a good job on his website explaining it. $\endgroup$
    – RunnyKine
    Jan 16, 2014 at 6:47
  • $\begingroup$ As a curiosity I might add that I learned about DeveloperPartitionMap, which is undocumented, that I use from Sal Manganos' Mathematica Cookbook. I've since seen it on here. But he implements moving average, which is the same kind of operation, not with PartitionMap but with ListConvolve` thus making him a possible reference for all three methods hehe :) $\endgroup$
    – C. E.
    Jan 16, 2014 at 7:27
  • $\begingroup$ @Anon, I also learned about Developer`PartitionMap from Sal's book $\endgroup$
    – RunnyKine
    Jan 16, 2014 at 7:29
  • 1
    $\begingroup$ @Anon I used double (``) at the beginning and end $\endgroup$
    – RunnyKine
    Jan 16, 2014 at 7:36
13
$\begingroup$

Another option is Developer`PartitionMap. In RunnyKine's solution we first partition the list and sweep through it to add Max to every element. With Developer`PartitionMap we can do both at the same time, which is faster.

Here's a table for reference. My first table was incorrect and I apologize for that, it was an honest mistake which I am not sure how it happened:

lis = RandomInteger[10, 10^6];

AbsoluteTiming[Developer`PartitionMap[Max, lis, 3, 1, {2, 2}, {}]][[1]]
(* Out: 0.578836 *)

(* RunnyKine's solution: *)
AbsoluteTiming[Max /@ Partition[lis, 3, 1, {2, 2}, {}]][[1]]
(* Out: 0.698822 *)

(* Kuba's solution: *)
AbsoluteTiming[ListConvolve[{1, 1, 1}, lis, {2, -2}, {}, Times, Max]][[1]]
(* Out: 1.294132 *)

Did not see this coming! Rasher's method that he just posted is way faster:

AbsoluteTiming[MaxFilter[lis, 1]][[1]]
(* Out: 0.070911 *)
$\endgroup$
2
  • 1
    $\begingroup$ Faster and less memory hungry $\endgroup$
    – Rojo
    Jan 16, 2014 at 7:20
  • 1
    $\begingroup$ ;-) - one of the seldom used but sometimes really useful functions! $\endgroup$
    – ciao
    Jan 16, 2014 at 7:47
10
$\begingroup$

Just a different method:

lis = {5, 6, 9, 3, 2, 6, 7, 8, 1, 1, 4, 7};

ListConvolve[{1, 1, 1}, lis, {2, -2}, {}, Times, Max]
{6, 9, 9, 9, 6, 7, 8, 8, 8, 4, 7, 7}
$\endgroup$
2
  • 1
    $\begingroup$ A modification: ListConvolve[{1, 1, 1}, li, {2, -2}, -Infinity, #2 &, Max], which unfortunately isn't any faster (as opposed to what I wrote previously in a comment! - I compared with different sizes of data by mistake) $\endgroup$
    – Aky
    Jan 16, 2014 at 7:36
  • $\begingroup$ @Aky Indeed it isn't but I find you comment useful anyway :) $\endgroup$
    – Kuba
    Jan 16, 2014 at 7:37
6
+500
$\begingroup$

Dilation produces the same output as MaxFilter and has comparable speed.

test = {5, 6, 9, 3, 2, 6, 7, 8, 1, 1, 4, 7};

Dilation[test, 1]
(* {6, 9, 9, 9, 6, 7, 8, 8, 8, 4, 7, 7} *)

It also has a Padding option which may be convenient:

Dilation[test, 1, Padding -> 10]
(* {10, 9, 9, 9, 6, 7, 8, 8, 8, 4, 7, 10} *)
Dilation[test, 1, Padding -> "Periodic"]
(* {7, 9, 9, 9, 6, 7, 8, 8, 8, 4, 7, 7} *)

Using RunnyKine's test setup we get speeds comparable to rasher's two methods:

lis2 = RandomInteger[10, 10^7];

AbsoluteTiming[Dilation[lis2, 1]][[1]]
(* 0.754053 *)
AbsoluteTiming[MaxFilter[lis2, 1]][[1]]
(* 0.678272 *)
AbsoluteTiming[Max /@ Transpose[{Rest[Append[#, 0]], #, 
  Most[Prepend[#, 0]]}] &[lis2]][[1]]
(* 0.786325 *)
$\endgroup$
1
  • $\begingroup$ Not quite the same results - you're kind of forced to check ends and pad or jump through other hoops (e.g. where there's a negative on one or both ends)... and as span of what's to be maxed increases, you need to check for more and more cases like that... but neat answer, so +1 and stay tuned for more. $\endgroup$
    – ciao
    Apr 25, 2015 at 22:34

Your Answer

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

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