1

I need to take the max of a subset of a vector and then shift through the vector. For instance the column vector

a=[1;2;3;4;5;6;7]

how can I take max(a(1:3)), max(a(2:4)), ..., max(a(5:end)) and place all the output in another vector? I can easily do this with a for loop however I'm looking for an elegant way of doing so using matrix operations in MATLAB and preferably in a single line of code (even though I realize that the matrix operations in MATLAB to do so may likely be using a for loop to implement).

Thank you!

Rody Oldenhuis
  • 37,726
  • 7
  • 50
  • 96
John Councill
  • 67
  • 1
  • 4

5 Answers5

2

Assuming that your shift and window length is constant (1 and 3 in your case resp.), you can use nlfilter to define a general sliding-window operations on a vector:

a = [1:7];

fun = @(x) max(x(:));
b = nlfilter(a, [1 4], fun);
gevang
  • 4,994
  • 25
  • 33
  • 1
    This is indeed exactly what the OP wants, however, it's not elementary Matlab -- to use `nlfilter`, you need the image processing toolbox. – Rody Oldenhuis Oct 13 '12 at 10:22
  • Thanks for the help. Based on the literature on nlfilter, why do we use [1 4] as the block instead of [1 3]? It seems that [1 3] inclusive would be more appropriate. – John Councill Oct 13 '12 at 15:47
1

For the example you provide:

b = max([a(1:end-2) a(2:end-1) a(3:end)], [], 2)

I feel there is also a much more elegant, more general accumarray solution, although I don't have time right now to conjure one up :)

Rody Oldenhuis
  • 37,726
  • 7
  • 50
  • 96
1

Here is my attempt. First, define the 'window' length (3 in your case). Next, use bsxfun to create indices into your a vector for every window. The last step is a simple max function call:

flen = 3;
idx = bsxfun(@plus, [0:flen-1]', 1:numel(a)-flen+1);
max(a(idx))

It is general, i.e., you can change the window size.

It is basic matlab and should be faster than the nlfilter, although I can not verify it right now.

Edit Here is a brief performance comparison of the above bsxfun solution and the proposedim2col:

a = rand(10^7,1);

tic;
idx = bsxfun(@plus, [0:2]', 1:numel(a)-2);
m1 = max(a(idx));
toc

tic;
m2 = max(im2col(a,[3 1],'sliding'));
toc;

isequal(m1, m2)

Elapsed time is 0.839869 seconds.
Elapsed time is 1.797665 seconds.

ans =

 1

bsxfun of course works, and is more than twice faster.

angainor
  • 11,760
  • 2
  • 36
  • 56
0

If you have the Signal Processing Toolbox, you could use the buffer function. It takes a vector and produces a matrix where each column contains the vector's values within a sliding window.

In your case:

>> a = (1:7)';
>> buffer(a,3,2,'nodelay')

ans =

     1     2     3     4     5
     2     3     4     5     6
     3     4     5     6     7

Then, just apply MATLAB's max function to compute the maximum of each column. In conclusion, with a single line of code,

>> max(buffer(a,3,2,'nodelay'))

ans =

     3     4     5     6     7
avivr
  • 1,393
  • 15
  • 28
-1

Thanks for all the help. This gave me some ideas, especially the suggestion to use nlfitler. It turns out that nlfitler calls im2col and taking the max of this would work for me. Especially since the sample vector presented is a simplification of the real vector which contains random variables so the bsxfun option present would not work.

max(im2col(a,[3 1],'sliding'))

John Councill
  • 67
  • 1
  • 4
  • Why would `bsxfun` not work? It does not care about the values you store in the vector. It operates on indices of a vector, which are always integers. Did you even try to run it and see what it does? In particular, did you look at `a(idx)` and compare it to the output of `im2col`? – angainor Oct 13 '12 at 18:55