2009-03-25 82 views
10

Tôi muốn xóa một số giá trị cụ thể khỏi ma trận (nếu chúng tồn tại). Rất có thể có nhiều bản sao của các giá trị trong ma trận.Cách hiệu quả nhất/thanh lịch để xóa các phần tử khỏi ma trận trong MATLAB là gì?

Ví dụ: xem xét ma trận N-by-2 intersections. Nếu các cặp giá trị và [c d] tồn tại dưới dạng các hàng trong ma trận đó, tôi muốn xóa chúng.

Hãy nói rằng tôi muốn xóa hàng như [-2.0 0.5][7 7] trong ma trận sau:

intersections = 

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

Vì vậy mà sau khi xóa tôi nhận được:

intersections = 

    -4.0000 0.5000 
    2.0000 3.0000 
    4.0000 0.5000 

là gì/cách thanh lịch hiệu quả nhất để làm điều này?

Trả lời

13

Hãy thử điều này một liner (nơi Một là ma trận ngã tư của bạn và B là giá trị để loại 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),:); 

Sau đó, chỉ cần lặp lại dòng cuối cùng cho mỗi mới B bạn muốn để loại bỏ.

EDIT:

... và đây là một lựa chọn:

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

CẢNH BÁO: Những câu trả lời ở đây được sử dụng tốt nhất cho trường hợp sai sót điểm nhỏ nổi không mong đợi (ví dụ với giá trị số nguyên). Như đã lưu ý trong số follow-up question này, việc sử dụng toán tử "==" và "~ =" có thể gây ra kết quả không mong muốn. Trong những trường hợp như vậy, các tùy chọn ở trên nên được sửa đổi để sử dụng các toán tử quan hệ thay vì các toán tử bình đẳng. Ví dụ: tùy chọn thứ hai tôi đã thêm sẽ được thay đổi thành:

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

Chỉ cần nhanh lên! =)


MỘT SỐ THỜI GIAN thô sơ:

Trong trường hợp bất cứ ai đã thực sự quan tâm đến hiệu quả, tôi chỉ làm một số thời gian đơn giản cho ba cách khác nhau để có được những subindex cho ma trận (hai tùy chọn tôi đã liệt kê ở trên và Fanfan's tùy chọn 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. 

Như bạn có thể thấy, các tùy chọn STRMATCH là nhanh hơn so với đề nghị đầu tiên của tôi, nhưng đề nghị thứ hai của tôi là nhanh nhất trong cả ba. Tuy nhiên, lưu ý rằng các tùy chọn của tôi và Fanfan làm những việc hơi khác nhau: các tùy chọn của tôi trả về các chỉ số hợp lý của các hàng thành giữ và chỉ số tuyến tính trả về của hàng của Fanfan là xóa.Đó là lý do lựa chọn STRMATCH sử dụng các hình thức:

A(index,:) = []; 

khi tôi sử dụng các hình thức:

A = A(index,:); 

Tuy nhiên, chỉ số của tôi có thể được phủ nhận để sử dụng các hình thức đầu tiên (hàng lập chỉ mục để loại bỏ):

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

tôi gần như đã có một giải pháp vector nhưng không nhiều hơn một chút tiết hơn của bạn. Đẹp một lót. – Azim

+0

WoW ... rất thanh lịch ... –

5

Bạn cũng có thể lạm dụng hàm strmatch cho phù hợp với nhu cầu của mình: mã sau xóa tất cả các lần xuất hiện của một hàng nhất định b trong ma trận A

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

Nếu bạn cần phải xoá bỏ nhiều hơn một dòng, chẳng hạn như tất cả các hàng từ ma trận B, lặp qua chúng:

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

+1 Rất lén lút khi sử dụng STRMATCH để thực hiện việc này! Tôi không chắc tại sao ai đó đã downvoted nó ... Tôi chỉ thử nó bản thân mình và nó xuất hiện để làm việc tốt. Tôi đã làm một số thời gian đơn giản và thấy rằng sử dụng STRMATCH để có được một chỉ số ma trận là về cùng một tốc độ như tùy chọn đầu tiên của tôi ở trên, nhưng tùy chọn thứ hai của tôi là nhanh nhất. – gnovice

7

Giải pháp đơn giản ở đây là để tìm kiếm để thiết lập hàm thành viên, tức là, setdiff, union và ismember.

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

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

Xem nội dung nào thuộc về hai mảng. Sử dụng tùy chọn 'hàng'.

ismember(A,B,'rows') 

ans = 
    0 
    1 
    0 
    0 
    1 

Vì chúng ta muốn xóa hàng của A mà cũng nằm trong B, chỉ cần làm điều này:

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

A = 
     -4   0.5 
     2   3 
     4   0.5 

Ghi chú rằng chức năng bộ thành viên tìm kiếm một trận đấu chính xác. Số nguyên hoặc bội số của 1/2 chẳng hạn như trong A thỏa mãn yêu cầu đó. Chúng được biểu diễn chính xác trong số học dấu chấm động trong MATLAB.

Nếu những con số này là số dấu chấm động thực, tôi đã cẩn thận hơn. Ở đó tôi đã sử dụng một sự khoan dung về sự khác biệt. Trong trường hợp đó, tôi có thể đã tính ma trận khoảng cách giữa hai bộ số, loại bỏ một hàng A chỉ khi nó nằm trong khoảng cách nhất định của một trong các hàng của B.

+1

+1 Tôi quên mất cách ISMEMBER có thể hoạt động trên các hàng. Ngoài ra, chào mừng bạn đến với SO! ;) – gnovice

0

Không chắc chắn khi nào chức năng này là giới thiệu (sử dụng 2012b) nhưng bạn chỉ có thể làm:

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

    -4.0000 0.5000 
    2.0000 3.0000 
    4.0000 0.5000 

Dựa trên:

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]; 
Các vấn đề liên quan