Qual è il / modo elegante più efficace per eliminare gli elementi da una matrice in MATLAB?

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

  •  22-08-2019
  •  | 
  •  

Domanda

Voglio eliminare più valori specifici da una matrice (se esistono). E 'molto probabile che ci sono più copie dei valori nella matrice.

Per esempio, si consideri una matrice intersections N-per-2. Se esistono le coppie di valori [a b] e [c d] come righe in quella matrice, voglio eliminarli.

Diciamo che voglio eliminare le righe come [-2.0 0.5] e [7 7] nella seguente matrice:

intersections =

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

In modo che dopo la cancellazione ottengo:

intersections = 

   -4.0000    0.5000
    2.0000    3.0000
    4.0000    0.5000

Qual è il più efficiente / elegante modo per fare questo?

È stato utile?

Soluzione

Prova questo-rivestimento (dove A è tua matrice intersezione e B è il valore da rimuovere):

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

Poi basta ripetere l'ultima riga per ogni nuovo B che si desidera rimuovere.

Modifica

... ed ecco un'altra opzione:

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

ATTENZIONE: Le risposte qui sono i più utilizzati nei casi in cui non sono previsti piccoli errori virgola mobile (cioè con valori interi). Come notato in questo follow-up domanda , utilizzando il "==" e "~ =" Gli operatori possono causare risultati indesiderati. In questi casi, le opzioni di cui sopra devono essere modificati per utilizzare gli operatori relazionali invece di operatori di uguaglianza. Ad esempio, la seconda opzione ho aggiunto sarebbe stato cambiato in:

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

Basta un rapido di testa! =)


Alcuni TIMING rudimentale:

Nel caso qualcuno era davvero interessati in termini di efficienza, ho solo fatto qualche semplice tempistica per tre diversi modi per ottenere il sottoindice per la matrice (le due opzioni che ho elencato sopra e Fanfan's opzione STRMATCH):

>> % 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.

Come si può vedere, l'opzione STRMATCH è più veloce di mio primo suggerimento, ma il mio secondo suggerimento è il più veloce di tutti e tre. Si noti comunque che le mie opzioni e Fanfan di fare le cose un po 'diverse: le mie opzioni restituiscono indici logici delle righe per mantenere , indici rendimenti lineari e Fanfan delle righe a Rimuovi . Ecco perché l'opzione STRMATCH usa la forma:

A(index,:) = [];

mentre la mia utilizzare la forma:

A = A(index,:);

Tuttavia, miei indici possono essere annullati per utilizzare la prima forma (righe indicizzazione a eliminare ):

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

Altri suggerimenti

La soluzione più semplice è quella di cercare di impostare funzioni di appartenenza, vale a dire, setdiff, unione, e IsMember.

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

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

Vedere che cosa IsMember fa con i due array. Utilizzare l'opzione 'file'.

ismember(A,B,'rows')

ans =
     0
     1
     0
     0
     1

Dal momento che vogliamo eliminare le righe di A che sono anche in B, solo fare questo:

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

A = 
      -4          0.5
       2            3
       4          0.5

Attenzione che insieme funzioni di appartenenza cercare una corrispondenza esatta. Interi o multipli di 1/2 come sono in A soddisfare tale requisito. Sono esattamente rappresentati in aritmetica in virgola mobile in MATLAB.

avuto questi numeri stati numeri in virgola reale galleggiante, sarei stato più attento. Ci avrei utilizzato una tolleranza sulla differenza. In quel caso, avrei calcolata la matrice di distanza interpunto tra le due serie di numeri, rimuovendo una fila di A solo se cade entro una certa distanza determinata di una delle righe di B.

Si può anche abusare della funzione strmatch base alle proprie esigenze: il seguente codice rimuove tutte le occorrenze di una determinata riga b in una matrice A

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

Se è necessario eliminare più di una riga, come ad esempio tutte le righe matrice B, scorrere su di loro:

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

Non so quando questa funzione è stata introdotta (usando 2012b), ma si può solo fare:

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

   -4.0000    0.5000
    2.0000    3.0000
    4.0000    0.5000

In base a:

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];
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top