Filter löschen
Filter löschen

Extract indices of two related arrays based on uniqueness and minimum WITHOUT FOR LOOP

2 Ansichten (letzte 30 Tage)
I have two arrays, A and B, whose indices relate to inputs of an analysis (i.e. A(1) and B(1) are two tracked outputs of run 1 of a simulation with distinct inputs, A(7) and B(7) correspond to the 7th run, whose inputs vary from all other runs).
A B
0.5 1
0.75 1
0.75 3
1 7
0.5 4
0.75 9
1 2
1 6
0.5 1
I am trying to create a vector of indices which tracks all minimum values of B for each unique value of A So from the above example the desired output is
index
1
2
8
10
*Note that if there are multiple indices which have the minimum value of B for the same value of A, i want to keep both indices.
I am currently using a for loop (below), but because my array A and B are extremely large (1*10^8 entries) it take hours to run the script
%Extract unique elements of A
Unique_A = unique(A);
%preallocate index vector for speed (no clue how big this will be so intentionally large
Alternative_index = zeros(1*10^6,1);
for i = 1:length(Unique_A)
% Find the indices of the Benefit vector which correspond to a each unique value of A
indices = find(A==Unique_A(i));
%Now determine which of the corresponding indices in B are equal to the minimum value
min_B_indices = (indices(B(indices)== min(B(indices))))
%since the index vector was pre allocated, I need to make sure I am appending the vector without overwriting entries. So determine how many entries will need to be appended:
length_of_append = length(min_B_indices)
%Now find the first zero element of the preallocated vector
first_zero = find(Alternative_index==0, 1, 'first')
%Now add the indices to the preallocated vector
Alternative_index(first_zero:first_zero+length_of_append-1,1) = min_B_indices;
end
%remove trailing zeros
Alternative_index = Alternative_index(Alternative_index~=0);
I have wracked my brain on how to do this without a for loop...anyone have a suggestion?
  3 Kommentare
Matt J
Matt J am 22 Jun. 2018
from the above example the desired output is index 1 2 8 10
How are you able to get an index of 10 in the example output when your A,B are only 9 elements long?

Melden Sie sich an, um zu kommentieren.

Akzeptierte Antwort

Jan
Jan am 22 Jun. 2018
Bearbeitet: Jan am 23 Jun. 2018
A leaner version using a cell instead of a pre-allocation:
A = randi([1,1000], 1, 1e6);
B = randi([1,1000], 1, 1e6);
uniqA = unique(A);
OutC = cell(1, length(uniqA));
for i = 1:length(uniqA)
index = find(A == uniqA(i));
BB = B(index);
OutC{i} = index(BB == min(BB));
end
Out = cat(2, OutC{:}).';
It takes 1.60 sec instead of 2.25 sec for the original code.
But Matt J's splitapply approach takes 0.33 sec:
G = findgroups(A);
C = 1:numel(B);
OutC = splitapply(@(b,c) {c(b==min(b))}, B, C, G);
Out = cat(2, OutC{:}).';
  1 Kommentar
Jan
Jan am 23 Jun. 2018
Bearbeitet: Jan am 23 Jun. 2018
I prefer Matt J's solution. I tried it hard to convert it to an accumarray approach, but without success.
It saves some percent to replace G = findgroups(A) by:
[sA, iSA] = sort(A);
groupA = [true, diff(sA) ~= 0];
G = cumsum(groupA);
G(iSA) = G;

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (1)

Matt J
Matt J am 22 Jun. 2018
Bearbeitet: Matt J am 22 Jun. 2018
G=findgroups(A);
C=(1:numel(B)).';
output = splitapply( @(b,c) {c(b==min(b))} , B,C,G),

Kategorien

Mehr zu Loops and Conditional Statements finden Sie in Help Center und File Exchange

Produkte


Version

R2018a

Community Treasure Hunt

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

Start Hunting!

Translated by