posicion - Matlab: evite los bucles para encontrar el máximo entre los valores con las mismas etiquetas
posicion de un valor en un vector matlab (2)
Necesito encontrar el máximo entre los valores con las mismas etiquetas, en matlab, y estoy tratando de evitar el uso de bucles for.
Específicamente, tengo una matriz L
de etiquetas y una matriz V
de valores, del mismo tamaño. Necesito producir una matriz S
que contenga, para cada valor de L
, el valor máximo de V
Un ejemplo explicará mejor:
L = [1,1,1,2,2,2,3,3,3,4,4,4,1,2,3,4]
V = [5,4,3,2,1,2,3,4,5,6,7,8,9,8,7,6]
Entonces, los valores de la matriz de salida S serán:
s(1) = 9 (the values V(i) such that L(i) == 1 are: 5,4,3,9 -> max = 9)
s(2) = 8 (the values V(i) such that L(i) == 2 are: 2,1,2,8 -> max = 8)
s(3) = 7 (the values V(i) such that L(i) == 3 are: 3,4,5,7 -> max = 7)
s(4) = 8 (the values V(i) such that L(i) == 4 are: 6,7,8,6 -> max = 8)
esto se puede implementar trivialmente al atravesar las matrices L
y V
con un bucle for, pero en Matlab los bucles son lentos, así que estaba buscando una solución más rápida. ¿Alguna idea?
Este es un trabajo estándar para accumarray
.
Se deben considerar tres casos, con una generalidad creciente:
- Etiquetas enteras.
- Etiquetas enteras, especifique el valor de relleno.
- Eliminar huecos; o etiquetas no enteras. Caso general.
Etiquetas enteras
Puedes simplemente usar
S = accumarray(L(:), V(:), [], @max).'';
En tu ejemplo, esto da
>> L = [1 1 1 2 2 2 3 3 3 4 4 4 1 2 3 7];
>> V = [5 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6];
>> S = accumarray(L(:), V(:), [], @max).''
S =
9 8 7 8
Etiquetas enteras, especifique el valor de relleno
Si hay espacios entre enteros en L
, lo anterior dará un resultado de 0
para las etiquetas que no existen. Si desea cambiar ese valor de relleno (por ejemplo, a NaN
), use un quinto argumento de entrada en acccumarray
:
S = accumarray(L(:), V(:), [], @max, NaN).'';
Ejemplo:
>> L = [1 1 1 2 2 2 3 3 3 4 4 4 1 2 3 7]; %// last element changed
>> V = [5 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6]; %// same as in your example
>> S = accumarray(L(:), V(:), [], @max, NaN).''
S =
9 8 7 8 NaN NaN 6
Eliminar huecos; o etiquetas no enteras. Caso general
Cuando los espacios entre las etiquetas de enteros son grandes, usar un valor de relleno puede ser ineficiente. En ese caso, es posible que desee obtener solo los valores significativos en S
, sin valores de relleno, es decir, omita las etiquetas que no existen . Además, puede ser que L
no contenga necesariamente enteros .
Estos dos problemas se resuelven mediante la aplicación unique
de las etiquetas antes de usar accumarray
:
[~, ~, Li] = unique(L); %// transform L into consecutive integers
S = accumarray(Li(:), V(:), [], @max, NaN).'';
Ejemplo:
>> L = [1.5 1.5 1.5 2 2 2 3 3 3 4 4 4 1 2 3 7.8]; %// note: non-integer values
>> V = [5 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6 ]; %// same as in your example
>> [~, ~, Li] = unique(L); %// transform L into consecutive integers
>> S = accumarray(Li(:), V(:), [], @max, NaN).''
S =
9 5 8 7 8 6
helper=[L.'', V.''];
helper=sortrows(helper,-2);
[~,idx,~]=unique(helper(:,1));
S=helper(idx,2);
Lo que hago es: unir las dos matrices como columnas. Luego los ordené primero con respecto a la segunda columna con el elemento más grande. Luego obtengo el idx de los valores únicos en L
antes de devolver los valores correspondientes de V
La solución de Luis Mendo es más rápida. Pero, por lo que veo, su solución no funciona si hay un cero, un valor negativo o un no integrado dentro de L
:
Luis solution: Elapsed time is 0.722189 seconds.
My solution: Elapsed time is 2.575943 seconds.
Solía:
L= ceil(rand(1,500)*10);
V= ceil(rand(1,500)*250);
y ejecuté el código 10000 veces.