Convertir una matriz binaria en un vector del último índice distinto de cero de forma rápida y vectorizada

StackOverflow https://stackoverflow.com/questions/831721

Pregunta

Suponga, 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 forma más rápida y vectorizada?

podría hacer

[B, I] = max (cumsum (A));

y use I , pero ¿hay alguna forma más rápida? (Supongo que cumsum costaría un poco de tiempo incluso sumar 0 y 1).

Editar: supongo que vectoricé incluso más de lo que necesito rápidamente: el bucle del Sr. Fooz es excelente, pero cada bucle en MATLAB parece costarme mucho tiempo de depuración incluso si es rápido.

¿Fue útil?

Solución

Como se muestra por Mr Fooz , porque los bucles pueden ser bastante rápidos ahora con las versiones más recientes de MATLAB. Sin embargo, si realmente desea tener un código vectorizado compacto, le sugiero que pruebe 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 tener en cuenta:

  • ¿Qué resultados desea obtener para una columna que no tiene ninguno? Con la opción anterior que le di, creo que obtendrá 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 de bucles anidados del Sr. Fooz le dará un 0.

  • La velocidad relativa de estas diferentes opciones probablemente variará según el tamaño de A y el número de ceros distintos de lo que espera que tenga.

Otros consejos

Rápido es de lo que debe preocuparse, no necesariamente de una vectorización completa. Las versiones recientes de Matlab son mucho más inteligentes sobre el manejo eficiente de los bucles. Si hay una forma compacta y vectorizada de expresar algo, generalmente es más rápido, pero los bucles no deberían (siempre) temerse como solían ser.

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 tarda 0.9 segundos. La versión de bucle y búsqueda tarda 0.02 segundos. La versión de doble bucle lleva solo 0.001 segundos.

EDITAR: cuál es el más rápido depende de los datos reales. El doble ciclo toma 0.05 segundos cuando cambia el 0.5 a 0.999 (porque toma más tiempo alcanzar el descanso; en promedio). cumsum y la implementación loop & amp; find tienen velocidades más consistentes.

EDIT 2: la solución flipud de gnovice es inteligente. Desafortunadamente, en mi máquina de prueba tarda 0.1 segundos, por lo que es mucho más rápido que cumsum, pero más lento que las versiones en bucle.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top