Fastest way of generating a logical matrix by given row indices of true values? - matlab

What is the most efficient way of generating
>> A
A =
0 1 1
1 1 0
1 0 1
0 0 0
with
>> B = [2 3; 1 2; 1 3]
B =
2 3
1 2
1 3
in MATLAB?
E.g., B(1, :), which is [2 3], means that A(2, 1) and A(3, 1) are true.
My attempt still requires one for loop, iterating through B's row. Is there a loop-free or more efficient way of doing this?

This is one way of many, though sub2ind is the dedicated function for that:
%// given row indices
B = [2 3; 1 2; 1 3]
%// size of row index matrix
[n,m] = size(B)
%// size of output matrix
[N,M] = deal( max(B(:)), n)
%// preallocation of output matrix
A = zeros(N,M)
%// get col indices to given row indices
cols = bsxfun(#times, ones(n,m),(1:n).')
%// set values
A( sub2ind([N,M],B,cols) ) = 1
A =
0 1 1
1 1 0
1 0 1
If you want a logical matrix, change the following to lines
A = false(N,M)
A( sub2ind([N,M],B,cols) ) = true
Alternative solution
%// given row indices
B = [2 3; 1 2; 1 3];
%// number if rows
r = 4; %// e.g. = max(B(:))
%// number if cols
c = 3; %// size(B,1)
%// preallocation of output matrix
A = zeros(r,c);
%// set values
A( bsxfun(#plus, B.', 0:r:(r*(c-1))) ) = 1;

Here's a way, using the sparse function:
A = full(sparse(cumsum(ones(size(B))), B, 1));
This gives
A =
0 1 1
1 1 0
1 0 1
If you need a predefined number of rows in the output, say r (in your example r = 4):
A = full(sparse(cumsum(ones(size(B))), B, 1, 4, size(B,1)));
which gives
A =
0 1 1
1 1 0
1 0 1
0 0 0
You can equivalently use the accumarrray function:
A = accumarray([repmat((1:size(B,1)).',size(B,2),1), B(:)], 1);
gives
A =
0 1 1
1 1 0
1 0 1
Or with a predefined number of rows, r = 4,
A = accumarray([repmat((1:size(B,1)).',size(B,2),1), B(:)], 1, [r size(B,1)]);
gives
A =
0 1 1
1 1 0
1 0 1
0 0 0

Related

How to leave only the elements that appear once per specific row x column per page?

I have a 3D array. I need to remove any elements that are in the same row, column position but on the next page (3rd dimension), and only use the first occurrence at that position. So if all pages were to multiply the result would be 0.
Since the 3D array may be of any size, I can't hard code solutions like isMember. I also can't use unique because elements can be the same, just not share the same position.
For example, input:
A(:,:,1) = [ 1 0 2];
A(:,:,2) = [ 1 1 0];
A(:,:,3) = [ 0 1 0];
the desired output is:
A(:,:,1) = [ 1 0 2];
A(:,:,2) = [ 0 1 0];
A(:,:,3) = [ 0 0 0];
How can I accomplish this?
Not the most elegant, but at least it works.
A(:,:,1) = [ 1 0 2 ];
A(:,:,2) = [ 1 1 0 ];
A(:,:,3) = [ 0 1 0 ];
for ii = 1:size(A,1)
for jj = 1:size(A,2)
unique_el = unique(A(ii, jj, :)); % Grab unique elements
for kk = 1:numel(unique_el)
idx = find(A(ii,jj,:) == kk); % Contains indices of unique elements
if numel(idx) > 1 % If an element occurs more than once
A(ii, jj, idx(2:end)) = 0; % Set to 0
end
end
end
end
A
A(:,:,1) =
1 0 2
A(:,:,2) =
0 1 0
A(:,:,3) =
0 0 0
I loop over the first two dimensions of A (rows and columns), find any unique elements which occur on a certain row and column location through the third dimensions (pages). Then set all occurrences of a unique element after the first to 0.
Given a more elaborate 3D matrix this still works:
A(:,:,1) = [1 0 2 0; 2 1 3 0];
A(:,:,2) = [1 1 0 0; 2 2 1 0];
A(:,:,3) = [0 1 1 3; 1 2 2 4];
A(:,:,1) =
1 0 2 0
2 1 3 0
A(:,:,2) =
0 1 0 0
0 2 1 0
A(:,:,3) =
0 0 1 3
1 0 2 4
If you want the first non-zero element and discard any element occurring afterwards, simply get rid of the unique() call:
A(:,:,1) = [1 0 2 0; 2 1 3 0];
A(:,:,2) = [1 1 0 0; 2 2 1 0];
A(:,:,3) = [0 1 1 3; 1 2 2 4];
for ii = 1:size(A,1)
for jj = 1:size(A,2)
idx = find(A(ii,jj,:) ~= 0); % Contains indices of nonzero elements
if numel(idx) > 1 % If more than one element
A(ii, jj, idx(2:end)) = 0; % Set rest to 0
end
end
end
A(:,:,1) =
1 0 2 0
2 1 3 0
A(:,:,2) =
0 1 0 0
0 0 0 0
A(:,:,3) =
0 0 0 3
0 0 0 4
My solution assumes, that, for a given "position", EVERY value after the first occurence of any value is cleared. Some of the MATLAB regulars around here had some discussions on that, from there comes the "extended" example as also used in Adriaan's answer.
I use permute and reshape to rearrange the input, so that we have all "positions" as "page" columns in a 2D array. Then, we can use arrayfun to find the proper indices of the first occurence of a non-zero value (kudos to LuisMendo's answer here). Using this approach again, we find all indices to be set to 0.
Let's have a look at the following code:
A(:,:,1) = [1 0 2 0; 2 1 3 0];
A(:,:,2) = [1 1 0 0; 2 2 1 0];
A(:,:,3) = [0 1 1 3; 1 2 2 4]
[m, n, o] = size(A);
B = reshape(permute(A, [3 1 2]), o, m*n);
idx = arrayfun(#(x) find(B(:, x), 1, 'first'), 1:size(B, 2));
idx = arrayfun(#(x) find(B(idx(x)+1:end, x)) + idx(x) + 3*(x-1), 1:size(B, 2), 'UniformOutput', false);
idx = vertcat(idx{:});
B(idx) = 0;
B = permute(reshape(B, o, m , n), [2, 3, 1])
Definitely, it makes sense to have a look at the intermediate outputs to understand the functioning of my approach. (Of course, some lines can be combined, but I wanted to keep a certain degree of readability.)
And, here's the output:
A =
ans(:,:,1) =
1 0 2 0
2 1 3 0
ans(:,:,2) =
1 1 0 0
2 2 1 0
ans(:,:,3) =
0 1 1 3
1 2 2 4
B =
ans(:,:,1) =
1 0 2 0
2 1 3 0
ans(:,:,2) =
0 1 0 0
0 0 0 0
ans(:,:,3) =
0 0 0 3
0 0 0 4
As you can see, it's identical to Adriaan's second version.
Hope that helps!
A vectorized solution. You can use the second output of max to find the index of the first occurence of a nonzero value along the third dimension and then use sub2ind to convert that to linear index.
A(:,:,1) = [ 1 0 2];
A(:,:,2) = [ 1 1 0];
A(:,:,3) = [ 0 1 0];
[~, mi] =max(logical(A) ,[], 3);
sz=size(A) ;
[x, y] =ndgrid(1:sz(1),1:sz(2));
idx=sub2ind( sz, x,y,mi);
result=zeros(sz) ;
result(idx) =A(idx);

Cartesian product of row-indices in Matlab

I have a binary matrix A of dimension mxn with m>n in Matlab. I want to construct a matrix B of dimension cxn listing row wise each element of the Cartesian product of the row indices of the ones contained in A. To be more clear consider the following example.
Example:
%m=4;
%n=3;
A=[1 0 1;
0 0 1;
1 1 0;
0 0 1];
%column 1: "1" are at rows {1,3}
%column 2: "1" are at row {3}
%column 3: "1" are at rows {1,2,4}
%Hence, the Cartesian product {1,3}x{3}x{1,2,4} is
%{(1,3,1),(1,3,2),(1,3,4),(3,3,1),(3,3,2),(3,3,4)}
%I construct B by disposing row-wise each 3-tuple in the Cartesian product
%c=6
B=[1 3 1;
1 3 2;
1 3 4;
3 3 1;
3 3 2;
3 3 4];
You can get the cartesian product with the combvec command, for your example:
A=[1 0 1;...
0 0 1;...
1 1 0;...
0 0 1];
[x y]=find(A);
B=combvec(x(y==1).',x(y==2).',x(y==3).').';
% B =
% 1 3 1
% 3 3 1
% 1 3 2
% 3 3 2
% 1 3 4
% 3 3 4
You can expand this to an unknown number of columns by using the associative property of the product.
[x y]=find(A);
u_y=unique(y);
B=x(y==u_y(1)).';
for i=2:length(u_y)
B=combvec(B, x(y==u_y(i)).');
end
B=B.';
One solution (without toolbox):
A= [1 0 1;
0 0 1;
1 1 0;
0 0 1];
[ii,jj] = find(A)
kk = unique(jj);
for i = 1:length(kk)
v{i} = ii(jj==kk(i));
end
t=cell(1,length(kk));
[t{:}]= ndgrid(v{:});
product = []
for i = 1:length(kk)
product = [product,t{i}(:)];
end
You can use use accumarray to obtain vectors with the row indices of nonzero elements for each column. This works for an arbitrary number of columns:
[ii, jj] = find(A);
vectors = accumarray(jj, ii, [], #(x){sort(x.')});
Then apply this answer to efficiently compute the Cartesian product of those vectors:
n = numel(vectors);
B = cell(1,n);
[B{end:-1:1}] = ndgrid(vectors{end:-1:1});
B = cat(n+1, B{:});
B = reshape(B,[],n);
In your example, this gives
B =
1 3 1
1 3 2
1 3 4
3 3 1
3 3 2
3 3 4
In short, I would use find to generate the indices needed for the Cartesian product and then use ndgrid to perform the Cartesian product of these indices. The code to do so is:
clear
close all
clc
A = [1 0 1;
0 0 1;
1 1 0;
0 0 1];
[row,col] = find(A);
[~,ia,~] = unique(col);
n_cols = size(A,2);
indices = cell(n_cols,1);
for ii = 1:n_cols-1
indices{ii} = row(ia(ii):ia(ii+1)-1);
end
indices{end} = row(ia(end):end);
cp_temp = cell(n_cols,1);
[cp_temp{:}] = ndgrid(indices{:});
cp = NaN(numel(cp_temp{1}),n_cols);
for ii = 1:n_cols
cp(:,ii) = cp_temp{ii}(:);
end
cp = sortrows(cp);
cp

MATLAB - finding max/min in selected rows/columns of a matrix

if i have a matrix, say:
A = [ 0 2 4 0
2 0 5 0
4 5 0 3
0 0 3 0 ]
and i want to find the maximum value in the matrix i can type:
max(max(A))
or
max(A(:))
if i only want to find the maximum of rows 1 and 2 and columns 3 and 4 i can do this:
a = [1 2]
b = [3 4]
max(max(A(a,b))
but what if i want to find the indices of the rows and columns that correspond to that value?
according to the matlab documentation, if i am using the whole matrix i can use the ind2sub function:
[val,idx] = max(A(:))
[row,col] = ind2sub(size(A),idx)
but how can i get that working for my example where i am using vectors a and b to determine the rows and columns it finds the values over?
here is the only way i have been able to work it out so far:
max_val = 0;
max_idx = [1 1];
for ii = a
[val,idx] = max(A(ii,b))
if val > max_val
max_val = val
max_idx = [ii idx]
but that seems rather clunky to me.. any ideas?
Assuming that the submatrix A(a,b) is contiguous (like in your example):
A = [ 0 2 4 0
2 0 5 0
4 5 0 3
0 0 3 0 ]
a = [1 2]; b = [3 4];
B = A(a,b)
[val,idx] = max(B(:));
[row,col] = ind2sub(size(B),idx);
maxrow = row + a(1) - 1;
maxcol = col + b(1) - 1;
You are finding the relative index in the submatrix B. Which is equivalent to the additional rows and columns from the upper left corner of the submatrix.
Now assuming that a and b result in a set of rows and columns that are NOT a contiguous submatrix, e.g. a = [1 3], b = [3 4], the result is very similar. "row" and "col" are the index in the a and b vectors:
A = [ 0 2 4 0
2 0 5 0
4 5 0 3
0 0 3 0 ]
a = [1 3]; b = [3 4];
B = A(a,b)
[val,idx] = max(B(:));
[row,col] = ind2sub(size(B),idx);
maxrow = a(row);
maxcol = b(col);
Now you're working in the index of indexes.

Create a horizontically stretched upper triangular matrix

I'd like to create a 4x12 matrix which is very similar to a upper triangle matrix, it looks like this:
1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 1 1 1
So my question is. What is the most efficient way to create it? no loops, no cellfun. Thanks.
One vectorized approach -
nrows = 4;
ncols = 12;
row_idx = repmat(1:nrows,ncols/nrows,1)
out = bsxfun(#le,[1:nrows]',row_idx(:).')
The Matlab R2015a and later approach using the newly introduced repelem:
n = 4;
m = 3;
out = repelem(triu(ones(n)),1,m);
out =
1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 1 1 1
It even seems faster than the bsxfun approach, though I can't believe this ;)
Benchmark
Unfortunately I couldn't consider andrew's solution as it is not complete and I didn't got it totally.
function [t] = bench()
n = 4;
m = 12;
t = zeros(3,15);
for ii = 1:15
fcns = {
#() thewaywewalk(ii*n,ii*m);
#() Divakar(ii*n,ii*m);
#() LuisMendo(ii*n,ii*m);
};
% timeit
for jj = 1:100;
t(:,ii) = t(:,ii) + cellfun(#timeit, fcns);
end
end
plot(1:15,t(1,:)); hold on;
plot(1:15,t(2,:)); hold on;
plot(1:15,t(3,:)); hold on;
xlabel('Matrix size: n = x*4, m = x*12')
ylabel('timing')
legend({'thewaywewalk','Divakar','Luis Mendo'},'location','northwest')
end
function Z = thewaywewalk(n,m)
Z = repelem(triu(ones(n)),1,m/n);
end
function Z = Divakar(n,m)
row_idx = repmat(1:n,m/n,1);
Z = bsxfun(#le,[1:n]',row_idx(:).');
end
function Z = LuisMendo(n,m)
Z = reshape(repmat(permute(triu(ones(n,n)), [1 3 2]), [1 m/n 1]), [n m]);
end
First bottomline - small matrices:
The new repelem does a very good job, but also the reshape(repmat(permute... does not disappoint. The bsxfun approach stays a little behind for some medium-sized matrices, before it becomes the leader for large matrices:
Second bottomline - big matrices:
As predicted by Divakar, bsxfun is the fastest for larger matrices, actually as expected as bsxfun is always the fastest! Interesting that the other two align perfectly, on could guess they almost work the same internally.
Create an upper triangular matrix of ones, permute second and third dimensions, repeat along second dimension, and reshape into desired shape:
m = 4;
n = 12;
result = reshape(repmat(permute(triu(ones(m,m)), [1 3 2]), [1 n/m 1]), [m n]);
depending on your matlab version
m = 4;
n = 12;
dec2bin(bitshift(num,-1*[0:n/m:n-1])) %this prints out a string
these should be logical arrays (i don't have either of these so I cant test it)
decimalToBinaryVector(bitshift(num,-1*[0:n/m:n-1]))
de2bi(bitshift(num,-1*[0:n/m:n-1]))

Permute elements within rows of matrix

I have a matrix A
A = [0 0 0 0 1; 0 0 0 0 2; 0 1 2 3 4];
and I would like to randomly permute the elements within each row. For example, matrix A2
A2 = [1 0 0 0 0; 0 0 0 2 0; 4 1 3 2 0]; % example of desired output
I can do this with a vector:
Av = [0 1 2 3 4];
Bv = Av(randperm(5));
But I am unsure how to do this a row at time for a matrix and to only permute the elements within a given row. Is this possible to do? I could construct a matrix from many permuted vectors, but I would prefer not to do it this way.
Thanks.
You can use sort on a random array of any size (which is what randperm does). After that, all you need to do is some index-trickery to properly reshuffle the array
A = [0 0 0 0 1; 0 0 0 0 2; 0 1 2 3 4];
[nRows,nCols] = size(A);
[~,idx] = sort(rand(nRows,nCols),2);
%# convert column indices into linear indices
idx = (idx-1)*nRows + ndgrid(1:nRows,1:nCols);
%# rearrange A
B = A;
B(:) = B(idx)
B =
0 0 1 0 0
0 2 0 0 0
2 1 3 4 0