Splitting a matrix based on its contents in MATLAB - matlab

A matrix has m rows and n columns (n being a number not exceeding 10), and the nth column contains either 1 or 0 (binary). I want to use this binary as a decision to take out the associated row (if 1, or otherwise if 0). I understand that this can be done through iteration with the use of the IF conditional.
However, this may become impractical with matrices whose number of rows m gets into the hundreds (up to 1000). What other procedures are available?

You can use logical datatypes for indexing. For example,
M =
1 2 0
4 5 1
7 8 0
M = [1 2 0;4 5 1;7 8 0];
v = (M(:,n) == 1);
M(v,2) = 1;
M =
1 2 0
4 1 1
7 8 0
Now you have set all the elements in column 2 to 1 if the corresponding element in column n is true.
Note that the v = (M(:,n) == 1) converts the nth column to a logical vector. You can accomplish the same with v = logical(M(:,n));
I would recommend this blog entry for a detailed look at logical indexing.
Update:
If you want to erase rows, then use:
M(v,:) = [];

Related

How do I compare elements of one array to a column of a matrix, then shorten the matrix correspondingly?

I have a matrix 'Z' sized 100000x2 and imported as an Excel file using readmatrix. I have a created array 'Time' (Time = [-200:0.1:300]'). I would like to compare all values in column 1 of 'Z' to 'Time' and eliminate all values of column 1 of 'Z' that do not equal a value of 'Time', thus shortening my 'Z' matrix to match my desired time values. Column 2 are pressure traces, so this would give me my desired time values and the corresponding pressure trace.
This sort of thing can be done without loops:
x = [1,2,3,4,1,1,2,3,4];
x = [x', (x+1)'] % this is your 'Z' data from the excel file (toy example here)
x =
1 2
2 3
3 4
4 5
1 2
1 2
2 3
3 4
4 5
y = [1,2]; % this is your row of times you want eliminated
z = x(:,1)==y % create a matrix logical arrays indicating the matches in the first column
z =
9×2 logical array
1 0
0 1
0 0
0 0
1 0
1 0
0 1
0 0
0 0
z = z(:,1)+z(:,2); % there is probably another summing technique that is better for your case
b = [x(z~=1,1), x(z~=1,2)] % use matrix operations to extract the desired rows
b =
3 4
4 5
3 4
4 5
All the entries of x where the first column did not equal 1 or 2 are now gone.
x = ismember(Z(:,1),Time); % logical indexes of the rows you want to keep
Z(~x,:) = []; % get rid of the other rows
Or instead of shortening Z you could create a new array to use downstream in your code:
x = ismember(Z(:,1),Time); % logical indexes of the rows you want to keep
Znew = Z(x,:); % the subset you want
You have to loop over all rows, use a nested if statement to check the item, and delete the row if it doesn't match.
Syntax for loops:
for n = 1:100000:
//(operation)//
end
Syntax for if statements:
if x == y
//(operation)//
Syntax for deleting a row: Z(rownum,:) = [];

Output a matrix size n x m, 1 when the sum of the indices is even, 0 otherwise

I'm attempting the following as a hobby, not as homework. In Computer Programming with MATLAB: J. Michael Fitpatrick and Akos Ledeczi, there is a practice problem that asks this:
Write a function called alternate that takes two positive integers, n and m, as input arguments (the function does not have to check the format of the input) and returns one matrix as an output argument. Each element of the n-by-m output matrix for which the sum of its indices is even is 1.
All other elements are zero.
A previous problem was similar, and I wrote a very simple function that does what it asks:
function A = alternate(n,m)
A(1:n,1:m)=0;
A(2:2:n,2:2:m)=1;
A(1:2:n,1:2:m)=1;
end
Now my question is, is that good enough? It outputs exactly what it asks for, but it's not checking for the sum. So far we haven't discussed nested if statements or anything of that sort, we just started going over very basic functions. I feel like giving it more functionality would allow it to be recycled better for future use.
Great to see you're learning, step 1 in learning any programming language should be to ensure you always add relevant comments! This helps you, and anyone reading your code. So the first improvement would be this:
function A = alternate(n,m)
% Function to return an n*m matrix, which is 1 when the sum of the indices is even
A(1:n,1:m)=0; % Create the n*m array of zeros
A(2:2:n,2:2:m)=1; % All elements with even row and col indices: even+even=even
A(1:2:n,1:2:m)=1; % All elements with odd row and col indicies: odd+odd=even
end
You can, however, make this more concise (discounting comments), and perhaps more clearly relate to the brief:
function A = alternate(n,m)
% Function to return an n*m matrix, which is 1 when the sum of the indices is even
% Sum of row and col indices. Uses implicit expansion (R2016b+) to form
% a matrix from a row and column array
idx = (1:n).' + (1:m);
% We want 1 when x is even, 0 when odd. mod(x,2) is the opposite, so 1-mod(x,2) works:
A = 1 - mod( idx, 2 );
end
Both functions do the same thing, and it's personal preference (and performance related for large problems) which you should use.
I'd argue that, even without comments, the alternative I've written more clearly does what it says on the tin. You don't have to know the brief to understand you're looking for the even index sums, since I've done the sum and tested if even. Your code requires interpretation.
It can also be written as a one-liner, whereas the indexing approach can't be (as you've done it).
A = 1 - mod( (1:n).' + (1:m), 2 ); % 1 when row + column index is even
Your function works fine and output the desired result, let me propose you an alternative:
function A = alternate(n,m)
A = zeros( n , m ) ; % pre-allocate result (all elements at 0)
[x,y] = meshgrid(1:m,1:n) ; % define a grid of indices
A(mod(x+y,2)==0) = 1 ; % modify elements of "A" whose indices verify the condition
end
Which returns:
>> alternate(4,5)
ans =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
initialisation:
The first line is the equivalent to your first line, but it is the cannonical MATLAB way of creating a new matrix.
It uses the function zeros(n,m).
Note that MATLAB has similar functions to create and preallocate matrices for different types, for examples:
ones(n,m) Create
a matrix of double, size [n,m] with all elements set to 1
nan(n,m) Create a
matrix of double, size [n,m] with all elements set to NaN
false(n,m) Create a
matrix of boolean size [n,m] with all elements set to false
There are several other matrix construction predefined function, some more specialised (like eye), so before trying hard to generate your initial matrix, you can look in the documentation if a specialised function exist for your case.
indices
The second line generate 2 matrices x and y which will be the indices of A. It uses the function meshgrid. For example in the case shown above, x and y look like:
| x = | y = |
| 1 2 3 4 5 | 1 1 1 1 1 |
| 1 2 3 4 5 | 2 2 2 2 2 |
| 1 2 3 4 5 | 3 3 3 3 3 |
| 1 2 3 4 5 | 4 4 4 4 4 |
odd/even indices
To calculate the sum of the indices, it is now trivial in MATLAB, as easy as:
>> x+y
ans =
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
Now we just need to know which ones are even. For this we'll use the modulo operator (mod) on this summed matrix:
>> mod(x+y,2)==0
ans =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
This result logical matrix is the same size as A and contain 1 where the sum of the indices is even, and 0 otherwise. We can use this logical matrix to modify only the elements of A which satisfied the condition:
>> A(mod(x+y,2)==0) = 1
A =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
Note that in this case the logical matrix found in the previous step would have been ok since the value to assign to the special indices is 1, which is the same as the numeric representation of true for MATLAB. In case you wanted to assign a different value, but the same indices condition, simply replace the last assignment:
A(mod(x+y,2)==0) = your_target_value ;
I don't like spoiling the learning. So let me just give you some hints.
Matlab is very efficient if you do operations on vectors, not on individual elements. So, why not creating two matrices (e.g. N, M) that holds all the indices? Have a look at the meshgrid() function.
Than you might be able find all positions with an even sum of indices in one line.
Second hint is that the outputs of a logic operation, e.g. B = A==4, yields a logic matrix. You can convert this to a matrix of zeros by using B = double(B).
Have fun!

in matlab delete an entire row if the elements in certain columns equal zero

I matlab I have a matrix of 4 columns. I want to delete an entire row if the elements in columns two, three and four all equal zero.
So, I think I need to do something like this but I want to ignore column one:
data(all(data==0,2),:) = [];
You can use logical indexing + any
data = data(any(data(:,2:4),2),:);
Here instead of deleting rows we preserve a row if the conditions is not satisfied for it.
% toy example
>> a = [1,0,0,1;
2,0,2,0;
3,0,0,0;
4,4,0,4;
5,0,0,0;
6,0,0,0;
7,0,7,0;
8,0,0,8;
9,0,0,0];
% solution
>> a(sum ( a(:, 2:4) == 0, 2) == 3, :) = []
a =
1 0 0 1
2 0 2 0
4 4 0 4
7 0 7 0
8 0 0 8
I.e. if the sum of the number of zeros across columns 2 to 4 equals 3, delete that row.
(i.e. use logical indexing)
You can do it like that:
a(sum((a(:,2:4)==0)')==3,:)=[]

Condition execute for different columns in each row

A = [0,0,1,0,1,0,1,0,0,0;
0,0,0,0,1,0,1,0,0,0;
0,0,1,0,1,0,1,0,0,0];
B = [2,5;
1,6;
3,10];
Expected Output Cell Array:
C = [1,1,1,1; %// 2-3-4, 3-4-5, 4-5, 5
0,0,1,1,1,0; %// 1-2-3, 2-3-4, 3-4-5, 4-5-6, 5-6, 6
1,1,1,1,1,0,0,0]; %// 3-4-5, 4-5-6, 5-6-7, 7-8-9, 8-9-10, 9-10, 10
Matrix B includes which columns should be used to execute the condition on Matrix A. For example, first row of B is 2 and 5; so elements between 2nd 5th column of matrix A should be used to execute the condition. Second row of B is 1 and 6; so elements between 1st 6th column should be used to execute the condition. And so on...
The condition: if sum of successive 3 elements is bigger than or equal to 1 then write 1 to matrix C; otherwise write 0. For example, A includes 0,1,0 as three successive elements (sum is 0+1+0=1), so write 1 to matrix C. Another example, first three elements of A in second row are 0,0,0 (sum is 0), so write 0 to matrix C. And so on...
"Sometimes it can be considered only 1 or 2 successive elements."
For example, condition execution of first row of A ends with 5th column, so only value 5th column should be considered; which is 1. So 1 is written to matrix C.
Explaining the first row of C:
1, since (sum of 2,3,4 elements of A(1,:)) >= 1
1, since (sum of 3,4,5 elements of A(1,:)) >= 1
since max limit is 5, only 2 successive elements are taken here
1, since (sum of 4,5 elements alone of A(1,:)) >= 1
since max limit is 5, only 1 successive element is taken here
1, since (sum of 5th element alone of A(1,:)) >= 1
Without for loop, only with matrix operations, how can I do this complex task? or any trick?
Using mat2cell, cellfun, im2col and any
subMatLen = 3;
%// Converting both A & B matrix to Cell Arrays to perform operations Row-wise
AC = mat2cell(A,ones(1,size(A,1)),size(A,2));
BC = mat2cell(B,ones(1,size(B,1)),size(B,2));
%// Getting only the columns of each rows within the limits specified by Matrix B
%// Also appended with zeros for my own convenience as it wont affect the 'summing' process
out = cellfun(#(x,y) [x(y(1):y(2)),zeros(1,subMatLen-1)],AC, BC, 'uni', 0);
%// Finally taking each 1x3 sliding sub-matrix and returning 1 if `any` of it is non-zero
%// which is equivalent to summing and checking whether they are >= 1
out = cellfun(#(x) any(im2col(x, [1,subMatLen], 'sliding')), out, 'uni', 0);
Your Sample Input:
A = [0,0,1,0,1,0,1,0,0,0;
0,0,0,0,1,0,1,0,0,0;
0,0,1,0,1,0,1,0,0,0];
B = [2,5;
1,6;
3,10];
Output:
>> celldisp(out)
out{1} =
1 1 1 1
out{2} =
0 0 1 1 1 0
out{3} =
1 1 1 1 1 0 0 0
If you want them as a single row or column matrix, you could add this to the bottom of the code:
out = cat(2,out{:})
or
out = (cat(2,out{:})).'

Eliminating zeros in a matrix - Matlab

Hi I have the following matrix:
A= 1 2 3;
0 4 0;
1 0 9
I want matrix A to be:
A= 1 2 3;
1 4 9
PS - semicolon represents the end of each column and new column starts.
How can I do that in Matlab 2014a? Any help?
Thanks
The problem you run into with your problem statement is the fact that you don't know the shape of the "squeezed" matrix ahead of time - and in particular, you cannot know whether the number of nonzero elements is a multiple of either the rows or columns of the original matrix.
As was pointed out, there is a simple function, nonzeros, that returns the nonzero elements of the input, ordered by columns. In your case,
A = [1 2 3;
0 4 0;
1 0 9];
B = nonzeros(A)
produces
1
1
2
4
3
9
What you wanted was
1 2 3
1 4 9
which happens to be what you get when you "squeeze out" the zeros by column. This would be obtained (when the number of zeros in each column is the same) with
reshape(B, 2, 3);
I think it would be better to assume that the number of elements may not be the same in each column - then you need to create a sparse array. That is actually very easy:
S = sparse(A);
The resulting object S is a sparse array - that is, it contains only the non-zero elements. It is very efficient (both for storage and computation) when lots of elements are zero: once more than 1/3 of the elements are nonzero it quickly becomes slower / bigger. But it has the advantage of maintaining the shape of your matrix regardless of the distribution of zeros.
A more robust solution would have to check the number of nonzero elements in each column and decide what the shape of the final matrix will be:
cc = sum(A~=0);
will count the number of nonzero elements in each column of the matrix.
nmin = min(cc);
nmax = max(cc);
finds the smallest and largest number of nonzero elements in any column
[i j s] = find(A); % the i, j coordinates and value of nonzero elements of A
nc = size(A, 2); % number of columns
B = zeros(nmax, nc);
for k = 1:nc
B(1:cc(k), k) = s(j == k);
end
Now B has all the nonzero elements: for columns with fewer nonzero elements, there will be zero padding at the end. Finally you can decide if / how much you want to trim your matrix B - if you want to have no zeros at all, you will need to trim some values from the longer columns. For example:
B = B(1:nmin, :);
Simple solution:
A = [1 2 3;0 4 0;1 0 9]
A =
1 2 3
0 4 0
1 0 9
A(A==0) = [];
A =
1 1 2 4 3 9
reshape(A,2,3)
ans =
1 2 3
1 4 9
It's very simple though and might be slow. Do you need to perform this operation on very large/many matrices?
From your question it's not clear what you want (how to arrange the non-zero values, specially if the number of zeros in each column is not the same). Maybe this:
A = reshape(nonzeros(A),[],size(A,2));
Matlab's logical indexing is extremely powerful. The best way to do this is create a logical array:
>> lZeros = A==0
then use this logical array to index into A and delete these zeros
>> A(lZeros) = []
Finally, reshape the array to your desired size using the built in reshape command
>> A = reshape(A, 2, 3)