¿Qué es lo más eficiente y elegante para eliminar elementos de una matriz en MATLAB?

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

  •  22-08-2019
  •  | 
  •  

Pregunta

Quiero eliminar varios valores específicos a partir de una matriz (si es que existen).Es muy probable que hay varias copias de los valores de la matriz.

Por ejemplo, considere la posibilidad de un N-por-2 de la matriz intersections.Si los pares de valores [a b] y [c d] existen como las filas de dicha matriz, quiero eliminarlos.

Digamos que yo quiero para eliminar filas como [-2.0 0.5] y [7 7] en la siguiente matriz:

intersections =

   -4.0000    0.5000
   -2.0000    0.5000
    2.0000    3.0000
    4.0000    0.5000
   -2.0000    0.5000

Así que después de la eliminación de recibir:

intersections = 

   -4.0000    0.5000
    2.0000    3.0000
    4.0000    0.5000

¿Qué es lo más eficiente y elegante manera de hacer esto?

¿Fue útil?

Solución

Pruebe este one-liner (donde Un es tu intersección de la matriz y B es el valor de quitar):

A = [-4.0 0.5;
     -2.0 0.5;
      2.0 3.0;
      4.0 0.5;
     -2.0 0.5];
B = [-2.0 0.5];
A = A(~all(A == repmat(B,size(A,1),1),2),:);

A continuación, sólo tiene que repetir la última línea de cada nuevo B desea quitar.

EDITAR:

...y aquí es otra opción:

A = A((A(:,1) ~= B(1)) | (A(:,2) ~= B(2)),:);

ADVERTENCIA: Las respuestas aquí son los más utilizados para los casos donde los pequeños errores de punto flotante no se espera (es decir,con valores enteros).Como se señala en esta pregunta de seguimiento, utilizando el "==" y "~=" operadores pueden causar resultados no deseados.En tales casos, las opciones de arriba debe ser modificado para utilizar los operadores relacionales en lugar de operadores de igualdad.Por ejemplo, la segunda opción que he añadido sería cambiado a:

tolerance = 0.001;   % Or whatever limit you want to set
A = A((abs(A(:,1)-B(1)) > tolerance) | (abs(A(:,2)-B(2)) > tolerance),:);

Sólo una rápida cabeza!=)


ALGUNOS RUDIMENTARIOS TIEMPO:

En caso de que alguien se realmente interesados en la eficiencia, lo hice de algunos de temporización simple para tres maneras diferentes para obtener el subíndice de la matriz (las dos opciones que he enumerado anteriormente y Fanfan la STRMATCH opción):

>> % Timing for option #1 indexing:
>> tic; for i=1:10000, index = ~all(A == repmat(B,size(A,1),1),2); end; toc;
Elapsed time is 0.262648 seconds.
>> % Timing for option #2 indexing:
>> tic; for i=1:10000, index = (A(:,1) ~= B(1)) | (A(:,2) ~= B(2)); end; toc;
Elapsed time is 0.100858 seconds.
>> % Timing for STRMATCH indexing:
>> tic; for i=1:10000, index = strmatch(B,A); end; toc;
Elapsed time is 0.192306 seconds.

Como se puede ver, la STRMATCH opción es más rápida que mi primera sugerencia, pero mi segunda sugerencia es el más rápido de los tres.Nota sin embargo, que mis opciones y Fanfan a hacer cosas ligeramente diferentes:mis opciones de retorno lógica de los índices de las filas para mantener, y Fanfan la devuelve lineal de los índices de las filas para quitar.Es por eso que el STRMATCH opción utiliza el formulario:

A(index,:) = [];

mientras que la mía utilizar el formulario:

A = A(index,:);

Sin embargo, mi índices pueden ser invalidados para el uso de la primera forma (indexación filas a quitar):

A(all(A == repmat(B,size(A,1),1),2),:) = [];    % For option #1
A((A(:,1) == B(1)) & (A(:,2) == B(2)),:) = [];  % For option #2

Otros consejos

La solución simple aquí es mirar para establecer funciones de pertenencia, es decir, setdiff, unión y IsMember.

A = [-4  0.5;
   -2    0.5;
    2    3;
    4    0.5;
   -2    0.5];

B = [-2 .5;7 7];

Vea lo IsMember hace con las dos matrices. Utilice la opción 'filas'.

ismember(A,B,'rows')

ans =
     0
     1
     0
     0
     1

Dado que deseamos eliminar filas de A que también están en B, acaba de hacer esto:

A(ismember(A,B,'rows'),:) = []

A = 
      -4          0.5
       2            3
       4          0.5

Cuidado con ese conjunto de funciones de pertenencia buscan una coincidencia exacta. Números enteros o múltiplos de 1/2, como se encuentran en un cumple este requisito. Están representados exactamente en aritmética de punto flotante en MATLAB.

Si estos números sido número real en coma flotante, habría sido más cuidadoso. No me hubiera utilizado una tolerancia de la diferencia. En ese caso, podría haber calculado la matriz de distancias entre entre los dos conjuntos de números, la eliminación de una fila de A sólo si cayó dentro de una cierta distancia dada de una de las filas de B.

También se puede abusar de la función strmatch para satisfacer sus necesidades: el código siguiente quita todas las apariciones de una fila dada b en una matriz A

A(strmatch(b, A),:) = [];

Si necesita borrar más de una fila, como todas las filas de la matriz B, iterar sobre ellos:

for b = B'
   A(strmatch(b, A),:) = [];
end

No está seguro cuando esta función se introdujo (usando 2012b) pero sólo puede hacerlo:

setdiff(A, B, 'rows')
ans =

   -4.0000    0.5000
    2.0000    3.0000
    4.0000    0.5000

Basado en:

A = [-4.0 0.5;
     -2.0 0.5;
      2.0 3.0;
      4.0 0.5;
     -2.0 0.5];
B = [-2.0 0.5];
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top