How can I separate a sorted array into multiple at points referenced by another array?

1 view (last 30 days)
Say I have:
A = [1 4 7 9];
B = [0 2 3 5 6 8 10 11 12 13];
And I want to separate and organize the arrays as follows:
C{1} = [1 2 3];
C{2} = [4 5 6];
C{3} = [7 8];
C{4} = [9 10 11 12 13];
Any ideas how to efficiently do this?
I've done it with a for loop as such, but it's very slow as the real arrays are much larger:
for i = 1:length(A)-1
idx = A(i) > B & A(i+1)<= B;
C{i} = B(idx)';
end
C{:}
ans = 0×1 empty double column vector ans = 0×1 empty double column vector ans = 0×1 empty double column vector
ans = 1×5
9 10 11 12 13

Accepted Answer

Matt J
Matt J on 30 Jul 2021
This might be what you want:
A = [1 4 7 9];
B = [1 2 3 4 5 6 7 8 9 10 11 12 13];
G=discretize(B,[A,inf]);
C=splitapply(@(x) {x}, B,G);
C{:}
ans = 1×3
1 2 3
ans = 1×3
4 5 6
ans = 1×2
7 8
ans = 1×5
9 10 11 12 13

More Answers (1)

Dave B
Dave B on 30 Jul 2021
Edited: Dave B on 30 Jul 2021
The discretize function (or the third output of thie histcounts function) is good for this kind of problem (it'll do the > and <= for you), you can avoid a loop with arrayfun although I'm not sure it'll really be faster nor easier to read.
I adjusted your data slightly to capture to capture the case where there's a value in B that's bigger than the largest A, and your solution slightly as it didn't produce any results as written.
A = [1 4 7 9 13];
B = [0 2 3 5 6 8 10 11 12 13 15];
% original solution
for i = 1:length(A)-1
idx = B > A(i) & B <= A(i+1);
C{i} = B(idx)';
end
% Alternate solution
%[~,~,ind]=histcounts(B,A); % this returns which 'bin'
ind = discretize(B,A);
C2 = arrayfun(@(x){B(ind==x)'}, 1:numel(A)-1);
isequal(C,C2)
% note that B(1) and B(end) are not in any bin, histcounts reports these as
% B(ind==0), while discretize reports these as B(isnan(ind))
Alternatives to arrayfun here include accumarray/splitapply. The syntax of accumarray is not for the faint of heart, I only mention it because often when doing an operation like this youre next step is to 'do something' with the contents of each group...like take the average etc. So if that's where you're headed next you might consider it. splitapply is a more modern and friendlier version, and (if you're really after a summary statistic) groupsummary may be even easier!
accumarray(ind(ind>0)',B(ind>0)',[],@(x) {x})'
splitapply(@(x) {x},B(ind>0)',ind(ind>0)')'
*edited to use discretize instead of histcounts, I always forget about this very useful function! Credit goes to @Matt J

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

Products


Release

R2018a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!