Vectorizing the Notion of Colon (:) - values betwe

2020-01-26 10:14发布

问题:

I have two vectors, idx1 and idx2, and I want to obtain the values between them. If idx1 and idx2 were numbers and not vectors, I could do that the following way:

idx1=1;
idx2=5;
values=idx1:idx2 

% Result
 % values =
 % 
 %    1     2     3     4     5

But in my case, idx1 and idx2 are vectors of variable length. For example, for length=2:

idx1=[5,9];
idx2=[9 11];

Can I use the colon operator to directly obtain the values in between? This is, something similar to the following:

values = [5     6     7     8     9     9    10    11]

I know I can do idx1(1):idx2(1) and idx1(2):idx2(2), this is, extract the values for each column separately, so if there is no other solution, I can do this with a for-loop, but maybe Matlab can do this more easily.

回答1:

Your sample output is not legal. A matrix cannot have rows of different length. What you can do is create a cell array using arrayfun:

values = arrayfun(@colon, idx1, idx2, 'Uniform', false)

To convert the resulting cell array into a vector, you can use cell2mat:

values = cell2mat(values);

Alternatively, if all vectors in the resulting cell array have the same length, you can construct an output matrix as follows:

values = vertcat(values{:});


回答2:

Try taking the union of the sets. Given the values of idx1 and idx2 you supplied, run

values = union(idx1(1):idx1(2), idx2(1):idx2(2));

Which will yield a vector with the values [5 6 7 8 9 10 11], as desired.



回答3:

I couldn't get @Eitan's solution to work, apparently you need to specify parameters to colon. The small modification that follows got it working on my R2010b version:

step = 1; 
idx1 = [5, 9];
idx2 = [9, 11];
values = arrayfun(@(x,y)colon(x, step, y), idx1, idx2, 'UniformOutput', false);
values=vertcat(cell2mat(values));

Note that step = 1 is actually the default value in colon, and Uniform can be used in place of UniformOutput, but I've included these for the sake of completeness.



回答4:

There is a great blog post by Loren called Vectorizing the Notion of Colon (:). It includes an answer that is about 5 times faster (for large arrays) than using arrayfun or a for-loop and is similar to run-length-decoding:

The idea is to expand the colon sequences out. I know the lengths of each sequence so I know the starting points in the output array. Fill the values after the start values with 1s. Then I figure out how much to jump from the end of one sequence to the beginning of the next one. If there are repeated start values, the jumps might be negative. Once this array is filled, the output is simply the cumulative sum or cumsum of the sequence.

function x = coloncatrld(start, stop)
% COLONCAT Concatenate colon expressions
%    X = COLONCAT(START,STOP) returns a vector containing the values
%    [START(1):STOP(1) START(2):STOP(2) START(END):STOP(END)].

% Based on Peter Acklam's code for run length decoding.
len = stop - start + 1;

% keep only sequences whose length is positive
pos = len > 0;
start = start(pos);
stop = stop(pos);
len = len(pos);
if isempty(len)
    x = [];
    return;
end

% expand out the colon expressions
endlocs = cumsum(len);  
incr = ones(1, endlocs(end));  
jumps = start(2:end) - stop(1:end-1);  
incr(endlocs(1:end-1)+1) = jumps;
incr(1) = start(1);
x = cumsum(incr);