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

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

Например, рассмотрим матрицу 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 ответа

Решение

Попробуйте эту однострочную строку (где 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, который вы хотите удалить.

РЕДАКТИРОВАТЬ:

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

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

Просто быстренько! знак равно


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

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

Простое решение здесь состоит в том, чтобы посмотреть, как установить функции принадлежности, то есть 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.

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

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

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

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

Не уверен, когда эта функция была введена (с использованием 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];
Другие вопросы по тегам