Comparing only nonzero elements - matlab

The specific task I'm trying to achieve is hard to describe, so here's an example: given A and x
A = [1 2;
3 0;
3 5;
4 0];
x = [1 2 3];
I want the algorithm to output
output: [1 2]
meaning that all of the nonzero elements in rows 1 and 2 in A are in x.
I have done this using cell arrays and loops; however, A and x are very large and my approach is not at all efficient. Also, I can't seem to figure out how to rework ismember to give me what I want. What is the fastest/least memory intensive method?
EDIT: Apologies, my original example was too simplistic. It is corrected now.

The first answer is good, but I would recommend to not using arrayfun. There are more eloquent ways to do what you ask. Use ismember combined with all, then index into the matrix A when you're done. Basically, your problem is to determine if a row has all of the values found in x and ignoring the zero values. In this case, we can find all of the values in the matrix A that are actually zero, then use this to augment our result.
Using A as the first input and x as the second input will return a matrix of the same size as A that tells you whether an element in A is found in x. If you want to check if all elements in the matrix A for a row can be found in x, check if all elements in a row is 1. On top of this, find all of the elements that are zero, then with the output of ismember set these to 1. This can be done with using a logical OR. After, you can use all and check each row independently by using the output of ismember as the first input into all and setting the second argument to 2. This would then return all of the rows in the matrix A where any column is found in x ignoring any values that are zero for a row in A which is what you're looking for:
A = [1 2; 3 0; 4 0];
x = [1 2 3];
mask = ismember(A, x);
ind = all(mask | A == 0, 2);
I'm also in favour of one-liners. We can consolidate this into one line of code:
ind = all(ismember(A, x) | A == 0, 2);
Even shorter is to simply invert A. All zero elements become true and false otherwise:
ind = all(ismember(A, x) | ~A, 2);
ind would thus be:
>> ind
ind =
3×1 logical array
1
1
0
Since you want the actual row indices, you can just use find on top of this:
>> find(ind)
ans =
1
2
To verify, let's use your second example in your comments:
>> A = [1 2;3 5;4 0];
>> x = [1 2 3];
>> ind = all(ismember(A, x) | ~A, 2)
ind =
3×1 logical array
1
0
0
>> find(ind)
ans =
1

