Какой самый эффективный / элегантный способ удаления элементов из матрицы в MATLAB? - PullRequest
10 голосов
/ 26 марта 2009

Я хочу удалить несколько определенных значений из матрицы (если они существуют). Весьма вероятно, что в матрице имеется несколько копий значений.

Например, рассмотрим матрицу N-2 intersections. Если пары значений [a b] и [c d] существуют в виде строк в этой матрице, я хочу удалить их.

Допустим, я хочу удалить такие строки, как [-2.0 0.5] и [7 7] в следующей матрице:

intersections =

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

Чтобы после удаления я получил:

intersections = 

   -4.0000    0.5000
    2.0000    3.0000
    4.0000    0.5000

Какой самый эффективный / элегантный способ сделать это?

Ответы [ 4 ]

13 голосов
/ 26 марта 2009

Попробуйте эту однострочную (где A - ваша матрица пересечений, а B - значение, которое нужно удалить):

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

Затем просто повторите последнюю строку для каждого нового B , который вы хотите удалить.

EDIT:

... и вот еще один вариант:

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

ПРЕДУПРЕЖДЕНИЕ: Ответы здесь лучше всего использовать в случаях, когда небольшие ошибки с плавающей запятой не ожидаются (то есть с целочисленными значениями). Как отмечено в этом последующем вопросе , использование операторов "==" и "~ =" может привести к нежелательным результатам. В таких случаях вышеупомянутые параметры должны быть изменены, чтобы использовать реляционные операторы вместо операторов равенства. Например, второй вариант, который я добавил, будет изменен на:

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

Просто быстренько! =) * * Один тысяча двадцать-шесть


НЕКОТОРЫЕ РУДИМЕНТНЫЕ СРОКИ:

В случае, если кто-то действительно заинтересован в эффективности, я просто выполнил простую синхронизацию для трех разных способов получить субиндекс для матрицы (два варианта, которые я перечислил выше, и Фанфана опция 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.

Как видите, опция STRMATCH работает быстрее, чем мое первое предложение, но второе предложение является самым быстрым из всех трех. Однако обратите внимание, что мои параметры и функции Fanfan выполняют несколько разные вещи: мои параметры возвращают логические индексы строк в keep , а функции Fanfan возвращают линейные индексы строк в remove . Вот почему опция STRMATCH использует форму:

A(index,:) = [];

пока я использую форму:

A = A(index,:);

Однако мои индексы могут быть отменены, чтобы использовать первую форму (индексирование строк до удалить ):

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
7 голосов
/ 31 марта 2009

Простое решение здесь состоит в том, чтобы посмотреть, как установить функции членства, то есть setdiff, union и ismember.

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

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

Посмотрите, что ismember делает с двумя массивами. Используйте параметр «строки».

ismember(A,B,'rows')

ans =
     0
     1
     0
     0
     1

Так как мы хотим удалить строки A, которые также находятся в B, просто сделайте это:

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

A = 
      -4          0.5
       2            3
       4          0.5

Остерегайтесь того, что установленные функции членства ищут ТОЧНОЕ совпадение. Целые числа или кратные 1/2, такие как в A, удовлетворяют этому требованию. Они точно представлены в арифметике с плавающей точкой в ​​MATLAB.

Если бы эти числа были настоящими числами с плавающей точкой, я был бы более осторожен. Там я бы использовал допуск на разницу. В этом случае я мог бы вычислить матрицу расстояний между точками между двумя наборами чисел, удалив строку A только в том случае, если она находилась в пределах некоторого заданного расстояния одной из строк B.

5 голосов
/ 26 марта 2009

Вы также можете использовать функцию strmatch в соответствии с вашими потребностями: следующий код удаляет все вхождения данной строки b в матрице A

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

Если вам нужно удалить более одной строки, например, все строки из матрицы B, выполните итерации по ним:

for b = B'
   A(strmatch(b, A),:) = [];
end
0 голосов
/ 31 октября 2013

Не уверен, когда эта функция была введена (с использованием 2012b), но вы можете просто сделать:

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

   -4.0000    0.5000
    2.0000    3.0000
    4.0000    0.5000

На основании:

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];
...