matlab - script - Convertir una matriz binaria en un vector del último índice distinto de cero de forma rápida y vectorizada
posicion de un valor en un vector matlab (2)
Supongamos, en MATLAB, que tengo una matriz, A, cuyos elementos son 0 o 1.
¿Cómo obtengo un vector del índice del último elemento distinto de cero de cada columna de una manera más rápida y vectorizada?
Yo podría hacer
[B, I] = max(cumsum(A));
y uso I
, pero ¿hay una manera más rápida? (Supongo que cumsum costaría un poco de tiempo incluso sumar 0 y 1).
Editar: Supongo que he vectorizado incluso más de lo que necesito rápido: el bucle del Sr. Fooz es genial, pero cada bucle en MATLAB parece que me cuesta mucho tiempo de depuración incluso si es rápido.
Rápido es lo que debe preocuparse, no necesariamente la vectorización completa. Las versiones recientes de Matlab son mucho más inteligentes sobre la manipulación de bucles de manera eficiente. Si hay una forma vectorizada compacta de expresar algo, generalmente es más rápido, pero los bucles no deberían (siempre) temerse como solían serlo.
clc
A = rand(5000)>0.5;
A(1,find(sum(A,1)==0)) = 1; % make sure there is at least one match
% Slow because it is doing too much work
tic;[B,I1]=max(cumsum(A));toc
% Fast because FIND is fast and it runs the inner loop
tic;
I3=zeros(1,5000);
for i=1:5000
I3(i) = find(A(:,i),1,''last'');
end
toc;
assert(all(I1==I3));
% Even faster because the JIT in Matlab is smart enough now
tic;
I2=zeros(1,5000);
for i=1:5000
I2(i) = 0;
for j=5000:-1:1
if A(j,i)
I2(i) = j;
break;
end
end
end
toc;
assert(all(I1==I2));
En R2008a, Windows, x64, la versión cumsum demora 0.9 segundos. El bucle y la versión de búsqueda tardan 0.02 segundos. La versión de doble bucle toma solo 0.001 segundos.
EDITAR: Cuál es el más rápido depende de los datos reales. El doble-loop toma 0.05 segundos cuando cambia el 0.5 a 0.999 (porque toma más tiempo llegar al descanso, en promedio). cumsum y la implementación loop y find tienen velocidades más consistentes.
EDIT 2: la solución flipud de gnovice es inteligente. Desafortunadamente, en mi máquina de prueba lleva 0.1 segundos, por lo que es mucho más rápido que cumsum, pero más lento que las versiones en bucle.
Como lo muestra el Sr. Fooz , los bucles ahora pueden ser bastante rápidos con las versiones más nuevas de MATLAB. Sin embargo, si realmente quieres tener un código vectorizado compacto, te sugiero que intentes esto:
[B,I] = max(flipud(A));
I = size(A,1)-I+1;
Esto es más rápido que su respuesta basada en CUMSUM, pero aún no es tan rápido como las opciones de bucle del Sr. Fooz.
Dos cosas adicionales a considerar:
¿Qué resultados desea obtener para una columna que no tiene nada en absoluto? Con la opción anterior que te di, creo que obtendrás un índice de tamaño (A, 1) (es decir, el número de filas en A ) en tal caso. Para su opción, creo que obtendrá un 1 en tal caso, mientras que la opción anidados para bucles de Mr Fooz le dará un 0.
La velocidad relativa de estas opciones diferentes probablemente variará según el tamaño de A y el número de no-ceros que usted espera que tenga.