Matlab: dividing a vector into overlapping pieces of a fixed size

I have a vector that I would like to split into overlapping subvectors of size cs at shifts. Imagine an input vector:

 v=[1 2 3 4 5 6 7 8 9 10 11 12 13]; % A=[1:13] 

given a chunksize of 4 ( cs=4 ) and shift 2 ( sh=2 ), the result should look like this:

 [1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9 10] [9 10 11 12] 

note that the input vector is not necessarily divided by chunksize , and therefore some subvections are discarded. Is there any quick way to figure this out without the need for use, for example. a for loop? In a related post, I found how to do this, but when looking at non-overlapping subvectors.

+8
split vector matlab range
source share
5 answers

You can use the bsxfun function as follows:

 v=[1 2 3 4 5 6 7 8 9 10 11 12 13]; % A=[1:13] cs=4; sh=2; A = v(bsxfun(@plus,(1:cs),(0:sh:length(v)-cs)')); 

Here's how it works. bsxfun applies some basic functions on 2 arrays and performs some repmat like if the input data sizes are not suitable. In this case, I generate the indices of the first block and add the offset of each chunck. Since one input is a row vector and the other is a column vector, the result is a matrix. Finally, when indexing a vector with a matrix, the result is a matrix, which is exactly what you expect.

And this one-line (almost) always fun :).

+5
source share

I guess the easiest way is actually a loop. The decision to vectorize may be faster, but if the result is properly pre-distributed, the loop should also run decently.

 v = 1:13 cs = 4; sh = 2; myMat = NaN(floor((numel(v) - cs) / sh) + 1,cs); count = 0; for t = cs:sh:numel(v) count = count+1; myMat(count,:) = v(t-cs+1:t); end 
+2
source share

Do you have a signal processing toolbar? Then the buffer command. First look at the bare output:

 buffer(v, 4, 2) ans = 0 1 3 5 7 9 11 0 2 4 6 8 10 12 1 3 5 7 9 11 13 2 4 6 8 10 12 0 

This is clearly the right idea, with a little tweaking necessary to give you exactly what you want:

 [yz] = buffer(v, 4, 2, 'nodelay'); y.' ans = 1 2 3 4 3 4 5 6 5 6 7 8 7 8 9 10 9 10 11 12 

However, consider leaving vectors in half, as this is better suited to most use cases. For example, the average for each window is simply mean matrix, because the default column is.

+1
source share

You can accomplish this with ndgrid :

 >> v=1:13; cs=4; sh=2; >> [Y,X]=ndgrid(1:(cs-sh):(numel(v)-cs+1),0:cs-1) >> chunks = X+Y chunks = 1 2 3 4 3 4 5 6 5 6 7 8 7 8 9 10 9 10 11 12 

The good thing about the second syntax of the colon operator ( j:i:k ) is that you do not need to calculate k exactly (for example, 1:2:6 gives [1 3 5] ) if you plan to undo additional entries, as in this task. It automatically goes into j+m*i , where m = fix((kj)/i) ;

Various tests:

 >> v=1:14; cs=5; sh=2; % or v=1:15 or v=1:16 >> [Y,X]=ndgrid(1:(cs-sh):(numel(v)-cs+1),0:cs-1); chunks = X+Y chunks = 1 2 3 4 5 4 5 6 7 8 7 8 9 10 11 10 11 12 13 14 

And a new line will be formed using v=1:17 . Does this support all cases as needed?

+1
source share

How about this? First, I generate starting indices based on cs and sh for clipping individual vectors outside the full-sized vector, then I delete all indices for which idx+cs will exceed the length of the vector, and then I cut from the individual subvectors via arrayfun and then transforming them into a matrix :

 v=[1 2 3 4 5 6 7 8 9 10 11 12 13]; % A=[1:13] cs=4; sh=2; idx = 1:(cs-sh):length(v); idx = idx(idx+cs-1 <= length(v)) A = arrayfun(@(i) v(i:(i+cs-1)), idx, 'UniformOutput', false); cell2mat(A') 

eg. for cs=5; sh=3; cs=5; sh=3; this will give:

 idx = 1 3 5 7 ans = 1 2 3 4 5 3 4 5 6 7 5 6 7 8 9 7 8 9 10 11 

Depending on where the cs; sh values ​​come from cs; sh cs; sh , you probably want to introduce simple error checking so that cs > 0; as well as sh < cs . sh < 0 would be theoretically possible if you want to leave some values ​​between them.

EDIT : A very small bug has been fixed, now it should work for different combinations of sh and cs.

0
source share

All Articles