2009-03-25 20 views
10

Quiero eliminar varios valores específicos de una matriz (si existen). Es muy probable que haya múltiples copias de los valores en la matriz. Por ejemplo, considere una matriz N-por-2 intersections. Si los pares de valores y [c d] existen como filas en esa matriz, quiero eliminarlos.¿Cuál es la forma más eficiente/elegante de eliminar elementos de una matriz en MATLAB?

Digamos que quiero 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 consigo:

intersections = 

    -4.0000 0.5000 
    2.0000 3.0000 
    4.0000 0.5000 

Cuál es la manera más eficiente/elegante de hacer ¿esta?

Respuesta

13

Prueba esto de una sola línea (donde Un es su matriz de cruce y B es el valor para eliminar):

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),:); 

Entonces sólo tiene que repetir la última línea para cada nuevo B desea para eliminar.

EDIT:

... 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 en que no se espera que los errores de coma flotante pequeña (es decir, con valores enteros). Como se indica en este follow-up question, el uso de los operadores "==" y "~ =" puede causar resultados no deseados. En tales casos, las opciones anteriores deberían modificarse para usar operadores relacionales en lugar de operadores de igualdad. Por ejemplo, la segunda opción que agregué se cambió 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),:); 

¡Solo una rápida cabeza arriba! =)


POCO DE TIEMPO rudimentaria:

En caso de que alguien era realmente interesados ​​en la eficiencia, que acabo de hacer un poco de tiempo simple para tres maneras diferentes de obtener el subíndice de la matriz (los dos opciones que he mencionado anteriormente y la opción STRMATCH Fanfan's):

>> % 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 opción STRMATCH es más rápido que mi primera sugerencia, pero mi segunda sugerencia es la más rápida de las tres. Sin embargo, tenga en cuenta que mis opciones y Fanfan hacen cosas ligeramente diferentes: mis opciones devuelven los índices lógicos de las filas mantener, y los índices lineales devueltos de Fanfan de las filas a eliminar.Es por eso que la opción STRMATCH utiliza la forma:

A(index,:) = []; 

mientras que la mía, utilice el formulario:

A = A(index,:); 

Sin embargo, mis índices se pueden negar a utilizar la primera forma (filas de indexación a eliminar):

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 
+0

casi me da un vector solución, pero no un poco más detallado de lo suyo. Buen trazador de líneas. – Azim

+0

WoW ... manera tan elegante .... –

5

también 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 
+0

+1 ¡Muy astuto usando STRMATCH para hacer esto! No estoy seguro de por qué alguien votó negativamente ... Lo probé yo mismo y parece funcionar bien. Hice algunos tiempos simples y descubrí que usar STRMATCH para obtener un índice de matriz es aproximadamente la misma velocidad que mi primera opción anterior, pero mi segunda opción es la más rápida. – gnovice

7

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

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

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

Vea lo que ismember hace con las dos matrices. Use 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 

Tenga en cuenta que las funciones de pertenencia conjunto buscan una coincidencia exacta. Los enteros o múltiplos de 1/2 tal como están en A satisfacen ese requisito. Están exactamente representados en aritmética de coma flotante en MATLAB.

Si estos números hubieran sido números flotantes reales, habría tenido más cuidado. Hubiera usado una tolerancia en la diferencia. En ese caso, podría haber calculado la matriz de distancia entre los dos conjuntos de números, eliminando una fila de A solo si caía dentro de una distancia dada de una de las filas de B.

+1

+1 Me olvidé de cómo ISMEMBER puede operar en todas las filas. Además, bienvenido a SO! ;) – gnovice

0

No estoy seguro de cuándo se realizó esta función introdujo (usando 2012b), pero sólo se puede hacer:

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]; 
Cuestiones relacionadas