2009-03-25 6 views
10

Je souhaite supprimer plusieurs valeurs spécifiques d'une matrice (si elles existent). Il est hautement probable qu'il existe plusieurs copies des valeurs dans la matrice.Quelle est la manière la plus efficace/élégante de supprimer des éléments d'une matrice dans MATLAB?

Par exemple, considérons une matrice N-by-2 intersections. Si les paires de valeurs et [c d] existent en tant que lignes dans cette matrice, je veux les supprimer.

Disons que je veux supprimer des lignes comme [-2.0 0.5] et [7 7] dans la matrice suivante:

intersections = 

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

Alors qu'après la suppression je reçois:

intersections = 

    -4.0000 0.5000 
    2.0000 3.0000 
    4.0000 0.5000 

Quelle est la façon la plus efficace/élégante de le faire ce?

Répondre

13

Essayez ce one-liner (où A est votre matrice d'intersection et B est la valeur à supprimer):

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

Ensuite, il suffit de répéter la dernière ligne pour chaque nouvelle B vous voulez retirer.

EDIT:

... et voici une autre option:

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

AVERTISSEMENT: Les réponses ici sont mieux utilisées pour les cas où des erreurs ponctuelles petites Floating ne sont pas attendus (c.-à valeurs entières). Comme indiqué dans ce follow-up question, l'utilisation des opérateurs "==" et "~ =" peut entraîner des résultats indésirables. Dans ce cas, les options ci-dessus doivent être modifiées pour utiliser des opérateurs relationnels au lieu des opérateurs d'égalité. Par exemple, la deuxième option que j'ai ajoutée serait modifiée en:

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

Juste une petite tête! =)


QUELQUES TEMPS RUDIMENTAIRE:

En cas où quelqu'un était vraiment intéressé par l'efficacité, je viens de faire un certain temps simple pour trois façons différentes pour obtenir le sous-indice pour la matrice (les deux les options répertoriées ci-dessus et Fanfan's option 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. 

Comme vous pouvez le voir, l'option strmatch est plus rapide que ma première suggestion, mais ma deuxième suggestion est la plus rapide des trois. Notez cependant que mes options et Fanfan font des choses légèrement différentes: mes options retournent les indices logiques des lignes à garder, et Fanfan retourne les index linéaires des rangées à en supprimant.Voilà pourquoi l'option strmatch utilise la forme:

A(index,:) = []; 

alors que le mien utiliser la forme:

A = A(index,:); 

Cependant, mes indices peuvent être réduits à néant pour utiliser la première forme (rangées d'indexation pour supprimer):

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

J'ai presque eu une solution de vecteur mais pas un peu plus bavard que le vôtre. Nice one-liner. – Azim

+0

WoW ... façon si élégante .... –

5

Vous pouvez également abuser de la fonction strmatch en fonction de vos besoins: le code suivant supprime toutes les occurences d'une b ligne donnée dans une matrice a

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

Si vous devez supprimer plus d'une ligne, comme toutes les lignes de la matrice B, itérer sur eux:

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

+1 Très sournois en utilisant STRMATCH pour ce faire! Je ne sais pas pourquoi quelqu'un l'a déprécié ... Je l'ai essayé moi-même et ça a l'air de bien fonctionner. J'ai fait un timing simple et j'ai trouvé que l'utilisation de STRMATCH pour obtenir un index matriciel est à peu près la même vitesse que ma première option ci-dessus, mais ma deuxième option est la plus rapide. – gnovice

7

La solution simple est de regarder ici pour définir des fonctions d'appartenance, à savoir, setdiff, union et ismember.

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

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

Voir ce que ismember fait avec les deux tableaux. Utilisez l'option 'rows'.

ismember(A,B,'rows') 

ans = 
    0 
    1 
    0 
    0 
    1 

Puisque nous voulons supprimer des lignes de A qui sont également en B, faites ceci:

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

A = 
     -4   0.5 
     2   3 
     4   0.5 

Attention: les fonctions d'appartenance de jeu recherchent une correspondance EXACT. Les entiers ou les multiples de 1/2 tels que sont dans A répondent à cette exigence. Ils sont exactement représentés en arithmétique en virgule flottante dans MATLAB.

Si ces nombres avaient été de vrais nombres à virgule flottante, j'aurais été plus prudent. Là j'aurais utilisé une tolérance sur la différence. Dans ce cas, j'aurais pu calculer la matrice de distance interpoint entre les deux ensembles de nombres, en supprimant une ligne de A seulement si elle se trouvait à une certaine distance de l'une des rangées de B.

+1

+1 J'ai oublié comment ISMEMBER peut fonctionner sur plusieurs lignes. Aussi, bienvenue à SO! ;) – gnovice

0

Je ne sais pas quand cette fonction était introduit (en utilisant 2012b), mais vous pouvez juste faire:

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

    -4.0000 0.5000 
    2.0000 3.0000 
    4.0000 0.5000 

Basé sur:

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]; 
Questions connexes