Effacer les filtres
Effacer les filtres

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

2 vues (au cours des 30 derniers jours)
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 commentaires
Matt J
Matt J le 22 Juin 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?
Still Learning Matlab
Still Learning Matlab le 23 Juin 2018
Apologies, I meant to create 10 entries.

Connectez-vous pour commenter.

Réponse acceptée

Jan
Jan le 22 Juin 2018
Modifié(e) : Jan le 23 Juin 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 commentaire
Jan
Jan le 23 Juin 2018
Modifié(e) : Jan le 23 Juin 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;

Connectez-vous pour commenter.

Plus de réponses (1)

Matt J
Matt J le 22 Juin 2018
Modifié(e) : Matt J le 22 Juin 2018
G=findgroups(A);
C=(1:numel(B)).';
output = splitapply( @(b,c) {c(b==min(b))} , B,C,G),

Catégories

En savoir plus sur Loops and Conditional Statements dans Help Center et File Exchange

Produits


Version

R2018a

Community Treasure Hunt

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

Start Hunting!

Translated by