Condition for columns based on same index as vector - matlab

I'm trying to get a logical matrix as a result of a condition that is specific for each column M(:,i) of the original matrix, based on the value of the same index i in vector N, that is, N(i).
I have looked this up online, but can't find anything quite like it. There must be a simple and clean way of doing this.
M =
3 -1 100 8
200 2 300 4
-10 0 0 400
N =
4 0 90 7
and my desired solution is, for each column of M(:,i), the values less than N(i):
1 1 0 0
0 0 0 1
1 0 1 0

It's a standard use-case for bsxfun:
O = bsxfun(#lt, M, N)
Here #lt is calling the "less than" function, i.e. it is the function handle to the < operator. bsxfun will then "expand" N along its singleton dimension by applying the function #lt to each row of M and the whole of N.
Note that you can easily achieve the same thing using a for-loop:
O = zeros(size(M));
for row = 1:size(M,1)
O(row,:) = M(row,:) < N;
end
Or by using repmat:
O = M < repmat(N, size(M,1), 1);
but in MATLAB the bsxfun is usually the most efficient.

Possible two-line solution using arrayfun to apply the comparison to each column and index pair:
T = arrayfun(#(jj)M(:,jj) < N(jj), 1:numel(N), 'UniformOutput', false);
result = cat(2,T{:});
Edit: Of course, the bsxfun solution is much more efficient.

Related

How to get indexes of logical matrix without using find in matlab?

Let's assume my matrix A is the output of comparison function i.e. logical matrix having values 0 and 1's only. For a small matrix of size 3*4, we might have something like:
A =
1 1 0 0
0 0 1 0
0 0 1 1
Now, I am generating another matrix B which is of the same size as A, but its rows are filled with indexes of A and any leftover values in each row are set to zero.
B =
1 2 0 0
3 0 0 0
3 4 0 0
Currently, I am using find function on each row of A to get matrix B. Complete code can be written as:
A=[1,1,0,0;0,0,1,0;0,0,1,1];
[rows,columns]=size(A);
B=zeros(rows,columns);
for i=1:rows
currRow=find(A(i,:));
B(i,1:length(currRow))=currRow;
end
For large martixes, "find" function is taking time in the calculation as per Matlab Profiler. Is there any way to generate matrix B faster?
Note:
Matrix A is having more than 1000 columns in each row but non-zero elements are never more than 50. Here, I am taking Matrix B as the same size as A but Matrix B can be of much smaller size column-wise.
I would suggest using parfor, but the overhead is too much here, and there are more issues with it, so it is not a good solution.
rows = 5e5;
cols = 1000;
A = rand(rows, cols) < 0.050;
I = uint16(1:cols);
B = zeros(size(A), 'uint16');
% [r,c] = find(A);
tic
for i=1:rows
% currRow = find(A(i,:));
currRow = I(A(i,:));
B(i,1:length(currRow)) = currRow;
end
toc
#Cris suggests replacing find with an indexing operation. It increases the performance by about 10%.
Apparently, there is not a better optimization unless B is required to be in that specific form you tell. I suggest using [r,c] = find(A); if the indexes are not required in a matrix form.

Find equal rows between two Matlab matrices

I have a matrix index in Matlab with size GxN and a matrix A with size MxN.
Let me provide an example before presenting my question.
clear
N=3;
G=2;
M=5;
index=[1 2 3;
13 14 15]; %GxN
A=[1 2 3;
5 6 7;
21 22 23;
1 2 3;
13 14 15]; %MxN
I would like your help to construct a matrix Response with size GxM with Response(g,m)=1 if the row A(m,:) is equal to index(g,:) and zero otherwise.
Continuing the example above
Response= [1 0 0 1 0;
0 0 0 0 1]; %GxM
This code does what I want (taken from a previous question of mine - just to clarify: the current question is different)
Response=permute(any(all(bsxfun(#eq, reshape(index.', N, [], G), permute(A, [2 3 4 1])), 1), 2), [3 4 1 2]);
However, the command is extremely slow for my real matrix sizes (N=19, M=500, G=524288). I understand that I will not be able to get huge speed but anything that can improve on this is welcome.
Aproach 1: computing distances
If you have the Statistics Toolbox:
Response = ~(pdist2(index, A));
or:
Response = ~(pdist2(index, A, 'hamming'));
This works because pdist2 computes the distance between each pair of rows. Equal rows have distance 0. The logical negation ~ gives 1 for those pairs of rows, and 0 otherwise.
Approach 2: reducing rows to unique integer labels
This approach is faster on my machine:
[~,~,u] = unique([index; A], 'rows');
Response = bsxfun(#eq, u(1:G), u(G+1:end).');
It works by reducing rows to unique integer labels (using the third output of unique), and comparing the latter instead of the former.
For your size values this takes approximately 1 second on my computer:
clear
N = 19; M = 500; G = 524288;
index = randi(5,G,N); A = randi(5,M,N);
tic
[~,~,u] = unique([index; A], 'rows');
Response = bsxfun(#eq, u(1:G), u(G+1:end).');
toc
gives
Elapsed time is 1.081043 seconds.
MATLAB has a multitude of functions for working with sets, including setdiff, intersect, union etc. In this case, you can use the ismember function:
[~, Loc] = ismember(A,index,'rows');
Which gives:
Loc =
1
0
0
1
2
And Response would be constructed as follows:
Response = (1:size(index,1) == Loc).';
Response =
2×5 logical array
1 0 0 1 0
0 0 0 0 1
You could reshape the matrices so that each row instead lies along the 3rd dimension. Then we can use implicit expansion (see bsxfun for R2016b or earlier) for equality of all elements, and all to aggregate on the rows (i.e. false if not all equal for a given row).
Response = all( reshape( index, [], 1, size(index,2) ) == reshape( A, 1, [], size(A,2) ), 3 );
You might even be able to avoid some reshaping by using all in another dimension, but it's easier for me to visualise it this way.

Finding equal rows in Matlab

I have a matrix suppX in Matlab with size GxN and a matrix A with size MxN. I would like your help to construct a matrix Xresponse with size GxM with Xresponse(g,m)=1 if the row A(m,:) is equal to the row suppX(g,:) and zero otherwise.
Let me explain better with an example.
suppX=[1 2 3 4;
5 6 7 8;
9 10 11 12]; %GxN
A=[1 2 3 4;
1 2 3 4;
9 10 11 12;
1 2 3 4]; %MxN
Xresponse=[1 1 0 1;
0 0 0 0;
0 0 1 0]; %GxM
I have written a code that does what I want.
Xresponsemy=zeros(size(suppX,1), size(A,1));
for x=1:size(suppX,1)
Xresponsemy(x,:)=ismember(A, suppX(x,:), 'rows').';
end
My code uses a loop. I would like to avoid this because in my real case this piece of code is part of another big loop. Do you have suggestions without looping?
One way to do this would be to treat each matrix as vectors in N dimensional space and you can find the L2 norm (or the Euclidean distance) of each vector. After, check if the distance is 0. If it is, then you have a match. Specifically, you can create a matrix such that element (i,j) in this matrix calculates the distance between row i in one matrix to row j in the other matrix.
You can treat your problem by modifying the distance matrix that results from this problem such that 1 means the two vectors completely similar and 0 otherwise.
This post should be of interest: Efficiently compute pairwise squared Euclidean distance in Matlab.
I would specifically look at the answer by Shai Bagon that uses matrix multiplication and broadcasting. You would then modify it so that you find distances that would be equal to 0:
nA = sum(A.^2, 2); % norm of A's elements
nB = sum(suppX.^2, 2); % norm of B's elements
Xresponse = bsxfun(#plus, nB, nA.') - 2 * suppX * A.';
Xresponse = Xresponse == 0;
We get:
Xresponse =
3×4 logical array
1 1 0 1
0 0 0 0
0 0 1 0
Note on floating-point efficiency
Because you are using ismember in your implementation, it's implicit to me that you expect all values to be integer. In this case, you can very much compare directly with the zero distance without loss of accuracy. If you intend to move to floating-point, you should always compare with some small threshold instead of 0, like Xresponse = Xresponse <= 1e-10; or something to that effect. I don't believe that is needed for your scenario.
Here's an alternative to #rayryeng's answer: reduce each row of the two matrices to a unique identifier using the third output of unique with the 'rows' input flag, and then compare the identifiers with singleton expansion (broadcast) using bsxfun:
[~, ~, w] = unique([A; suppX], 'rows');
Xresponse = bsxfun(#eq, w(1:size(A,1)).', w(size(A,1)+1:end));

Matlab:Efficient assignment of values in a sparse matrix

I'm working in Matlab and I have the next problem:
I have a B matrix of nx2 elements, which contains indexes for the assignment of a big sparse matrix A (almost 500,000x80,000). For each row of B, the first column is the column index of A that has to contain a 1, and the second column is the column index of A that has to contain -1.
For example:
B= 1 3
2 5
1 5
4 1
5 2
For this B matrix, The Corresponding A matrix has to be like this:
A= 1 0 -1 0 0
0 1 0 0 -1
1 0 0 0 -1
-1 0 0 1 0
0 -1 0 0 1
So, for the row i of B, the corresponding row i of A must be full of zeros except on A(i,B(i,1))=1 and A(i,B(i,2))=-1
This is very easy with a for loop over all the rows of B, but it's extremely slow. I also tried the next formulation:
A(:,B(:,1))=1
A(:,B(:,2))=-1
But matlab gave me an "Out of Memory Error". If anybody knows a more efficient way to achieve this, please let me know.
Thanks in advance!
You can use the sparse function:
m = size(B,1); %// number of rows of A. Or choose larger if needed
n = max(B(:)); %// number of columns of A. Or choose larger if needed
s = size(B,1);
A = sparse(1:s, B(:,1), 1, m, n) + sparse(1:s, B(:,2), -1, m, n);
I think you should be able to do this using the sub2ind function. This function converts matrix subscripts to linear indices. You should be able to do it like so:
pind = sub2ind(size(A),1:n,B(:,1)); % positive indices
nind = sub2ind(size(A),1:n,B(:,2)); % negative indices
A(pind) = 1;
A(nind) = -1;
EDIT: I (wrongly, I think) assumed the sparse matrix A already existed. If it doesn't exist, then this method wouldn't be the best option.

Entropy of Matrix using Matlab

Given a matrix A with dimension m x n and the entries in the matrix lies [0,1]
For example
A = [0.5 0 0 0.5 0
0 0.5 0 0 0.5
1 0 0 0 0]
I would like to calculate sum(sum(a_ij log(a_ij))), where a_ij is the i th row and j th col entry in the matrix A. Since there exist an 0 entry in the matrix, i always get NAN as a result.
How do i consider only non-zero entries to calculate sum(sum(a_ij log(a_ij))) [entropy of the matrix].
To consider only specific elements of a matrix you can use logical indexing. For example if you only want to select non-zero entries of A you can use A(A~=0). So for your problem the solution can be written:
sum(A(A~=0).*log(A(A~=0)));
EDIT: wow that is some kind of coincidence, I've just seen your comment after posting this. Well, glad you've worked it out yourself.
If it is a very large array:
sum(A.*log(A+eps))
which should be faster than indexing.
Another possibility:
x = A(:);
E = x' * log(x + (x==0))