I think the best way to rework ismember is to make sure there are no "no members" by just checking for the nonzero elements in A.
arrayfun can do the work in a fast way. It uses the most efficient parallel computing for your specific machine. The following line should return the correct output:
find(arrayfun(#(a) sum(~ismember(A(a,A(a,:)>0),x)),1:size(A,1))==0)
Is this what you were looking for?
However, if your problem is related to memory, then you may have to break the arrayfun operation into pieces (1:floor(size(A,1)/2), floor(size(A,1)/2):size(A,1) or smaller chunks), since MATLAB puts a bunch of workers to do the task, and may use all your available RAM memory...

Related

MATLAB: Applying vectors of row and column indices without looping

I have a situation analogous to the following
z = magic(3) % Data matrix
y = [1 2 2]' % Column indices
So,
z =
8 1 6
3 5 7
4 9 2
y represents the column index I want for each row. It's saying I should take row 1 column 1, row 2 column 2, and row 3 column 2. The correct output is therefore 8 5 9.
I worked out I can get the correct output with the following
x = 1:3;
for i = 1:3
result(i) = z(x(i),y(i));
end
However, is it possible to do this without looping?
Two other possible ways I can suggest is to use sub2ind to find the linear indices that you can use to sample the matrix directly:
z = magic(3);
y = [1 2 2];
ind = sub2ind(size(z), 1:size(z,1), y);
result = z(ind);
We get:
>> result
result =
8 5 9
Another way is to use sparse to create a sparse matrix which you can turn into a logical matrix and then sample from the matrix with this logical matrix.
s = sparse(1:size(z,1), y, 1, size(z,1), size(z,2)) == 1; % Turn into logical
result = z(s);
We also get:
>> result
result =
8
5
9
Be advised that this only works provided that each row index linearly increases from 1 up to the end of the rows. This conveniently allows you to read the elements in the right order taking advantage of the column-major readout that MATLAB is based on. Also note that the output is also a column vector as opposed to a row vector.
The link posted by Adriaan is a great read for the next steps in accessing elements in a vectorized way: Linear indexing, logical indexing, and all that.
there are many ways to do this, one interesting way is to directly work out the indexes you want:
v = 0:size(y,2)-1; %generates a number from 0 to the size of your y vector -1
ind = y+v*size(z,2); %generates the indices you are looking for in each row
zinv = z';
zinv(ind)
>> ans =
8 5 9

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)

Does matrix contain a vector?

I'm looking for a fast / concise way to check whether some matrix contains given vector, e.g.:
bigMatrix = [1 1 1; 2 2 2; 4 4 4; 5 5 5];
someFunction(bigMatrix, [1 1 1]) % = true
someFunction(bigMatrix, [3 3 3]) % = false
Is there such function/operator, or I need a loop?
I would suggest the following solution:
bigMatrix = [1 1 1; 2 2 2; 4 4 4; 5 5 5];
Vec = [2 2 2];
Index = ismember(bigMatrix, Vec, 'rows');
The result?
Index =
0
1
0
0
ismember is an incredibly useful function that checks whether the elements of one set are in another set. Here, I exploit the rows option to force the function to compare rows, rather than individual elements.
UPDATE: On the other hand, it is always worth doing a few speed tests! I just compared the ismember approach to the following alternative method:
N = size(bigMatrix, 1);
Index2 = zeros(N, 1);
for n = 1:N
if all(bigMatrix(n, :) == Vec)
Index2(n) = 1;
end
end
My findings? The size of bigMatrix matters! In particular, if bigMatrix is on the small side (somewhat of a misnomer), then the loop is much faster. The first approach is preferable only when bigMatrix becomes big. Further, the results are also dependent on how many columns bigMatrix has, as well as rows! I suggest you test both approaches for your application and then go with whichever is faster. (EDIT: This was on R2011a)
General Note: I am continually surprised by how much faster Matlab's loops have gotten in the last few years. Methinks vectorized code is no longer the holy grail that it once was.

Update only one matrix element for iterative computation

I have a 3x3 matrix, A. I also compute a value, g, as the maximum eigen value of A. I am trying to change the element A(3,3) = 0 for all values from zero to one in 0.10 increments and then update g for each of the values. I'd like all of the other matrix elements to remain the same.
I thought a for loop would be the way to do this, but I do not know how to update only one element in a matrix without storing this update as one increasingly larger matrix. If I call the element at A(3,3) = p (thereby creating a new matrix Atry) I am able (below) to get all of the values from 0 to 1 that I desired. I do not know how to update Atry to get all of the values of g that I desire. The state of the code now will give me the same value of g for all iterations, as expected, as I do not know how to to update Atry with the different values of p to then compute the values for g.
Any suggestions on how to do this or suggestions for jargon or phrases for me to web search would be appreciated.
A = [1 1 1; 2 2 2; 3 3 0];
g = max(eig(A));
% This below is what I attempted to achieve my solution
clear all
p(1) = 0;
Atry = [1 1 1; 2 2 2; 3 3 p];
g(1) = max(eig(Atry));
for i=1:100;
p(i+1) = p(i)+ 0.01;
% this makes a one giant matrix, not many
%Atry(:,i+1) = Atry(:,i);
g(i+1) = max(eig(Atry));
end
This will also accomplish what you want to do:
A = #(x) [1 1 1; 2 2 2; 3 3 x];
p = 0:0.01:1;
g = arrayfun(#(x) eigs(A(x),1), p);
Breakdown:
Define A as an anonymous function. This means that the command A(x) will return your matrix A with the (3,3) element equal to x.
Define all steps you want to take in vector p
Then "loop" through all elements in p by using arrayfun instead of an actual loop.
The function looped over by arrayfun is not max(eig(A)) but eigs(A,1), i.e., the 1 largest eigenvalue. The result will be the same, but the algorithm used by eigs is more suited for your type of problem -- instead of computing all eigenvalues and then only using the maximum one, you only compute the maximum one. Needless to say, this is much faster.
First, you say 0.1 increments in the text of your question, but your code suggests you are actually interested in 0.01 increments? I'm going to operate under the assumption you mean 0.01 increments.
Now, with that out of the way, let me state what I believe you are after given my interpretation of your question. You want to iterate over the matrix A, where for each iteration you increase A(3, 3) by 0.01. Given that you want all values from 0 to 1, this implies 101 iterations. For each iteration, you want to calculate the maximum eigenvalue of A, and store all these eigenvalues in some vector (which I will call gVec). If this is correct, then I believe you just want the following:
% Specify the "Current" A
CurA = [1 1 1; 2 2 2; 3 3 0];
% Pre-allocate the values we want to iterate over for element (3, 3)
A33Vec = (0:0.01:1)';
% Pre-allocate a vector to store the maximum eigenvalues
gVec = NaN * ones(length(A33Vec), 1);
% Loop over A33Vec
for i = 1:1:length(A33Vec)
% Obtain the version of A that we want for the current i
CurA(3, 3) = A33Vec(i);
% Obtain the maximum eigen value of the current A, and store in gVec
gVec(i, 1) = max(eig(CurA));
end
EDIT: Probably best to paste this code into your matlab editor. The stack-overflow automatic text highlighting hasn't done it any favors :-)
EDIT: Go with Rody's solution (+1) - it is much better!

Creating matrix from two vectors of (duplicated) indices in MATLAB

Suppose now I have two vectors of same length:
A = [1 2 2 1];
B = [2 1 2 2];
I would like to create a matrix C whose dim=m*n, m=max(A), n=max(B).
C = zeros(m,n);
for i = 1:length(A)
u = A(i);
v = B(i);
C(u,v)=C(u,v)+1;
end
and get
C =[0 2;
1 1]
More precisely, we treat the according indices in A and B as rows and columns in C, and C(u,v) is the number of elements in {k | A(i)=u and B(i)=v, i = 1,2,...,length(A)}
Is there a faster way to do that?
Yes. Use sparse. It assembles (i.e., sums up) the matrix values for repeating row-column pairs for you. You need an additional vector with the values that will be assembled into the matrix entries. If you use ones(size(A)), you will have exactly what you need - counting of repeated row-column pairs
spA=sparse(A, B, ones(size(A)));
full(spA)
ans =
0 2
1 1
The same can be obtained by simply passing scalar 1 to sparse function instead of a vector of values.
For matrices that have a large number of zero entries this is absolutely crucial that you use sparse storage. Another function you could use is accumarray. It can essentially do the same thing, but also works on dense matrix structure:
AA=accumarray([A;B]', 1);
AA =
0 2
1 1
You can pass size argument to accumarray if you want to create a matrix of specific size
AA=accumarray([A;B]', 1, [2 3]);
AA =
0 2 0
1 1 0
Note that you can actually also make it produce sparse matrices, and use a different operator in assembly (i.e., not necessarily a sum)
AA=accumarray([A;B]', 1, [2 3], #sum, 0, true)
will produce a sparse matrix (last parameter set to true) using sum for assembly and 0 as a fill value, i.e. a value which is used in cases a given row-column pair does not exist in A/B.