Filter some rows from a matrix - matrix

Suppose I have this matrix:
matrix = [2 2; 2 3; 3 4; 4 5]
And now I'd like to filter out all rows which do not begin with an even number to produce
[2 2; 2 3; 4 5]
Is there a high-level procedure for doing this, or do I have to code for it?

You can get a logical index for the rows whose first element is even, and use : to select all the columns. Here's how it's done, line by line:
octave> matrix = [2 2; 2 3; 3 4; 4 5]
matrix =
2 2
2 3
3 4
4 5
octave> ! mod (matrix(:,1), 2)
ans =
1
1
0
1
octave> matrix(! mod (matrix(:,1), 2),:)
ans =
2 2
2 3
4 5
EDIT: in the comments below it was asked for other selection methods. I'm unaware of any specific function for it, but the thing above is indexing with a function:
even_rows = matrix(! mod (matrix(:,1), 2), :) # first element is even
s3_rows = matrix(matrix(:,1) == 3, :); # first element is 3
int_rows = matrix(fix (matrix(:,1)), == matrix(:,1), :); # first element is an integer
IF there was a function, one would still have to write the function, it wouldn't be any easier shorter or easier to read. But if you want to write a function, you could:
function selec = select_rows (func, mt)
selec = mt(func (mt(:,1)),:);
endfunction
even_rows = select_rows (#(x) ! mod (x, 2), matrix);
se_rows = select_rows (#(x) x == 3, matrix);
int_rows = select_rows (#(x) fix (x) == x, matrix);
EDIT2: to have the rows that have already matched, simply keep track of them on the mask. Example:
mask = ! mod (matrix(:,1), 2); # mask for even numbers
even = matrix(mask,:);
mask = ! mask & matrix(:,1) == 3; # mask for left overs starting with a 3
s3 = matrix(mask,:);
rest = matrix(! mask, :); # get the leftovers
As above, you could write a function that does it. It would take a matrix as the first argument plus any number of function handles. It would iterate over the function handles modifying the mask everytime and filling a cell array with the matrices.

Related

remove non matching elements from matrix

I am trying to compare two matrices A and B. If elements in the first two columns of A match those in B, I want to delete all non matching rows from A. The third column in B should not factor into the comparison.
A = [1 2 3 B = [1 2 8
3 4 5 3 4 5]
6 7 8]
Desired result:
A = [1 2 3
3 4 5]
So far I only found ways to remove duplicate entries, which is the exact opposite of what I want. How can I do this?
You can efficiently use ismember for this task:
% Input matrices
A = [1 2 3; 3 4 5; 7 8 9];
B = [1 2 8; 3 4 5];
A1 = A(:,1:2); % Extract first two columns for both matrices
B1 = B(:,1:2);
[~,ii] = ismember(A1,B1,'rows'); % Returns which rows in A1 are also in B1
ii = ii(ii>0); % Where ii is zero, it's a non-matching row
A(ii,:) % Index to keep only matching rows
All of this can be written more compactly, but I wanted to show the step-by-step process first:
[~,ii] = ismember(A(:,1:2),B(:,1:2),'rows');
A(ii(ii>0),:)
A = [1 2 3;3 4 5;7 8 9];
B = [1 2 8; 3 4 5];
tmp = min([size(A,1) size(B,1)]); % get size to loop over
k = false(tmp,1); % storage counter
for ii = 1:tmp
if all(A(ii,1:2)==B(ii,1:2)) % if the first two columns match
k(ii)=true; % store
end
end
C = A(k,:) % extract requested rows

Scanning Elements of A Matrix

I need to write a code that scan a matrix from the most left and down element to the right moving with diagonals.
For example for the matrix [1 2 3; 4 5 6] it should return 4,5,1,6,2,3
Any ideas where to start?
Since you didn't show your attempts, I'll let you figure out how this code works :-)
x = [1 2 3; 4 5 6];
m = bsxfun(#minus, (1:size(x,1)).', 1:size(x,2));
[~, ind] = sort(reshape(m, 1, []));
result = x(flip(ind));
You may need to read about
linear indexing;
bsxfun.
A solution using spdiags*:
x = [1 2 3; 4 5 6];
result = x(nonzeros(flipud(spdiags(reshape(1:numel(x),size(x))))));
*It may not be as fast as #LuisMendo's solution but it is one liner!

Vectorizing range setting - MATLAB

I have got the following code. I need to rewrite it without looping. How should I do it?
l1 = [1 2 3 2 1];
l2 = [3 4 4 5 4];
A = zeros(5,5);
for i=1:5
A(i, l1(i):l2(i)) = 1;
end
A
You can use bsxfun -
I = 1:5 % Array corresponding to iterator : "for i=1:5"
out = bsxfun(#le,l1(:),I) & bsxfun(#ge,l2(:),I)
If you need a double datatype array, convert to double, like so -
out_double = double(out)
Add one more into the mix then! This one simply uses a cumsum to generate all the 1s - so it does not use the : operator at all - It's also fully parallel :D
l1 = [1 2 3 2 1];
l2 = [3 4 4 5 4];
A = zeros(5,5);
L1 = l1+(1:5)*5-5; %Convert to matrix location index
L2 = l2+(1:5)*5-5; %Convert to matrix location index
A(L1) = 1; %Place 1 in that location
A(L2) = 1; %Place 1 in that location
B = cumsum(A,1) ==1 ; %So fast
Answer = (A|B)'; %Lightning fast
Answer =
1 1 1 0 0
0 1 1 1 0
0 0 1 1 0
0 1 1 1 1
1 1 1 1 0
Here is how you could build the matrix without using a loop.
% Our starting values
l1 = [1 2 3 2 1];
l2 = [3 4 4 5 4];
% Coordinate grid of the right size (we don't need r, but I keep it there for illustration)
[r,c] = ndgrid(1:5);
% Build the logical index based on our lower and upper bounds on the column indices
idx_l1=bsxfun(#ge,c,l1');
idx_l2=bsxfun(#le,c,l2');
% The result
A = zeros(size(idx_l1));
A(idx_l1&idx_l2)=1
You may need something like [r,c] = ndgrid(1:numel(l1),1:10).
Also if your matrix size is truly huge and memory becomes an issue, you may want to stick to a loop anyway, but for 'normal size' this could be faster.
There should be some skepticism in every vectorization. If you measure the time actually your loop is faster than the given answers, mostly because you only perform in place write.
Here is another one that would probably get faster for larger sizes but I haven't tested:
tic
myind = [];
for i = 1:5
myind = [myind (5*(i-1))+[l1(i):l2(i)]];
end
A(myind) = 1;
toc
gives the transposed A because of the linear indexing order.

Matlab: sorting a matrix in a unique way

I have a problem with sorting some finance data based on firmnumbers. So given is a matrix that looks like:
[1 3 4 7;
1 2 7 8;
2 3 7 8;]
On Matlab i would like the matrix to be sorted as follows:
[1 0 3 4 7 0;
1 2 0 0 7 8;
0 2 3 0 7 8;]
So basically every column needs to consist of 1 type of number.
I have tried many things but i cant get the matrix sorted properly.
A = [1 3 4 7;
1 2 7 8;
2 3 7 8;]
%// Get a unique list of numbers in the order that you want them to appear as the new columns
U = unique(A(:))'
%'//For each column (of your output, same as columns of U), find which rows have that number. Do this by making A 3D so that bsxfun compares each element with each element
temp1 = bsxfun(#eq,permute(A,[1,3,2]),U)
%// Consolidate this into a boolean matrix with the right dimensions and 1 where you'll have a number in your final answer
temp2 = any(temp1,3)
%// Finally multiply each line with U
bsxfun(#times, temp2, U)
So you can do that all in one line but I broke it up to make it easier to understand. I suggest you run each line and look at the output to see how it works. It might seem complicated but it's worthwhile getting to understand bsxfun as it's a really useful function. The first use which also uses permute is a bit more tricky so I suggest you first make sure you understand that last line and then work backwards.
What you are asking can also be seen as an histogram
A = [1 3 4 7;
1 2 7 8;
2 3 7 8;]
uniquevalues = unique(A(:))
N = histc(A,uniquevalues' ,2) %//'
B = bsxfun(#times,N,uniquevalues') %//'
%// bsxfun can replace the following instructions:
%//(the instructions are equivalent only when each value appears only once per row )
%// B = repmat(uniquevalues', size(A,1),1)
%// B(N==0) = 0
Answer without assumptions - Simplified
I did not feel comfortable with my old answer that makes the assumption of everything being an integer and removed the possibility of duplicates, so I came up with a different solution based on #lib's suggestion of using a histogram and counting method.
The only case I can see this not working for is if a 0 is entered. you will end up with a column of all zeros, which one might interpret as all rows initially containing a zero, but that would be incorrect. you could uses nan instead of zeros in that case, but not sure what this data is being put into, and if it that processing would freak out.
EDITED
Includes sorting of secondary matrix, B, along with A.
A = [-1 3 4 7 9; 0 2 2 7 8.2; 2 3 5 9 8];
B = [5 4 3 2 1; 1 2 3 4 5; 10 9 8 7 6];
keys = unique(A);
[counts,bin] = histc(A,transpose(unique(A)),2);
A_sorted = cell(size(A,1),1);
for ii = 1:size(A,1)
for jj = 1:numel(keys)
temp = zeros(1,max(counts(:,jj)));
temp(1:counts(ii,jj)) = keys(jj);
A_sorted{ii} = [A_sorted{ii},temp];
end
end
A_sorted = cell2mat(A_sorted);
B_sorted = nan(size(A_sorted));
for ii = 1:size(bin,1)
for jj = 1:size(bin,2)
idx = bin(ii,jj);
while ~isnan(B_sorted(ii,idx))
idx = idx+1;
end
B_sorted(ii,idx) = B(ii,jj);
end
end
B_sorted(isnan(B_sorted)) = 0
You can create at the beginning a matrix with 9 columns , and treat the values in your original matrix as column indexes.
A = [1 3 4 7;
1 2 7 8;
2 3 7 8;]
B = zeros(3,max(A(:)))
for i = 1:size(A,1)
B(i,A(i,:)) = A(i,:)
end
B(:,~any(B,1)) = []

Efficient way of finding rows in which A>B

Suppose M is a matrix where each row represents a randomized sequence of a pool of N objects, e.g.,
1 2 3 4
3 4 1 2
2 1 3 4
How can I efficiently find all the rows in which a number A comes before a number B?
e.g., A=1 and B=2; I want to retrieve the first and the second rows (in which 1 comes before 2)
There you go:
[iA jA] = find(M.'==A);
[iB jB] = find(M.'==B);
sol = find(iA<iB)
Note that this works because, according to the problem specification, every number is guaranteed to appear once in each row.
To find rows of M with a given prefix (as requested in the comments): let prefix be a vector with the sought prefix (for example, prefix = [1 2]):
find(all(bsxfun(#eq, M(:,1:numel(prefix)).', prefix(:))))
something like the following code should work. It will look to see if A comes before B in each row.
temp = [1 2 3 4;
3 4 1 2;
2 1 3 4];
A = 1;
B = 2;
orderMatch = zeros(1,size(temp,1));
for i = 1:size(temp,1)
match1= temp(i,:) == A;
match2= temp(i,:) == B;
aIndex = find(match1,1);
bIndex = find(match2,1);
if aIndex < bIndex
orderMatch(i) = 1;
end
end
solution = find(orderMatch);
This will result in [1,1,0] because the first two rows have 1 coming before 2, but the third row does not.
UPDATE
added find function on ordermatch to give row indices as suggested by Luis

Resources