Borrar filtros
Borrar filtros

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

2 visualizaciones (últimos 30 días)
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 comentarios
Matt J
Matt J el 22 de Jun. de 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?

Iniciar sesión para comentar.

Respuesta aceptada

Jan
Jan el 22 de Jun. de 2018
Editada: Jan el 23 de Jun. de 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 comentario
Jan
Jan el 23 de Jun. de 2018
Editada: Jan el 23 de Jun. de 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;

Iniciar sesión para comentar.

Más respuestas (1)

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

Categorías

Más información sobre Loops and Conditional Statements en Help Center y File Exchange.

Productos


Versión

R2018a

Community Treasure Hunt

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

Start Hunting!

Translated by