Mathbeginner
Mathbeginner

Reputation: 201

MATLAB: check whether next n entries of vector are less than certain value

I'm working with a large vector which contains the values of a linear combination of multiple sine functions (where the first entry is at a time of 0 miliseconds and the nth entry is at the nth miliseconds). Now I want to extract the pieces of the vector which contain (the same combination of ) sine functions, and store them separately.

Hence I want to check whether 5 entries next to each other equal zero, which means this would be a 'cut' between two separate linear combinations. If there are 5 or more entries next to each other which equal zero, the this is where the cut should be made. As an example the following (short) vector.

Let vector = [ 0 0 0 0 0 0 0 8 9 1 0 0 3 2 0 0 0 0 0 6 6 2 0 0 0 1 ] which should be cut into two separate vectors, namely vec1= [8 9 1 0 0 3 2] and vec2= [6 6 2 0 0 0 1].

I think I could use the built-in find function but I don't think it can look for 'at least' 5 entries next to each other which equal 0, correct?

Upvotes: 0

Views: 85

Answers (2)

obchardon
obchardon

Reputation: 10792

You can use regexp with the split option:

v   = [0 0 0 0 0 0 0 8 90 0 0 0 0 1 0 0 3 2 0 0 0 0 0 6 6 2 0 0 0 1 ];
%     vector to string then extract the subvector
r   = regexp(num2str(v),'(?<![0-9])(0\s+){5,}','split')
%     string to vector
out = cellfun(@str2num,r,'UniformOutput',0)

With this regular expression: (?<![1-9])(0\s+){5,}

(?<![0-9]) : check that a zero is not preceded by a digit, so 90 0 0 0 0 should not split the vector.

(0\s+){5,} : check for, at least, 5 consecutive 0.

Upvotes: 1

Wolfie
Wolfie

Reputation: 30046

Starting with your example...

v = [ 0 0 0 0 0 0 0 8 9 1 0 0 3 2 0 0 0 0 0 6 6 2 0 0 0 1 ];

We could do the following:

% 1. Create an index `idx` which groups elements either as zeros or non-zeros, 
%    with an increasing group number for each subsequent set
idx = cumsum( [1, diff(v==0) ~= 0] );
% = [ 1 1 1 1 1 1 1 2 2 2 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 ];

% 2. Split by this group, regardless of what's in it
grps = splitapply( @(x){x}, v, idx );
% = { {1×7 double}, {1×3 double}, {1×2 double}, {1×2 double}, {1×5 double}, ... }

% 3. Get the indices for groups, incremented when the 5-zeros condition is met
zIdx = cellfun( @(x) x(1) == 0, grps ); % Just alternating 0/1, as groups alternate
idx = cellfun( @numel, grps ) >= 5 & zIdx;
idx = cumsum( idx ) .* ~idx;
% = [ 0 1 1 1 0 2 2 2 ]

% 4. Group everything together
out = arrayfun( @(x) [grps{idx==x}], 1:max(idx), 'uni', 0 );
% { [8 9 1 0 0 3 2], [6 6 2 0 0 0 1] }

I'm not sure this is any quicker than just writing a loop...

Upvotes: 2

Related Questions