How to generate a single vector of block-consecutive values from 2 vectors of same size without a loop ?
2 vues (au cours des 30 derniers jours)
Afficher commentaires plus anciens
Hello, I have 2 vectors of same size let's say a = [3 7 19 22] and b = [5 10 20 24]
And from those 2 vectors I want to generate without a loop a single vector:
v = [a(1):b(1) a(2):b(2) ... a(end):b(end)].
so here v=[3 4 5 7 8 9 10 19 20 22 23 24].
I found linspaceNDim(a,b,N) on File Exchange Linearly spaced multidimensional matrix without loop but it doesn't work here as I dont want a fixed number of values between a(i) and b(i), but only block-consecutive values : a(i):b(i). So here N is changing at each index i.
I tried a:.b but this syntax doesn't work on Matlab.
Thanks for you help.
1 commentaire
Amro
le 20 Juin 2014
here is a similar question on Stack Overflow: Vectorized array creation from a list of start/end indices
Réponse acceptée
José-Luis
le 16 Juin 2014
Modifié(e) : José-Luis
le 17 Juin 2014
Any particular reason you don't want to use a loop? This is one of those cases where you should use one, if performance is an issue. Arrayfun() is just syntactic sugar around a loop. Plus the added overheads of invoking cell arrays:
a = [3 7 19 22];
b = [5 10 20 24];
tic
your_mat = cell2mat(arrayfun(@(x,y) x:y,a,b,'uniformoutput',false));
toc
tic
numEl = b - a;
pos = 1;
your_data = NaN * ones(sum(numEl),1);
for ii = 1:numel(numEl)
your_data(pos:pos+numEl(ii)) = a(ii):b(ii);
pos = pos + numEl(ii) + 1;
end
toc
Elapsed time is 0.000602 seconds.
Elapsed time is 0.000038 seconds.
EDIT
I had made a mistake, the pre-allocation should read:
your_data = NaN * ones(sum(numEl) + numel(numEl),1);
EDIT
A bit cleaner:
vals = reshape(sort(randperm(10^6,1000)),2,[]);
a = vals(1,:);
b = vals(2,:);
tic
offset = b - a;
numVal = sum(offset + 1);
pos = 1;
your_data = ones(numVal,1);
for ii = 1:numel(offset)
your_data(pos:pos+offset(ii)) = a(ii):b(ii);
pos = pos + offset(ii) + 1;
end
toc
2 commentaires
José-Luis
le 17 Juin 2014
I had done the pre-allocation wrong. Please replace by:
your_data = ones(b(end),1);
or
your_data = (sum(numEl) + numel(numEl),1);
Plus de réponses (6)
Andrei Bobrov
le 16 Juin 2014
Modifié(e) : Andrei Bobrov
le 17 Juin 2014
v = a(1):b(end);
v = v(any(bsxfun(@ge,v,a.')&bsxfun(@le,v,b.')));
other variant
zo = zeros(b(end) - a(1) + 2,1);
zo(a - a(1) + 1) = 1;
zo(b - a(1) + 2) = -1;
t = cumsum(zo(1:end-1)) > 0;
out = a(1):b(end);
v = out(t);
0 commentaires
Sean de Wolski
le 17 Juin 2014
Modifié(e) : Sean de Wolski
le 17 Juin 2014
There's also FEX:mcolon which does exactly what you're looking for and has a mex implementation that may very well be the fastest.
Azzi Abdelmalek
le 16 Juin 2014
Modifié(e) : Azzi Abdelmalek
le 16 Juin 2014
cell2mat(arrayfun(@(x,y) x:y,a,b,'un',0))
1 commentaire
jyloup p
le 17 Juin 2014
2 commentaires
José-Luis
le 17 Juin 2014
Modifié(e) : José-Luis
le 17 Juin 2014
That's not a fair comparison. If you generate your limits like that, with common intervals, then the for loop produces garbage. Also the pre-allocation becomes meaningless and the output keeps changing size, leading to poor performance.
If the data is set with non-overlapping intervals, like in the original question, then the loop is still faster:
vals = reshape(sort(randperm(10^6,1000)),2,[]);
a = vals(1,:);
b = vals(2,:);
tic
offset = b - a;
numVal = sum(offset + 1);
pos = 1;
your_data = ones(numVal,1);
for ii = 1:numel(offset)
your_data(pos:pos+offset(ii)) = a(ii):b(ii);
pos = pos + offset(ii) + 1;
end
toc
tic
zo = zeros(b(end) - a(1) + 2,1);
zo(a - a(1) + 1) = 1;
zo(b - a(1) + 2) = -1;
t = cumsum(zo(1:end-1)) > 0;
out = a(1):b(end);
v = out(t);
toc
Elapsed time is 0.003596 seconds.
Elapsed time is 0.024514 seconds.
That being said, I am always impressed by Andrei's tricks.
Voir également
Catégories
En savoir plus sur Logical dans Help Center et File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!